Bumping manifests a=b2g-bump
[gecko.git] / dom / base / nsXHTMLContentSerializer.cpp
blobfd78254bdb99c7a2929035f2b2f2cbd336111db1
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/. */
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.
13 #include "nsXHTMLContentSerializer.h"
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 #include "nsComputedDOMStyle.h"
37 #include "mozilla/dom/Element.h"
39 static const int32_t kLongLineLen = 128;
41 #define kXMLNS "xmlns"
43 nsresult NS_NewXHTMLContentSerializer(nsIContentSerializer** aSerializer)
45 nsXHTMLContentSerializer* it = new nsXHTMLContentSerializer();
46 if (!it) {
47 return NS_ERROR_OUT_OF_MEMORY;
50 return CallQueryInterface(it, aSerializer);
53 nsXHTMLContentSerializer::nsXHTMLContentSerializer()
54 : mIsHTMLSerializer(false)
58 nsXHTMLContentSerializer::~nsXHTMLContentSerializer()
60 NS_ASSERTION(mOLStateStack.IsEmpty(), "Expected OL State stack to be empty");
63 NS_IMETHODIMP
64 nsXHTMLContentSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn,
65 const char* aCharSet, bool aIsCopying,
66 bool aRewriteEncodingDeclaration)
68 // The previous version of the HTML serializer did implicit wrapping
69 // when there is no flags, so we keep wrapping in order to keep
70 // compatibility with the existing calling code
71 // XXXLJ perhaps should we remove this default settings later ?
72 if (aFlags & nsIDocumentEncoder::OutputFormatted ) {
73 aFlags = aFlags | nsIDocumentEncoder::OutputWrap;
76 nsresult rv;
77 rv = nsXMLContentSerializer::Init(aFlags, aWrapColumn, aCharSet, aIsCopying, aRewriteEncodingDeclaration);
78 NS_ENSURE_SUCCESS(rv, rv);
80 mRewriteEncodingDeclaration = aRewriteEncodingDeclaration;
81 mIsCopying = aIsCopying;
82 mIsFirstChildOfOL = false;
83 mInBody = 0;
84 mDisableEntityEncoding = 0;
85 mBodyOnly = (mFlags & nsIDocumentEncoder::OutputBodyOnly) ? true
86 : false;
88 // set up entity converter if we are going to need it
89 if (mFlags & nsIDocumentEncoder::OutputEncodeW3CEntities) {
90 mEntityConverter = do_CreateInstance(NS_ENTITYCONVERTER_CONTRACTID);
92 return NS_OK;
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)
102 uint32_t start=0;
103 uint32_t theLen = text.Length();
104 bool rv = false;
105 aLastNewlineOffset = kNotFound;
106 for (start = 0; start < theLen; ) {
107 int32_t eol = text.FindChar('\n', start);
108 if (eol < 0) {
109 eol = text.Length();
111 else {
112 aLastNewlineOffset = eol;
114 if (int32_t(eol - start) > kLongLineLen)
115 rv = true;
116 start = eol + 1;
118 return rv;
121 NS_IMETHODIMP
122 nsXHTMLContentSerializer::AppendText(nsIContent* aText,
123 int32_t aStartOffset,
124 int32_t aEndOffset,
125 nsAString& aStr)
127 NS_ENSURE_ARG(aText);
129 nsAutoString data;
130 nsresult rv;
132 rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true);
133 if (NS_FAILED(rv))
134 return NS_ERROR_FAILURE;
136 if (mDoRaw || PreLevel() > 0) {
137 AppendToStringConvertLF(data, aStr);
139 else if (mDoFormat) {
140 AppendToStringFormatedWrapped(data, aStr);
142 else if (mDoWrap) {
143 AppendToStringWrapped(data, aStr);
145 else {
146 int32_t lastNewlineOffset = kNotFound;
147 if (HasLongLines(data, lastNewlineOffset)) {
148 // We have long lines, rewrap
149 mDoWrap = true;
150 AppendToStringWrapped(data, aStr);
151 mDoWrap = false;
153 else {
154 AppendToStringConvertLF(data, aStr);
158 return NS_OK;
161 nsresult
162 nsXHTMLContentSerializer::EscapeURI(nsIContent* aContent, const nsAString& aURI, nsAString& aEscapedURI)
164 // URL escape %xx cannot be used in JS.
165 // No escaping if the scheme is 'javascript'.
166 if (IsJavaScript(aContent, nsGkAtoms::href, kNameSpaceID_None, aURI)) {
167 aEscapedURI = aURI;
168 return NS_OK;
171 // nsITextToSubURI does charset convert plus uri escape
172 // This is needed to convert to a document charset which is needed to support existing browsers.
173 // But we eventually want to use UTF-8 instead of a document charset, then the code would be much simpler.
174 // See HTML 4.01 spec, "Appendix B.2.1 Non-ASCII characters in URI attribute values"
175 nsCOMPtr<nsITextToSubURI> textToSubURI;
176 nsAutoString uri(aURI); // in order to use FindCharInSet()
177 nsresult rv = NS_OK;
179 if (!mCharset.IsEmpty() && !IsASCII(uri)) {
180 textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
181 NS_ENSURE_SUCCESS(rv, rv);
184 int32_t start = 0;
185 int32_t end;
186 nsAutoString part;
187 nsXPIDLCString escapedURI;
188 aEscapedURI.Truncate(0);
190 // Loop and escape parts by avoiding escaping reserved characters
191 // (and '%', '#', as well as '[' and ']' for IPv6 address literals).
192 while ((end = uri.FindCharInSet("%#;/?:@&=+$,[]", start)) != -1) {
193 part = Substring(aURI, start, (end-start));
194 if (textToSubURI && !IsASCII(part)) {
195 rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
196 NS_ENSURE_SUCCESS(rv, rv);
198 else {
199 escapedURI.Adopt(nsEscape(NS_ConvertUTF16toUTF8(part).get(), url_Path));
201 AppendASCIItoUTF16(escapedURI, aEscapedURI);
203 // Append a reserved character without escaping.
204 part = Substring(aURI, end, 1);
205 aEscapedURI.Append(part);
206 start = end + 1;
209 if (start < (int32_t) aURI.Length()) {
210 // Escape the remaining part.
211 part = Substring(aURI, start, aURI.Length()-start);
212 if (textToSubURI) {
213 rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
214 NS_ENSURE_SUCCESS(rv, rv);
216 else {
217 escapedURI.Adopt(nsEscape(NS_ConvertUTF16toUTF8(part).get(), url_Path));
219 AppendASCIItoUTF16(escapedURI, aEscapedURI);
222 return rv;
225 void
226 nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
227 nsIContent *aOriginalElement,
228 nsAString& aTagPrefix,
229 const nsAString& aTagNamespaceURI,
230 nsIAtom* aTagName,
231 nsAString& aStr,
232 uint32_t aSkipAttr,
233 bool aAddNSAttr)
235 nsresult rv;
236 uint32_t index, count;
237 nsAutoString prefixStr, uriStr, valueStr;
238 nsAutoString xmlnsStr;
239 xmlnsStr.AssignLiteral(kXMLNS);
241 int32_t contentNamespaceID = aContent->GetNameSpaceID();
243 // this method is not called by nsHTMLContentSerializer
244 // so we don't have to check HTML element, just XHTML
246 if (mIsCopying && kNameSpaceID_XHTML == contentNamespaceID) {
248 // Need to keep track of OL and LI elements in order to get ordinal number
249 // for the LI.
250 if (aTagName == nsGkAtoms::ol) {
251 // We are copying and current node is an OL;
252 // Store its start attribute value in olState->startVal.
253 nsAutoString start;
254 int32_t startAttrVal = 0;
255 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::start, start);
256 if (!start.IsEmpty()) {
257 nsresult rv = NS_OK;
258 startAttrVal = start.ToInteger(&rv);
259 //If OL has "start" attribute, first LI element has to start with that value
260 //Therefore subtracting 1 as all the LI elements are incrementing it before using it;
261 //In failure of ToInteger(), default StartAttrValue to 0.
262 if (NS_SUCCEEDED(rv))
263 --startAttrVal;
264 else
265 startAttrVal = 0;
267 olState state (startAttrVal, true);
268 mOLStateStack.AppendElement(state);
270 else if (aTagName == nsGkAtoms::li) {
271 mIsFirstChildOfOL = IsFirstChildOfOL(aOriginalElement);
272 if (mIsFirstChildOfOL) {
273 // If OL is parent of this LI, serialize attributes in different manner.
274 SerializeLIValueAttribute(aContent, aStr);
279 // If we had to add a new namespace declaration, serialize
280 // and push it on the namespace stack
281 if (aAddNSAttr) {
282 if (aTagPrefix.IsEmpty()) {
283 // Serialize default namespace decl
284 SerializeAttr(EmptyString(), xmlnsStr, aTagNamespaceURI, aStr, true);
285 } else {
286 // Serialize namespace decl
287 SerializeAttr(xmlnsStr, aTagPrefix, aTagNamespaceURI, aStr, true);
289 PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement);
292 NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
294 count = aContent->GetAttrCount();
296 // Now serialize each of the attributes
297 // XXX Unfortunately we need a namespace manager to get
298 // attribute URIs.
299 for (index = 0; index < count; index++) {
301 if (aSkipAttr == index) {
302 continue;
305 const nsAttrName* name = aContent->GetAttrNameAt(index);
306 int32_t namespaceID = name->NamespaceID();
307 nsIAtom* attrName = name->LocalName();
308 nsIAtom* attrPrefix = name->GetPrefix();
310 // Filter out any attribute starting with [-|_]moz
311 nsDependentAtomString attrNameStr(attrName);
312 if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
313 StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
314 continue;
317 if (attrPrefix) {
318 attrPrefix->ToString(prefixStr);
320 else {
321 prefixStr.Truncate();
324 bool addNSAttr = false;
325 if (kNameSpaceID_XMLNS != namespaceID) {
326 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr);
327 addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true);
330 aContent->GetAttr(namespaceID, attrName, valueStr);
332 nsDependentAtomString nameStr(attrName);
333 bool isJS = false;
335 if (kNameSpaceID_XHTML == contentNamespaceID) {
337 // Filter out special case of <br type="_moz"> or <br _moz*>,
338 // used by the editor. Bug 16988. Yuck.
340 if (namespaceID == kNameSpaceID_None && aTagName == nsGkAtoms::br && attrName == nsGkAtoms::type
341 && StringBeginsWith(valueStr, _mozStr)) {
342 continue;
345 if (mIsCopying && mIsFirstChildOfOL && (aTagName == nsGkAtoms::li)
346 && (attrName == nsGkAtoms::value)) {
347 // This is handled separately in SerializeLIValueAttribute()
348 continue;
351 isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
353 if (namespaceID == kNameSpaceID_None &&
354 ((attrName == nsGkAtoms::href) ||
355 (attrName == nsGkAtoms::src))) {
356 // Make all links absolute when converting only the selection:
357 if (mFlags & nsIDocumentEncoder::OutputAbsoluteLinks) {
358 // Would be nice to handle OBJECT and APPLET tags,
359 // but that gets more complicated since we have to
360 // search the tag list for CODEBASE as well.
361 // For now, just leave them relative.
362 nsCOMPtr<nsIURI> uri = aContent->GetBaseURI();
363 if (uri) {
364 nsAutoString absURI;
365 rv = NS_MakeAbsoluteURI(absURI, valueStr, uri);
366 if (NS_SUCCEEDED(rv)) {
367 valueStr = absURI;
371 // Need to escape URI.
372 nsAutoString tempURI(valueStr);
373 if (!isJS && NS_FAILED(EscapeURI(aContent, tempURI, valueStr)))
374 valueStr = tempURI;
377 if (mRewriteEncodingDeclaration && aTagName == nsGkAtoms::meta &&
378 attrName == nsGkAtoms::content) {
379 // If we're serializing a <meta http-equiv="content-type">,
380 // use the proper value, rather than what's in the document.
381 nsAutoString header;
382 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
383 if (header.LowerCaseEqualsLiteral("content-type")) {
384 valueStr = NS_LITERAL_STRING("text/html; charset=") +
385 NS_ConvertASCIItoUTF16(mCharset);
389 // Expand shorthand attribute.
390 if (namespaceID == kNameSpaceID_None && IsShorthandAttr(attrName, aTagName) && valueStr.IsEmpty()) {
391 valueStr = nameStr;
394 else {
395 isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
398 SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS);
400 if (addNSAttr) {
401 NS_ASSERTION(!prefixStr.IsEmpty(),
402 "Namespaced attributes must have a prefix");
403 SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, true);
404 PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement);
410 void
411 nsXHTMLContentSerializer::AppendEndOfElementStart(nsIContent *aOriginalElement,
412 nsIAtom * aName,
413 int32_t aNamespaceID,
414 nsAString& aStr)
416 // this method is not called by nsHTMLContentSerializer
417 // so we don't have to check HTML element, just XHTML
418 NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
420 if (kNameSpaceID_XHTML != aNamespaceID) {
421 nsXMLContentSerializer::AppendEndOfElementStart(aOriginalElement, aName,
422 aNamespaceID, aStr);
423 return;
426 nsIContent* content = aOriginalElement;
428 // for non empty elements, even if they are not a container, we always
429 // serialize their content, because the XHTML element could contain non XHTML
430 // nodes useful in some context, like in an XSLT stylesheet
431 if (HasNoChildren(content)) {
433 nsIParserService* parserService = nsContentUtils::GetParserService();
435 if (parserService) {
436 bool isContainer;
437 parserService->
438 IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(aName),
439 isContainer);
440 if (!isContainer) {
441 // for backward compatibility with HTML 4 user agents
442 // only non-container HTML elements can be closed immediatly,
443 // and a space is added before />
444 AppendToString(NS_LITERAL_STRING(" />"), aStr);
445 return;
449 AppendToString(kGreaterThan, aStr);
452 void
453 nsXHTMLContentSerializer::AfterElementStart(nsIContent * aContent,
454 nsIContent *aOriginalElement,
455 nsAString& aStr)
457 nsIAtom *name = aContent->Tag();
458 if (aContent->GetNameSpaceID() == kNameSpaceID_XHTML &&
459 mRewriteEncodingDeclaration &&
460 name == nsGkAtoms::head) {
462 // Check if there already are any content-type meta children.
463 // If there are, they will be modified to use the correct charset.
464 // If there aren't, we'll insert one here.
465 bool hasMeta = false;
466 for (nsIContent* child = aContent->GetFirstChild();
467 child;
468 child = child->GetNextSibling()) {
469 if (child->IsHTML(nsGkAtoms::meta) &&
470 child->HasAttr(kNameSpaceID_None, nsGkAtoms::content)) {
471 nsAutoString header;
472 child->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
474 if (header.LowerCaseEqualsLiteral("content-type")) {
475 hasMeta = true;
476 break;
481 if (!hasMeta) {
482 AppendNewLineToString(aStr);
483 if (mDoFormat) {
484 AppendIndentation(aStr);
486 AppendToString(NS_LITERAL_STRING("<meta http-equiv=\"content-type\""),
487 aStr);
488 AppendToString(NS_LITERAL_STRING(" content=\"text/html; charset="), aStr);
489 AppendToString(NS_ConvertASCIItoUTF16(mCharset), aStr);
490 if (mIsHTMLSerializer)
491 AppendToString(NS_LITERAL_STRING("\">"), aStr);
492 else
493 AppendToString(NS_LITERAL_STRING("\" />"), aStr);
498 void
499 nsXHTMLContentSerializer::AfterElementEnd(nsIContent * aContent,
500 nsAString& aStr)
502 NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
504 int32_t namespaceID = aContent->GetNameSpaceID();
505 nsIAtom *name = aContent->Tag();
507 // this method is not called by nsHTMLContentSerializer
508 // so we don't have to check HTML element, just XHTML
509 if (kNameSpaceID_XHTML == namespaceID && name == nsGkAtoms::body) {
510 --mInBody;
515 NS_IMETHODIMP
516 nsXHTMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument,
517 nsAString& aStr)
519 if (!mBodyOnly)
520 return nsXMLContentSerializer::AppendDocumentStart(aDocument, aStr);
522 return NS_OK;
525 bool
526 nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent,
527 bool & aForceFormat,
528 nsAString& aStr)
530 // The _moz_dirty attribute is emitted by the editor to
531 // indicate that this element should be pretty printed
532 // even if we're not in pretty printing mode
533 aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
534 aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
536 nsIAtom *name = aContent->Tag();
537 int32_t namespaceID = aContent->GetNameSpaceID();
539 if (namespaceID == kNameSpaceID_XHTML) {
540 if (name == nsGkAtoms::br &&
541 (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre) &&
542 PreLevel() > 0) {
543 AppendNewLineToString(aStr);
544 return false;
547 if (name == nsGkAtoms::body) {
548 ++mInBody;
551 return true;
554 bool
555 nsXHTMLContentSerializer::CheckElementEnd(nsIContent * aContent,
556 bool & aForceFormat,
557 nsAString& aStr)
559 NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
561 aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
562 aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
564 nsIAtom *name = aContent->Tag();
565 int32_t namespaceID = aContent->GetNameSpaceID();
567 // this method is not called by nsHTMLContentSerializer
568 // so we don't have to check HTML element, just XHTML
569 if (namespaceID == kNameSpaceID_XHTML) {
570 if (mIsCopying && name == nsGkAtoms::ol) {
571 NS_ASSERTION((!mOLStateStack.IsEmpty()), "Cannot have an empty OL Stack");
572 /* Though at this point we must always have an state to be deleted as all
573 the OL opening tags are supposed to push an olState object to the stack*/
574 if (!mOLStateStack.IsEmpty()) {
575 mOLStateStack.RemoveElementAt(mOLStateStack.Length() -1);
579 if (HasNoChildren(aContent)) {
580 nsIParserService* parserService = nsContentUtils::GetParserService();
582 if (parserService) {
583 bool isContainer;
585 parserService->
586 IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(name),
587 isContainer);
588 if (!isContainer) {
589 // non-container HTML elements are already closed,
590 // see AppendEndOfElementStart
591 return false;
595 // for backward compatibility with old HTML user agents,
596 // empty elements should have an ending tag, so we mustn't call
597 // nsXMLContentSerializer::CheckElementEnd
598 return true;
601 bool dummyFormat;
602 return nsXMLContentSerializer::CheckElementEnd(aContent, dummyFormat, aStr);
605 void
606 nsXHTMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr,
607 nsAString& aOutputStr)
609 if (mBodyOnly && !mInBody) {
610 return;
613 if (mDisableEntityEncoding) {
614 aOutputStr.Append(aStr);
615 return;
618 nsXMLContentSerializer::AppendAndTranslateEntities(aStr, aOutputStr);
621 bool
622 nsXHTMLContentSerializer::IsShorthandAttr(const nsIAtom* aAttrName,
623 const nsIAtom* aElementName)
625 // checked
626 if ((aAttrName == nsGkAtoms::checked) &&
627 (aElementName == nsGkAtoms::input)) {
628 return true;
631 // compact
632 if ((aAttrName == nsGkAtoms::compact) &&
633 (aElementName == nsGkAtoms::dir ||
634 aElementName == nsGkAtoms::dl ||
635 aElementName == nsGkAtoms::menu ||
636 aElementName == nsGkAtoms::ol ||
637 aElementName == nsGkAtoms::ul)) {
638 return true;
641 // declare
642 if ((aAttrName == nsGkAtoms::declare) &&
643 (aElementName == nsGkAtoms::object)) {
644 return true;
647 // defer
648 if ((aAttrName == nsGkAtoms::defer) &&
649 (aElementName == nsGkAtoms::script)) {
650 return true;
653 // disabled
654 if ((aAttrName == nsGkAtoms::disabled) &&
655 (aElementName == nsGkAtoms::button ||
656 aElementName == nsGkAtoms::input ||
657 aElementName == nsGkAtoms::optgroup ||
658 aElementName == nsGkAtoms::option ||
659 aElementName == nsGkAtoms::select ||
660 aElementName == nsGkAtoms::textarea)) {
661 return true;
664 // ismap
665 if ((aAttrName == nsGkAtoms::ismap) &&
666 (aElementName == nsGkAtoms::img ||
667 aElementName == nsGkAtoms::input)) {
668 return true;
671 // multiple
672 if ((aAttrName == nsGkAtoms::multiple) &&
673 (aElementName == nsGkAtoms::select)) {
674 return true;
677 // noresize
678 if ((aAttrName == nsGkAtoms::noresize) &&
679 (aElementName == nsGkAtoms::frame)) {
680 return true;
683 // noshade
684 if ((aAttrName == nsGkAtoms::noshade) &&
685 (aElementName == nsGkAtoms::hr)) {
686 return true;
689 // nowrap
690 if ((aAttrName == nsGkAtoms::nowrap) &&
691 (aElementName == nsGkAtoms::td ||
692 aElementName == nsGkAtoms::th)) {
693 return true;
696 // readonly
697 if ((aAttrName == nsGkAtoms::readonly) &&
698 (aElementName == nsGkAtoms::input ||
699 aElementName == nsGkAtoms::textarea)) {
700 return true;
703 // selected
704 if ((aAttrName == nsGkAtoms::selected) &&
705 (aElementName == nsGkAtoms::option)) {
706 return true;
709 // autoplay and controls
710 if ((aElementName == nsGkAtoms::video || aElementName == nsGkAtoms::audio) &&
711 (aAttrName == nsGkAtoms::autoplay || aAttrName == nsGkAtoms::muted ||
712 aAttrName == nsGkAtoms::controls)) {
713 return true;
716 return false;
719 bool
720 nsXHTMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID, nsIAtom* aName)
723 if (aNamespaceID != kNameSpaceID_XHTML) {
724 return mAddSpace;
727 if (aName == nsGkAtoms::title ||
728 aName == nsGkAtoms::meta ||
729 aName == nsGkAtoms::link ||
730 aName == nsGkAtoms::style ||
731 aName == nsGkAtoms::select ||
732 aName == nsGkAtoms::option ||
733 aName == nsGkAtoms::script ||
734 aName == nsGkAtoms::html) {
735 return true;
737 else {
738 nsIParserService* parserService = nsContentUtils::GetParserService();
740 if (parserService) {
741 bool res;
742 parserService->
743 IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res);
744 return res;
748 return mAddSpace;
751 bool
752 nsXHTMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID, nsIAtom* aName)
755 if (aNamespaceID != kNameSpaceID_XHTML) {
756 return false;
759 if ((aName == nsGkAtoms::html) ||
760 (aName == nsGkAtoms::head) ||
761 (aName == nsGkAtoms::body) ||
762 (aName == nsGkAtoms::ul) ||
763 (aName == nsGkAtoms::ol) ||
764 (aName == nsGkAtoms::dl) ||
765 (aName == nsGkAtoms::table) ||
766 (aName == nsGkAtoms::tbody) ||
767 (aName == nsGkAtoms::tr) ||
768 (aName == nsGkAtoms::br) ||
769 (aName == nsGkAtoms::meta) ||
770 (aName == nsGkAtoms::link) ||
771 (aName == nsGkAtoms::script) ||
772 (aName == nsGkAtoms::select) ||
773 (aName == nsGkAtoms::map) ||
774 (aName == nsGkAtoms::area) ||
775 (aName == nsGkAtoms::style)) {
776 return true;
779 return false;
782 bool
783 nsXHTMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID, nsIAtom* aName)
786 if (aNamespaceID != kNameSpaceID_XHTML) {
787 return false;
790 if ((aName == nsGkAtoms::html) ||
791 (aName == nsGkAtoms::head) ||
792 (aName == nsGkAtoms::body) ||
793 (aName == nsGkAtoms::ul) ||
794 (aName == nsGkAtoms::ol) ||
795 (aName == nsGkAtoms::dl) ||
796 (aName == nsGkAtoms::select) ||
797 (aName == nsGkAtoms::table) ||
798 (aName == nsGkAtoms::tbody)) {
799 return true;
801 return false;
804 bool
805 nsXHTMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID, nsIAtom* aName)
808 if (aNamespaceID != kNameSpaceID_XHTML) {
809 return false;
812 if ((aName == nsGkAtoms::html) ||
813 (aName == nsGkAtoms::head) ||
814 (aName == nsGkAtoms::body) ||
815 (aName == nsGkAtoms::tr) ||
816 (aName == nsGkAtoms::th) ||
817 (aName == nsGkAtoms::td) ||
818 (aName == nsGkAtoms::pre) ||
819 (aName == nsGkAtoms::title) ||
820 (aName == nsGkAtoms::li) ||
821 (aName == nsGkAtoms::dt) ||
822 (aName == nsGkAtoms::dd) ||
823 (aName == nsGkAtoms::blockquote) ||
824 (aName == nsGkAtoms::select) ||
825 (aName == nsGkAtoms::option) ||
826 (aName == nsGkAtoms::p) ||
827 (aName == nsGkAtoms::map) ||
828 (aName == nsGkAtoms::div)) {
829 return true;
831 else {
832 nsIParserService* parserService = nsContentUtils::GetParserService();
834 if (parserService) {
835 bool res;
836 parserService->
837 IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res);
838 return res;
842 return false;
846 void
847 nsXHTMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode)
849 if (!ShouldMaintainPreLevel() ||
850 aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
851 return;
854 nsIAtom *name = aNode->Tag();
856 if (IsElementPreformatted(aNode) ||
857 name == nsGkAtoms::script ||
858 name == nsGkAtoms::style ||
859 name == nsGkAtoms::noscript ||
860 name == nsGkAtoms::noframes
862 PreLevel()++;
866 void
867 nsXHTMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode)
869 if (!ShouldMaintainPreLevel() ||
870 aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
871 return;
874 nsIAtom *name = aNode->Tag();
875 if (IsElementPreformatted(aNode) ||
876 name == nsGkAtoms::script ||
877 name == nsGkAtoms::style ||
878 name == nsGkAtoms::noscript ||
879 name == nsGkAtoms::noframes
881 --PreLevel();
885 bool
886 nsXHTMLContentSerializer::IsElementPreformatted(nsIContent* aNode)
888 MOZ_ASSERT(ShouldMaintainPreLevel(), "We should not be calling this needlessly");
890 if (!aNode->IsElement()) {
891 return false;
893 nsRefPtr<nsStyleContext> styleContext =
894 nsComputedDOMStyle::GetStyleContextForElementNoFlush(aNode->AsElement(),
895 nullptr, nullptr);
896 if (styleContext) {
897 const nsStyleText* textStyle = styleContext->StyleText();
898 return textStyle->WhiteSpaceOrNewlineIsSignificant();
900 return false;
903 void
904 nsXHTMLContentSerializer::SerializeLIValueAttribute(nsIContent* aElement,
905 nsAString& aStr)
907 // We are copying and we are at the "first" LI node of OL in selected range.
908 // It may not be the first LI child of OL but it's first in the selected range.
909 // Note that we get into this condition only once per a OL.
910 bool found = false;
911 nsCOMPtr<nsIDOMNode> currNode = do_QueryInterface(aElement);
912 nsAutoString valueStr;
914 olState state (0, false);
916 if (!mOLStateStack.IsEmpty()) {
917 state = mOLStateStack[mOLStateStack.Length()-1];
918 // isFirstListItem should be true only before the serialization of the
919 // first item in the list.
920 state.isFirstListItem = false;
921 mOLStateStack[mOLStateStack.Length()-1] = state;
924 int32_t startVal = state.startVal;
925 int32_t offset = 0;
927 // Traverse previous siblings until we find one with "value" attribute.
928 // offset keeps track of how many previous siblings we had tocurrNode traverse.
929 while (currNode && !found) {
930 nsCOMPtr<nsIDOMElement> currElement = do_QueryInterface(currNode);
931 // currElement may be null if it were a text node.
932 if (currElement) {
933 nsAutoString tagName;
934 currElement->GetTagName(tagName);
935 if (tagName.LowerCaseEqualsLiteral("li")) {
936 currElement->GetAttribute(NS_LITERAL_STRING("value"), valueStr);
937 if (valueStr.IsEmpty())
938 offset++;
939 else {
940 found = true;
941 nsresult rv = NS_OK;
942 startVal = valueStr.ToInteger(&rv);
946 nsCOMPtr<nsIDOMNode> tmp;
947 currNode->GetPreviousSibling(getter_AddRefs(tmp));
948 currNode.swap(tmp);
950 // If LI was not having "value", Set the "value" attribute for it.
951 // Note that We are at the first LI in the selected range of OL.
952 if (offset == 0 && found) {
953 // offset = 0 => LI itself has the value attribute and we did not need to traverse back.
954 // Just serialize value attribute like other tags.
955 SerializeAttr(EmptyString(), NS_LITERAL_STRING("value"), valueStr, aStr, false);
957 else if (offset == 1 && !found) {
958 /*(offset = 1 && !found) means either LI is the first child node of OL
959 and LI is not having "value" attribute.
960 In that case we would not like to set "value" attribute to reduce the changes.
962 //do nothing...
964 else if (offset > 0) {
965 // Set value attribute.
966 nsAutoString valueStr;
968 //As serializer needs to use this valueAttr we are creating here,
969 valueStr.AppendInt(startVal + offset);
970 SerializeAttr(EmptyString(), NS_LITERAL_STRING("value"), valueStr, aStr, false);
974 bool
975 nsXHTMLContentSerializer::IsFirstChildOfOL(nsIContent* aElement)
977 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
978 nsAutoString parentName;
980 nsCOMPtr<nsIDOMNode> parentNode;
981 node->GetParentNode(getter_AddRefs(parentNode));
982 if (parentNode)
983 parentNode->GetNodeName(parentName);
984 else
985 return false;
987 if (parentName.LowerCaseEqualsLiteral("ol")) {
989 if (!mOLStateStack.IsEmpty()) {
990 olState state = mOLStateStack[mOLStateStack.Length()-1];
991 if (state.isFirstListItem)
992 return true;
995 return false;
997 else
998 return false;
1001 bool
1002 nsXHTMLContentSerializer::HasNoChildren(nsIContent * aContent) {
1004 for (nsIContent* child = aContent->GetFirstChild();
1005 child;
1006 child = child->GetNextSibling()) {
1008 if (!child->IsNodeOfType(nsINode::eTEXT))
1009 return false;
1011 if (child->TextLength())
1012 return false;
1015 return true;