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