editor/libeditor/html/nsHTMLEditUtils.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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 "nsEditProperty.h"             // for nsEditProperty, etc
    14 #include "nsEditor.h"                   // for nsEditor
    15 #include "nsError.h"                    // for NS_SUCCEEDED
    16 #include "nsGkAtoms.h"                  // for nsGkAtoms, nsGkAtoms::a, etc
    17 #include "nsHTMLEditUtils.h"
    18 #include "nsHTMLTags.h"
    19 #include "nsIAtom.h"                    // for nsIAtom
    20 #include "nsIDOMHTMLAnchorElement.h"    // for nsIDOMHTMLAnchorElement
    21 #include "nsIDOMNode.h"                 // for nsIDOMNode
    22 #include "nsNameSpaceManager.h"        // for kNameSpaceID_None
    23 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
    24 #include "nsString.h"                   // for nsAutoString
    25 #include "nsTextEditUtils.h"            // for nsTextEditUtils
    27 using namespace mozilla;
    29 ///////////////////////////////////////////////////////////////////////////
    30 //                  
    31 bool 
    32 nsHTMLEditUtils::IsBig(nsIDOMNode* aNode)
    33 {
    34   return nsEditor::NodeIsType(aNode, nsEditProperty::big);
    35 }
    38 ///////////////////////////////////////////////////////////////////////////
    39 // IsInlineStyle true if node is an inline style
    40 //                  
    41 bool 
    42 nsHTMLEditUtils::IsInlineStyle(nsIDOMNode* aNode)
    43 {
    44   NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsInlineStyle");
    45   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
    46   return node && IsInlineStyle(node);
    47 }
    49 bool
    50 nsHTMLEditUtils::IsInlineStyle(nsINode* aNode)
    51 {
    52   MOZ_ASSERT(aNode);
    53   nsIAtom* nodeAtom = aNode->Tag();
    54   return (nodeAtom == nsEditProperty::b)
    55       || (nodeAtom == nsEditProperty::i)
    56       || (nodeAtom == nsEditProperty::u)
    57       || (nodeAtom == nsEditProperty::tt)
    58       || (nodeAtom == nsEditProperty::s)
    59       || (nodeAtom == nsEditProperty::strike)
    60       || (nodeAtom == nsEditProperty::big)
    61       || (nodeAtom == nsEditProperty::small)
    62       || (nodeAtom == nsEditProperty::sub)
    63       || (nodeAtom == nsEditProperty::sup)
    64       || (nodeAtom == nsEditProperty::font);
    65 }
    67 ///////////////////////////////////////////////////////////////////////////
    68 // IsFormatNode true if node is a format node
    69 // 
    70 bool
    71 nsHTMLEditUtils::IsFormatNode(nsIDOMNode* aNode)
    72 {
    73   NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsFormatNode");
    74   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
    75   return node && IsFormatNode(node);
    76 }
    78 bool
    79 nsHTMLEditUtils::IsFormatNode(nsINode* aNode)
    80 {
    81   MOZ_ASSERT(aNode);
    82   nsIAtom* nodeAtom = aNode->Tag();
    83   return (nodeAtom == nsEditProperty::p)
    84       || (nodeAtom == nsEditProperty::pre)
    85       || (nodeAtom == nsEditProperty::h1)
    86       || (nodeAtom == nsEditProperty::h2)
    87       || (nodeAtom == nsEditProperty::h3)
    88       || (nodeAtom == nsEditProperty::h4)
    89       || (nodeAtom == nsEditProperty::h5)
    90       || (nodeAtom == nsEditProperty::h6)
    91       || (nodeAtom == nsEditProperty::address);
    92 }
    94 ///////////////////////////////////////////////////////////////////////////
    95 // IsNodeThatCanOutdent true if node is a list, list item, or blockquote      
    96 //
    97 bool
    98 nsHTMLEditUtils::IsNodeThatCanOutdent(nsIDOMNode* aNode)
    99 {
   100   NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsNodeThatCanOutdent");
   101   nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode);
   102   return (nodeAtom == nsEditProperty::ul)
   103       || (nodeAtom == nsEditProperty::ol)
   104       || (nodeAtom == nsEditProperty::dl)
   105       || (nodeAtom == nsEditProperty::li)
   106       || (nodeAtom == nsEditProperty::dd)
   107       || (nodeAtom == nsEditProperty::dt)
   108       || (nodeAtom == nsEditProperty::blockquote);
   109 }
   111 ///////////////////////////////////////////////////////////////////////////
   112 //                  
   113 bool 
   114 nsHTMLEditUtils::IsSmall(nsIDOMNode* aNode)
   115 {
   116   return nsEditor::NodeIsType(aNode, nsEditProperty::small);
   117 }
   120 /********************************************************
   121  *  helper methods from nsHTMLEditRules
   122  ********************************************************/
   124 ///////////////////////////////////////////////////////////////////////////
   125 // IsHeader: true if node an html header
   126 //                  
   127 bool 
   128 nsHTMLEditUtils::IsHeader(nsIDOMNode* aNode)
   129 {
   130   NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsHeader");
   131   nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode);
   132   return (nodeAtom == nsEditProperty::h1)
   133       || (nodeAtom == nsEditProperty::h2)
   134       || (nodeAtom == nsEditProperty::h3)
   135       || (nodeAtom == nsEditProperty::h4)
   136       || (nodeAtom == nsEditProperty::h5)
   137       || (nodeAtom == nsEditProperty::h6);
   138 }
   141 ///////////////////////////////////////////////////////////////////////////
   142 // IsParagraph: true if node an html paragraph
   143 //                  
   144 bool 
   145 nsHTMLEditUtils::IsParagraph(nsIDOMNode* aNode)
   146 {
   147   return nsEditor::NodeIsType(aNode, nsEditProperty::p);
   148 }
   151 ///////////////////////////////////////////////////////////////////////////
   152 // IsHR: true if node an horizontal rule
   153 //                  
   154 bool 
   155 nsHTMLEditUtils::IsHR(nsIDOMNode* aNode)
   156 {
   157   return nsEditor::NodeIsType(aNode, nsEditProperty::hr);
   158 }
   161 ///////////////////////////////////////////////////////////////////////////
   162 // IsListItem: true if node an html list item
   163 //                  
   164 bool 
   165 nsHTMLEditUtils::IsListItem(nsIDOMNode* aNode)
   166 {
   167   NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsListItem");
   168   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   169   return node && IsListItem(node);
   170 }
   172 bool
   173 nsHTMLEditUtils::IsListItem(nsINode* node)
   174 {
   175   MOZ_ASSERT(node);
   176   nsCOMPtr<nsIAtom> nodeAtom = node->Tag();
   177   return (nodeAtom == nsEditProperty::li)
   178       || (nodeAtom == nsEditProperty::dd)
   179       || (nodeAtom == nsEditProperty::dt);
   180 }
   183 ///////////////////////////////////////////////////////////////////////////
   184 // IsTableElement: true if node an html table, td, tr, ...
   185 //                  
   186 bool
   187 nsHTMLEditUtils::IsTableElement(nsIDOMNode* aNode)
   188 {
   189   NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::IsTableElement");
   190   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   191   return node && IsTableElement(node);
   192 }
   194 bool
   195 nsHTMLEditUtils::IsTableElement(nsINode* node)
   196 {
   197   MOZ_ASSERT(node);
   198   nsCOMPtr<nsIAtom> nodeAtom = node->Tag();
   199   return (nodeAtom == nsEditProperty::table)
   200       || (nodeAtom == nsEditProperty::tr)
   201       || (nodeAtom == nsEditProperty::td)
   202       || (nodeAtom == nsEditProperty::th)
   203       || (nodeAtom == nsEditProperty::thead)
   204       || (nodeAtom == nsEditProperty::tfoot)
   205       || (nodeAtom == nsEditProperty::tbody)
   206       || (nodeAtom == nsEditProperty::caption);
   207 }
   209 ///////////////////////////////////////////////////////////////////////////
   210 // IsTableElementButNotTable: true if node an html td, tr, ... (doesn't include table)
   211 //                  
   212 bool 
   213 nsHTMLEditUtils::IsTableElementButNotTable(nsIDOMNode* aNode)
   214 {
   215   NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::IsTableElementButNotTable");
   216   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   217   return node && IsTableElementButNotTable(node);
   218 }
   220 bool
   221 nsHTMLEditUtils::IsTableElementButNotTable(nsINode* aNode)
   222 {
   223   MOZ_ASSERT(aNode);
   224   nsCOMPtr<nsIAtom> nodeAtom = aNode->Tag();
   225   return (nodeAtom == nsEditProperty::tr)
   226       || (nodeAtom == nsEditProperty::td)
   227       || (nodeAtom == nsEditProperty::th)
   228       || (nodeAtom == nsEditProperty::thead)
   229       || (nodeAtom == nsEditProperty::tfoot)
   230       || (nodeAtom == nsEditProperty::tbody)
   231       || (nodeAtom == nsEditProperty::caption);
   232 }
   234 ///////////////////////////////////////////////////////////////////////////
   235 // IsTable: true if node an html table
   236 //                  
   237 bool 
   238 nsHTMLEditUtils::IsTable(nsIDOMNode* aNode)
   239 {
   240   return nsEditor::NodeIsType(aNode, nsEditProperty::table);
   241 }
   243 ///////////////////////////////////////////////////////////////////////////
   244 // IsTableRow: true if node an html tr
   245 //                  
   246 bool 
   247 nsHTMLEditUtils::IsTableRow(nsIDOMNode* aNode)
   248 {
   249   return nsEditor::NodeIsType(aNode, nsEditProperty::tr);
   250 }
   253 ///////////////////////////////////////////////////////////////////////////
   254 // IsTableCell: true if node an html td or th
   255 //                  
   256 bool 
   257 nsHTMLEditUtils::IsTableCell(nsIDOMNode* aNode)
   258 {
   259   NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsTableCell");
   260   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   261   return node && IsTableCell(node);
   262 }
   264 bool
   265 nsHTMLEditUtils::IsTableCell(nsINode* node)
   266 {
   267   MOZ_ASSERT(node);
   268   nsCOMPtr<nsIAtom> nodeAtom = node->Tag();
   269   return (nodeAtom == nsEditProperty::td)
   270       || (nodeAtom == nsEditProperty::th);
   271 }
   274 ///////////////////////////////////////////////////////////////////////////
   275 // IsTableCell: true if node an html td or th
   276 //                  
   277 bool 
   278 nsHTMLEditUtils::IsTableCellOrCaption(nsIDOMNode* aNode)
   279 {
   280   NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsTableCell");
   281   nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode);
   282   return (nodeAtom == nsEditProperty::td)
   283       || (nodeAtom == nsEditProperty::th)
   284       || (nodeAtom == nsEditProperty::caption);
   285 }
   288 ///////////////////////////////////////////////////////////////////////////
   289 // IsList: true if node an html list
   290 //                  
   291 bool
   292 nsHTMLEditUtils::IsList(nsIDOMNode* aNode)
   293 {
   294   NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsList");
   295   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   296   return node && IsList(node);
   297 }
   299 bool
   300 nsHTMLEditUtils::IsList(nsINode* node)
   301 {
   302   MOZ_ASSERT(node);
   303   nsCOMPtr<nsIAtom> nodeAtom = node->Tag();
   304   return (nodeAtom == nsEditProperty::ul)
   305       || (nodeAtom == nsEditProperty::ol)
   306       || (nodeAtom == nsEditProperty::dl);
   307 }
   310 ///////////////////////////////////////////////////////////////////////////
   311 // IsOrderedList: true if node an html ordered list
   312 //                  
   313 bool 
   314 nsHTMLEditUtils::IsOrderedList(nsIDOMNode* aNode)
   315 {
   316   return nsEditor::NodeIsType(aNode, nsEditProperty::ol);
   317 }
   320 ///////////////////////////////////////////////////////////////////////////
   321 // IsUnorderedList: true if node an html unordered list
   322 //                  
   323 bool 
   324 nsHTMLEditUtils::IsUnorderedList(nsIDOMNode* aNode)
   325 {
   326   return nsEditor::NodeIsType(aNode, nsEditProperty::ul);
   327 }
   330 ///////////////////////////////////////////////////////////////////////////
   331 // IsBlockquote: true if node an html blockquote node
   332 //                  
   333 bool 
   334 nsHTMLEditUtils::IsBlockquote(nsIDOMNode* aNode)
   335 {
   336   return nsEditor::NodeIsType(aNode, nsEditProperty::blockquote);
   337 }
   340 ///////////////////////////////////////////////////////////////////////////
   341 // IsPre: true if node an html pre node
   342 //                  
   343 bool 
   344 nsHTMLEditUtils::IsPre(nsIDOMNode* aNode)
   345 {
   346   return nsEditor::NodeIsType(aNode, nsEditProperty::pre);
   347 }
   350 ///////////////////////////////////////////////////////////////////////////
   351 // IsImage: true if node an html image node
   352 //                  
   353 bool 
   354 nsHTMLEditUtils::IsImage(nsIDOMNode* aNode)
   355 {
   356   return nsEditor::NodeIsType(aNode, nsEditProperty::img);
   357 }
   359 bool 
   360 nsHTMLEditUtils::IsLink(nsIDOMNode *aNode)
   361 {
   362   NS_ENSURE_TRUE(aNode, false);
   363   nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aNode);
   364   if (anchor)
   365   {
   366     nsAutoString tmpText;
   367     if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && !tmpText.IsEmpty())
   368       return true;
   369   }
   370   return false;
   371 }
   373 bool 
   374 nsHTMLEditUtils::IsNamedAnchor(nsIDOMNode *aNode)
   375 {
   376   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   377   return node && IsNamedAnchor(node);
   378 }
   380 bool
   381 nsHTMLEditUtils::IsNamedAnchor(nsINode* aNode)
   382 {
   383   MOZ_ASSERT(aNode);
   384   if (!aNode->IsElement() || !aNode->AsElement()->IsHTML(nsGkAtoms::a)) {
   385     return false;
   386   }
   388   nsAutoString text;
   389   return aNode->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name,
   390                                      text) && !text.IsEmpty();
   391 }
   394 ///////////////////////////////////////////////////////////////////////////
   395 // IsDiv: true if node an html div node
   396 //                  
   397 bool 
   398 nsHTMLEditUtils::IsDiv(nsIDOMNode* aNode)
   399 {
   400   return nsEditor::NodeIsType(aNode, nsEditProperty::div);
   401 }
   404 ///////////////////////////////////////////////////////////////////////////
   405 // IsMozDiv: true if node an html div node with type = _moz
   406 //                  
   407 bool 
   408 nsHTMLEditUtils::IsMozDiv(nsIDOMNode* aNode)
   409 {
   410   if (IsDiv(aNode) && nsTextEditUtils::HasMozAttr(aNode)) return true;
   411   return false;
   412 }
   416 ///////////////////////////////////////////////////////////////////////////
   417 // IsMailCite: true if node an html blockquote with type=cite
   418 //                  
   419 bool
   420 nsHTMLEditUtils::IsMailCite(nsIDOMNode* aNode)
   421 {
   422   NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsMailCite");
   423   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   424   return node && IsMailCite(node);
   425 }
   427 bool
   428 nsHTMLEditUtils::IsMailCite(nsINode* aNode)
   429 {
   430   MOZ_ASSERT(aNode);
   432   // don't ask me why, but our html mailcites are id'd by "type=cite"...
   433   if (aNode->IsElement() &&
   434       aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
   435                                       NS_LITERAL_STRING("cite"),
   436                                       eIgnoreCase)) {
   437     return true;
   438   }
   440   // ... but our plaintext mailcites by "_moz_quote=true".  go figure.
   441   if (aNode->IsElement() &&
   442       aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozquote,
   443                                       NS_LITERAL_STRING("true"),
   444                                       eIgnoreCase)) {
   445     return true;
   446   }
   448   return false;
   449 }
   452 ///////////////////////////////////////////////////////////////////////////
   453 // IsFormWidget: true if node is a form widget of some kind
   454 //                  
   455 bool
   456 nsHTMLEditUtils::IsFormWidget(nsIDOMNode* aNode)
   457 {
   458   NS_PRECONDITION(aNode, "null node passed to nsHTMLEditUtils::IsFormWidget");
   459   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   460   return node && IsFormWidget(node);
   461 }
   463 bool
   464 nsHTMLEditUtils::IsFormWidget(nsINode* aNode)
   465 {
   466   MOZ_ASSERT(aNode);
   467   nsCOMPtr<nsIAtom> nodeAtom = aNode->Tag();
   468   return (nodeAtom == nsEditProperty::textarea)
   469       || (nodeAtom == nsEditProperty::select)
   470       || (nodeAtom == nsEditProperty::button)
   471       || (nodeAtom == nsEditProperty::output)
   472       || (nodeAtom == nsEditProperty::keygen)
   473       || (nodeAtom == nsEditProperty::progress)
   474       || (nodeAtom == nsEditProperty::meter)
   475       || (nodeAtom == nsEditProperty::input);
   476 }
   478 bool
   479 nsHTMLEditUtils::SupportsAlignAttr(nsIDOMNode* aNode)
   480 {
   481   NS_PRECONDITION(aNode, "null node passed to nsHTMLEditUtils::SupportsAlignAttr");
   482   nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode);
   483   return (nodeAtom == nsEditProperty::hr)
   484       || (nodeAtom == nsEditProperty::table)
   485       || (nodeAtom == nsEditProperty::tbody)
   486       || (nodeAtom == nsEditProperty::tfoot)
   487       || (nodeAtom == nsEditProperty::thead)
   488       || (nodeAtom == nsEditProperty::tr)
   489       || (nodeAtom == nsEditProperty::td)
   490       || (nodeAtom == nsEditProperty::th)
   491       || (nodeAtom == nsEditProperty::div)
   492       || (nodeAtom == nsEditProperty::p)
   493       || (nodeAtom == nsEditProperty::h1)
   494       || (nodeAtom == nsEditProperty::h2)
   495       || (nodeAtom == nsEditProperty::h3)
   496       || (nodeAtom == nsEditProperty::h4)
   497       || (nodeAtom == nsEditProperty::h5)
   498       || (nodeAtom == nsEditProperty::h6);
   499 }
   501 // We use bitmasks to test containment of elements. Elements are marked to be
   502 // in certain groups by setting the mGroup member of the nsElementInfo struct
   503 // to the corresponding GROUP_ values (OR'ed together). Similarly, elements are
   504 // marked to allow containment of certain groups by setting the
   505 // mCanContainGroups member of the nsElementInfo struct to the corresponding
   506 // GROUP_ values (OR'ed together).
   507 // Testing containment then simply consists of checking whether the
   508 // mCanContainGroups bitmask of an element and the mGroup bitmask of a
   509 // potential child overlap.
   511 #define GROUP_NONE             0
   513 // body, head, html
   514 #define GROUP_TOPLEVEL         (1 << 1)  
   516 // base, link, meta, script, style, title
   517 #define GROUP_HEAD_CONTENT     (1 << 2)
   519 // b, big, i, s, small, strike, tt, u
   520 #define GROUP_FONTSTYLE        (1 << 3)
   522 // abbr, acronym, cite, code, datalist, del, dfn, em, ins, kbd, mark, samp,
   523 // strong, var
   524 #define GROUP_PHRASE           (1 << 4)
   526 // a, applet, basefont, bdo, br, font, iframe, img, map, meter, object, output,
   527 // progress, q, script, span, sub, sup
   528 #define GROUP_SPECIAL          (1 << 5)
   530 // button, form, input, label, select, textarea
   531 #define GROUP_FORMCONTROL      (1 << 6)
   533 // address, applet, article, aside, blockquote, button, center, del, dir, div,
   534 // dl, fieldset, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup,
   535 // hr, iframe, ins, main, map, menu, nav, noframes, noscript, object, ol, p,
   536 // pre, table, section, ul
   537 #define GROUP_BLOCK            (1 << 7)
   539 // frame, frameset
   540 #define GROUP_FRAME            (1 << 8)
   542 // col, tbody
   543 #define GROUP_TABLE_CONTENT    (1 << 9)
   545 // tr
   546 #define GROUP_TBODY_CONTENT    (1 << 10)
   548 // td, th
   549 #define GROUP_TR_CONTENT       (1 << 11)
   551 // col
   552 #define GROUP_COLGROUP_CONTENT (1 << 12)
   554 // param
   555 #define GROUP_OBJECT_CONTENT   (1 << 13)
   557 // li
   558 #define GROUP_LI               (1 << 14)
   560 // area
   561 #define GROUP_MAP_CONTENT      (1 << 15)
   563 // optgroup, option
   564 #define GROUP_SELECT_CONTENT   (1 << 16)
   566 // option
   567 #define GROUP_OPTIONS          (1 << 17)
   569 // dd, dt
   570 #define GROUP_DL_CONTENT       (1 << 18)
   572 // p
   573 #define GROUP_P                (1 << 19)
   575 // text, whitespace, newline, comment
   576 #define GROUP_LEAF             (1 << 20)
   578 // XXX This is because the editor does sublists illegally. 
   579 // ol, ul
   580 #define GROUP_OL_UL            (1 << 21)
   582 // h1, h2, h3, h4, h5, h6
   583 #define GROUP_HEADING          (1 << 22)
   585 // figcaption
   586 #define GROUP_FIGCAPTION       (1 << 23)
   588 #define GROUP_INLINE_ELEMENT \
   589   (GROUP_FONTSTYLE | GROUP_PHRASE | GROUP_SPECIAL | GROUP_FORMCONTROL | \
   590    GROUP_LEAF)
   592 #define GROUP_FLOW_ELEMENT (GROUP_INLINE_ELEMENT | GROUP_BLOCK)
   594 struct nsElementInfo
   595 {
   596 #ifdef DEBUG
   597   eHTMLTags mTag;
   598 #endif
   599   uint32_t mGroup;
   600   uint32_t mCanContainGroups;
   601   bool mIsContainer;
   602   bool mCanContainSelf;
   603 };
   605 #ifdef DEBUG
   606 #define ELEM(_tag, _isContainer, _canContainSelf, _group, _canContainGroups) \
   607   { eHTMLTag_##_tag, _group, _canContainGroups, _isContainer, _canContainSelf }
   608 #else
   609 #define ELEM(_tag, _isContainer, _canContainSelf, _group, _canContainGroups) \
   610   { _group, _canContainGroups, _isContainer, _canContainSelf }
   611 #endif
   613 static const nsElementInfo kElements[eHTMLTag_userdefined] = {
   614   ELEM(a, true, false, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   615   ELEM(abbr, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   616   ELEM(acronym, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   617   ELEM(address, true, true, GROUP_BLOCK,
   618        GROUP_INLINE_ELEMENT | GROUP_P),
   619   ELEM(applet, true, true, GROUP_SPECIAL | GROUP_BLOCK,
   620        GROUP_FLOW_ELEMENT | GROUP_OBJECT_CONTENT),
   621   ELEM(area, false, false, GROUP_MAP_CONTENT, GROUP_NONE),
   622   ELEM(article, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   623   ELEM(aside, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   624   ELEM(audio, false, false, GROUP_NONE, GROUP_NONE),
   625   ELEM(b, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   626   ELEM(base, false, false, GROUP_HEAD_CONTENT, GROUP_NONE),
   627   ELEM(basefont, false, false, GROUP_SPECIAL, GROUP_NONE),
   628   ELEM(bdo, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   629   ELEM(bgsound, false, false, GROUP_NONE, GROUP_NONE),
   630   ELEM(big, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   631   ELEM(blockquote, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   632   ELEM(body, true, true, GROUP_TOPLEVEL, GROUP_FLOW_ELEMENT),
   633   ELEM(br, false, false, GROUP_SPECIAL, GROUP_NONE),
   634   ELEM(button, true, true, GROUP_FORMCONTROL | GROUP_BLOCK,
   635        GROUP_FLOW_ELEMENT),
   636   ELEM(canvas, false, false, GROUP_NONE, GROUP_NONE),
   637   ELEM(caption, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT),
   638   ELEM(center, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   639   ELEM(cite, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   640   ELEM(code, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   641   ELEM(col, false, false, GROUP_TABLE_CONTENT | GROUP_COLGROUP_CONTENT,
   642        GROUP_NONE),
   643   ELEM(colgroup, true, false, GROUP_NONE, GROUP_COLGROUP_CONTENT),
   644   ELEM(content, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT),
   645   ELEM(data, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   646   ELEM(datalist, true, false, GROUP_PHRASE,
   647        GROUP_OPTIONS | GROUP_INLINE_ELEMENT),
   648   ELEM(dd, true, false, GROUP_DL_CONTENT, GROUP_FLOW_ELEMENT),
   649   ELEM(del, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   650   ELEM(dfn, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   651   ELEM(dir, true, false, GROUP_BLOCK, GROUP_LI),
   652   ELEM(div, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   653   ELEM(dl, true, false, GROUP_BLOCK, GROUP_DL_CONTENT),
   654   ELEM(dt, true, true, GROUP_DL_CONTENT, GROUP_INLINE_ELEMENT),
   655   ELEM(em, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   656   ELEM(embed, false, false, GROUP_NONE, GROUP_NONE),
   657   ELEM(fieldset, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   658   ELEM(figcaption, true, false, GROUP_FIGCAPTION, GROUP_FLOW_ELEMENT),
   659   ELEM(figure, true, true, GROUP_BLOCK,
   660        GROUP_FLOW_ELEMENT | GROUP_FIGCAPTION),
   661   ELEM(font, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   662   ELEM(footer, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   663   ELEM(form, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   664   ELEM(frame, false, false, GROUP_FRAME, GROUP_NONE),
   665   ELEM(frameset, true, true, GROUP_FRAME, GROUP_FRAME),
   666   ELEM(h1, true, false, GROUP_BLOCK | GROUP_HEADING,
   667        GROUP_INLINE_ELEMENT),
   668   ELEM(h2, true, false, GROUP_BLOCK | GROUP_HEADING,
   669        GROUP_INLINE_ELEMENT),
   670   ELEM(h3, true, false, GROUP_BLOCK | GROUP_HEADING,
   671        GROUP_INLINE_ELEMENT),
   672   ELEM(h4, true, false, GROUP_BLOCK | GROUP_HEADING,
   673        GROUP_INLINE_ELEMENT),
   674   ELEM(h5, true, false, GROUP_BLOCK | GROUP_HEADING,
   675        GROUP_INLINE_ELEMENT),
   676   ELEM(h6, true, false, GROUP_BLOCK | GROUP_HEADING,
   677        GROUP_INLINE_ELEMENT),
   678   ELEM(head, true, false, GROUP_TOPLEVEL, GROUP_HEAD_CONTENT),
   679   ELEM(header, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   680   ELEM(hgroup, true, false, GROUP_BLOCK, GROUP_HEADING),
   681   ELEM(hr, false, false, GROUP_BLOCK, GROUP_NONE),
   682   ELEM(html, true, false, GROUP_TOPLEVEL, GROUP_TOPLEVEL),
   683   ELEM(i, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   684   ELEM(iframe, true, true, GROUP_SPECIAL | GROUP_BLOCK,
   685        GROUP_FLOW_ELEMENT),
   686   ELEM(image, false, false, GROUP_NONE, GROUP_NONE),
   687   ELEM(img, false, false, GROUP_SPECIAL, GROUP_NONE),
   688   ELEM(input, false, false, GROUP_FORMCONTROL, GROUP_NONE),
   689   ELEM(ins, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   690   ELEM(kbd, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   691   ELEM(keygen, false, false, GROUP_FORMCONTROL, GROUP_NONE),
   692   ELEM(label, true, false, GROUP_FORMCONTROL, GROUP_INLINE_ELEMENT),
   693   ELEM(legend, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT),
   694   ELEM(li, true, false, GROUP_LI, GROUP_FLOW_ELEMENT),
   695   ELEM(link, false, false, GROUP_HEAD_CONTENT, GROUP_NONE),
   696   ELEM(listing, false, false, GROUP_NONE, GROUP_NONE),
   697   ELEM(main, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   698   ELEM(map, true, true, GROUP_SPECIAL, GROUP_BLOCK | GROUP_MAP_CONTENT),
   699   ELEM(mark, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   700   ELEM(marquee, false, false, GROUP_NONE, GROUP_NONE),
   701   ELEM(menu, true, true, GROUP_BLOCK, GROUP_LI | GROUP_FLOW_ELEMENT),
   702   ELEM(menuitem, false, false, GROUP_NONE, GROUP_NONE),
   703   ELEM(meta, false, false, GROUP_HEAD_CONTENT, GROUP_NONE),
   704   ELEM(meter, true, false, GROUP_SPECIAL, GROUP_FLOW_ELEMENT),
   705   ELEM(multicol, false, false, GROUP_NONE, GROUP_NONE),
   706   ELEM(nav, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   707   ELEM(nobr, false, false, GROUP_NONE, GROUP_NONE),
   708   ELEM(noembed, false, false, GROUP_NONE, GROUP_NONE),
   709   ELEM(noframes, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   710   ELEM(noscript, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   711   ELEM(object, true, true, GROUP_SPECIAL | GROUP_BLOCK,
   712        GROUP_FLOW_ELEMENT | GROUP_OBJECT_CONTENT),
   713   // XXX Can contain self and ul because editor does sublists illegally.
   714   ELEM(ol, true, true, GROUP_BLOCK | GROUP_OL_UL,
   715        GROUP_LI | GROUP_OL_UL),
   716   ELEM(optgroup, true, false, GROUP_SELECT_CONTENT,
   717        GROUP_OPTIONS),
   718   ELEM(option, true, false,
   719        GROUP_SELECT_CONTENT | GROUP_OPTIONS, GROUP_LEAF),
   720   ELEM(output, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   721   ELEM(p, true, false, GROUP_BLOCK | GROUP_P, GROUP_INLINE_ELEMENT),
   722   ELEM(param, false, false, GROUP_OBJECT_CONTENT, GROUP_NONE),
   723   ELEM(plaintext, false, false, GROUP_NONE, GROUP_NONE),
   724   ELEM(pre, true, true, GROUP_BLOCK, GROUP_INLINE_ELEMENT),
   725   ELEM(progress, true, false, GROUP_SPECIAL, GROUP_FLOW_ELEMENT),
   726   ELEM(q, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   727   ELEM(s, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   728   ELEM(samp, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   729   ELEM(script, true, false, GROUP_HEAD_CONTENT | GROUP_SPECIAL,
   730        GROUP_LEAF),
   731   ELEM(section, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   732   ELEM(select, true, false, GROUP_FORMCONTROL, GROUP_SELECT_CONTENT),
   733   ELEM(shadow, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT),
   734   ELEM(small, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   735   ELEM(source, false, false, GROUP_NONE, GROUP_NONE),
   736   ELEM(span, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   737   ELEM(strike, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   738   ELEM(strong, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   739   ELEM(style, true, false, GROUP_HEAD_CONTENT, GROUP_LEAF),
   740   ELEM(sub, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   741   ELEM(sup, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   742   ELEM(table, true, false, GROUP_BLOCK, GROUP_TABLE_CONTENT),
   743   ELEM(tbody, true, false, GROUP_TABLE_CONTENT, GROUP_TBODY_CONTENT),
   744   ELEM(td, true, false, GROUP_TR_CONTENT, GROUP_FLOW_ELEMENT),
   745   ELEM(textarea, true, false, GROUP_FORMCONTROL, GROUP_LEAF),
   746   ELEM(tfoot, true, false, GROUP_NONE, GROUP_TBODY_CONTENT),
   747   ELEM(th, true, false, GROUP_TR_CONTENT, GROUP_FLOW_ELEMENT),
   748   ELEM(thead, true, false, GROUP_NONE, GROUP_TBODY_CONTENT),
   749   ELEM(template, false, false, GROUP_NONE, GROUP_NONE),
   750   ELEM(time, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   751   ELEM(title, true, false, GROUP_HEAD_CONTENT, GROUP_LEAF),
   752   ELEM(tr, true, false, GROUP_TBODY_CONTENT, GROUP_TR_CONTENT),
   753   ELEM(track, false, false, GROUP_NONE, GROUP_NONE),
   754   ELEM(tt, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   755   ELEM(u, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   756   // XXX Can contain self and ol because editor does sublists illegally.
   757   ELEM(ul, true, true, GROUP_BLOCK | GROUP_OL_UL,
   758        GROUP_LI | GROUP_OL_UL),
   759   ELEM(var, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   760   ELEM(video, false, false, GROUP_NONE, GROUP_NONE),
   761   ELEM(wbr, false, false, GROUP_NONE, GROUP_NONE),
   762   ELEM(xmp, false, false, GROUP_NONE, GROUP_NONE),
   764   // These aren't elements.
   765   ELEM(text, false, false, GROUP_LEAF, GROUP_NONE),
   766   ELEM(whitespace, false, false, GROUP_LEAF, GROUP_NONE),
   767   ELEM(newline, false, false, GROUP_LEAF, GROUP_NONE),
   768   ELEM(comment, false, false, GROUP_LEAF, GROUP_NONE),
   769   ELEM(entity, false, false, GROUP_NONE, GROUP_NONE),
   770   ELEM(doctypeDecl, false, false, GROUP_NONE, GROUP_NONE),
   771   ELEM(markupDecl, false, false, GROUP_NONE, GROUP_NONE),
   772   ELEM(instruction, false, false, GROUP_NONE, GROUP_NONE),
   774   ELEM(userdefined, true, false, GROUP_NONE, GROUP_FLOW_ELEMENT)
   775 };
   777 bool
   778 nsHTMLEditUtils::CanContain(int32_t aParent, int32_t aChild)
   779 {
   780   NS_ASSERTION(aParent > eHTMLTag_unknown && aParent <= eHTMLTag_userdefined,
   781                "aParent out of range!");
   782   NS_ASSERTION(aChild > eHTMLTag_unknown && aChild <= eHTMLTag_userdefined,
   783                "aChild out of range!");
   785 #ifdef DEBUG
   786   static bool checked = false;
   787   if (!checked) {
   788     checked = true;
   789     int32_t i;
   790     for (i = 1; i <= eHTMLTag_userdefined; ++i) {
   791       NS_ASSERTION(kElements[i - 1].mTag == i,
   792                    "You need to update kElements (missing tags).");
   793     }
   794   }
   795 #endif
   797   // Special-case button.
   798   if (aParent == eHTMLTag_button) {
   799     static const eHTMLTags kButtonExcludeKids[] = {
   800       eHTMLTag_a,
   801       eHTMLTag_fieldset,
   802       eHTMLTag_form,
   803       eHTMLTag_iframe,
   804       eHTMLTag_input,
   805       eHTMLTag_select,
   806       eHTMLTag_textarea
   807     };
   809     uint32_t j;
   810     for (j = 0; j < ArrayLength(kButtonExcludeKids); ++j) {
   811       if (kButtonExcludeKids[j] == aChild) {
   812         return false;
   813       }
   814     }
   815   }
   817   // Deprecated elements.
   818   if (aChild == eHTMLTag_bgsound) {
   819     return false;
   820   }
   822   // Bug #67007, dont strip userdefined tags.
   823   if (aChild == eHTMLTag_userdefined) {
   824     return true;
   825   }
   827   const nsElementInfo& parent = kElements[aParent - 1];
   828   if (aParent == aChild) {
   829     return parent.mCanContainSelf;
   830   }
   832   const nsElementInfo& child = kElements[aChild - 1];
   833   return (parent.mCanContainGroups & child.mGroup) != 0;
   834 } 
   836 bool
   837 nsHTMLEditUtils::IsContainer(int32_t aTag)
   838 {
   839   NS_ASSERTION(aTag > eHTMLTag_unknown && aTag <= eHTMLTag_userdefined,
   840                "aTag out of range!");
   842   return kElements[aTag - 1].mIsContainer;
   843 }

mercurial