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