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