content/base/src/nsXHTMLContentSerializer.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:db5385ef484a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et 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 "nsIDOMElement.h"
16 #include "nsIContent.h"
17 #include "nsIDocument.h"
18 #include "nsNameSpaceManager.h"
19 #include "nsString.h"
20 #include "nsUnicharUtils.h"
21 #include "nsXPIDLString.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 "nsITextToSubURI.h"
29 #include "nsCRT.h"
30 #include "nsIParserService.h"
31 #include "nsContentUtils.h"
32 #include "nsLWBrkCIID.h"
33 #include "nsIScriptElement.h"
34 #include "nsAttrName.h"
35 #include "nsParserConstants.h"
36
37 static const int32_t kLongLineLen = 128;
38
39 #define kXMLNS "xmlns"
40
41 nsresult NS_NewXHTMLContentSerializer(nsIContentSerializer** aSerializer)
42 {
43 nsXHTMLContentSerializer* it = new nsXHTMLContentSerializer();
44 if (!it) {
45 return NS_ERROR_OUT_OF_MEMORY;
46 }
47
48 return CallQueryInterface(it, aSerializer);
49 }
50
51 nsXHTMLContentSerializer::nsXHTMLContentSerializer()
52 : mIsHTMLSerializer(false)
53 {
54 }
55
56 nsXHTMLContentSerializer::~nsXHTMLContentSerializer()
57 {
58 NS_ASSERTION(mOLStateStack.IsEmpty(), "Expected OL State stack to be empty");
59 }
60
61 NS_IMETHODIMP
62 nsXHTMLContentSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn,
63 const char* aCharSet, bool aIsCopying,
64 bool aRewriteEncodingDeclaration)
65 {
66 // The previous version of the HTML serializer did implicit wrapping
67 // when there is no flags, so we keep wrapping in order to keep
68 // compatibility with the existing calling code
69 // XXXLJ perhaps should we remove this default settings later ?
70 if (aFlags & nsIDocumentEncoder::OutputFormatted ) {
71 aFlags = aFlags | nsIDocumentEncoder::OutputWrap;
72 }
73
74 nsresult rv;
75 rv = nsXMLContentSerializer::Init(aFlags, aWrapColumn, aCharSet, aIsCopying, aRewriteEncodingDeclaration);
76 NS_ENSURE_SUCCESS(rv, rv);
77
78 mRewriteEncodingDeclaration = aRewriteEncodingDeclaration;
79 mIsCopying = aIsCopying;
80 mIsFirstChildOfOL = false;
81 mInBody = 0;
82 mDisableEntityEncoding = 0;
83 mBodyOnly = (mFlags & nsIDocumentEncoder::OutputBodyOnly) ? true
84 : false;
85
86 // set up entity converter if we are going to need it
87 if (mFlags & nsIDocumentEncoder::OutputEncodeW3CEntities) {
88 mEntityConverter = do_CreateInstance(NS_ENTITYCONVERTER_CONTRACTID);
89 }
90 return NS_OK;
91 }
92
93
94 // See if the string has any lines longer than longLineLen:
95 // if so, we presume formatting is wonky (e.g. the node has been edited)
96 // and we'd better rewrap the whole text node.
97 bool
98 nsXHTMLContentSerializer::HasLongLines(const nsString& text, int32_t& aLastNewlineOffset)
99 {
100 uint32_t start=0;
101 uint32_t theLen = text.Length();
102 bool rv = false;
103 aLastNewlineOffset = kNotFound;
104 for (start = 0; start < theLen; ) {
105 int32_t eol = text.FindChar('\n', start);
106 if (eol < 0) {
107 eol = text.Length();
108 }
109 else {
110 aLastNewlineOffset = eol;
111 }
112 if (int32_t(eol - start) > kLongLineLen)
113 rv = true;
114 start = eol + 1;
115 }
116 return rv;
117 }
118
119 NS_IMETHODIMP
120 nsXHTMLContentSerializer::AppendText(nsIContent* aText,
121 int32_t aStartOffset,
122 int32_t aEndOffset,
123 nsAString& aStr)
124 {
125 NS_ENSURE_ARG(aText);
126
127 nsAutoString data;
128 nsresult rv;
129
130 rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true);
131 if (NS_FAILED(rv))
132 return NS_ERROR_FAILURE;
133
134 if (mPreLevel > 0 || mDoRaw) {
135 AppendToStringConvertLF(data, aStr);
136 }
137 else if (mDoFormat) {
138 AppendToStringFormatedWrapped(data, aStr);
139 }
140 else if (mDoWrap) {
141 AppendToStringWrapped(data, aStr);
142 }
143 else {
144 int32_t lastNewlineOffset = kNotFound;
145 if (HasLongLines(data, lastNewlineOffset)) {
146 // We have long lines, rewrap
147 mDoWrap = true;
148 AppendToStringWrapped(data, aStr);
149 mDoWrap = false;
150 }
151 else {
152 AppendToStringConvertLF(data, aStr);
153 }
154 }
155
156 return NS_OK;
157 }
158
159 nsresult
160 nsXHTMLContentSerializer::EscapeURI(nsIContent* aContent, const nsAString& aURI, nsAString& aEscapedURI)
161 {
162 // URL escape %xx cannot be used in JS.
163 // No escaping if the scheme is 'javascript'.
164 if (IsJavaScript(aContent, nsGkAtoms::href, kNameSpaceID_None, aURI)) {
165 aEscapedURI = aURI;
166 return NS_OK;
167 }
168
169 // nsITextToSubURI does charset convert plus uri escape
170 // This is needed to convert to a document charset which is needed to support existing browsers.
171 // But we eventually want to use UTF-8 instead of a document charset, then the code would be much simpler.
172 // See HTML 4.01 spec, "Appendix B.2.1 Non-ASCII characters in URI attribute values"
173 nsCOMPtr<nsITextToSubURI> textToSubURI;
174 nsAutoString uri(aURI); // in order to use FindCharInSet()
175 nsresult rv = NS_OK;
176
177 if (!mCharset.IsEmpty() && !IsASCII(uri)) {
178 textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
179 NS_ENSURE_SUCCESS(rv, rv);
180 }
181
182 int32_t start = 0;
183 int32_t end;
184 nsAutoString part;
185 nsXPIDLCString escapedURI;
186 aEscapedURI.Truncate(0);
187
188 // Loop and escape parts by avoiding escaping reserved characters
189 // (and '%', '#', as well as '[' and ']' for IPv6 address literals).
190 while ((end = uri.FindCharInSet("%#;/?:@&=+$,[]", start)) != -1) {
191 part = Substring(aURI, start, (end-start));
192 if (textToSubURI && !IsASCII(part)) {
193 rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
194 NS_ENSURE_SUCCESS(rv, rv);
195 }
196 else {
197 escapedURI.Adopt(nsEscape(NS_ConvertUTF16toUTF8(part).get(), url_Path));
198 }
199 AppendASCIItoUTF16(escapedURI, aEscapedURI);
200
201 // Append a reserved character without escaping.
202 part = Substring(aURI, end, 1);
203 aEscapedURI.Append(part);
204 start = end + 1;
205 }
206
207 if (start < (int32_t) aURI.Length()) {
208 // Escape the remaining part.
209 part = Substring(aURI, start, aURI.Length()-start);
210 if (textToSubURI) {
211 rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
212 NS_ENSURE_SUCCESS(rv, rv);
213 }
214 else {
215 escapedURI.Adopt(nsEscape(NS_ConvertUTF16toUTF8(part).get(), url_Path));
216 }
217 AppendASCIItoUTF16(escapedURI, aEscapedURI);
218 }
219
220 return rv;
221 }
222
223 void
224 nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
225 nsIContent *aOriginalElement,
226 nsAString& aTagPrefix,
227 const nsAString& aTagNamespaceURI,
228 nsIAtom* aTagName,
229 nsAString& aStr,
230 uint32_t aSkipAttr,
231 bool aAddNSAttr)
232 {
233 nsresult rv;
234 uint32_t index, count;
235 nsAutoString prefixStr, uriStr, valueStr;
236 nsAutoString xmlnsStr;
237 xmlnsStr.AssignLiteral(kXMLNS);
238
239 int32_t contentNamespaceID = aContent->GetNameSpaceID();
240
241 // this method is not called by nsHTMLContentSerializer
242 // so we don't have to check HTML element, just XHTML
243
244 if (mIsCopying && kNameSpaceID_XHTML == contentNamespaceID) {
245
246 // Need to keep track of OL and LI elements in order to get ordinal number
247 // for the LI.
248 if (aTagName == nsGkAtoms::ol) {
249 // We are copying and current node is an OL;
250 // Store its start attribute value in olState->startVal.
251 nsAutoString start;
252 int32_t startAttrVal = 0;
253 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::start, start);
254 if (!start.IsEmpty()) {
255 nsresult rv = NS_OK;
256 startAttrVal = start.ToInteger(&rv);
257 //If OL has "start" attribute, first LI element has to start with that value
258 //Therefore subtracting 1 as all the LI elements are incrementing it before using it;
259 //In failure of ToInteger(), default StartAttrValue to 0.
260 if (NS_SUCCEEDED(rv))
261 --startAttrVal;
262 else
263 startAttrVal = 0;
264 }
265 olState state (startAttrVal, true);
266 mOLStateStack.AppendElement(state);
267 }
268 else if (aTagName == nsGkAtoms::li) {
269 mIsFirstChildOfOL = IsFirstChildOfOL(aOriginalElement);
270 if (mIsFirstChildOfOL) {
271 // If OL is parent of this LI, serialize attributes in different manner.
272 SerializeLIValueAttribute(aContent, aStr);
273 }
274 }
275 }
276
277 // If we had to add a new namespace declaration, serialize
278 // and push it on the namespace stack
279 if (aAddNSAttr) {
280 if (aTagPrefix.IsEmpty()) {
281 // Serialize default namespace decl
282 SerializeAttr(EmptyString(), xmlnsStr, aTagNamespaceURI, aStr, true);
283 } else {
284 // Serialize namespace decl
285 SerializeAttr(xmlnsStr, aTagPrefix, aTagNamespaceURI, aStr, true);
286 }
287 PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement);
288 }
289
290 NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
291
292 count = aContent->GetAttrCount();
293
294 // Now serialize each of the attributes
295 // XXX Unfortunately we need a namespace manager to get
296 // attribute URIs.
297 for (index = 0; index < count; index++) {
298
299 if (aSkipAttr == index) {
300 continue;
301 }
302
303 const nsAttrName* name = aContent->GetAttrNameAt(index);
304 int32_t namespaceID = name->NamespaceID();
305 nsIAtom* attrName = name->LocalName();
306 nsIAtom* attrPrefix = name->GetPrefix();
307
308 // Filter out any attribute starting with [-|_]moz
309 nsDependentAtomString attrNameStr(attrName);
310 if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
311 StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
312 continue;
313 }
314
315 if (attrPrefix) {
316 attrPrefix->ToString(prefixStr);
317 }
318 else {
319 prefixStr.Truncate();
320 }
321
322 bool addNSAttr = false;
323 if (kNameSpaceID_XMLNS != namespaceID) {
324 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr);
325 addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true);
326 }
327
328 aContent->GetAttr(namespaceID, attrName, valueStr);
329
330 nsDependentAtomString nameStr(attrName);
331 bool isJS = false;
332
333 if (kNameSpaceID_XHTML == contentNamespaceID) {
334 //
335 // Filter out special case of <br type="_moz"> or <br _moz*>,
336 // used by the editor. Bug 16988. Yuck.
337 //
338 if (namespaceID == kNameSpaceID_None && aTagName == nsGkAtoms::br && attrName == nsGkAtoms::type
339 && StringBeginsWith(valueStr, _mozStr)) {
340 continue;
341 }
342
343 if (mIsCopying && mIsFirstChildOfOL && (aTagName == nsGkAtoms::li)
344 && (attrName == nsGkAtoms::value)) {
345 // This is handled separately in SerializeLIValueAttribute()
346 continue;
347 }
348
349 isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
350
351 if (namespaceID == kNameSpaceID_None &&
352 ((attrName == nsGkAtoms::href) ||
353 (attrName == nsGkAtoms::src))) {
354 // Make all links absolute when converting only the selection:
355 if (mFlags & nsIDocumentEncoder::OutputAbsoluteLinks) {
356 // Would be nice to handle OBJECT and APPLET tags,
357 // but that gets more complicated since we have to
358 // search the tag list for CODEBASE as well.
359 // For now, just leave them relative.
360 nsCOMPtr<nsIURI> uri = aContent->GetBaseURI();
361 if (uri) {
362 nsAutoString absURI;
363 rv = NS_MakeAbsoluteURI(absURI, valueStr, uri);
364 if (NS_SUCCEEDED(rv)) {
365 valueStr = absURI;
366 }
367 }
368 }
369 // Need to escape URI.
370 nsAutoString tempURI(valueStr);
371 if (!isJS && NS_FAILED(EscapeURI(aContent, tempURI, valueStr)))
372 valueStr = tempURI;
373 }
374
375 if (mRewriteEncodingDeclaration && aTagName == nsGkAtoms::meta &&
376 attrName == nsGkAtoms::content) {
377 // If we're serializing a <meta http-equiv="content-type">,
378 // use the proper value, rather than what's in the document.
379 nsAutoString header;
380 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
381 if (header.LowerCaseEqualsLiteral("content-type")) {
382 valueStr = NS_LITERAL_STRING("text/html; charset=") +
383 NS_ConvertASCIItoUTF16(mCharset);
384 }
385 }
386
387 // Expand shorthand attribute.
388 if (namespaceID == kNameSpaceID_None && IsShorthandAttr(attrName, aTagName) && valueStr.IsEmpty()) {
389 valueStr = nameStr;
390 }
391 }
392 else {
393 isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
394 }
395
396 SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS);
397
398 if (addNSAttr) {
399 NS_ASSERTION(!prefixStr.IsEmpty(),
400 "Namespaced attributes must have a prefix");
401 SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, true);
402 PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement);
403 }
404 }
405 }
406
407
408 void
409 nsXHTMLContentSerializer::AppendEndOfElementStart(nsIContent *aOriginalElement,
410 nsIAtom * aName,
411 int32_t aNamespaceID,
412 nsAString& aStr)
413 {
414 // this method is not called by nsHTMLContentSerializer
415 // so we don't have to check HTML element, just XHTML
416 NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
417
418 if (kNameSpaceID_XHTML != aNamespaceID) {
419 nsXMLContentSerializer::AppendEndOfElementStart(aOriginalElement, aName,
420 aNamespaceID, aStr);
421 return;
422 }
423
424 nsIContent* content = aOriginalElement;
425
426 // for non empty elements, even if they are not a container, we always
427 // serialize their content, because the XHTML element could contain non XHTML
428 // nodes useful in some context, like in an XSLT stylesheet
429 if (HasNoChildren(content)) {
430
431 nsIParserService* parserService = nsContentUtils::GetParserService();
432
433 if (parserService) {
434 bool isContainer;
435 parserService->
436 IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(aName),
437 isContainer);
438 if (!isContainer) {
439 // for backward compatibility with HTML 4 user agents
440 // only non-container HTML elements can be closed immediatly,
441 // and a space is added before />
442 AppendToString(NS_LITERAL_STRING(" />"), aStr);
443 return;
444 }
445 }
446 }
447 AppendToString(kGreaterThan, aStr);
448 }
449
450 void
451 nsXHTMLContentSerializer::AfterElementStart(nsIContent * aContent,
452 nsIContent *aOriginalElement,
453 nsAString& aStr)
454 {
455 nsIAtom *name = aContent->Tag();
456 if (aContent->GetNameSpaceID() == kNameSpaceID_XHTML &&
457 mRewriteEncodingDeclaration &&
458 name == nsGkAtoms::head) {
459
460 // Check if there already are any content-type meta children.
461 // If there are, they will be modified to use the correct charset.
462 // If there aren't, we'll insert one here.
463 bool hasMeta = false;
464 for (nsIContent* child = aContent->GetFirstChild();
465 child;
466 child = child->GetNextSibling()) {
467 if (child->IsHTML(nsGkAtoms::meta) &&
468 child->HasAttr(kNameSpaceID_None, nsGkAtoms::content)) {
469 nsAutoString header;
470 child->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
471
472 if (header.LowerCaseEqualsLiteral("content-type")) {
473 hasMeta = true;
474 break;
475 }
476 }
477 }
478
479 if (!hasMeta) {
480 AppendNewLineToString(aStr);
481 if (mDoFormat) {
482 AppendIndentation(aStr);
483 }
484 AppendToString(NS_LITERAL_STRING("<meta http-equiv=\"content-type\""),
485 aStr);
486 AppendToString(NS_LITERAL_STRING(" content=\"text/html; charset="), aStr);
487 AppendToString(NS_ConvertASCIItoUTF16(mCharset), aStr);
488 if (mIsHTMLSerializer)
489 AppendToString(NS_LITERAL_STRING("\">"), aStr);
490 else
491 AppendToString(NS_LITERAL_STRING("\" />"), aStr);
492 }
493 }
494 }
495
496 void
497 nsXHTMLContentSerializer::AfterElementEnd(nsIContent * aContent,
498 nsAString& aStr)
499 {
500 NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
501
502 int32_t namespaceID = aContent->GetNameSpaceID();
503 nsIAtom *name = aContent->Tag();
504
505 // this method is not called by nsHTMLContentSerializer
506 // so we don't have to check HTML element, just XHTML
507 if (kNameSpaceID_XHTML == namespaceID && name == nsGkAtoms::body) {
508 --mInBody;
509 }
510 }
511
512
513 NS_IMETHODIMP
514 nsXHTMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument,
515 nsAString& aStr)
516 {
517 if (!mBodyOnly)
518 return nsXMLContentSerializer::AppendDocumentStart(aDocument, aStr);
519
520 return NS_OK;
521 }
522
523 bool
524 nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent,
525 bool & aForceFormat,
526 nsAString& aStr)
527 {
528 // The _moz_dirty attribute is emitted by the editor to
529 // indicate that this element should be pretty printed
530 // even if we're not in pretty printing mode
531 aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
532 aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
533
534 nsIAtom *name = aContent->Tag();
535 int32_t namespaceID = aContent->GetNameSpaceID();
536
537 if (namespaceID == kNameSpaceID_XHTML) {
538 if (name == nsGkAtoms::br && mPreLevel > 0 &&
539 (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre)) {
540 AppendNewLineToString(aStr);
541 return false;
542 }
543
544 if (name == nsGkAtoms::body) {
545 ++mInBody;
546 }
547 }
548 return true;
549 }
550
551 bool
552 nsXHTMLContentSerializer::CheckElementEnd(nsIContent * aContent,
553 bool & aForceFormat,
554 nsAString& aStr)
555 {
556 NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
557
558 aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
559 aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
560
561 nsIAtom *name = aContent->Tag();
562 int32_t namespaceID = aContent->GetNameSpaceID();
563
564 // this method is not called by nsHTMLContentSerializer
565 // so we don't have to check HTML element, just XHTML
566 if (namespaceID == kNameSpaceID_XHTML) {
567 if (mIsCopying && name == nsGkAtoms::ol) {
568 NS_ASSERTION((!mOLStateStack.IsEmpty()), "Cannot have an empty OL Stack");
569 /* Though at this point we must always have an state to be deleted as all
570 the OL opening tags are supposed to push an olState object to the stack*/
571 if (!mOLStateStack.IsEmpty()) {
572 mOLStateStack.RemoveElementAt(mOLStateStack.Length() -1);
573 }
574 }
575
576 if (HasNoChildren(aContent)) {
577 nsIParserService* parserService = nsContentUtils::GetParserService();
578
579 if (parserService) {
580 bool isContainer;
581
582 parserService->
583 IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(name),
584 isContainer);
585 if (!isContainer) {
586 // non-container HTML elements are already closed,
587 // see AppendEndOfElementStart
588 return false;
589 }
590 }
591 }
592 // for backward compatibility with old HTML user agents,
593 // empty elements should have an ending tag, so we mustn't call
594 // nsXMLContentSerializer::CheckElementEnd
595 return true;
596 }
597
598 bool dummyFormat;
599 return nsXMLContentSerializer::CheckElementEnd(aContent, dummyFormat, aStr);
600 }
601
602 void
603 nsXHTMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr,
604 nsAString& aOutputStr)
605 {
606 if (mBodyOnly && !mInBody) {
607 return;
608 }
609
610 if (mDisableEntityEncoding) {
611 aOutputStr.Append(aStr);
612 return;
613 }
614
615 nsXMLContentSerializer::AppendAndTranslateEntities(aStr, aOutputStr);
616 }
617
618 bool
619 nsXHTMLContentSerializer::IsShorthandAttr(const nsIAtom* aAttrName,
620 const nsIAtom* aElementName)
621 {
622 // checked
623 if ((aAttrName == nsGkAtoms::checked) &&
624 (aElementName == nsGkAtoms::input)) {
625 return true;
626 }
627
628 // compact
629 if ((aAttrName == nsGkAtoms::compact) &&
630 (aElementName == nsGkAtoms::dir ||
631 aElementName == nsGkAtoms::dl ||
632 aElementName == nsGkAtoms::menu ||
633 aElementName == nsGkAtoms::ol ||
634 aElementName == nsGkAtoms::ul)) {
635 return true;
636 }
637
638 // declare
639 if ((aAttrName == nsGkAtoms::declare) &&
640 (aElementName == nsGkAtoms::object)) {
641 return true;
642 }
643
644 // defer
645 if ((aAttrName == nsGkAtoms::defer) &&
646 (aElementName == nsGkAtoms::script)) {
647 return true;
648 }
649
650 // disabled
651 if ((aAttrName == nsGkAtoms::disabled) &&
652 (aElementName == nsGkAtoms::button ||
653 aElementName == nsGkAtoms::input ||
654 aElementName == nsGkAtoms::optgroup ||
655 aElementName == nsGkAtoms::option ||
656 aElementName == nsGkAtoms::select ||
657 aElementName == nsGkAtoms::textarea)) {
658 return true;
659 }
660
661 // ismap
662 if ((aAttrName == nsGkAtoms::ismap) &&
663 (aElementName == nsGkAtoms::img ||
664 aElementName == nsGkAtoms::input)) {
665 return true;
666 }
667
668 // multiple
669 if ((aAttrName == nsGkAtoms::multiple) &&
670 (aElementName == nsGkAtoms::select)) {
671 return true;
672 }
673
674 // noresize
675 if ((aAttrName == nsGkAtoms::noresize) &&
676 (aElementName == nsGkAtoms::frame)) {
677 return true;
678 }
679
680 // noshade
681 if ((aAttrName == nsGkAtoms::noshade) &&
682 (aElementName == nsGkAtoms::hr)) {
683 return true;
684 }
685
686 // nowrap
687 if ((aAttrName == nsGkAtoms::nowrap) &&
688 (aElementName == nsGkAtoms::td ||
689 aElementName == nsGkAtoms::th)) {
690 return true;
691 }
692
693 // readonly
694 if ((aAttrName == nsGkAtoms::readonly) &&
695 (aElementName == nsGkAtoms::input ||
696 aElementName == nsGkAtoms::textarea)) {
697 return true;
698 }
699
700 // selected
701 if ((aAttrName == nsGkAtoms::selected) &&
702 (aElementName == nsGkAtoms::option)) {
703 return true;
704 }
705
706 // autoplay and controls
707 if ((aElementName == nsGkAtoms::video || aElementName == nsGkAtoms::audio) &&
708 (aAttrName == nsGkAtoms::autoplay || aAttrName == nsGkAtoms::muted ||
709 aAttrName == nsGkAtoms::controls)) {
710 return true;
711 }
712
713 return false;
714 }
715
716 bool
717 nsXHTMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID, nsIAtom* aName)
718 {
719
720 if (aNamespaceID != kNameSpaceID_XHTML) {
721 return mAddSpace;
722 }
723
724 if (aName == nsGkAtoms::title ||
725 aName == nsGkAtoms::meta ||
726 aName == nsGkAtoms::link ||
727 aName == nsGkAtoms::style ||
728 aName == nsGkAtoms::select ||
729 aName == nsGkAtoms::option ||
730 aName == nsGkAtoms::script ||
731 aName == nsGkAtoms::html) {
732 return true;
733 }
734 else {
735 nsIParserService* parserService = nsContentUtils::GetParserService();
736
737 if (parserService) {
738 bool res;
739 parserService->
740 IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res);
741 return res;
742 }
743 }
744
745 return mAddSpace;
746 }
747
748 bool
749 nsXHTMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID, nsIAtom* aName)
750 {
751
752 if (aNamespaceID != kNameSpaceID_XHTML) {
753 return false;
754 }
755
756 if ((aName == nsGkAtoms::html) ||
757 (aName == nsGkAtoms::head) ||
758 (aName == nsGkAtoms::body) ||
759 (aName == nsGkAtoms::ul) ||
760 (aName == nsGkAtoms::ol) ||
761 (aName == nsGkAtoms::dl) ||
762 (aName == nsGkAtoms::table) ||
763 (aName == nsGkAtoms::tbody) ||
764 (aName == nsGkAtoms::tr) ||
765 (aName == nsGkAtoms::br) ||
766 (aName == nsGkAtoms::meta) ||
767 (aName == nsGkAtoms::link) ||
768 (aName == nsGkAtoms::script) ||
769 (aName == nsGkAtoms::select) ||
770 (aName == nsGkAtoms::map) ||
771 (aName == nsGkAtoms::area) ||
772 (aName == nsGkAtoms::style)) {
773 return true;
774 }
775
776 return false;
777 }
778
779 bool
780 nsXHTMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID, nsIAtom* aName)
781 {
782
783 if (aNamespaceID != kNameSpaceID_XHTML) {
784 return false;
785 }
786
787 if ((aName == nsGkAtoms::html) ||
788 (aName == nsGkAtoms::head) ||
789 (aName == nsGkAtoms::body) ||
790 (aName == nsGkAtoms::ul) ||
791 (aName == nsGkAtoms::ol) ||
792 (aName == nsGkAtoms::dl) ||
793 (aName == nsGkAtoms::select) ||
794 (aName == nsGkAtoms::table) ||
795 (aName == nsGkAtoms::tbody)) {
796 return true;
797 }
798 return false;
799 }
800
801 bool
802 nsXHTMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID, nsIAtom* aName)
803 {
804
805 if (aNamespaceID != kNameSpaceID_XHTML) {
806 return false;
807 }
808
809 if ((aName == nsGkAtoms::html) ||
810 (aName == nsGkAtoms::head) ||
811 (aName == nsGkAtoms::body) ||
812 (aName == nsGkAtoms::tr) ||
813 (aName == nsGkAtoms::th) ||
814 (aName == nsGkAtoms::td) ||
815 (aName == nsGkAtoms::pre) ||
816 (aName == nsGkAtoms::title) ||
817 (aName == nsGkAtoms::li) ||
818 (aName == nsGkAtoms::dt) ||
819 (aName == nsGkAtoms::dd) ||
820 (aName == nsGkAtoms::blockquote) ||
821 (aName == nsGkAtoms::select) ||
822 (aName == nsGkAtoms::option) ||
823 (aName == nsGkAtoms::p) ||
824 (aName == nsGkAtoms::map) ||
825 (aName == nsGkAtoms::div)) {
826 return true;
827 }
828 else {
829 nsIParserService* parserService = nsContentUtils::GetParserService();
830
831 if (parserService) {
832 bool res;
833 parserService->
834 IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res);
835 return res;
836 }
837 }
838
839 return false;
840 }
841
842
843 void
844 nsXHTMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode)
845 {
846
847 if (aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
848 return;
849 }
850
851 nsIAtom *name = aNode->Tag();
852
853 if (name == nsGkAtoms::pre ||
854 name == nsGkAtoms::script ||
855 name == nsGkAtoms::style ||
856 name == nsGkAtoms::noscript ||
857 name == nsGkAtoms::noframes
858 ) {
859 mPreLevel++;
860 }
861 }
862
863 void
864 nsXHTMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode)
865 {
866 if (aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
867 return;
868 }
869
870 nsIAtom *name = aNode->Tag();
871 if (name == nsGkAtoms::pre ||
872 name == nsGkAtoms::script ||
873 name == nsGkAtoms::style ||
874 name == nsGkAtoms::noscript ||
875 name == nsGkAtoms::noframes
876 ) {
877 --mPreLevel;
878 }
879 }
880
881 void
882 nsXHTMLContentSerializer::SerializeLIValueAttribute(nsIContent* aElement,
883 nsAString& aStr)
884 {
885 // We are copying and we are at the "first" LI node of OL in selected range.
886 // It may not be the first LI child of OL but it's first in the selected range.
887 // Note that we get into this condition only once per a OL.
888 bool found = false;
889 nsCOMPtr<nsIDOMNode> currNode = do_QueryInterface(aElement);
890 nsAutoString valueStr;
891
892 olState state (0, false);
893
894 if (!mOLStateStack.IsEmpty()) {
895 state = mOLStateStack[mOLStateStack.Length()-1];
896 // isFirstListItem should be true only before the serialization of the
897 // first item in the list.
898 state.isFirstListItem = false;
899 mOLStateStack[mOLStateStack.Length()-1] = state;
900 }
901
902 int32_t startVal = state.startVal;
903 int32_t offset = 0;
904
905 // Traverse previous siblings until we find one with "value" attribute.
906 // offset keeps track of how many previous siblings we had tocurrNode traverse.
907 while (currNode && !found) {
908 nsCOMPtr<nsIDOMElement> currElement = do_QueryInterface(currNode);
909 // currElement may be null if it were a text node.
910 if (currElement) {
911 nsAutoString tagName;
912 currElement->GetTagName(tagName);
913 if (tagName.LowerCaseEqualsLiteral("li")) {
914 currElement->GetAttribute(NS_LITERAL_STRING("value"), valueStr);
915 if (valueStr.IsEmpty())
916 offset++;
917 else {
918 found = true;
919 nsresult rv = NS_OK;
920 startVal = valueStr.ToInteger(&rv);
921 }
922 }
923 }
924 nsCOMPtr<nsIDOMNode> tmp;
925 currNode->GetPreviousSibling(getter_AddRefs(tmp));
926 currNode.swap(tmp);
927 }
928 // If LI was not having "value", Set the "value" attribute for it.
929 // Note that We are at the first LI in the selected range of OL.
930 if (offset == 0 && found) {
931 // offset = 0 => LI itself has the value attribute and we did not need to traverse back.
932 // Just serialize value attribute like other tags.
933 SerializeAttr(EmptyString(), NS_LITERAL_STRING("value"), valueStr, aStr, false);
934 }
935 else if (offset == 1 && !found) {
936 /*(offset = 1 && !found) means either LI is the first child node of OL
937 and LI is not having "value" attribute.
938 In that case we would not like to set "value" attribute to reduce the changes.
939 */
940 //do nothing...
941 }
942 else if (offset > 0) {
943 // Set value attribute.
944 nsAutoString valueStr;
945
946 //As serializer needs to use this valueAttr we are creating here,
947 valueStr.AppendInt(startVal + offset);
948 SerializeAttr(EmptyString(), NS_LITERAL_STRING("value"), valueStr, aStr, false);
949 }
950 }
951
952 bool
953 nsXHTMLContentSerializer::IsFirstChildOfOL(nsIContent* aElement)
954 {
955 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
956 nsAutoString parentName;
957
958 nsCOMPtr<nsIDOMNode> parentNode;
959 node->GetParentNode(getter_AddRefs(parentNode));
960 if (parentNode)
961 parentNode->GetNodeName(parentName);
962 else
963 return false;
964
965 if (parentName.LowerCaseEqualsLiteral("ol")) {
966
967 if (!mOLStateStack.IsEmpty()) {
968 olState state = mOLStateStack[mOLStateStack.Length()-1];
969 if (state.isFirstListItem)
970 return true;
971 }
972
973 return false;
974 }
975 else
976 return false;
977 }
978
979 bool
980 nsXHTMLContentSerializer::HasNoChildren(nsIContent * aContent) {
981
982 for (nsIContent* child = aContent->GetFirstChild();
983 child;
984 child = child->GetNextSibling()) {
985
986 if (!child->IsNodeOfType(nsINode::eTEXT))
987 return false;
988
989 if (child->TextLength())
990 return false;
991 }
992
993 return true;
994 }

mercurial