Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsXHTMLContentSerializer.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/*
8
 * nsIContentSerializer implementation that can be used with an
9
 * nsIDocumentEncoder to convert an XHTML (not HTML!) DOM to an XHTML
10
 * string that could be parsed into more or less the original DOM.
11
 */
12
13
#include "nsXHTMLContentSerializer.h"
14
15
#include "mozilla/dom/Element.h"
16
#include "nsIContent.h"
17
#include "nsIDocument.h"
18
#include "nsElementTable.h"
19
#include "nsNameSpaceManager.h"
20
#include "nsString.h"
21
#include "nsUnicharUtils.h"
22
#include "nsIServiceManager.h"
23
#include "nsIDocumentEncoder.h"
24
#include "nsGkAtoms.h"
25
#include "nsIURI.h"
26
#include "nsNetUtil.h"
27
#include "nsEscape.h"
28
#include "nsCRT.h"
29
#include "nsContentUtils.h"
30
#include "nsIScriptElement.h"
31
#include "nsStubMutationObserver.h"
32
#include "nsAttrName.h"
33
#include "nsComputedDOMStyle.h"
34
35
using namespace mozilla;
36
37
static const int32_t kLongLineLen = 128;
38
39
0
#define kXMLNS "xmlns"
40
41
nsresult
42
NS_NewXHTMLContentSerializer(nsIContentSerializer** aSerializer)
43
0
{
44
0
  RefPtr<nsXHTMLContentSerializer> it = new nsXHTMLContentSerializer();
45
0
  it.forget(aSerializer);
46
0
  return NS_OK;
47
0
}
48
49
nsXHTMLContentSerializer::nsXHTMLContentSerializer()
50
  : mIsHTMLSerializer(false)
51
  , mIsCopying(false)
52
  , mDisableEntityEncoding(0)
53
  , mRewriteEncodingDeclaration(false)
54
  , mIsFirstChildOfOL(false)
55
0
{
56
0
}
57
58
nsXHTMLContentSerializer::~nsXHTMLContentSerializer()
59
0
{
60
0
  NS_ASSERTION(mOLStateStack.IsEmpty(), "Expected OL State stack to be empty");
61
0
}
62
63
NS_IMETHODIMP
64
nsXHTMLContentSerializer::Init(uint32_t aFlags,
65
                               uint32_t aWrapColumn,
66
                               const Encoding* aEncoding,
67
                               bool aIsCopying,
68
                               bool aRewriteEncodingDeclaration,
69
                               bool* aNeedsPreformatScanning)
70
0
{
71
0
  // The previous version of the HTML serializer did implicit wrapping
72
0
  // when there is no flags, so we keep wrapping in order to keep
73
0
  // compatibility with the existing calling code
74
0
  // XXXLJ perhaps should we remove this default settings later ?
75
0
  if (aFlags & nsIDocumentEncoder::OutputFormatted ) {
76
0
      aFlags = aFlags | nsIDocumentEncoder::OutputWrap;
77
0
  }
78
0
79
0
  nsresult rv;
80
0
  rv = nsXMLContentSerializer::Init(
81
0
    aFlags, aWrapColumn, aEncoding, aIsCopying, aRewriteEncodingDeclaration, aNeedsPreformatScanning);
82
0
  NS_ENSURE_SUCCESS(rv, rv);
83
0
84
0
  mRewriteEncodingDeclaration = aRewriteEncodingDeclaration;
85
0
  mIsCopying = aIsCopying;
86
0
  mIsFirstChildOfOL = false;
87
0
  mInBody = 0;
88
0
  mDisableEntityEncoding = 0;
89
0
  mBodyOnly = (mFlags & nsIDocumentEncoder::OutputBodyOnly) ? true
90
0
                                                            : false;
91
0
92
0
  return NS_OK;
93
0
}
94
95
96
// See if the string has any lines longer than longLineLen:
97
// if so, we presume formatting is wonky (e.g. the node has been edited)
98
// and we'd better rewrap the whole text node.
99
bool
100
nsXHTMLContentSerializer::HasLongLines(const nsString& text, int32_t& aLastNewlineOffset)
101
0
{
102
0
  uint32_t start=0;
103
0
  uint32_t theLen = text.Length();
104
0
  bool rv = false;
105
0
  aLastNewlineOffset = kNotFound;
106
0
  for (start = 0; start < theLen; ) {
107
0
    int32_t eol = text.FindChar('\n', start);
108
0
    if (eol < 0) {
109
0
      eol = text.Length();
110
0
    }
111
0
    else {
112
0
      aLastNewlineOffset = eol;
113
0
    }
114
0
    if (int32_t(eol - start) > kLongLineLen)
115
0
      rv = true;
116
0
    start = eol + 1;
117
0
  }
118
0
  return rv;
119
0
}
120
121
NS_IMETHODIMP
122
nsXHTMLContentSerializer::AppendText(nsIContent* aText,
123
                                     int32_t aStartOffset,
124
                                     int32_t aEndOffset,
125
                                     nsAString& aStr)
