Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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.
11 */
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"
37 static const int32_t kLongLineLen = 128;
39 #define kXMLNS "xmlns"
41 nsresult NS_NewXHTMLContentSerializer(nsIContentSerializer** aSerializer)
42 {
43 nsXHTMLContentSerializer* it = new nsXHTMLContentSerializer();
44 if (!it) {
45 return NS_ERROR_OUT_OF_MEMORY;
46 }
48 return CallQueryInterface(it, aSerializer);
49 }
51 nsXHTMLContentSerializer::nsXHTMLContentSerializer()
52 : mIsHTMLSerializer(false)
53 {
54 }
56 nsXHTMLContentSerializer::~nsXHTMLContentSerializer()
57 {
58 NS_ASSERTION(mOLStateStack.IsEmpty(), "Expected OL State stack to be empty");
59 }
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 }
74 nsresult rv;
75 rv = nsXMLContentSerializer::Init(aFlags, aWrapColumn, aCharSet, aIsCopying, aRewriteEncodingDeclaration);
76 NS_ENSURE_SUCCESS(rv, rv);
78 mRewriteEncodingDeclaration = aRewriteEncodingDeclaration;
79 mIsCopying = aIsCopying;
80 mIsFirstChildOfOL = false;
81 mInBody = 0;
82 mDisableEntityEncoding = 0;
83 mBodyOnly = (mFlags & nsIDocumentEncoder::OutputBodyOnly) ? true
84 : false;
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 }
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 }
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);
127 nsAutoString data;
128 nsresult rv;
130 rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true);
131 if (NS_FAILED(rv))
132 return NS_ERROR_FAILURE;
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 }
156 return NS_OK;
157 }
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 }
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;
177 if (!mCharset.IsEmpty() && !IsASCII(uri)) {
178 textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
179 NS_ENSURE_SUCCESS(rv, rv);
180 }
182 int32_t start = 0;
183 int32_t end;
184 nsAutoString part;
185 nsXPIDLCString escapedURI;
186 aEscapedURI.Truncate(0);
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);
201 // Append a reserved character without escaping.
202 part = Substring(aURI, end, 1);
203 aEscapedURI.Append(part);
204 start = end + 1;
205 }
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 }
220 return rv;
221 }
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);
239 int32_t contentNamespaceID = aContent->GetNameSpaceID();
241 // this method is not called by nsHTMLContentSerializer
242 // so we don't have to check HTML element, just XHTML
244 if (mIsCopying && kNameSpaceID_XHTML == contentNamespaceID) {
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 }
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 }
290 NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
292 count = aContent->GetAttrCount();
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++) {
299 if (aSkipAttr == index) {
300 continue;
301 }
303 const nsAttrName* name = aContent->GetAttrNameAt(index);
304 int32_t namespaceID = name->NamespaceID();
305 nsIAtom* attrName = name->LocalName();
306 nsIAtom* attrPrefix = name->GetPrefix();
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 }
315 if (attrPrefix) {
316 attrPrefix->ToString(prefixStr);
317 }
318 else {
319 prefixStr.Truncate();
320 }
322 bool addNSAttr = false;
323 if (kNameSpaceID_XMLNS != namespaceID) {
324 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr);
325 addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true);
326 }
328 aContent->GetAttr(namespaceID, attrName, valueStr);
330 nsDependentAtomString nameStr(attrName);
331 bool isJS = false;
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 }
343 if (mIsCopying && mIsFirstChildOfOL && (aTagName == nsGkAtoms::li)
344 && (attrName == nsGkAtoms::value)) {
345 // This is handled separately in SerializeLIValueAttribute()
346 continue;
347 }
349 isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
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 }
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 }
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 }
396 SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS);
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 }
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 !");
418 if (kNameSpaceID_XHTML != aNamespaceID) {
419 nsXMLContentSerializer::AppendEndOfElementStart(aOriginalElement, aName,
420 aNamespaceID, aStr);
421 return;
422 }
424 nsIContent* content = aOriginalElement;
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)) {
431 nsIParserService* parserService = nsContentUtils::GetParserService();
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 }
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) {
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);
472 if (header.LowerCaseEqualsLiteral("content-type")) {
473 hasMeta = true;
474 break;
475 }
476 }
477 }
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 }
496 void
497 nsXHTMLContentSerializer::AfterElementEnd(nsIContent * aContent,
498 nsAString& aStr)
499 {
500 NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
502 int32_t namespaceID = aContent->GetNameSpaceID();
503 nsIAtom *name = aContent->Tag();
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 }
513 NS_IMETHODIMP
514 nsXHTMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument,
515 nsAString& aStr)
516 {
517 if (!mBodyOnly)
518 return nsXMLContentSerializer::AppendDocumentStart(aDocument, aStr);
520 return NS_OK;
521 }
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);
534 nsIAtom *name = aContent->Tag();
535 int32_t namespaceID = aContent->GetNameSpaceID();
537 if (namespaceID == kNameSpaceID_XHTML) {
538 if (name == nsGkAtoms::br && mPreLevel > 0 &&
539 (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre)) {
540 AppendNewLineToString(aStr);
541 return false;
542 }
544 if (name == nsGkAtoms::body) {
545 ++mInBody;
546 }
547 }
548 return true;
549 }
551 bool
552 nsXHTMLContentSerializer::CheckElementEnd(nsIContent * aContent,
553 bool & aForceFormat,
554 nsAString& aStr)
555 {
556 NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
558 aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
559 aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
561 nsIAtom *name = aContent->Tag();
562 int32_t namespaceID = aContent->GetNameSpaceID();
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 }
576 if (HasNoChildren(aContent)) {
577 nsIParserService* parserService = nsContentUtils::GetParserService();
579 if (parserService) {
580 bool isContainer;
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 }
598 bool dummyFormat;
599 return nsXMLContentSerializer::CheckElementEnd(aContent, dummyFormat, aStr);
600 }
602 void
603 nsXHTMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr,
604 nsAString& aOutputStr)
605 {
606 if (mBodyOnly && !mInBody) {
607 return;
608 }
610 if (mDisableEntityEncoding) {
611 aOutputStr.Append(aStr);
612 return;
613 }
615 nsXMLContentSerializer::AppendAndTranslateEntities(aStr, aOutputStr);
616 }
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 }
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 }
638 // declare
639 if ((aAttrName == nsGkAtoms::declare) &&
640 (aElementName == nsGkAtoms::object)) {
641 return true;
642 }
644 // defer
645 if ((aAttrName == nsGkAtoms::defer) &&
646 (aElementName == nsGkAtoms::script)) {
647 return true;
648 }
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 }
661 // ismap
662 if ((aAttrName == nsGkAtoms::ismap) &&
663 (aElementName == nsGkAtoms::img ||
664 aElementName == nsGkAtoms::input)) {
665 return true;
666 }
668 // multiple
669 if ((aAttrName == nsGkAtoms::multiple) &&
670 (aElementName == nsGkAtoms::select)) {
671 return true;
672 }
674 // noresize
675 if ((aAttrName == nsGkAtoms::noresize) &&
676 (aElementName == nsGkAtoms::frame)) {
677 return true;
678 }
680 // noshade
681 if ((aAttrName == nsGkAtoms::noshade) &&
682 (aElementName == nsGkAtoms::hr)) {
683 return true;
684 }
686 // nowrap
687 if ((aAttrName == nsGkAtoms::nowrap) &&
688 (aElementName == nsGkAtoms::td ||
689 aElementName == nsGkAtoms::th)) {
690 return true;
691 }
693 // readonly
694 if ((aAttrName == nsGkAtoms::readonly) &&
695 (aElementName == nsGkAtoms::input ||
696 aElementName == nsGkAtoms::textarea)) {
697 return true;
698 }
700 // selected
701 if ((aAttrName == nsGkAtoms::selected) &&
702 (aElementName == nsGkAtoms::option)) {
703 return true;
704 }
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 }
713 return false;
714 }
716 bool
717 nsXHTMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID, nsIAtom* aName)
718 {
720 if (aNamespaceID != kNameSpaceID_XHTML) {
721 return mAddSpace;
722 }
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();
737 if (parserService) {
738 bool res;
739 parserService->
740 IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res);
741 return res;
742 }
743 }
745 return mAddSpace;
746 }
748 bool
749 nsXHTMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID, nsIAtom* aName)
750 {
752 if (aNamespaceID != kNameSpaceID_XHTML) {
753 return false;
754 }
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 }
776 return false;
777 }
779 bool
780 nsXHTMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID, nsIAtom* aName)
781 {
783 if (aNamespaceID != kNameSpaceID_XHTML) {
784 return false;
785 }
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 }
801 bool
802 nsXHTMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID, nsIAtom* aName)
803 {
805 if (aNamespaceID != kNameSpaceID_XHTML) {
806 return false;
807 }
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();
831 if (parserService) {
832 bool res;
833 parserService->
834 IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res);
835 return res;
836 }
837 }
839 return false;
840 }
843 void
844 nsXHTMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode)
845 {
847 if (aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
848 return;
849 }
851 nsIAtom *name = aNode->Tag();
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 }
863 void
864 nsXHTMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode)
865 {
866 if (aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
867 return;
868 }
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 }
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;
892 olState state (0, false);
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 }
902 int32_t startVal = state.startVal;
903 int32_t offset = 0;
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;
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 }
952 bool
953 nsXHTMLContentSerializer::IsFirstChildOfOL(nsIContent* aElement)
954 {
955 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
956 nsAutoString parentName;
958 nsCOMPtr<nsIDOMNode> parentNode;
959 node->GetParentNode(getter_AddRefs(parentNode));
960 if (parentNode)
961 parentNode->GetNodeName(parentName);
962 else
963 return false;
965 if (parentName.LowerCaseEqualsLiteral("ol")) {
967 if (!mOLStateStack.IsEmpty()) {
968 olState state = mOLStateStack[mOLStateStack.Length()-1];
969 if (state.isFirstListItem)
970 return true;
971 }
973 return false;
974 }
975 else
976 return false;
977 }
979 bool
980 nsXHTMLContentSerializer::HasNoChildren(nsIContent * aContent) {
982 for (nsIContent* child = aContent->GetFirstChild();
983 child;
984 child = child->GetNextSibling()) {
986 if (!child->IsNodeOfType(nsINode::eTEXT))
987 return false;
989 if (child->TextLength())
990 return false;
991 }
993 return true;
994 }