1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/editor/libeditor/html/nsHTMLEditUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,843 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/ArrayUtils.h" // for ArrayLength 1.10 +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc 1.11 +#include "mozilla/dom/Element.h" // for Element, nsINode 1.12 +#include "nsAString.h" // for nsAString_internal::IsEmpty 1.13 +#include "nsCOMPtr.h" // for nsCOMPtr, operator==, etc 1.14 +#include "nsCaseTreatment.h" 1.15 +#include "nsDebug.h" // for NS_PRECONDITION, etc 1.16 +#include "nsEditProperty.h" // for nsEditProperty, etc 1.17 +#include "nsEditor.h" // for nsEditor 1.18 +#include "nsError.h" // for NS_SUCCEEDED 1.19 +#include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::a, etc 1.20 +#include "nsHTMLEditUtils.h" 1.21 +#include "nsHTMLTags.h" 1.22 +#include "nsIAtom.h" // for nsIAtom 1.23 +#include "nsIDOMHTMLAnchorElement.h" // for nsIDOMHTMLAnchorElement 1.24 +#include "nsIDOMNode.h" // for nsIDOMNode 1.25 +#include "nsNameSpaceManager.h" // for kNameSpaceID_None 1.26 +#include "nsLiteralString.h" // for NS_LITERAL_STRING 1.27 +#include "nsString.h" // for nsAutoString 1.28 +#include "nsTextEditUtils.h" // for nsTextEditUtils 1.29 + 1.30 +using namespace mozilla; 1.31 + 1.32 +/////////////////////////////////////////////////////////////////////////// 1.33 +// 1.34 +bool 1.35 +nsHTMLEditUtils::IsBig(nsIDOMNode* aNode) 1.36 +{ 1.37 + return nsEditor::NodeIsType(aNode, nsEditProperty::big); 1.38 +} 1.39 + 1.40 + 1.41 +/////////////////////////////////////////////////////////////////////////// 1.42 +// IsInlineStyle true if node is an inline style 1.43 +// 1.44 +bool 1.45 +nsHTMLEditUtils::IsInlineStyle(nsIDOMNode* aNode) 1.46 +{ 1.47 + NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsInlineStyle"); 1.48 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.49 + return node && IsInlineStyle(node); 1.50 +} 1.51 + 1.52 +bool 1.53 +nsHTMLEditUtils::IsInlineStyle(nsINode* aNode) 1.54 +{ 1.55 + MOZ_ASSERT(aNode); 1.56 + nsIAtom* nodeAtom = aNode->Tag(); 1.57 + return (nodeAtom == nsEditProperty::b) 1.58 + || (nodeAtom == nsEditProperty::i) 1.59 + || (nodeAtom == nsEditProperty::u) 1.60 + || (nodeAtom == nsEditProperty::tt) 1.61 + || (nodeAtom == nsEditProperty::s) 1.62 + || (nodeAtom == nsEditProperty::strike) 1.63 + || (nodeAtom == nsEditProperty::big) 1.64 + || (nodeAtom == nsEditProperty::small) 1.65 + || (nodeAtom == nsEditProperty::sub) 1.66 + || (nodeAtom == nsEditProperty::sup) 1.67 + || (nodeAtom == nsEditProperty::font); 1.68 +} 1.69 + 1.70 +/////////////////////////////////////////////////////////////////////////// 1.71 +// IsFormatNode true if node is a format node 1.72 +// 1.73 +bool 1.74 +nsHTMLEditUtils::IsFormatNode(nsIDOMNode* aNode) 1.75 +{ 1.76 + NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsFormatNode"); 1.77 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.78 + return node && IsFormatNode(node); 1.79 +} 1.80 + 1.81 +bool 1.82 +nsHTMLEditUtils::IsFormatNode(nsINode* aNode) 1.83 +{ 1.84 + MOZ_ASSERT(aNode); 1.85 + nsIAtom* nodeAtom = aNode->Tag(); 1.86 + return (nodeAtom == nsEditProperty::p) 1.87 + || (nodeAtom == nsEditProperty::pre) 1.88 + || (nodeAtom == nsEditProperty::h1) 1.89 + || (nodeAtom == nsEditProperty::h2) 1.90 + || (nodeAtom == nsEditProperty::h3) 1.91 + || (nodeAtom == nsEditProperty::h4) 1.92 + || (nodeAtom == nsEditProperty::h5) 1.93 + || (nodeAtom == nsEditProperty::h6) 1.94 + || (nodeAtom == nsEditProperty::address); 1.95 +} 1.96 + 1.97 +/////////////////////////////////////////////////////////////////////////// 1.98 +// IsNodeThatCanOutdent true if node is a list, list item, or blockquote 1.99 +// 1.100 +bool 1.101 +nsHTMLEditUtils::IsNodeThatCanOutdent(nsIDOMNode* aNode) 1.102 +{ 1.103 + NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsNodeThatCanOutdent"); 1.104 + nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode); 1.105 + return (nodeAtom == nsEditProperty::ul) 1.106 + || (nodeAtom == nsEditProperty::ol) 1.107 + || (nodeAtom == nsEditProperty::dl) 1.108 + || (nodeAtom == nsEditProperty::li) 1.109 + || (nodeAtom == nsEditProperty::dd) 1.110 + || (nodeAtom == nsEditProperty::dt) 1.111 + || (nodeAtom == nsEditProperty::blockquote); 1.112 +} 1.113 + 1.114 +/////////////////////////////////////////////////////////////////////////// 1.115 +// 1.116 +bool 1.117 +nsHTMLEditUtils::IsSmall(nsIDOMNode* aNode) 1.118 +{ 1.119 + return nsEditor::NodeIsType(aNode, nsEditProperty::small); 1.120 +} 1.121 + 1.122 + 1.123 +/******************************************************** 1.124 + * helper methods from nsHTMLEditRules 1.125 + ********************************************************/ 1.126 + 1.127 +/////////////////////////////////////////////////////////////////////////// 1.128 +// IsHeader: true if node an html header 1.129 +// 1.130 +bool 1.131 +nsHTMLEditUtils::IsHeader(nsIDOMNode* aNode) 1.132 +{ 1.133 + NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsHeader"); 1.134 + nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode); 1.135 + return (nodeAtom == nsEditProperty::h1) 1.136 + || (nodeAtom == nsEditProperty::h2) 1.137 + || (nodeAtom == nsEditProperty::h3) 1.138 + || (nodeAtom == nsEditProperty::h4) 1.139 + || (nodeAtom == nsEditProperty::h5) 1.140 + || (nodeAtom == nsEditProperty::h6); 1.141 +} 1.142 + 1.143 + 1.144 +/////////////////////////////////////////////////////////////////////////// 1.145 +// IsParagraph: true if node an html paragraph 1.146 +// 1.147 +bool 1.148 +nsHTMLEditUtils::IsParagraph(nsIDOMNode* aNode) 1.149 +{ 1.150 + return nsEditor::NodeIsType(aNode, nsEditProperty::p); 1.151 +} 1.152 + 1.153 + 1.154 +/////////////////////////////////////////////////////////////////////////// 1.155 +// IsHR: true if node an horizontal rule 1.156 +// 1.157 +bool 1.158 +nsHTMLEditUtils::IsHR(nsIDOMNode* aNode) 1.159 +{ 1.160 + return nsEditor::NodeIsType(aNode, nsEditProperty::hr); 1.161 +} 1.162 + 1.163 + 1.164 +/////////////////////////////////////////////////////////////////////////// 1.165 +// IsListItem: true if node an html list item 1.166 +// 1.167 +bool 1.168 +nsHTMLEditUtils::IsListItem(nsIDOMNode* aNode) 1.169 +{ 1.170 + NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsListItem"); 1.171 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.172 + return node && IsListItem(node); 1.173 +} 1.174 + 1.175 +bool 1.176 +nsHTMLEditUtils::IsListItem(nsINode* node) 1.177 +{ 1.178 + MOZ_ASSERT(node); 1.179 + nsCOMPtr<nsIAtom> nodeAtom = node->Tag(); 1.180 + return (nodeAtom == nsEditProperty::li) 1.181 + || (nodeAtom == nsEditProperty::dd) 1.182 + || (nodeAtom == nsEditProperty::dt); 1.183 +} 1.184 + 1.185 + 1.186 +/////////////////////////////////////////////////////////////////////////// 1.187 +// IsTableElement: true if node an html table, td, tr, ... 1.188 +// 1.189 +bool 1.190 +nsHTMLEditUtils::IsTableElement(nsIDOMNode* aNode) 1.191 +{ 1.192 + NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::IsTableElement"); 1.193 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.194 + return node && IsTableElement(node); 1.195 +} 1.196 + 1.197 +bool 1.198 +nsHTMLEditUtils::IsTableElement(nsINode* node) 1.199 +{ 1.200 + MOZ_ASSERT(node); 1.201 + nsCOMPtr<nsIAtom> nodeAtom = node->Tag(); 1.202 + return (nodeAtom == nsEditProperty::table) 1.203 + || (nodeAtom == nsEditProperty::tr) 1.204 + || (nodeAtom == nsEditProperty::td) 1.205 + || (nodeAtom == nsEditProperty::th) 1.206 + || (nodeAtom == nsEditProperty::thead) 1.207 + || (nodeAtom == nsEditProperty::tfoot) 1.208 + || (nodeAtom == nsEditProperty::tbody) 1.209 + || (nodeAtom == nsEditProperty::caption); 1.210 +} 1.211 + 1.212 +/////////////////////////////////////////////////////////////////////////// 1.213 +// IsTableElementButNotTable: true if node an html td, tr, ... (doesn't include table) 1.214 +// 1.215 +bool 1.216 +nsHTMLEditUtils::IsTableElementButNotTable(nsIDOMNode* aNode) 1.217 +{ 1.218 + NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::IsTableElementButNotTable"); 1.219 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.220 + return node && IsTableElementButNotTable(node); 1.221 +} 1.222 + 1.223 +bool 1.224 +nsHTMLEditUtils::IsTableElementButNotTable(nsINode* aNode) 1.225 +{ 1.226 + MOZ_ASSERT(aNode); 1.227 + nsCOMPtr<nsIAtom> nodeAtom = aNode->Tag(); 1.228 + return (nodeAtom == nsEditProperty::tr) 1.229 + || (nodeAtom == nsEditProperty::td) 1.230 + || (nodeAtom == nsEditProperty::th) 1.231 + || (nodeAtom == nsEditProperty::thead) 1.232 + || (nodeAtom == nsEditProperty::tfoot) 1.233 + || (nodeAtom == nsEditProperty::tbody) 1.234 + || (nodeAtom == nsEditProperty::caption); 1.235 +} 1.236 + 1.237 +/////////////////////////////////////////////////////////////////////////// 1.238 +// IsTable: true if node an html table 1.239 +// 1.240 +bool 1.241 +nsHTMLEditUtils::IsTable(nsIDOMNode* aNode) 1.242 +{ 1.243 + return nsEditor::NodeIsType(aNode, nsEditProperty::table); 1.244 +} 1.245 + 1.246 +/////////////////////////////////////////////////////////////////////////// 1.247 +// IsTableRow: true if node an html tr 1.248 +// 1.249 +bool 1.250 +nsHTMLEditUtils::IsTableRow(nsIDOMNode* aNode) 1.251 +{ 1.252 + return nsEditor::NodeIsType(aNode, nsEditProperty::tr); 1.253 +} 1.254 + 1.255 + 1.256 +/////////////////////////////////////////////////////////////////////////// 1.257 +// IsTableCell: true if node an html td or th 1.258 +// 1.259 +bool 1.260 +nsHTMLEditUtils::IsTableCell(nsIDOMNode* aNode) 1.261 +{ 1.262 + NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsTableCell"); 1.263 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.264 + return node && IsTableCell(node); 1.265 +} 1.266 + 1.267 +bool 1.268 +nsHTMLEditUtils::IsTableCell(nsINode* node) 1.269 +{ 1.270 + MOZ_ASSERT(node); 1.271 + nsCOMPtr<nsIAtom> nodeAtom = node->Tag(); 1.272 + return (nodeAtom == nsEditProperty::td) 1.273 + || (nodeAtom == nsEditProperty::th); 1.274 +} 1.275 + 1.276 + 1.277 +/////////////////////////////////////////////////////////////////////////// 1.278 +// IsTableCell: true if node an html td or th 1.279 +// 1.280 +bool 1.281 +nsHTMLEditUtils::IsTableCellOrCaption(nsIDOMNode* aNode) 1.282 +{ 1.283 + NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsTableCell"); 1.284 + nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode); 1.285 + return (nodeAtom == nsEditProperty::td) 1.286 + || (nodeAtom == nsEditProperty::th) 1.287 + || (nodeAtom == nsEditProperty::caption); 1.288 +} 1.289 + 1.290 + 1.291 +/////////////////////////////////////////////////////////////////////////// 1.292 +// IsList: true if node an html list 1.293 +// 1.294 +bool 1.295 +nsHTMLEditUtils::IsList(nsIDOMNode* aNode) 1.296 +{ 1.297 + NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsList"); 1.298 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.299 + return node && IsList(node); 1.300 +} 1.301 + 1.302 +bool 1.303 +nsHTMLEditUtils::IsList(nsINode* node) 1.304 +{ 1.305 + MOZ_ASSERT(node); 1.306 + nsCOMPtr<nsIAtom> nodeAtom = node->Tag(); 1.307 + return (nodeAtom == nsEditProperty::ul) 1.308 + || (nodeAtom == nsEditProperty::ol) 1.309 + || (nodeAtom == nsEditProperty::dl); 1.310 +} 1.311 + 1.312 + 1.313 +/////////////////////////////////////////////////////////////////////////// 1.314 +// IsOrderedList: true if node an html ordered list 1.315 +// 1.316 +bool 1.317 +nsHTMLEditUtils::IsOrderedList(nsIDOMNode* aNode) 1.318 +{ 1.319 + return nsEditor::NodeIsType(aNode, nsEditProperty::ol); 1.320 +} 1.321 + 1.322 + 1.323 +/////////////////////////////////////////////////////////////////////////// 1.324 +// IsUnorderedList: true if node an html unordered list 1.325 +// 1.326 +bool 1.327 +nsHTMLEditUtils::IsUnorderedList(nsIDOMNode* aNode) 1.328 +{ 1.329 + return nsEditor::NodeIsType(aNode, nsEditProperty::ul); 1.330 +} 1.331 + 1.332 + 1.333 +/////////////////////////////////////////////////////////////////////////// 1.334 +// IsBlockquote: true if node an html blockquote node 1.335 +// 1.336 +bool 1.337 +nsHTMLEditUtils::IsBlockquote(nsIDOMNode* aNode) 1.338 +{ 1.339 + return nsEditor::NodeIsType(aNode, nsEditProperty::blockquote); 1.340 +} 1.341 + 1.342 + 1.343 +/////////////////////////////////////////////////////////////////////////// 1.344 +// IsPre: true if node an html pre node 1.345 +// 1.346 +bool 1.347 +nsHTMLEditUtils::IsPre(nsIDOMNode* aNode) 1.348 +{ 1.349 + return nsEditor::NodeIsType(aNode, nsEditProperty::pre); 1.350 +} 1.351 + 1.352 + 1.353 +/////////////////////////////////////////////////////////////////////////// 1.354 +// IsImage: true if node an html image node 1.355 +// 1.356 +bool 1.357 +nsHTMLEditUtils::IsImage(nsIDOMNode* aNode) 1.358 +{ 1.359 + return nsEditor::NodeIsType(aNode, nsEditProperty::img); 1.360 +} 1.361 + 1.362 +bool 1.363 +nsHTMLEditUtils::IsLink(nsIDOMNode *aNode) 1.364 +{ 1.365 + NS_ENSURE_TRUE(aNode, false); 1.366 + nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aNode); 1.367 + if (anchor) 1.368 + { 1.369 + nsAutoString tmpText; 1.370 + if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && !tmpText.IsEmpty()) 1.371 + return true; 1.372 + } 1.373 + return false; 1.374 +} 1.375 + 1.376 +bool 1.377 +nsHTMLEditUtils::IsNamedAnchor(nsIDOMNode *aNode) 1.378 +{ 1.379 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.380 + return node && IsNamedAnchor(node); 1.381 +} 1.382 + 1.383 +bool 1.384 +nsHTMLEditUtils::IsNamedAnchor(nsINode* aNode) 1.385 +{ 1.386 + MOZ_ASSERT(aNode); 1.387 + if (!aNode->IsElement() || !aNode->AsElement()->IsHTML(nsGkAtoms::a)) { 1.388 + return false; 1.389 + } 1.390 + 1.391 + nsAutoString text; 1.392 + return aNode->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name, 1.393 + text) && !text.IsEmpty(); 1.394 +} 1.395 + 1.396 + 1.397 +/////////////////////////////////////////////////////////////////////////// 1.398 +// IsDiv: true if node an html div node 1.399 +// 1.400 +bool 1.401 +nsHTMLEditUtils::IsDiv(nsIDOMNode* aNode) 1.402 +{ 1.403 + return nsEditor::NodeIsType(aNode, nsEditProperty::div); 1.404 +} 1.405 + 1.406 + 1.407 +/////////////////////////////////////////////////////////////////////////// 1.408 +// IsMozDiv: true if node an html div node with type = _moz 1.409 +// 1.410 +bool 1.411 +nsHTMLEditUtils::IsMozDiv(nsIDOMNode* aNode) 1.412 +{ 1.413 + if (IsDiv(aNode) && nsTextEditUtils::HasMozAttr(aNode)) return true; 1.414 + return false; 1.415 +} 1.416 + 1.417 + 1.418 + 1.419 +/////////////////////////////////////////////////////////////////////////// 1.420 +// IsMailCite: true if node an html blockquote with type=cite 1.421 +// 1.422 +bool 1.423 +nsHTMLEditUtils::IsMailCite(nsIDOMNode* aNode) 1.424 +{ 1.425 + NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsMailCite"); 1.426 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.427 + return node && IsMailCite(node); 1.428 +} 1.429 + 1.430 +bool 1.431 +nsHTMLEditUtils::IsMailCite(nsINode* aNode) 1.432 +{ 1.433 + MOZ_ASSERT(aNode); 1.434 + 1.435 + // don't ask me why, but our html mailcites are id'd by "type=cite"... 1.436 + if (aNode->IsElement() && 1.437 + aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, 1.438 + NS_LITERAL_STRING("cite"), 1.439 + eIgnoreCase)) { 1.440 + return true; 1.441 + } 1.442 + 1.443 + // ... but our plaintext mailcites by "_moz_quote=true". go figure. 1.444 + if (aNode->IsElement() && 1.445 + aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozquote, 1.446 + NS_LITERAL_STRING("true"), 1.447 + eIgnoreCase)) { 1.448 + return true; 1.449 + } 1.450 + 1.451 + return false; 1.452 +} 1.453 + 1.454 + 1.455 +/////////////////////////////////////////////////////////////////////////// 1.456 +// IsFormWidget: true if node is a form widget of some kind 1.457 +// 1.458 +bool 1.459 +nsHTMLEditUtils::IsFormWidget(nsIDOMNode* aNode) 1.460 +{ 1.461 + NS_PRECONDITION(aNode, "null node passed to nsHTMLEditUtils::IsFormWidget"); 1.462 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.463 + return node && IsFormWidget(node); 1.464 +} 1.465 + 1.466 +bool 1.467 +nsHTMLEditUtils::IsFormWidget(nsINode* aNode) 1.468 +{ 1.469 + MOZ_ASSERT(aNode); 1.470 + nsCOMPtr<nsIAtom> nodeAtom = aNode->Tag(); 1.471 + return (nodeAtom == nsEditProperty::textarea) 1.472 + || (nodeAtom == nsEditProperty::select) 1.473 + || (nodeAtom == nsEditProperty::button) 1.474 + || (nodeAtom == nsEditProperty::output) 1.475 + || (nodeAtom == nsEditProperty::keygen) 1.476 + || (nodeAtom == nsEditProperty::progress) 1.477 + || (nodeAtom == nsEditProperty::meter) 1.478 + || (nodeAtom == nsEditProperty::input); 1.479 +} 1.480 + 1.481 +bool 1.482 +nsHTMLEditUtils::SupportsAlignAttr(nsIDOMNode* aNode) 1.483 +{ 1.484 + NS_PRECONDITION(aNode, "null node passed to nsHTMLEditUtils::SupportsAlignAttr"); 1.485 + nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode); 1.486 + return (nodeAtom == nsEditProperty::hr) 1.487 + || (nodeAtom == nsEditProperty::table) 1.488 + || (nodeAtom == nsEditProperty::tbody) 1.489 + || (nodeAtom == nsEditProperty::tfoot) 1.490 + || (nodeAtom == nsEditProperty::thead) 1.491 + || (nodeAtom == nsEditProperty::tr) 1.492 + || (nodeAtom == nsEditProperty::td) 1.493 + || (nodeAtom == nsEditProperty::th) 1.494 + || (nodeAtom == nsEditProperty::div) 1.495 + || (nodeAtom == nsEditProperty::p) 1.496 + || (nodeAtom == nsEditProperty::h1) 1.497 + || (nodeAtom == nsEditProperty::h2) 1.498 + || (nodeAtom == nsEditProperty::h3) 1.499 + || (nodeAtom == nsEditProperty::h4) 1.500 + || (nodeAtom == nsEditProperty::h5) 1.501 + || (nodeAtom == nsEditProperty::h6); 1.502 +} 1.503 + 1.504 +// We use bitmasks to test containment of elements. Elements are marked to be 1.505 +// in certain groups by setting the mGroup member of the nsElementInfo struct 1.506 +// to the corresponding GROUP_ values (OR'ed together). Similarly, elements are 1.507 +// marked to allow containment of certain groups by setting the 1.508 +// mCanContainGroups member of the nsElementInfo struct to the corresponding 1.509 +// GROUP_ values (OR'ed together). 1.510 +// Testing containment then simply consists of checking whether the 1.511 +// mCanContainGroups bitmask of an element and the mGroup bitmask of a 1.512 +// potential child overlap. 1.513 + 1.514 +#define GROUP_NONE 0 1.515 + 1.516 +// body, head, html 1.517 +#define GROUP_TOPLEVEL (1 << 1) 1.518 + 1.519 +// base, link, meta, script, style, title 1.520 +#define GROUP_HEAD_CONTENT (1 << 2) 1.521 + 1.522 +// b, big, i, s, small, strike, tt, u 1.523 +#define GROUP_FONTSTYLE (1 << 3) 1.524 + 1.525 +// abbr, acronym, cite, code, datalist, del, dfn, em, ins, kbd, mark, samp, 1.526 +// strong, var 1.527 +#define GROUP_PHRASE (1 << 4) 1.528 + 1.529 +// a, applet, basefont, bdo, br, font, iframe, img, map, meter, object, output, 1.530 +// progress, q, script, span, sub, sup 1.531 +#define GROUP_SPECIAL (1 << 5) 1.532 + 1.533 +// button, form, input, label, select, textarea 1.534 +#define GROUP_FORMCONTROL (1 << 6) 1.535 + 1.536 +// address, applet, article, aside, blockquote, button, center, del, dir, div, 1.537 +// dl, fieldset, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup, 1.538 +// hr, iframe, ins, main, map, menu, nav, noframes, noscript, object, ol, p, 1.539 +// pre, table, section, ul 1.540 +#define GROUP_BLOCK (1 << 7) 1.541 + 1.542 +// frame, frameset 1.543 +#define GROUP_FRAME (1 << 8) 1.544 + 1.545 +// col, tbody 1.546 +#define GROUP_TABLE_CONTENT (1 << 9) 1.547 + 1.548 +// tr 1.549 +#define GROUP_TBODY_CONTENT (1 << 10) 1.550 + 1.551 +// td, th 1.552 +#define GROUP_TR_CONTENT (1 << 11) 1.553 + 1.554 +// col 1.555 +#define GROUP_COLGROUP_CONTENT (1 << 12) 1.556 + 1.557 +// param 1.558 +#define GROUP_OBJECT_CONTENT (1 << 13) 1.559 + 1.560 +// li 1.561 +#define GROUP_LI (1 << 14) 1.562 + 1.563 +// area 1.564 +#define GROUP_MAP_CONTENT (1 << 15) 1.565 + 1.566 +// optgroup, option 1.567 +#define GROUP_SELECT_CONTENT (1 << 16) 1.568 + 1.569 +// option 1.570 +#define GROUP_OPTIONS (1 << 17) 1.571 + 1.572 +// dd, dt 1.573 +#define GROUP_DL_CONTENT (1 << 18) 1.574 + 1.575 +// p 1.576 +#define GROUP_P (1 << 19) 1.577 + 1.578 +// text, whitespace, newline, comment 1.579 +#define GROUP_LEAF (1 << 20) 1.580 + 1.581 +// XXX This is because the editor does sublists illegally. 1.582 +// ol, ul 1.583 +#define GROUP_OL_UL (1 << 21) 1.584 + 1.585 +// h1, h2, h3, h4, h5, h6 1.586 +#define GROUP_HEADING (1 << 22) 1.587 + 1.588 +// figcaption 1.589 +#define GROUP_FIGCAPTION (1 << 23) 1.590 + 1.591 +#define GROUP_INLINE_ELEMENT \ 1.592 + (GROUP_FONTSTYLE | GROUP_PHRASE | GROUP_SPECIAL | GROUP_FORMCONTROL | \ 1.593 + GROUP_LEAF) 1.594 + 1.595 +#define GROUP_FLOW_ELEMENT (GROUP_INLINE_ELEMENT | GROUP_BLOCK) 1.596 + 1.597 +struct nsElementInfo 1.598 +{ 1.599 +#ifdef DEBUG 1.600 + eHTMLTags mTag; 1.601 +#endif 1.602 + uint32_t mGroup; 1.603 + uint32_t mCanContainGroups; 1.604 + bool mIsContainer; 1.605 + bool mCanContainSelf; 1.606 +}; 1.607 + 1.608 +#ifdef DEBUG 1.609 +#define ELEM(_tag, _isContainer, _canContainSelf, _group, _canContainGroups) \ 1.610 + { eHTMLTag_##_tag, _group, _canContainGroups, _isContainer, _canContainSelf } 1.611 +#else 1.612 +#define ELEM(_tag, _isContainer, _canContainSelf, _group, _canContainGroups) \ 1.613 + { _group, _canContainGroups, _isContainer, _canContainSelf } 1.614 +#endif 1.615 + 1.616 +static const nsElementInfo kElements[eHTMLTag_userdefined] = { 1.617 + ELEM(a, true, false, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), 1.618 + ELEM(abbr, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.619 + ELEM(acronym, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.620 + ELEM(address, true, true, GROUP_BLOCK, 1.621 + GROUP_INLINE_ELEMENT | GROUP_P), 1.622 + ELEM(applet, true, true, GROUP_SPECIAL | GROUP_BLOCK, 1.623 + GROUP_FLOW_ELEMENT | GROUP_OBJECT_CONTENT), 1.624 + ELEM(area, false, false, GROUP_MAP_CONTENT, GROUP_NONE), 1.625 + ELEM(article, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.626 + ELEM(aside, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.627 + ELEM(audio, false, false, GROUP_NONE, GROUP_NONE), 1.628 + ELEM(b, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), 1.629 + ELEM(base, false, false, GROUP_HEAD_CONTENT, GROUP_NONE), 1.630 + ELEM(basefont, false, false, GROUP_SPECIAL, GROUP_NONE), 1.631 + ELEM(bdo, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), 1.632 + ELEM(bgsound, false, false, GROUP_NONE, GROUP_NONE), 1.633 + ELEM(big, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), 1.634 + ELEM(blockquote, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.635 + ELEM(body, true, true, GROUP_TOPLEVEL, GROUP_FLOW_ELEMENT), 1.636 + ELEM(br, false, false, GROUP_SPECIAL, GROUP_NONE), 1.637 + ELEM(button, true, true, GROUP_FORMCONTROL | GROUP_BLOCK, 1.638 + GROUP_FLOW_ELEMENT), 1.639 + ELEM(canvas, false, false, GROUP_NONE, GROUP_NONE), 1.640 + ELEM(caption, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT), 1.641 + ELEM(center, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.642 + ELEM(cite, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.643 + ELEM(code, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.644 + ELEM(col, false, false, GROUP_TABLE_CONTENT | GROUP_COLGROUP_CONTENT, 1.645 + GROUP_NONE), 1.646 + ELEM(colgroup, true, false, GROUP_NONE, GROUP_COLGROUP_CONTENT), 1.647 + ELEM(content, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT), 1.648 + ELEM(data, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.649 + ELEM(datalist, true, false, GROUP_PHRASE, 1.650 + GROUP_OPTIONS | GROUP_INLINE_ELEMENT), 1.651 + ELEM(dd, true, false, GROUP_DL_CONTENT, GROUP_FLOW_ELEMENT), 1.652 + ELEM(del, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.653 + ELEM(dfn, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.654 + ELEM(dir, true, false, GROUP_BLOCK, GROUP_LI), 1.655 + ELEM(div, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.656 + ELEM(dl, true, false, GROUP_BLOCK, GROUP_DL_CONTENT), 1.657 + ELEM(dt, true, true, GROUP_DL_CONTENT, GROUP_INLINE_ELEMENT), 1.658 + ELEM(em, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.659 + ELEM(embed, false, false, GROUP_NONE, GROUP_NONE), 1.660 + ELEM(fieldset, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.661 + ELEM(figcaption, true, false, GROUP_FIGCAPTION, GROUP_FLOW_ELEMENT), 1.662 + ELEM(figure, true, true, GROUP_BLOCK, 1.663 + GROUP_FLOW_ELEMENT | GROUP_FIGCAPTION), 1.664 + ELEM(font, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), 1.665 + ELEM(footer, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.666 + ELEM(form, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.667 + ELEM(frame, false, false, GROUP_FRAME, GROUP_NONE), 1.668 + ELEM(frameset, true, true, GROUP_FRAME, GROUP_FRAME), 1.669 + ELEM(h1, true, false, GROUP_BLOCK | GROUP_HEADING, 1.670 + GROUP_INLINE_ELEMENT), 1.671 + ELEM(h2, true, false, GROUP_BLOCK | GROUP_HEADING, 1.672 + GROUP_INLINE_ELEMENT), 1.673 + ELEM(h3, true, false, GROUP_BLOCK | GROUP_HEADING, 1.674 + GROUP_INLINE_ELEMENT), 1.675 + ELEM(h4, true, false, GROUP_BLOCK | GROUP_HEADING, 1.676 + GROUP_INLINE_ELEMENT), 1.677 + ELEM(h5, true, false, GROUP_BLOCK | GROUP_HEADING, 1.678 + GROUP_INLINE_ELEMENT), 1.679 + ELEM(h6, true, false, GROUP_BLOCK | GROUP_HEADING, 1.680 + GROUP_INLINE_ELEMENT), 1.681 + ELEM(head, true, false, GROUP_TOPLEVEL, GROUP_HEAD_CONTENT), 1.682 + ELEM(header, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.683 + ELEM(hgroup, true, false, GROUP_BLOCK, GROUP_HEADING), 1.684 + ELEM(hr, false, false, GROUP_BLOCK, GROUP_NONE), 1.685 + ELEM(html, true, false, GROUP_TOPLEVEL, GROUP_TOPLEVEL), 1.686 + ELEM(i, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), 1.687 + ELEM(iframe, true, true, GROUP_SPECIAL | GROUP_BLOCK, 1.688 + GROUP_FLOW_ELEMENT), 1.689 + ELEM(image, false, false, GROUP_NONE, GROUP_NONE), 1.690 + ELEM(img, false, false, GROUP_SPECIAL, GROUP_NONE), 1.691 + ELEM(input, false, false, GROUP_FORMCONTROL, GROUP_NONE), 1.692 + ELEM(ins, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.693 + ELEM(kbd, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.694 + ELEM(keygen, false, false, GROUP_FORMCONTROL, GROUP_NONE), 1.695 + ELEM(label, true, false, GROUP_FORMCONTROL, GROUP_INLINE_ELEMENT), 1.696 + ELEM(legend, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT), 1.697 + ELEM(li, true, false, GROUP_LI, GROUP_FLOW_ELEMENT), 1.698 + ELEM(link, false, false, GROUP_HEAD_CONTENT, GROUP_NONE), 1.699 + ELEM(listing, false, false, GROUP_NONE, GROUP_NONE), 1.700 + ELEM(main, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.701 + ELEM(map, true, true, GROUP_SPECIAL, GROUP_BLOCK | GROUP_MAP_CONTENT), 1.702 + ELEM(mark, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.703 + ELEM(marquee, false, false, GROUP_NONE, GROUP_NONE), 1.704 + ELEM(menu, true, true, GROUP_BLOCK, GROUP_LI | GROUP_FLOW_ELEMENT), 1.705 + ELEM(menuitem, false, false, GROUP_NONE, GROUP_NONE), 1.706 + ELEM(meta, false, false, GROUP_HEAD_CONTENT, GROUP_NONE), 1.707 + ELEM(meter, true, false, GROUP_SPECIAL, GROUP_FLOW_ELEMENT), 1.708 + ELEM(multicol, false, false, GROUP_NONE, GROUP_NONE), 1.709 + ELEM(nav, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.710 + ELEM(nobr, false, false, GROUP_NONE, GROUP_NONE), 1.711 + ELEM(noembed, false, false, GROUP_NONE, GROUP_NONE), 1.712 + ELEM(noframes, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.713 + ELEM(noscript, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.714 + ELEM(object, true, true, GROUP_SPECIAL | GROUP_BLOCK, 1.715 + GROUP_FLOW_ELEMENT | GROUP_OBJECT_CONTENT), 1.716 + // XXX Can contain self and ul because editor does sublists illegally. 1.717 + ELEM(ol, true, true, GROUP_BLOCK | GROUP_OL_UL, 1.718 + GROUP_LI | GROUP_OL_UL), 1.719 + ELEM(optgroup, true, false, GROUP_SELECT_CONTENT, 1.720 + GROUP_OPTIONS), 1.721 + ELEM(option, true, false, 1.722 + GROUP_SELECT_CONTENT | GROUP_OPTIONS, GROUP_LEAF), 1.723 + ELEM(output, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), 1.724 + ELEM(p, true, false, GROUP_BLOCK | GROUP_P, GROUP_INLINE_ELEMENT), 1.725 + ELEM(param, false, false, GROUP_OBJECT_CONTENT, GROUP_NONE), 1.726 + ELEM(plaintext, false, false, GROUP_NONE, GROUP_NONE), 1.727 + ELEM(pre, true, true, GROUP_BLOCK, GROUP_INLINE_ELEMENT), 1.728 + ELEM(progress, true, false, GROUP_SPECIAL, GROUP_FLOW_ELEMENT), 1.729 + ELEM(q, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), 1.730 + ELEM(s, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), 1.731 + ELEM(samp, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.732 + ELEM(script, true, false, GROUP_HEAD_CONTENT | GROUP_SPECIAL, 1.733 + GROUP_LEAF), 1.734 + ELEM(section, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), 1.735 + ELEM(select, true, false, GROUP_FORMCONTROL, GROUP_SELECT_CONTENT), 1.736 + ELEM(shadow, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT), 1.737 + ELEM(small, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), 1.738 + ELEM(source, false, false, GROUP_NONE, GROUP_NONE), 1.739 + ELEM(span, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), 1.740 + ELEM(strike, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), 1.741 + ELEM(strong, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.742 + ELEM(style, true, false, GROUP_HEAD_CONTENT, GROUP_LEAF), 1.743 + ELEM(sub, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), 1.744 + ELEM(sup, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), 1.745 + ELEM(table, true, false, GROUP_BLOCK, GROUP_TABLE_CONTENT), 1.746 + ELEM(tbody, true, false, GROUP_TABLE_CONTENT, GROUP_TBODY_CONTENT), 1.747 + ELEM(td, true, false, GROUP_TR_CONTENT, GROUP_FLOW_ELEMENT), 1.748 + ELEM(textarea, true, false, GROUP_FORMCONTROL, GROUP_LEAF), 1.749 + ELEM(tfoot, true, false, GROUP_NONE, GROUP_TBODY_CONTENT), 1.750 + ELEM(th, true, false, GROUP_TR_CONTENT, GROUP_FLOW_ELEMENT), 1.751 + ELEM(thead, true, false, GROUP_NONE, GROUP_TBODY_CONTENT), 1.752 + ELEM(template, false, false, GROUP_NONE, GROUP_NONE), 1.753 + ELEM(time, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.754 + ELEM(title, true, false, GROUP_HEAD_CONTENT, GROUP_LEAF), 1.755 + ELEM(tr, true, false, GROUP_TBODY_CONTENT, GROUP_TR_CONTENT), 1.756 + ELEM(track, false, false, GROUP_NONE, GROUP_NONE), 1.757 + ELEM(tt, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), 1.758 + ELEM(u, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), 1.759 + // XXX Can contain self and ol because editor does sublists illegally. 1.760 + ELEM(ul, true, true, GROUP_BLOCK | GROUP_OL_UL, 1.761 + GROUP_LI | GROUP_OL_UL), 1.762 + ELEM(var, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), 1.763 + ELEM(video, false, false, GROUP_NONE, GROUP_NONE), 1.764 + ELEM(wbr, false, false, GROUP_NONE, GROUP_NONE), 1.765 + ELEM(xmp, false, false, GROUP_NONE, GROUP_NONE), 1.766 + 1.767 + // These aren't elements. 1.768 + ELEM(text, false, false, GROUP_LEAF, GROUP_NONE), 1.769 + ELEM(whitespace, false, false, GROUP_LEAF, GROUP_NONE), 1.770 + ELEM(newline, false, false, GROUP_LEAF, GROUP_NONE), 1.771 + ELEM(comment, false, false, GROUP_LEAF, GROUP_NONE), 1.772 + ELEM(entity, false, false, GROUP_NONE, GROUP_NONE), 1.773 + ELEM(doctypeDecl, false, false, GROUP_NONE, GROUP_NONE), 1.774 + ELEM(markupDecl, false, false, GROUP_NONE, GROUP_NONE), 1.775 + ELEM(instruction, false, false, GROUP_NONE, GROUP_NONE), 1.776 + 1.777 + ELEM(userdefined, true, false, GROUP_NONE, GROUP_FLOW_ELEMENT) 1.778 +}; 1.779 + 1.780 +bool 1.781 +nsHTMLEditUtils::CanContain(int32_t aParent, int32_t aChild) 1.782 +{ 1.783 + NS_ASSERTION(aParent > eHTMLTag_unknown && aParent <= eHTMLTag_userdefined, 1.784 + "aParent out of range!"); 1.785 + NS_ASSERTION(aChild > eHTMLTag_unknown && aChild <= eHTMLTag_userdefined, 1.786 + "aChild out of range!"); 1.787 + 1.788 +#ifdef DEBUG 1.789 + static bool checked = false; 1.790 + if (!checked) { 1.791 + checked = true; 1.792 + int32_t i; 1.793 + for (i = 1; i <= eHTMLTag_userdefined; ++i) { 1.794 + NS_ASSERTION(kElements[i - 1].mTag == i, 1.795 + "You need to update kElements (missing tags)."); 1.796 + } 1.797 + } 1.798 +#endif 1.799 + 1.800 + // Special-case button. 1.801 + if (aParent == eHTMLTag_button) { 1.802 + static const eHTMLTags kButtonExcludeKids[] = { 1.803 + eHTMLTag_a, 1.804 + eHTMLTag_fieldset, 1.805 + eHTMLTag_form, 1.806 + eHTMLTag_iframe, 1.807 + eHTMLTag_input, 1.808 + eHTMLTag_select, 1.809 + eHTMLTag_textarea 1.810 + }; 1.811 + 1.812 + uint32_t j; 1.813 + for (j = 0; j < ArrayLength(kButtonExcludeKids); ++j) { 1.814 + if (kButtonExcludeKids[j] == aChild) { 1.815 + return false; 1.816 + } 1.817 + } 1.818 + } 1.819 + 1.820 + // Deprecated elements. 1.821 + if (aChild == eHTMLTag_bgsound) { 1.822 + return false; 1.823 + } 1.824 + 1.825 + // Bug #67007, dont strip userdefined tags. 1.826 + if (aChild == eHTMLTag_userdefined) { 1.827 + return true; 1.828 + } 1.829 + 1.830 + const nsElementInfo& parent = kElements[aParent - 1]; 1.831 + if (aParent == aChild) { 1.832 + return parent.mCanContainSelf; 1.833 + } 1.834 + 1.835 + const nsElementInfo& child = kElements[aChild - 1]; 1.836 + return (parent.mCanContainGroups & child.mGroup) != 0; 1.837 +} 1.838 + 1.839 +bool 1.840 +nsHTMLEditUtils::IsContainer(int32_t aTag) 1.841 +{ 1.842 + NS_ASSERTION(aTag > eHTMLTag_unknown && aTag <= eHTMLTag_userdefined, 1.843 + "aTag out of range!"); 1.844 + 1.845 + return kElements[aTag - 1].mIsContainer; 1.846 +}