michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" // for ArrayLength michael@0: #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc michael@0: #include "mozilla/dom/Element.h" // for Element, nsINode michael@0: #include "nsAString.h" // for nsAString_internal::IsEmpty michael@0: #include "nsCOMPtr.h" // for nsCOMPtr, operator==, etc michael@0: #include "nsCaseTreatment.h" michael@0: #include "nsDebug.h" // for NS_PRECONDITION, etc michael@0: #include "nsEditProperty.h" // for nsEditProperty, etc michael@0: #include "nsEditor.h" // for nsEditor michael@0: #include "nsError.h" // for NS_SUCCEEDED michael@0: #include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::a, etc michael@0: #include "nsHTMLEditUtils.h" michael@0: #include "nsHTMLTags.h" michael@0: #include "nsIAtom.h" // for nsIAtom michael@0: #include "nsIDOMHTMLAnchorElement.h" // for nsIDOMHTMLAnchorElement michael@0: #include "nsIDOMNode.h" // for nsIDOMNode michael@0: #include "nsNameSpaceManager.h" // for kNameSpaceID_None michael@0: #include "nsLiteralString.h" // for NS_LITERAL_STRING michael@0: #include "nsString.h" // for nsAutoString michael@0: #include "nsTextEditUtils.h" // for nsTextEditUtils michael@0: michael@0: using namespace mozilla; michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsBig(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::big); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsInlineStyle true if node is an inline style michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsInlineStyle(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsInlineStyle"); michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: return node && IsInlineStyle(node); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsInlineStyle(nsINode* aNode) michael@0: { michael@0: MOZ_ASSERT(aNode); michael@0: nsIAtom* nodeAtom = aNode->Tag(); michael@0: return (nodeAtom == nsEditProperty::b) michael@0: || (nodeAtom == nsEditProperty::i) michael@0: || (nodeAtom == nsEditProperty::u) michael@0: || (nodeAtom == nsEditProperty::tt) michael@0: || (nodeAtom == nsEditProperty::s) michael@0: || (nodeAtom == nsEditProperty::strike) michael@0: || (nodeAtom == nsEditProperty::big) michael@0: || (nodeAtom == nsEditProperty::small) michael@0: || (nodeAtom == nsEditProperty::sub) michael@0: || (nodeAtom == nsEditProperty::sup) michael@0: || (nodeAtom == nsEditProperty::font); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsFormatNode true if node is a format node michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsFormatNode(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsFormatNode"); michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: return node && IsFormatNode(node); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsFormatNode(nsINode* aNode) michael@0: { michael@0: MOZ_ASSERT(aNode); michael@0: nsIAtom* nodeAtom = aNode->Tag(); michael@0: return (nodeAtom == nsEditProperty::p) michael@0: || (nodeAtom == nsEditProperty::pre) michael@0: || (nodeAtom == nsEditProperty::h1) michael@0: || (nodeAtom == nsEditProperty::h2) michael@0: || (nodeAtom == nsEditProperty::h3) michael@0: || (nodeAtom == nsEditProperty::h4) michael@0: || (nodeAtom == nsEditProperty::h5) michael@0: || (nodeAtom == nsEditProperty::h6) michael@0: || (nodeAtom == nsEditProperty::address); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsNodeThatCanOutdent true if node is a list, list item, or blockquote michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsNodeThatCanOutdent(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsNodeThatCanOutdent"); michael@0: nsCOMPtr nodeAtom = nsEditor::GetTag(aNode); michael@0: return (nodeAtom == nsEditProperty::ul) michael@0: || (nodeAtom == nsEditProperty::ol) michael@0: || (nodeAtom == nsEditProperty::dl) michael@0: || (nodeAtom == nsEditProperty::li) michael@0: || (nodeAtom == nsEditProperty::dd) michael@0: || (nodeAtom == nsEditProperty::dt) michael@0: || (nodeAtom == nsEditProperty::blockquote); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsSmall(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::small); michael@0: } michael@0: michael@0: michael@0: /******************************************************** michael@0: * helper methods from nsHTMLEditRules michael@0: ********************************************************/ michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsHeader: true if node an html header michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsHeader(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsHeader"); michael@0: nsCOMPtr nodeAtom = nsEditor::GetTag(aNode); michael@0: return (nodeAtom == nsEditProperty::h1) michael@0: || (nodeAtom == nsEditProperty::h2) michael@0: || (nodeAtom == nsEditProperty::h3) michael@0: || (nodeAtom == nsEditProperty::h4) michael@0: || (nodeAtom == nsEditProperty::h5) michael@0: || (nodeAtom == nsEditProperty::h6); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsParagraph: true if node an html paragraph michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsParagraph(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::p); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsHR: true if node an horizontal rule michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsHR(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::hr); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsListItem: true if node an html list item michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsListItem(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsListItem"); michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: return node && IsListItem(node); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsListItem(nsINode* node) michael@0: { michael@0: MOZ_ASSERT(node); michael@0: nsCOMPtr nodeAtom = node->Tag(); michael@0: return (nodeAtom == nsEditProperty::li) michael@0: || (nodeAtom == nsEditProperty::dd) michael@0: || (nodeAtom == nsEditProperty::dt); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsTableElement: true if node an html table, td, tr, ... michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsTableElement(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::IsTableElement"); michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: return node && IsTableElement(node); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsTableElement(nsINode* node) michael@0: { michael@0: MOZ_ASSERT(node); michael@0: nsCOMPtr nodeAtom = node->Tag(); michael@0: return (nodeAtom == nsEditProperty::table) michael@0: || (nodeAtom == nsEditProperty::tr) michael@0: || (nodeAtom == nsEditProperty::td) michael@0: || (nodeAtom == nsEditProperty::th) michael@0: || (nodeAtom == nsEditProperty::thead) michael@0: || (nodeAtom == nsEditProperty::tfoot) michael@0: || (nodeAtom == nsEditProperty::tbody) michael@0: || (nodeAtom == nsEditProperty::caption); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsTableElementButNotTable: true if node an html td, tr, ... (doesn't include table) michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsTableElementButNotTable(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::IsTableElementButNotTable"); michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: return node && IsTableElementButNotTable(node); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsTableElementButNotTable(nsINode* aNode) michael@0: { michael@0: MOZ_ASSERT(aNode); michael@0: nsCOMPtr nodeAtom = aNode->Tag(); michael@0: return (nodeAtom == nsEditProperty::tr) michael@0: || (nodeAtom == nsEditProperty::td) michael@0: || (nodeAtom == nsEditProperty::th) michael@0: || (nodeAtom == nsEditProperty::thead) michael@0: || (nodeAtom == nsEditProperty::tfoot) michael@0: || (nodeAtom == nsEditProperty::tbody) michael@0: || (nodeAtom == nsEditProperty::caption); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsTable: true if node an html table michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsTable(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::table); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsTableRow: true if node an html tr michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsTableRow(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::tr); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsTableCell: true if node an html td or th michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsTableCell(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsTableCell"); michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: return node && IsTableCell(node); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsTableCell(nsINode* node) michael@0: { michael@0: MOZ_ASSERT(node); michael@0: nsCOMPtr nodeAtom = node->Tag(); michael@0: return (nodeAtom == nsEditProperty::td) michael@0: || (nodeAtom == nsEditProperty::th); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsTableCell: true if node an html td or th michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsTableCellOrCaption(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsTableCell"); michael@0: nsCOMPtr nodeAtom = nsEditor::GetTag(aNode); michael@0: return (nodeAtom == nsEditProperty::td) michael@0: || (nodeAtom == nsEditProperty::th) michael@0: || (nodeAtom == nsEditProperty::caption); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsList: true if node an html list michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsList(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsList"); michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: return node && IsList(node); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsList(nsINode* node) michael@0: { michael@0: MOZ_ASSERT(node); michael@0: nsCOMPtr nodeAtom = node->Tag(); michael@0: return (nodeAtom == nsEditProperty::ul) michael@0: || (nodeAtom == nsEditProperty::ol) michael@0: || (nodeAtom == nsEditProperty::dl); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsOrderedList: true if node an html ordered list michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsOrderedList(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::ol); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsUnorderedList: true if node an html unordered list michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsUnorderedList(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::ul); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsBlockquote: true if node an html blockquote node michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsBlockquote(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::blockquote); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsPre: true if node an html pre node michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsPre(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::pre); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsImage: true if node an html image node michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsImage(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::img); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsLink(nsIDOMNode *aNode) michael@0: { michael@0: NS_ENSURE_TRUE(aNode, false); michael@0: nsCOMPtr anchor = do_QueryInterface(aNode); michael@0: if (anchor) michael@0: { michael@0: nsAutoString tmpText; michael@0: if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && !tmpText.IsEmpty()) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsNamedAnchor(nsIDOMNode *aNode) michael@0: { michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: return node && IsNamedAnchor(node); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsNamedAnchor(nsINode* aNode) michael@0: { michael@0: MOZ_ASSERT(aNode); michael@0: if (!aNode->IsElement() || !aNode->AsElement()->IsHTML(nsGkAtoms::a)) { michael@0: return false; michael@0: } michael@0: michael@0: nsAutoString text; michael@0: return aNode->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name, michael@0: text) && !text.IsEmpty(); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsDiv: true if node an html div node michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsDiv(nsIDOMNode* aNode) michael@0: { michael@0: return nsEditor::NodeIsType(aNode, nsEditProperty::div); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsMozDiv: true if node an html div node with type = _moz michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsMozDiv(nsIDOMNode* aNode) michael@0: { michael@0: if (IsDiv(aNode) && nsTextEditUtils::HasMozAttr(aNode)) return true; michael@0: return false; michael@0: } michael@0: michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsMailCite: true if node an html blockquote with type=cite michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsMailCite(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsMailCite"); michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: return node && IsMailCite(node); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsMailCite(nsINode* aNode) michael@0: { michael@0: MOZ_ASSERT(aNode); michael@0: michael@0: // don't ask me why, but our html mailcites are id'd by "type=cite"... michael@0: if (aNode->IsElement() && michael@0: aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, michael@0: NS_LITERAL_STRING("cite"), michael@0: eIgnoreCase)) { michael@0: return true; michael@0: } michael@0: michael@0: // ... but our plaintext mailcites by "_moz_quote=true". go figure. michael@0: if (aNode->IsElement() && michael@0: aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozquote, michael@0: NS_LITERAL_STRING("true"), michael@0: eIgnoreCase)) { michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // IsFormWidget: true if node is a form widget of some kind michael@0: // michael@0: bool michael@0: nsHTMLEditUtils::IsFormWidget(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null node passed to nsHTMLEditUtils::IsFormWidget"); michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: return node && IsFormWidget(node); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsFormWidget(nsINode* aNode) michael@0: { michael@0: MOZ_ASSERT(aNode); michael@0: nsCOMPtr nodeAtom = aNode->Tag(); michael@0: return (nodeAtom == nsEditProperty::textarea) michael@0: || (nodeAtom == nsEditProperty::select) michael@0: || (nodeAtom == nsEditProperty::button) michael@0: || (nodeAtom == nsEditProperty::output) michael@0: || (nodeAtom == nsEditProperty::keygen) michael@0: || (nodeAtom == nsEditProperty::progress) michael@0: || (nodeAtom == nsEditProperty::meter) michael@0: || (nodeAtom == nsEditProperty::input); michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::SupportsAlignAttr(nsIDOMNode* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "null node passed to nsHTMLEditUtils::SupportsAlignAttr"); michael@0: nsCOMPtr nodeAtom = nsEditor::GetTag(aNode); michael@0: return (nodeAtom == nsEditProperty::hr) michael@0: || (nodeAtom == nsEditProperty::table) michael@0: || (nodeAtom == nsEditProperty::tbody) michael@0: || (nodeAtom == nsEditProperty::tfoot) michael@0: || (nodeAtom == nsEditProperty::thead) michael@0: || (nodeAtom == nsEditProperty::tr) michael@0: || (nodeAtom == nsEditProperty::td) michael@0: || (nodeAtom == nsEditProperty::th) michael@0: || (nodeAtom == nsEditProperty::div) michael@0: || (nodeAtom == nsEditProperty::p) michael@0: || (nodeAtom == nsEditProperty::h1) michael@0: || (nodeAtom == nsEditProperty::h2) michael@0: || (nodeAtom == nsEditProperty::h3) michael@0: || (nodeAtom == nsEditProperty::h4) michael@0: || (nodeAtom == nsEditProperty::h5) michael@0: || (nodeAtom == nsEditProperty::h6); michael@0: } michael@0: michael@0: // We use bitmasks to test containment of elements. Elements are marked to be michael@0: // in certain groups by setting the mGroup member of the nsElementInfo struct michael@0: // to the corresponding GROUP_ values (OR'ed together). Similarly, elements are michael@0: // marked to allow containment of certain groups by setting the michael@0: // mCanContainGroups member of the nsElementInfo struct to the corresponding michael@0: // GROUP_ values (OR'ed together). michael@0: // Testing containment then simply consists of checking whether the michael@0: // mCanContainGroups bitmask of an element and the mGroup bitmask of a michael@0: // potential child overlap. michael@0: michael@0: #define GROUP_NONE 0 michael@0: michael@0: // body, head, html michael@0: #define GROUP_TOPLEVEL (1 << 1) michael@0: michael@0: // base, link, meta, script, style, title michael@0: #define GROUP_HEAD_CONTENT (1 << 2) michael@0: michael@0: // b, big, i, s, small, strike, tt, u michael@0: #define GROUP_FONTSTYLE (1 << 3) michael@0: michael@0: // abbr, acronym, cite, code, datalist, del, dfn, em, ins, kbd, mark, samp, michael@0: // strong, var michael@0: #define GROUP_PHRASE (1 << 4) michael@0: michael@0: // a, applet, basefont, bdo, br, font, iframe, img, map, meter, object, output, michael@0: // progress, q, script, span, sub, sup michael@0: #define GROUP_SPECIAL (1 << 5) michael@0: michael@0: // button, form, input, label, select, textarea michael@0: #define GROUP_FORMCONTROL (1 << 6) michael@0: michael@0: // address, applet, article, aside, blockquote, button, center, del, dir, div, michael@0: // dl, fieldset, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup, michael@0: // hr, iframe, ins, main, map, menu, nav, noframes, noscript, object, ol, p, michael@0: // pre, table, section, ul michael@0: #define GROUP_BLOCK (1 << 7) michael@0: michael@0: // frame, frameset michael@0: #define GROUP_FRAME (1 << 8) michael@0: michael@0: // col, tbody michael@0: #define GROUP_TABLE_CONTENT (1 << 9) michael@0: michael@0: // tr michael@0: #define GROUP_TBODY_CONTENT (1 << 10) michael@0: michael@0: // td, th michael@0: #define GROUP_TR_CONTENT (1 << 11) michael@0: michael@0: // col michael@0: #define GROUP_COLGROUP_CONTENT (1 << 12) michael@0: michael@0: // param michael@0: #define GROUP_OBJECT_CONTENT (1 << 13) michael@0: michael@0: // li michael@0: #define GROUP_LI (1 << 14) michael@0: michael@0: // area michael@0: #define GROUP_MAP_CONTENT (1 << 15) michael@0: michael@0: // optgroup, option michael@0: #define GROUP_SELECT_CONTENT (1 << 16) michael@0: michael@0: // option michael@0: #define GROUP_OPTIONS (1 << 17) michael@0: michael@0: // dd, dt michael@0: #define GROUP_DL_CONTENT (1 << 18) michael@0: michael@0: // p michael@0: #define GROUP_P (1 << 19) michael@0: michael@0: // text, whitespace, newline, comment michael@0: #define GROUP_LEAF (1 << 20) michael@0: michael@0: // XXX This is because the editor does sublists illegally. michael@0: // ol, ul michael@0: #define GROUP_OL_UL (1 << 21) michael@0: michael@0: // h1, h2, h3, h4, h5, h6 michael@0: #define GROUP_HEADING (1 << 22) michael@0: michael@0: // figcaption michael@0: #define GROUP_FIGCAPTION (1 << 23) michael@0: michael@0: #define GROUP_INLINE_ELEMENT \ michael@0: (GROUP_FONTSTYLE | GROUP_PHRASE | GROUP_SPECIAL | GROUP_FORMCONTROL | \ michael@0: GROUP_LEAF) michael@0: michael@0: #define GROUP_FLOW_ELEMENT (GROUP_INLINE_ELEMENT | GROUP_BLOCK) michael@0: michael@0: struct nsElementInfo michael@0: { michael@0: #ifdef DEBUG michael@0: eHTMLTags mTag; michael@0: #endif michael@0: uint32_t mGroup; michael@0: uint32_t mCanContainGroups; michael@0: bool mIsContainer; michael@0: bool mCanContainSelf; michael@0: }; michael@0: michael@0: #ifdef DEBUG michael@0: #define ELEM(_tag, _isContainer, _canContainSelf, _group, _canContainGroups) \ michael@0: { eHTMLTag_##_tag, _group, _canContainGroups, _isContainer, _canContainSelf } michael@0: #else michael@0: #define ELEM(_tag, _isContainer, _canContainSelf, _group, _canContainGroups) \ michael@0: { _group, _canContainGroups, _isContainer, _canContainSelf } michael@0: #endif michael@0: michael@0: static const nsElementInfo kElements[eHTMLTag_userdefined] = { michael@0: ELEM(a, true, false, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), michael@0: ELEM(abbr, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(acronym, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(address, true, true, GROUP_BLOCK, michael@0: GROUP_INLINE_ELEMENT | GROUP_P), michael@0: ELEM(applet, true, true, GROUP_SPECIAL | GROUP_BLOCK, michael@0: GROUP_FLOW_ELEMENT | GROUP_OBJECT_CONTENT), michael@0: ELEM(area, false, false, GROUP_MAP_CONTENT, GROUP_NONE), michael@0: ELEM(article, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(aside, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(audio, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(b, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), michael@0: ELEM(base, false, false, GROUP_HEAD_CONTENT, GROUP_NONE), michael@0: ELEM(basefont, false, false, GROUP_SPECIAL, GROUP_NONE), michael@0: ELEM(bdo, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), michael@0: ELEM(bgsound, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(big, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), michael@0: ELEM(blockquote, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(body, true, true, GROUP_TOPLEVEL, GROUP_FLOW_ELEMENT), michael@0: ELEM(br, false, false, GROUP_SPECIAL, GROUP_NONE), michael@0: ELEM(button, true, true, GROUP_FORMCONTROL | GROUP_BLOCK, michael@0: GROUP_FLOW_ELEMENT), michael@0: ELEM(canvas, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(caption, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT), michael@0: ELEM(center, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(cite, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(code, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(col, false, false, GROUP_TABLE_CONTENT | GROUP_COLGROUP_CONTENT, michael@0: GROUP_NONE), michael@0: ELEM(colgroup, true, false, GROUP_NONE, GROUP_COLGROUP_CONTENT), michael@0: ELEM(content, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT), michael@0: ELEM(data, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(datalist, true, false, GROUP_PHRASE, michael@0: GROUP_OPTIONS | GROUP_INLINE_ELEMENT), michael@0: ELEM(dd, true, false, GROUP_DL_CONTENT, GROUP_FLOW_ELEMENT), michael@0: ELEM(del, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(dfn, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(dir, true, false, GROUP_BLOCK, GROUP_LI), michael@0: ELEM(div, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(dl, true, false, GROUP_BLOCK, GROUP_DL_CONTENT), michael@0: ELEM(dt, true, true, GROUP_DL_CONTENT, GROUP_INLINE_ELEMENT), michael@0: ELEM(em, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(embed, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(fieldset, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(figcaption, true, false, GROUP_FIGCAPTION, GROUP_FLOW_ELEMENT), michael@0: ELEM(figure, true, true, GROUP_BLOCK, michael@0: GROUP_FLOW_ELEMENT | GROUP_FIGCAPTION), michael@0: ELEM(font, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), michael@0: ELEM(footer, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(form, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(frame, false, false, GROUP_FRAME, GROUP_NONE), michael@0: ELEM(frameset, true, true, GROUP_FRAME, GROUP_FRAME), michael@0: ELEM(h1, true, false, GROUP_BLOCK | GROUP_HEADING, michael@0: GROUP_INLINE_ELEMENT), michael@0: ELEM(h2, true, false, GROUP_BLOCK | GROUP_HEADING, michael@0: GROUP_INLINE_ELEMENT), michael@0: ELEM(h3, true, false, GROUP_BLOCK | GROUP_HEADING, michael@0: GROUP_INLINE_ELEMENT), michael@0: ELEM(h4, true, false, GROUP_BLOCK | GROUP_HEADING, michael@0: GROUP_INLINE_ELEMENT), michael@0: ELEM(h5, true, false, GROUP_BLOCK | GROUP_HEADING, michael@0: GROUP_INLINE_ELEMENT), michael@0: ELEM(h6, true, false, GROUP_BLOCK | GROUP_HEADING, michael@0: GROUP_INLINE_ELEMENT), michael@0: ELEM(head, true, false, GROUP_TOPLEVEL, GROUP_HEAD_CONTENT), michael@0: ELEM(header, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(hgroup, true, false, GROUP_BLOCK, GROUP_HEADING), michael@0: ELEM(hr, false, false, GROUP_BLOCK, GROUP_NONE), michael@0: ELEM(html, true, false, GROUP_TOPLEVEL, GROUP_TOPLEVEL), michael@0: ELEM(i, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), michael@0: ELEM(iframe, true, true, GROUP_SPECIAL | GROUP_BLOCK, michael@0: GROUP_FLOW_ELEMENT), michael@0: ELEM(image, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(img, false, false, GROUP_SPECIAL, GROUP_NONE), michael@0: ELEM(input, false, false, GROUP_FORMCONTROL, GROUP_NONE), michael@0: ELEM(ins, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(kbd, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(keygen, false, false, GROUP_FORMCONTROL, GROUP_NONE), michael@0: ELEM(label, true, false, GROUP_FORMCONTROL, GROUP_INLINE_ELEMENT), michael@0: ELEM(legend, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT), michael@0: ELEM(li, true, false, GROUP_LI, GROUP_FLOW_ELEMENT), michael@0: ELEM(link, false, false, GROUP_HEAD_CONTENT, GROUP_NONE), michael@0: ELEM(listing, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(main, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(map, true, true, GROUP_SPECIAL, GROUP_BLOCK | GROUP_MAP_CONTENT), michael@0: ELEM(mark, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(marquee, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(menu, true, true, GROUP_BLOCK, GROUP_LI | GROUP_FLOW_ELEMENT), michael@0: ELEM(menuitem, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(meta, false, false, GROUP_HEAD_CONTENT, GROUP_NONE), michael@0: ELEM(meter, true, false, GROUP_SPECIAL, GROUP_FLOW_ELEMENT), michael@0: ELEM(multicol, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(nav, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(nobr, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(noembed, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(noframes, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(noscript, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(object, true, true, GROUP_SPECIAL | GROUP_BLOCK, michael@0: GROUP_FLOW_ELEMENT | GROUP_OBJECT_CONTENT), michael@0: // XXX Can contain self and ul because editor does sublists illegally. michael@0: ELEM(ol, true, true, GROUP_BLOCK | GROUP_OL_UL, michael@0: GROUP_LI | GROUP_OL_UL), michael@0: ELEM(optgroup, true, false, GROUP_SELECT_CONTENT, michael@0: GROUP_OPTIONS), michael@0: ELEM(option, true, false, michael@0: GROUP_SELECT_CONTENT | GROUP_OPTIONS, GROUP_LEAF), michael@0: ELEM(output, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), michael@0: ELEM(p, true, false, GROUP_BLOCK | GROUP_P, GROUP_INLINE_ELEMENT), michael@0: ELEM(param, false, false, GROUP_OBJECT_CONTENT, GROUP_NONE), michael@0: ELEM(plaintext, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(pre, true, true, GROUP_BLOCK, GROUP_INLINE_ELEMENT), michael@0: ELEM(progress, true, false, GROUP_SPECIAL, GROUP_FLOW_ELEMENT), michael@0: ELEM(q, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), michael@0: ELEM(s, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), michael@0: ELEM(samp, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(script, true, false, GROUP_HEAD_CONTENT | GROUP_SPECIAL, michael@0: GROUP_LEAF), michael@0: ELEM(section, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), michael@0: ELEM(select, true, false, GROUP_FORMCONTROL, GROUP_SELECT_CONTENT), michael@0: ELEM(shadow, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT), michael@0: ELEM(small, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), michael@0: ELEM(source, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(span, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), michael@0: ELEM(strike, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), michael@0: ELEM(strong, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(style, true, false, GROUP_HEAD_CONTENT, GROUP_LEAF), michael@0: ELEM(sub, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), michael@0: ELEM(sup, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), michael@0: ELEM(table, true, false, GROUP_BLOCK, GROUP_TABLE_CONTENT), michael@0: ELEM(tbody, true, false, GROUP_TABLE_CONTENT, GROUP_TBODY_CONTENT), michael@0: ELEM(td, true, false, GROUP_TR_CONTENT, GROUP_FLOW_ELEMENT), michael@0: ELEM(textarea, true, false, GROUP_FORMCONTROL, GROUP_LEAF), michael@0: ELEM(tfoot, true, false, GROUP_NONE, GROUP_TBODY_CONTENT), michael@0: ELEM(th, true, false, GROUP_TR_CONTENT, GROUP_FLOW_ELEMENT), michael@0: ELEM(thead, true, false, GROUP_NONE, GROUP_TBODY_CONTENT), michael@0: ELEM(template, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(time, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(title, true, false, GROUP_HEAD_CONTENT, GROUP_LEAF), michael@0: ELEM(tr, true, false, GROUP_TBODY_CONTENT, GROUP_TR_CONTENT), michael@0: ELEM(track, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(tt, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), michael@0: ELEM(u, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), michael@0: // XXX Can contain self and ol because editor does sublists illegally. michael@0: ELEM(ul, true, true, GROUP_BLOCK | GROUP_OL_UL, michael@0: GROUP_LI | GROUP_OL_UL), michael@0: ELEM(var, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), michael@0: ELEM(video, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(wbr, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(xmp, false, false, GROUP_NONE, GROUP_NONE), michael@0: michael@0: // These aren't elements. michael@0: ELEM(text, false, false, GROUP_LEAF, GROUP_NONE), michael@0: ELEM(whitespace, false, false, GROUP_LEAF, GROUP_NONE), michael@0: ELEM(newline, false, false, GROUP_LEAF, GROUP_NONE), michael@0: ELEM(comment, false, false, GROUP_LEAF, GROUP_NONE), michael@0: ELEM(entity, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(doctypeDecl, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(markupDecl, false, false, GROUP_NONE, GROUP_NONE), michael@0: ELEM(instruction, false, false, GROUP_NONE, GROUP_NONE), michael@0: michael@0: ELEM(userdefined, true, false, GROUP_NONE, GROUP_FLOW_ELEMENT) michael@0: }; michael@0: michael@0: bool michael@0: nsHTMLEditUtils::CanContain(int32_t aParent, int32_t aChild) michael@0: { michael@0: NS_ASSERTION(aParent > eHTMLTag_unknown && aParent <= eHTMLTag_userdefined, michael@0: "aParent out of range!"); michael@0: NS_ASSERTION(aChild > eHTMLTag_unknown && aChild <= eHTMLTag_userdefined, michael@0: "aChild out of range!"); michael@0: michael@0: #ifdef DEBUG michael@0: static bool checked = false; michael@0: if (!checked) { michael@0: checked = true; michael@0: int32_t i; michael@0: for (i = 1; i <= eHTMLTag_userdefined; ++i) { michael@0: NS_ASSERTION(kElements[i - 1].mTag == i, michael@0: "You need to update kElements (missing tags)."); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // Special-case button. michael@0: if (aParent == eHTMLTag_button) { michael@0: static const eHTMLTags kButtonExcludeKids[] = { michael@0: eHTMLTag_a, michael@0: eHTMLTag_fieldset, michael@0: eHTMLTag_form, michael@0: eHTMLTag_iframe, michael@0: eHTMLTag_input, michael@0: eHTMLTag_select, michael@0: eHTMLTag_textarea michael@0: }; michael@0: michael@0: uint32_t j; michael@0: for (j = 0; j < ArrayLength(kButtonExcludeKids); ++j) { michael@0: if (kButtonExcludeKids[j] == aChild) { michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Deprecated elements. michael@0: if (aChild == eHTMLTag_bgsound) { michael@0: return false; michael@0: } michael@0: michael@0: // Bug #67007, dont strip userdefined tags. michael@0: if (aChild == eHTMLTag_userdefined) { michael@0: return true; michael@0: } michael@0: michael@0: const nsElementInfo& parent = kElements[aParent - 1]; michael@0: if (aParent == aChild) { michael@0: return parent.mCanContainSelf; michael@0: } michael@0: michael@0: const nsElementInfo& child = kElements[aChild - 1]; michael@0: return (parent.mCanContainGroups & child.mGroup) != 0; michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditUtils::IsContainer(int32_t aTag) michael@0: { michael@0: NS_ASSERTION(aTag > eHTMLTag_unknown && aTag <= eHTMLTag_userdefined, michael@0: "aTag out of range!"); michael@0: michael@0: return kElements[aTag - 1].mIsContainer; michael@0: }