126
0
{
127
0
  NS_ENSURE_ARG(aText);
128
0
129
0
  nsAutoString data;
130
0
  nsresult rv;
131
0
132
0
  rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true);
133
0
  if (NS_FAILED(rv))
134
0
    return NS_ERROR_FAILURE;
135
0
136
0
  if (mDoRaw || PreLevel() > 0) {
137
0
    NS_ENSURE_TRUE(AppendToStringConvertLF(data, aStr), NS_ERROR_OUT_OF_MEMORY);
138
0
  }
139
0
  else if (mDoFormat) {
140
0
    NS_ENSURE_TRUE(AppendToStringFormatedWrapped(data, aStr), NS_ERROR_OUT_OF_MEMORY);
141
0
  }
142
0
  else if (mDoWrap) {
143
0
    NS_ENSURE_TRUE(AppendToStringWrapped(data, aStr), NS_ERROR_OUT_OF_MEMORY);
144
0
  }
145
0
  else {
146
0
    int32_t lastNewlineOffset = kNotFound;
147
0
    if (HasLongLines(data, lastNewlineOffset)) {
148
0
      // We have long lines, rewrap
149
0
      mDoWrap = true;
150
0
      bool result = AppendToStringWrapped(data, aStr);
151
0
      mDoWrap = false;
152
0
      NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
153
0
    }
154
0
    else {
155
0
      NS_ENSURE_TRUE(AppendToStringConvertLF(data, aStr), NS_ERROR_OUT_OF_MEMORY);
156
0
    }
157
0
  }
158
0
159
0
  return NS_OK;
160
0
}
161
162
bool
163
nsXHTMLContentSerializer::SerializeAttributes(Element* aElement,
164
                                              Element* aOriginalElement,
165
                                              nsAString& aTagPrefix,
166
                                              const nsAString& aTagNamespaceURI,
167
                                              nsAtom* aTagName,
168
                                              nsAString& aStr,
169
                                              uint32_t aSkipAttr,
170
                                              bool aAddNSAttr)
171
0
{
172
0
  nsresult rv;
173
0
  uint32_t index, count;
174
0
  nsAutoString prefixStr, uriStr, valueStr;
175
0
  nsAutoString xmlnsStr;
176
0
  xmlnsStr.AssignLiteral(kXMLNS);
177
0
178
0
  int32_t contentNamespaceID = aElement->GetNameSpaceID();
179
0
180
0
  MaybeSerializeIsValue(aElement, aStr);
181
0
182
0
  // this method is not called by nsHTMLContentSerializer
183
0
  // so we don't have to check HTML element, just XHTML
184
0
185
0
  if (mIsCopying && kNameSpaceID_XHTML == contentNamespaceID) {
186
0
187
0
    // Need to keep track of OL and LI elements in order to get ordinal number
188
0
    // for the LI.
189
0
    if (aTagName == nsGkAtoms::ol) {
190
0
      // We are copying and current node is an OL;
191
0
      // Store its start attribute value in olState->startVal.
192
0
      nsAutoString start;
193
0
      int32_t startAttrVal = 0;
194
0
      aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::start, start);
195
0
      if (!start.IsEmpty()) {
196
0
        nsresult rv = NS_OK;
197
0
        startAttrVal = start.ToInteger(&rv);
198
0
        //If OL has "start" attribute, first LI element has to start with that value
199
0
        //Therefore subtracting 1 as all the LI elements are incrementing it before using it;
200
0
        //In failure of ToInteger(), default StartAttrValue to 0.
201
0
        if (NS_SUCCEEDED(rv))
202
0
          --startAttrVal;
203
0
        else
204
0
          startAttrVal = 0;
205
0
      }
206
0
      olState state (startAttrVal, true);
207
0
      mOLStateStack.AppendElement(state);
208
0
    }
209
0
    else if (aTagName == nsGkAtoms::li) {
210
0
      mIsFirstChildOfOL = IsFirstChildOfOL(aOriginalElement);
211
0
      if (mIsFirstChildOfOL) {
212
0
        // If OL is parent of this LI, serialize attributes in different manner.
213
0
        NS_ENSURE_TRUE(SerializeLIValueAttribute(aElement, aStr), false);
214
0
      }
215
0
    }
216
0
  }
