Coverage Report

Created: 2018-09-25 14:53

/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