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