217
0
218
0
  // If we had to add a new namespace declaration, serialize
219
0
  // and push it on the namespace stack
220
0
  if (aAddNSAttr) {
221
0
    if (aTagPrefix.IsEmpty()) {
222
0
      // Serialize default namespace decl
223
0
      NS_ENSURE_TRUE(SerializeAttr(EmptyString(), xmlnsStr,
224
0
                                   aTagNamespaceURI,
225
0
                                   aStr, true), false);
226
0
    } else {
227
0
      // Serialize namespace decl
228
0
      NS_ENSURE_TRUE(SerializeAttr(xmlnsStr, aTagPrefix,
229
0
                                   aTagNamespaceURI,
230
0
                                   aStr, true), false);
231
0
    }
232
0
    PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement);
233
0
  }
234
0
235
0
  NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
236
0
237
0
  count = aElement->GetAttrCount();
238
0
239
0
  // Now serialize each of the attributes
240
0
  // XXX Unfortunately we need a namespace manager to get
241
0
  // attribute URIs.
242
0
  for (index = 0; index < count; index++) {
243
0
244
0
    if (aSkipAttr == index) {
245
0
        continue;
246
0
    }
247
0
248
0
    dom::BorrowedAttrInfo info = aElement->GetAttrInfoAt(index);
249
0
    const nsAttrName* name = info.mName;
250
0
251
0
    int32_t namespaceID = name->NamespaceID();
252
0
    nsAtom* attrName = name->LocalName();
253
0
    nsAtom* attrPrefix = name->GetPrefix();
254
0
255
0
    // Filter out any attribute starting with [-|_]moz
256
0
    nsDependentAtomString attrNameStr(attrName);
257
0
    if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
258
0
        StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
259
0
      continue;
260
0
    }
261
0
262
0
    if (attrPrefix) {
263
0
      attrPrefix->ToString(prefixStr);
264
0
    }
265
0
    else {
266
0
      prefixStr.Truncate();
267
0
    }
268
0
269
0
    bool addNSAttr = false;
270
0
    if (kNameSpaceID_XMLNS != namespaceID) {
271
0
      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr);
272
0
      addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true);
273
0
    }
274
0
275
0
    info.mValue->ToString(valueStr);
276
0
277
0
    nsDependentAtomString nameStr(attrName);
278
0
    bool isJS = false;
279
0
280
0
    if (kNameSpaceID_XHTML == contentNamespaceID) {
281
0
      //
282
0
      // Filter out special case of <br type="_moz"> or <br _moz*>,
283
0
      // used by the editor.  Bug 16988.  Yuck.
284
0
      //
285
0
      if (namespaceID == kNameSpaceID_None && aTagName == nsGkAtoms::br && attrName == nsGkAtoms::type
286
0
          && StringBeginsWith(valueStr, _mozStr)) {
287
0
        continue;
288
0
      }
289
0
290
0
      if (mIsCopying && mIsFirstChildOfOL && (aTagName == nsGkAtoms::li)
291
0
          && (attrName == nsGkAtoms::value)) {
292
0
        // This is handled separately in SerializeLIValueAttribute()
293
0
        continue;
294
0
      }
295
0
296
0
      isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr);
