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