297
0
298
0
      if (namespaceID == kNameSpaceID_None &&
299
0
          ((attrName == nsGkAtoms::href) ||
300
0
          (attrName == nsGkAtoms::src))) {
301
0
        // Make all links absolute when converting only the selection:
302
0
        if (mFlags & nsIDocumentEncoder::OutputAbsoluteLinks) {
303
0
          // Would be nice to handle OBJECT tags,
304
0
          // but that gets more complicated since we have to
305
0
          // search the tag list for CODEBASE as well.
306
0
          // For now, just leave them relative.
307
0
          nsCOMPtr<nsIURI> uri = aElement->GetBaseURI();
308
0
          if (uri) {
309
0
            nsAutoString absURI;
310
0
            rv = NS_MakeAbsoluteURI(absURI, valueStr, uri);
311
0
            if (NS_SUCCEEDED(rv)) {
312
0
              valueStr = absURI;
313
0
            }
314
0
          }
315
0
        }
316
0
      }
317
0
318
0
      if (mRewriteEncodingDeclaration && aTagName == nsGkAtoms::meta &&
319
0
          attrName == nsGkAtoms::content) {
320
0
        // If we're serializing a <meta http-equiv="content-type">,
321
0
        // use the proper value, rather than what's in the document.
322
0
        nsAutoString header;
323
0
        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
324
0
        if (header.LowerCaseEqualsLiteral("content-type")) {
325
0
          valueStr = NS_LITERAL_STRING("text/html; charset=") +
326
0
            NS_ConvertASCIItoUTF16(mCharset);
327
0
        }
328
0
      }
329
0
330
0
      // Expand shorthand attribute.
331
0
      if (namespaceID == kNameSpaceID_None && IsShorthandAttr(attrName, aTagName) && valueStr.IsEmpty()) {
332
0
        valueStr = nameStr;
333
0
      }
334
0
    }
335
0
    else {
336
0
      isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr);
337
0
    }
338
0
339
0
    NS_ENSURE_TRUE(SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS), false);
340
0
341
0
    if (addNSAttr) {
342
0
      NS_ASSERTION(!prefixStr.IsEmpty(),
343
0
                   "Namespaced attributes must have a prefix");
344
0
      NS_ENSURE_TRUE(SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, true), false);
345
0
      PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement);
346
0
    }
347
0
  }
348
0
349
0
  return true;
350
0
}
351
352
bool
353
nsXHTMLContentSerializer::AfterElementStart(nsIContent* aContent,
354
                                            nsIContent* aOriginalElement,
355
                                            nsAString& aStr)
356
0
{
357
0
  if (mRewriteEncodingDeclaration &&
358
0
      aContent->IsHTMLElement(nsGkAtoms::head)) {
359
0
360
0
    // Check if there already are any content-type meta children.
361
0
    // If there are, they will be modified to use the correct charset.
362
0
    // If there aren't, we'll insert one here.
363
0
    bool hasMeta = false;
364
0
    for (nsIContent* child = aContent->GetFirstChild();
365
0
         child;
366
0
         child = child->GetNextSibling()) {
367
0
      if (child->IsHTMLElement(nsGkAtoms::meta) &&
368
0
          child->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::content)) {
369
0
        nsAutoString header;
370
0
        child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
371
0
372
0
        if (header.LowerCaseEqualsLiteral("content-type")) {
373
0
          hasMeta = true;
374
0
          break;
375
0
        }
376
0
      }
377
0
    }
378
0
379
0
    if (!hasMeta) {
380
0
      NS_ENSURE_TRUE(AppendNewLineToString(aStr), false);
381
0
      if (mDoFormat) {
382
0
        NS_ENSURE_TRUE(AppendIndentation(aStr), false);
383
0
      }
384
0
      NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING("<meta http-equiv=\"content-type\""), aStr), false);
385
0
      NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING(" content=\"text/html; charset="), aStr), false);
386
0
      NS_ENSURE_TRUE(AppendToString(NS_ConvertASCIItoUTF16(mCharset), aStr), false);
387
0
      if (mIsHTMLSerializer) {
388
0
        NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING("\">"), aStr), false);
389
0
      } else {
390
0
        NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING("\" />"), aStr), false);
391
0
      }
392
0
    }
393
0
  }
394
0
395
0
  return true;
396
0
}
397
398
void
399
nsXHTMLContentSerializer::AfterElementEnd(nsIContent * aContent,
400
                                          nsAString& aStr)
401
0
{
402
0
  NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
403
0
404
0
  // this method is not called by nsHTMLContentSerializer
405
0
  // so we don't have to check HTML element, just XHTML
406
0
  if (aContent->IsHTMLElement(nsGkAtoms::body)) {
407
0
    --mInBody;
408
0
  }
409
0
}
410
411
412
NS_IMETHODIMP
413
nsXHTMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument,
414
                                              nsAString& aStr)
415
0
{
416
0
  if (!mBodyOnly)
417
0
    return nsXMLContentSerializer::AppendDocumentStart(aDocument, aStr);
418
0
419
0
  return NS_OK;
420
0
}
421
422
bool
423
nsXHTMLContentSerializer::CheckElementStart(Element* aElement,
424
                                            bool& aForceFormat,
425
                                            nsAString& aStr,
426
                                            nsresult& aResult)
427
0
{
428
0
  aResult = NS_OK;
429
0
430
0
  // The _moz_dirty attribute is emitted by the editor to
431
0
  // indicate that this element should be pretty printed
432
0
  // even if we're not in pretty printing mode
433
0
  aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
434
0
                 aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
435
0
436
0
  if (aElement->IsHTMLElement(nsGkAtoms::br) &&
437
0
      (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre) &&
438
0
      PreLevel() > 0) {
439
0
    aResult = AppendNewLineToString(aStr) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
440
0
    return false;
441
0
  }
442
0
443
0
  if (aElement->IsHTMLElement(nsGkAtoms::body)) {
444
0
    ++mInBody;
445
0
  }
446
0
447
0
  return true;
448
0
}
449
450
bool
451
nsXHTMLContentSerializer::CheckElementEnd(dom::Element* aElement,
452
                                          bool& aForceFormat,
453
                                          nsAString& aStr)
454
0
{
455
0
  NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
456
0
457
0
  aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
458
0
                 aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
459
0
460
0
  if (mIsCopying && aElement->IsHTMLElement(nsGkAtoms::ol)) {
461
0
    NS_ASSERTION((!mOLStateStack.IsEmpty()), "Cannot have an empty OL Stack");
462
0
    /* Though at this point we must always have an state to be deleted as all
463
0
       the OL opening tags are supposed to push an olState object to the stack*/
464
0
    if (!mOLStateStack.IsEmpty()) {
465
0
      mOLStateStack.RemoveLastElement();
466
0
    }
467
0
  }
468
0
469
0
  bool dummyFormat;
470
0
  return nsXMLContentSerializer::CheckElementEnd(aElement, dummyFormat, aStr);
471
0
}
472
473
bool
474
nsXHTMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr,
475
                                                     nsAString& aOutputStr)
476
0
{
477
0
  if (mBodyOnly && !mInBody) {
478
0
    return true;
479
0
  }
480
0
481
0
  if (mDisableEntityEncoding) {
482
0
    return aOutputStr.Append(aStr, fallible);
483
0
  }
484
0
485
0
  return nsXMLContentSerializer::AppendAndTranslateEntities(aStr, aOutputStr);
486
0
}
487
488
bool
489
nsXHTMLContentSerializer::IsShorthandAttr(const nsAtom* aAttrName,
490
                                          const nsAtom* aElementName)
491
0
{
492
0
  // checked
493
0
  if ((aAttrName == nsGkAtoms::checked) &&
494
0
      (aElementName == nsGkAtoms::input)) {
495
0
    return true;
496
0
  }
497
0
498
0
  // compact
499
0
  if ((aAttrName == nsGkAtoms::compact) &&
500
0
      (aElementName == nsGkAtoms::dir ||
501
0
       aElementName == nsGkAtoms::dl ||
502
0
       aElementName == nsGkAtoms::menu ||
503
0
       aElementName == nsGkAtoms::ol ||
504
0
       aElementName == nsGkAtoms::ul)) {
505
0
    return true;
506
0
  }
507
0
508
0
  // declare
509
0
  if ((aAttrName == nsGkAtoms::declare) &&
510
0
      (aElementName == nsGkAtoms::object)) {
511
0
    return true;
512
0
  }
513
0
514
0
  // defer
515
0
  if ((aAttrName == nsGkAtoms::defer) &&
516
0
      (aElementName == nsGkAtoms::script)) {
517
0
    return true;
518
0
  }
519
0
520
0
  // disabled
521
0
  if ((aAttrName == nsGkAtoms::disabled) &&
522
0
      (aElementName == nsGkAtoms::button ||
523
0
       aElementName == nsGkAtoms::input ||
524
0
       aElementName == nsGkAtoms::optgroup ||
525
0
       aElementName == nsGkAtoms::option ||
526
0
       aElementName == nsGkAtoms::select ||
527
0
       aElementName == nsGkAtoms::textarea)) {
528
0
    return true;
529
0
  }
530
0
531
0
  // ismap
532
0
  if ((aAttrName == nsGkAtoms::ismap) &&
533
0
      (aElementName == nsGkAtoms::img ||
534
0
       aElementName == nsGkAtoms::input)) {
535
0
    return true;
536
0
  }
537
0
538
0
  // multiple
539
0
  if ((aAttrName == nsGkAtoms::multiple) &&
540
0
      (aElementName == nsGkAtoms::select)) {
541
0
    return true;
542
0
  }
543
0
544
0
  // noresize
545
0
  if ((aAttrName == nsGkAtoms::noresize) &&
546
0
      (aElementName == nsGkAtoms::frame)) {
547
0
    return true;
548
0
  }
549
0
550
0
  // noshade
551
0
  if ((aAttrName == nsGkAtoms::noshade) &&
552
0
      (aElementName == nsGkAtoms::hr)) {
553
0
    return true;
554
0
  }
555
0
556
0
  // nowrap
557
0
  if ((aAttrName == nsGkAtoms::nowrap) &&
558
0
      (aElementName == nsGkAtoms::td ||
559
0
       aElementName == nsGkAtoms::th)) {
560
0
    return true;
561
0
  }
562
0
563
0
  // readonly
564
0
  if ((aAttrName == nsGkAtoms::readonly) &&
565
0
      (aElementName == nsGkAtoms::input ||
566
0
       aElementName == nsGkAtoms::textarea)) {
567
0
    return true;
568
0
  }
569
0
570
0
  // selected
571
0
  if ((aAttrName == nsGkAtoms::selected) &&
572
0
      (aElementName == nsGkAtoms::option)) {
573
0
    return true;
574
0
  }
575
0
576
0
  // autoplay and controls
577
0
  if ((aElementName == nsGkAtoms::video || aElementName == nsGkAtoms::audio) &&
578
0
    (aAttrName == nsGkAtoms::autoplay || aAttrName == nsGkAtoms::muted ||
579
0
     aAttrName == nsGkAtoms::controls)) {
580
0
    return true;
581
0
  }
582
0
583
0
  return false;
584
0
}
585
586
bool
587
nsXHTMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID, nsAtom* aName)
588
0
{
589
0
590
0
  if (aNamespaceID != kNameSpaceID_XHTML) {
591
0
    return mAddSpace;
592
0
  }
593
0
594
0
  if (aName == nsGkAtoms::title ||
595
0
      aName == nsGkAtoms::meta  ||
596
0
      aName == nsGkAtoms::link  ||
597
0
      aName == nsGkAtoms::style ||
598
0
      aName == nsGkAtoms::select ||
599
0
      aName == nsGkAtoms::option ||
600
0
      aName == nsGkAtoms::script ||
601
0
      aName == nsGkAtoms::html) {
602
0
    return true;
603
0
  }
604
0
605
0
  return nsHTMLElement::IsBlock(nsHTMLTags::CaseSensitiveAtomTagToId(aName));
606
0
}
607
608
bool
609
nsXHTMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID, nsAtom* aName)
610
0
{
611
0
612
0
  if (aNamespaceID != kNameSpaceID_XHTML) {
613
0
    return false;
614
0
  }
615
0
616
0
  if ((aName == nsGkAtoms::html) ||
617
0
      (aName == nsGkAtoms::head) ||
618
0
      (aName == nsGkAtoms::body) ||
619
0
      (aName == nsGkAtoms::ul) ||
620
0
      (aName == nsGkAtoms::ol) ||
621
0
      (aName == nsGkAtoms::dl) ||
622
0
      (aName == nsGkAtoms::table) ||
623
0
      (aName == nsGkAtoms::tbody) ||
624
0
      (aName == nsGkAtoms::tr) ||
625
0
      (aName == nsGkAtoms::br) ||
626
0
      (aName == nsGkAtoms::meta) ||
627
0
      (aName == nsGkAtoms::link) ||
628
0
      (aName == nsGkAtoms::script) ||
629
0
      (aName == nsGkAtoms::select) ||
630
0
      (aName == nsGkAtoms::map) ||
631
0
      (aName == nsGkAtoms::area) ||
632
0
      (aName == nsGkAtoms::style)) {
633
0
    return true;
634
0
  }
635
0
636
0
  return false;
637
0
}
638
639
bool
640
nsXHTMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID, nsAtom* aName)
641
0
{
642
0
643
0
  if (aNamespaceID != kNameSpaceID_XHTML) {
644
0
    return false;
645
0
  }
646
0
647
0
  if ((aName == nsGkAtoms::html) ||
648
0
      (aName == nsGkAtoms::head) ||
649
0
      (aName == nsGkAtoms::body) ||
650
0
      (aName == nsGkAtoms::ul) ||
651
0
      (aName == nsGkAtoms::ol) ||
652
0
      (aName == nsGkAtoms::dl) ||
653
0
      (aName == nsGkAtoms::select) ||
654
0
      (aName == nsGkAtoms::table) ||
655
0
      (aName == nsGkAtoms::tbody)) {
656
0
    return true;
657
0
  }
658
0
  return false;
659
0
}
660
661
bool
662
nsXHTMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID, nsAtom* aName)
663
0
{
664
0
665
0
  if (aNamespaceID != kNameSpaceID_XHTML) {
666
0
    return false;
667
0
  }
668
0
669
0
  if ((aName == nsGkAtoms::html) ||
670
0
      (aName == nsGkAtoms::head) ||
671
0
      (aName == nsGkAtoms::body) ||
672
0
      (aName == nsGkAtoms::tr) ||
673
0
      (aName == nsGkAtoms::th) ||
674
0
      (aName == nsGkAtoms::td) ||
675
0
      (aName == nsGkAtoms::title) ||
676
0
      (aName == nsGkAtoms::dt) ||
677
0
      (aName == nsGkAtoms::dd) ||
678
0
      (aName == nsGkAtoms::select) ||
679
0
      (aName == nsGkAtoms::option) ||
680
0
      (aName == nsGkAtoms::map)) {
681
0
    return true;
682
0
  }
683
0
684
0
  return nsHTMLElement::IsBlock(nsHTMLTags::CaseSensitiveAtomTagToId(aName));
685
0
}
686
687
688
void
689
nsXHTMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode)
690
0
{
691
0
  if (!ShouldMaintainPreLevel() ||
692
0
      !aNode->IsHTMLElement()) {
693
0
    return;
694
0
  }
695
0
696
0
  if (IsElementPreformatted(aNode) ||
697
0
      aNode->IsAnyOfHTMLElements(nsGkAtoms::script,
698
0
                                 nsGkAtoms::style,
699
0
                                 nsGkAtoms::noscript,
700
0
                                 nsGkAtoms::noframes)) {
701
0
    PreLevel()++;
702
0
  }
703
0
}
704
705
void
706
nsXHTMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode)
707
0
{
708
0
  if (!ShouldMaintainPreLevel() ||
709
0
      !aNode->IsHTMLElement()) {
710
0
    return;
711
0
  }
712
0
713
0
  if (IsElementPreformatted(aNode) ||
714
0
      aNode->IsAnyOfHTMLElements(nsGkAtoms::script,
715
0
                                 nsGkAtoms::style,
716
0
                                 nsGkAtoms::noscript,
717
0
                                 nsGkAtoms::noframes)) {
718
0
    --PreLevel();
719
0
  }
720
0
}
721
722
bool
723
nsXHTMLContentSerializer::IsElementPreformatted(nsIContent* aNode)
724
0
{
725
0
  MOZ_ASSERT(ShouldMaintainPreLevel(), "We should not be calling this needlessly");
726
0
727
0
  if (!aNode->IsElement()) {
728
0
    return false;
729
0
  }
730
0
  RefPtr<ComputedStyle> computedStyle =
731
0
    nsComputedDOMStyle::GetComputedStyleNoFlush(aNode->AsElement(), nullptr);
732
0
  if (computedStyle) {
733
0
    const nsStyleText* textStyle = computedStyle->StyleText();
734
0
    return textStyle->WhiteSpaceOrNewlineIsSignificant();
735
0
  }
736
0
  return false;
737
0
}
738
739
bool
740
nsXHTMLContentSerializer::SerializeLIValueAttribute(nsIContent* aElement,
741
                                                    nsAString& aStr)
742
0
{
743
0
  // We are copying and we are at the "first" LI node of OL in selected range.
744
0
  // It may not be the first LI child of OL but it's first in the selected range.
745
0
  // Note that we get into this condition only once per a OL.
746
0
  bool found = false;
747
0
  nsAutoString valueStr;
748
0
749
0
  olState state (0, false);
750
0
751
0
  if (!mOLStateStack.IsEmpty()) {
752
0
    state = mOLStateStack[mOLStateStack.Length()-1];
753
0
    // isFirstListItem should be true only before the serialization of the
754
0
    // first item in the list.
755
0
    state.isFirstListItem = false;
756
0
    mOLStateStack[mOLStateStack.Length()-1] = state;
757
0
  }
758
0
759
0
  int32_t startVal = state.startVal;
760
0
  int32_t offset = 0;
761
0
762
0
  // Traverse previous siblings until we find one with "value" attribute.
763
0
  // offset keeps track of how many previous siblings we had to traverse.
764
0
  nsIContent* currNode = aElement;
765
0
  while (currNode && !found) {
766
0
    if (currNode->IsHTMLElement(nsGkAtoms::li)) {
767
0
      currNode->AsElement()->GetAttr(kNameSpaceID_None,
768
0
                                     nsGkAtoms::value, valueStr);
769
0
      if (valueStr.IsEmpty()) {
770
0
        offset++;
771
0
      } else {
772
0
        found = true;
773
0
        nsresult rv = NS_OK;
774
0
        startVal = valueStr.ToInteger(&rv);
775
0
      }
776
0
    }
777
0
    currNode = currNode->GetPreviousSibling();
778
0
  }
779
0
  // If LI was not having "value", Set the "value" attribute for it.
780
0
  // Note that We are at the first LI in the selected range of OL.
781
0
  if (offset == 0 && found) {
782
0
    // offset = 0 => LI itself has the value attribute and we did not need to traverse back.
783
0
    // Just serialize value attribute like other tags.
784
0
    NS_ENSURE_TRUE(SerializeAttr(EmptyString(), NS_LITERAL_STRING("value"),
785
0
                                 valueStr, aStr, false), false);
786
0
  }
787
0
  else if (offset == 1 && !found) {
788
0
    /*(offset = 1 && !found) means either LI is the first child node of OL
789
0
    and LI is not having "value" attribute.
790
0
    In that case we would not like to set "value" attribute to reduce the changes.
791
0
    */
792
0
    //do nothing...
793
0
  }
794
0
  else if (offset > 0) {
795
0
    // Set value attribute.
796
0
    nsAutoString valueStr;
797
0
798
0
    //As serializer needs to use this valueAttr we are creating here,
799
0
    valueStr.AppendInt(startVal + offset);
800
0
    NS_ENSURE_TRUE(SerializeAttr(EmptyString(), NS_LITERAL_STRING("value"),
801
0
                                 valueStr, aStr, false), false);
802
0
  }
803
0
804
0
  return true;
805
0
}
806
807
bool
808
nsXHTMLContentSerializer::IsFirstChildOfOL(nsIContent* aElement)
809
0
{
810
0
  nsIContent* parent = aElement->GetParent();
811
0
  if (parent && parent->NodeName().LowerCaseEqualsLiteral("ol")) {
812
0
    if (!mOLStateStack.IsEmpty()) {
813
0
      olState state = mOLStateStack[mOLStateStack.Length()-1];
814
0
      if (state.isFirstListItem)
815
0
        return true;
816
0
    }
817
0
  }
818
0
819
0
  return false;
820
0
}
821
822
bool
823
0
nsXHTMLContentSerializer::HasNoChildren(nsIContent* aContent) {
824
0
825
0
  for (nsIContent* child = aContent->GetFirstChild();
826
0
       child;
827
0
       child = child->GetNextSibling()) {
828
0
829
0
    if (!child->IsText())
830
0
      return false;
831
0
832
0
    if (child->TextLength())
833
0
      return false;
834
0
  }
835
0
836
0
  return true;
837
0
}