Files
palemoon27/dom/base/nsXHTMLContentSerializer.cpp
T
roytam1 5e324904fd import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1252212. Make the RIL WorkerRun implemetations not leave exceptions on the JSContext. r=khuey (a96b929707)
- Bug 1267893 part 4 - Make setting the start time set 'did seek' to true; r=hiro (581d057f37)
- Bug 1263063 - Part 6: Use TimingParams::EndTime() instead of re-calculation ComputedTiming each time. r=dholbert (1c70d3dd80)
- Bug 1244637 - implement AnimationEffectTiming fill. r=hiro (83f10328cf)
- Bug 1244642 - Implement AnimationEffectTiming.direction r=hiro (5fb010e6f4)
- Bug 1267937 - Part 1: Clear mProgressOnLastCompose once we are not in effect. r=birtles (ccc5ee4594)
- Bug 1267937 - Part 2: A reftest which checks mProgressOnLastCompose is surely cleared in before phase. r=birtles (850fb3a6b1)
- Bug 1259733 - use forward declarations for nsIDocument in a few places; r=dholbert (1cc3af25b0)
- Bug 1232906 - Use channel.asyncOpen2 within dom/apps/AppsUtils.jsm (r=sicking) (f8fe88724f)
- Bug 1250464 - Remove workaround to get path from AppsUtils.jsm. r=myk (e1c76d7a13)
- Bug 1263158 - Check if key is present in manifest object before using it. r=fabrice (44fe2c0468)
- Bug 1230091 - ReferenceError on using not defined aApp variable in OfflineCacheInstaller.jsm, r=fabrice (8c6ab3d45c)
- Bug 1228974 - correct the group ID in Offline Cache Installer, r=fabrice (f30de7eb6c)
- var-let (364b37d086)
- Bug 1267718 - Add a nsPIDOMWindow::GetScriptableParentOrNull method. r=bz (2cf8533883)
- Bug 1268953. The load events we fire on iframe/frame/object (in the document case) should not be cancelable. r=smaug (ec36a6e8c8)
- Bug 1266194 - Implement boolean or EventListenerOptions as 3rd param to addEventListener, r=smaug (069c30d74f)
- Bug 944616 - "Blob URLs don't allow query or fragment parts". r=bz (85923ee174)
- minor (bd5daf4059)
- Bug 1167395 - Mark CharacterDataChangeInfo::Details as MOZ_STACK_CLASS, and mark mNextSibling as MOZ_NON_OWNING_REF. r=smaug (83eb176677)
- Bug 1192855 - Check validity in advance for nsRange::InsertNode; r=hsivonen (485bd59ff4)
- Bug 1214495 - Bonus fix. r=bz (bf6a4b33ec)
- Bug 1266889 - Plugin block list blocks SWF network requests, but does not prevent plugin instantiation. r=francois (be7237639a)
- Bug 1183891 - Remove warning if invalid node type is passed to nsRange::SetStart. r=smaug (8ce58952af)
- Bug 1183893 - Remove warning if invalid node type is passed to nsRange::SetEnd. r=smaug (8675f2e21f)
- Bug 1163105 - Make nsReferencedElement work with referencing elements that are not in their document's DOM tree. r=roc (83bc0fc078)
- Bug 1172144 - Improve the size check of nsTextFragment::Append, r=ehsan (e6d47af1b1)
- Bug 1151366 - remove nsGkAtoms::mozdonotsend from treesanitizer. r=ehsan (9237c22bdb)
- Bug 1158500 - make writing-mode a mapped CSS property. r=cam (8d50bfb287)
- bit of 1131348 (f2b234976b)
- Bug 1245533 - nsXHTMLContentSerializer::CheckElementEnd - small compilation issue, r=smaug (85d8a50ddb)
- align tests (cfb773549f)
- Bug 1248836 - HID Features Implementation, r=jocelyn (0cb4482faf)
- Bug 1239979: Close sockets when deinitializing Bluetooth profile managers, r=btian (c8c449767b)
- Bug 1238991: Don't connect Bluetooth OPP manager before service channel is known, r=btian (9ad0d9ff7e)
- Bug 1239979: Get pointers to Bluetooth managers during each shutdown, r=shuang (71959acf40)
- Bug 1229697 - Cancel bond when user inputs empty pincode for pairing, r=shuang (e70ee96e6b)
- Bug 1252787 - Patch : Add HID profile when device is remote, r=shawnjohnjr (bbadf5f42b)
- Bug 1236724: Check the maximum length of each array in IPC; f=jhector, r=btian (e961ee7756)
- align tests (17b6369dfa)
- Bug 1268688 - Start browser API for frames swapping to HTML. r=bz (4a17ea38c7)
- Bug 1265427 - nsDOMCameraControl needs an mOwnedStream to be consistent towards its VideoStreamTrack r=me (e008b0e4c1)
- Bug 1154665 - Part 1. Provide gps processing data to avoid setParameters fail. r=aosmond (0564b157d3)
- Bug 1154665 - Part 2. Testcase against gps parameter. r=aosmond (c52efbcd07)
- Bug 1239752 - Create ImageBitmap from ImageData should preserve alpha. r=roc (1ec8ccd266)
- Bug 1266432: Use CopySurface in ImageBitmap::PrepareForDrawing even when using D2D 1.1. r=kaku (c27dcf42f2)
- Bug 1265598: Deal with the possibility of a write map failing. r=kaku r=milan (1916e69db1)
- Bug 1266390: Preserver mIsPremultipliedAlpha when creating an ImageBitmap from an existing ImageBitmap. r=kaku (385ad1f750)
- Bug 1267100 - add makeCurrent() for WebGLContext::GetFramebufferAttachmentParameter(). r=jgilbert (d7f957610b)
- Bug 1266262 - Remove nearly-unused GLContext::mGLFormats. - r=jrmuizel (039e2a851f)
- Bug 1186688 - Remove cached state check for DrawBuffer maximums, since it's invalid with min-cap mode. - r=jrmuizel (dc92031951)
- Bug 1193526 - Add generated files. r=jgilbert (c3f54b6cef)
- Bug 1264214 - WebGL check the conflict name when LinkProgram. r=jgilbert (1f268acc58)
- Bug 842818 - Inline CloneData() and clean up ImportKeyTask::SetKeyData() r=rbarnes (299a32176c)
- Bug 1137987 - Remove nonstandard let block from dom/downloads/tests. r=aus (94e98d64ff)
- Bug 1211454 - Avoid requesting a zero-terminated string in TextEncoder when zero-termination is not needed. r=emk. (2d52f98e86)
- Bug 1259669 Rename WidgetCommandEvent::command to WidgetCommandEvent::mCommand r=masayuki (744c283978)
- Bug 1264380 - Get Composed Document of Shadow DOM Element Properly. r=wchen (0b4404ef19)
- Bug 1188539 - Remove the deprecated TouchList::identifiedTouch method; r=jst (926c24d74d)
- Bug 918706 - Return NS_ERROR_DOM_SYNTAX_ERR if method is invalid, r=khuey (f209944a0c)
- Bug 1265610 - test_postMessages.html and some dom/filesystem tests requires 'dom.input.dirpicker' to be true, r=smaug (7092cef989)
- Bug 1265610 - Fixing a JS error in the tests, CLOSED TREE r=me (d203807a1e)
- Bug 1137151: Marked destructors of refcounted FM-radio classes as protected, r=pzhang (5b2ad86c5e)
- Bug 1206174 - Improve code readability of FMRadioService r=alwu (bfcf897714)
- Bug 1254298 - Bypass Gamepad Service Shutdown Timer on e10s; r=ted r=cleu (c40fcae327)
- Bug 1156957 - Make gamepad mochitests work on e10s; r=ted (f0a1be1440)
- Bug 1248794 - Clean up observer on WindowsGamepadService shutdown; r=ted r=smaug sec-approval=abillings (06660cc3fc)
- Bug 1237896 - [Gamepad] Button Event cannot be correctly triggered after reconnect. r=qdot (fa21602600)
- Bug 1249833 - Typo in nsGeolocation.h. r=jdm (42dad72688)
- Bug 1255198 - [Telemetry] Add geolocation Telemetry probes to record fulfilled requests according to document.isVisible. r=jdm, data-review=bsmedberg (5e4f5db476)
- Bug 1218080 - Don't send the url to the parent process when opening new windows, because it is not actually used. r=smaug (fca00714f6)
- Bug 1256061 - Hold a strong reference to a request when we call a method on it. r=jdm (7ed037ef37)
- Bug 1263001 - Don't Notify() an unlinked nsGeolocationRequest. r=jdm (0693c4688f)
- missing bit of Bug 1242668 - Hold more references. r=sotaro (ba173e46f1)
- Bug 1267246 - Remove bogus assertion. r=nical (c8ccfb73c8)
- Bug 1265638 - Reset some properties when the backend of SharedSurface is switching from Basic to layer-accelerated backend. r=jgilbert (a6cf6598b7)
- Bug 1266484 - Fix crash in libsystem_kernel.dylib@0x16db6 when using Pinboard bookmarklet. r=gabor (1a96a797c6)
- Bug 1253959 - per comment 10, #ifdef code that causes ReadMetadata() to fail on Windows XP debug. r=cpearce. (ab2e19f621)
- Bug 1256038: Remove special NotifyDataArrived handling in the DirectShow reader. r=cpearce (441a26dd97)
- Bug 1284198 - Don't USE_CLOCK_API on macOS. r=terrence (659cecb516)
- Bug 956899 - Add comments to ConditionVariable and handle some edge cases gracefully; r=froydnj (be45b6b271)
- Bug 1268822 - rename mozilla::gmp::Runnable/SyncRunnable in order not to confuse NS_LOG_ADDREF/NS_LOG_RELEASE. r=rjesup. (18aec0cd35)
2024-09-13 09:58:56 +08:00

946 lines
28 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* nsIContentSerializer implementation that can be used with an
* nsIDocumentEncoder to convert an XHTML (not HTML!) DOM to an XHTML
* string that could be parsed into more or less the original DOM.
*/
#include "nsXHTMLContentSerializer.h"
#include "nsIDOMElement.h"
#include "nsIContent.h"
#include "nsIDocument.h"
#include "nsNameSpaceManager.h"
#include "nsString.h"
#include "nsUnicharUtils.h"
#include "nsXPIDLString.h"
#include "nsIServiceManager.h"
#include "nsIDocumentEncoder.h"
#include "nsGkAtoms.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsEscape.h"
#include "nsITextToSubURI.h"
#include "nsCRT.h"
#include "nsIParserService.h"
#include "nsContentUtils.h"
#include "nsLWBrkCIID.h"
#include "nsIScriptElement.h"
#include "nsStubMutationObserver.h"
#include "nsAttrName.h"
#include "nsParserConstants.h"
#include "nsComputedDOMStyle.h"
#include "mozilla/dom/Element.h"
static const int32_t kLongLineLen = 128;
#define kXMLNS "xmlns"
nsresult
NS_NewXHTMLContentSerializer(nsIContentSerializer** aSerializer)
{
RefPtr<nsXHTMLContentSerializer> it = new nsXHTMLContentSerializer();
it.forget(aSerializer);
return NS_OK;
}
nsXHTMLContentSerializer::nsXHTMLContentSerializer()
: mIsHTMLSerializer(false)
{
}
nsXHTMLContentSerializer::~nsXHTMLContentSerializer()
{
NS_ASSERTION(mOLStateStack.IsEmpty(), "Expected OL State stack to be empty");
}
NS_IMETHODIMP
nsXHTMLContentSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn,
const char* aCharSet, bool aIsCopying,
bool aRewriteEncodingDeclaration)
{
// The previous version of the HTML serializer did implicit wrapping
// when there is no flags, so we keep wrapping in order to keep
// compatibility with the existing calling code
// XXXLJ perhaps should we remove this default settings later ?
if (aFlags & nsIDocumentEncoder::OutputFormatted ) {
aFlags = aFlags | nsIDocumentEncoder::OutputWrap;
}
nsresult rv;
rv = nsXMLContentSerializer::Init(aFlags, aWrapColumn, aCharSet, aIsCopying, aRewriteEncodingDeclaration);
NS_ENSURE_SUCCESS(rv, rv);
mRewriteEncodingDeclaration = aRewriteEncodingDeclaration;
mIsCopying = aIsCopying;
mIsFirstChildOfOL = false;
mInBody = 0;
mDisableEntityEncoding = 0;
mBodyOnly = (mFlags & nsIDocumentEncoder::OutputBodyOnly) ? true
: false;
// set up entity converter if we are going to need it
if (mFlags & nsIDocumentEncoder::OutputEncodeW3CEntities) {
mEntityConverter = do_CreateInstance(NS_ENTITYCONVERTER_CONTRACTID);
}
return NS_OK;
}
// See if the string has any lines longer than longLineLen:
// if so, we presume formatting is wonky (e.g. the node has been edited)
// and we'd better rewrap the whole text node.
bool
nsXHTMLContentSerializer::HasLongLines(const nsString& text, int32_t& aLastNewlineOffset)
{
uint32_t start=0;
uint32_t theLen = text.Length();
bool rv = false;
aLastNewlineOffset = kNotFound;
for (start = 0; start < theLen; ) {
int32_t eol = text.FindChar('\n', start);
if (eol < 0) {
eol = text.Length();
}
else {
aLastNewlineOffset = eol;
}
if (int32_t(eol - start) > kLongLineLen)
rv = true;
start = eol + 1;
}
return rv;
}
NS_IMETHODIMP
nsXHTMLContentSerializer::AppendText(nsIContent* aText,
int32_t aStartOffset,
int32_t aEndOffset,
nsAString& aStr)
{
NS_ENSURE_ARG(aText);
nsAutoString data;
nsresult rv;
rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true);
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
if (mDoRaw || PreLevel() > 0) {
NS_ENSURE_TRUE(AppendToStringConvertLF(data, aStr), NS_ERROR_OUT_OF_MEMORY);
}
else if (mDoFormat) {
NS_ENSURE_TRUE(AppendToStringFormatedWrapped(data, aStr), NS_ERROR_OUT_OF_MEMORY);
}
else if (mDoWrap) {
NS_ENSURE_TRUE(AppendToStringWrapped(data, aStr), NS_ERROR_OUT_OF_MEMORY);
}
else {
int32_t lastNewlineOffset = kNotFound;
if (HasLongLines(data, lastNewlineOffset)) {
// We have long lines, rewrap
mDoWrap = true;
bool result = AppendToStringWrapped(data, aStr);
mDoWrap = false;
NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
}
else {
NS_ENSURE_TRUE(AppendToStringConvertLF(data, aStr), NS_ERROR_OUT_OF_MEMORY);
}
}
return NS_OK;
}
nsresult
nsXHTMLContentSerializer::EscapeURI(nsIContent* aContent, const nsAString& aURI, nsAString& aEscapedURI)
{
// URL escape %xx cannot be used in JS.
// No escaping if the scheme is 'javascript'.
if (IsJavaScript(aContent, nsGkAtoms::href, kNameSpaceID_None, aURI)) {
aEscapedURI = aURI;
return NS_OK;
}
// nsITextToSubURI does charset convert plus uri escape
// This is needed to convert to a document charset which is needed to support existing browsers.
// But we eventually want to use UTF-8 instead of a document charset, then the code would be much simpler.
// See HTML 4.01 spec, "Appendix B.2.1 Non-ASCII characters in URI attribute values"
nsCOMPtr<nsITextToSubURI> textToSubURI;
nsAutoString uri(aURI); // in order to use FindCharInSet()
nsresult rv = NS_OK;
if (!mCharset.IsEmpty() && !IsASCII(uri)) {
textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
int32_t start = 0;
int32_t end;
nsAutoString part;
nsXPIDLCString escapedURI;
aEscapedURI.Truncate(0);
// Loop and escape parts by avoiding escaping reserved characters
// (and '%', '#', as well as '[' and ']' for IPv6 address literals).
while ((end = uri.FindCharInSet("%#;/?:@&=+$,[]", start)) != -1) {
part = Substring(aURI, start, (end-start));
if (textToSubURI && !IsASCII(part)) {
rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
NS_ENSURE_SUCCESS(rv, rv);
}
else {
escapedURI.Adopt(nsEscape(NS_ConvertUTF16toUTF8(part).get(), url_Path));
}
AppendASCIItoUTF16(escapedURI, aEscapedURI);
// Append a reserved character without escaping.
part = Substring(aURI, end, 1);
aEscapedURI.Append(part);
start = end + 1;
}
if (start < (int32_t) aURI.Length()) {
// Escape the remaining part.
part = Substring(aURI, start, aURI.Length()-start);
if (textToSubURI) {
rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
NS_ENSURE_SUCCESS(rv, rv);
}
else {
escapedURI.Adopt(nsEscape(NS_ConvertUTF16toUTF8(part).get(), url_Path));
}
AppendASCIItoUTF16(escapedURI, aEscapedURI);
}
return rv;
}
bool
nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
nsIContent *aOriginalElement,
nsAString& aTagPrefix,
const nsAString& aTagNamespaceURI,
nsIAtom* aTagName,
nsAString& aStr,
uint32_t aSkipAttr,
bool aAddNSAttr)
{
nsresult rv;
uint32_t index, count;
nsAutoString prefixStr, uriStr, valueStr;
nsAutoString xmlnsStr;
xmlnsStr.AssignLiteral(kXMLNS);
int32_t contentNamespaceID = aContent->GetNameSpaceID();
// this method is not called by nsHTMLContentSerializer
// so we don't have to check HTML element, just XHTML
if (mIsCopying && kNameSpaceID_XHTML == contentNamespaceID) {
// Need to keep track of OL and LI elements in order to get ordinal number
// for the LI.
if (aTagName == nsGkAtoms::ol) {
// We are copying and current node is an OL;
// Store its start attribute value in olState->startVal.
nsAutoString start;
int32_t startAttrVal = 0;
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::start, start);
if (!start.IsEmpty()) {
nsresult rv = NS_OK;
startAttrVal = start.ToInteger(&rv);
//If OL has "start" attribute, first LI element has to start with that value
//Therefore subtracting 1 as all the LI elements are incrementing it before using it;
//In failure of ToInteger(), default StartAttrValue to 0.
if (NS_SUCCEEDED(rv))
--startAttrVal;
else
startAttrVal = 0;
}
olState state (startAttrVal, true);
mOLStateStack.AppendElement(state);
}
else if (aTagName == nsGkAtoms::li) {
mIsFirstChildOfOL = IsFirstChildOfOL(aOriginalElement);
if (mIsFirstChildOfOL) {
// If OL is parent of this LI, serialize attributes in different manner.
NS_ENSURE_TRUE(SerializeLIValueAttribute(aContent, aStr), false);
}
}
}
// If we had to add a new namespace declaration, serialize
// and push it on the namespace stack
if (aAddNSAttr) {
if (aTagPrefix.IsEmpty()) {
// Serialize default namespace decl
NS_ENSURE_TRUE(SerializeAttr(EmptyString(), xmlnsStr,
aTagNamespaceURI,
aStr, true), false);
} else {
// Serialize namespace decl
NS_ENSURE_TRUE(SerializeAttr(xmlnsStr, aTagPrefix,
aTagNamespaceURI,
aStr, true), false);
}
PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement);
}
NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
count = aContent->GetAttrCount();
// Now serialize each of the attributes
// XXX Unfortunately we need a namespace manager to get
// attribute URIs.
for (index = 0; index < count; index++) {
if (aSkipAttr == index) {
continue;
}
const nsAttrName* name = aContent->GetAttrNameAt(index);
int32_t namespaceID = name->NamespaceID();
nsIAtom* attrName = name->LocalName();
nsIAtom* attrPrefix = name->GetPrefix();
// Filter out any attribute starting with [-|_]moz
nsDependentAtomString attrNameStr(attrName);
if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
continue;
}
if (attrPrefix) {
attrPrefix->ToString(prefixStr);
}
else {
prefixStr.Truncate();
}
bool addNSAttr = false;
if (kNameSpaceID_XMLNS != namespaceID) {
nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr);
addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true);
}
aContent->GetAttr(namespaceID, attrName, valueStr);
nsDependentAtomString nameStr(attrName);
bool isJS = false;
if (kNameSpaceID_XHTML == contentNamespaceID) {
//
// Filter out special case of <br type="_moz"> or <br _moz*>,
// used by the editor. Bug 16988. Yuck.
//
if (namespaceID == kNameSpaceID_None && aTagName == nsGkAtoms::br && attrName == nsGkAtoms::type
&& StringBeginsWith(valueStr, _mozStr)) {
continue;
}
if (mIsCopying && mIsFirstChildOfOL && (aTagName == nsGkAtoms::li)
&& (attrName == nsGkAtoms::value)) {
// This is handled separately in SerializeLIValueAttribute()
continue;
}
isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
if (namespaceID == kNameSpaceID_None &&
((attrName == nsGkAtoms::href) ||
(attrName == nsGkAtoms::src))) {
// Make all links absolute when converting only the selection:
if (mFlags & nsIDocumentEncoder::OutputAbsoluteLinks) {
// Would be nice to handle OBJECT and APPLET tags,
// but that gets more complicated since we have to
// search the tag list for CODEBASE as well.
// For now, just leave them relative.
nsCOMPtr<nsIURI> uri = aContent->GetBaseURI();
if (uri) {
nsAutoString absURI;
rv = NS_MakeAbsoluteURI(absURI, valueStr, uri);
if (NS_SUCCEEDED(rv)) {
valueStr = absURI;
}
}
}
// Need to escape URI.
nsAutoString tempURI(valueStr);
if (!isJS && NS_FAILED(EscapeURI(aContent, tempURI, valueStr)))
valueStr = tempURI;
}
if (mRewriteEncodingDeclaration && aTagName == nsGkAtoms::meta &&
attrName == nsGkAtoms::content) {
// If we're serializing a <meta http-equiv="content-type">,
// use the proper value, rather than what's in the document.
nsAutoString header;
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
if (header.LowerCaseEqualsLiteral("content-type")) {
valueStr = NS_LITERAL_STRING("text/html; charset=") +
NS_ConvertASCIItoUTF16(mCharset);
}
}
// Expand shorthand attribute.
if (namespaceID == kNameSpaceID_None && IsShorthandAttr(attrName, aTagName) && valueStr.IsEmpty()) {
valueStr = nameStr;
}
}
else {
isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
}
NS_ENSURE_TRUE(SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS), false);
if (addNSAttr) {
NS_ASSERTION(!prefixStr.IsEmpty(),
"Namespaced attributes must have a prefix");
NS_ENSURE_TRUE(SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, true), false);
PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement);
}
}
return true;
}
bool
nsXHTMLContentSerializer::AfterElementStart(nsIContent* aContent,
nsIContent* aOriginalElement,
nsAString& aStr)
{
if (mRewriteEncodingDeclaration &&
aContent->IsHTMLElement(nsGkAtoms::head)) {
// Check if there already are any content-type meta children.
// If there are, they will be modified to use the correct charset.
// If there aren't, we'll insert one here.
bool hasMeta = false;
for (nsIContent* child = aContent->GetFirstChild();
child;
child = child->GetNextSibling()) {
if (child->IsHTMLElement(nsGkAtoms::meta) &&
child->HasAttr(kNameSpaceID_None, nsGkAtoms::content)) {
nsAutoString header;
child->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
if (header.LowerCaseEqualsLiteral("content-type")) {
hasMeta = true;
break;
}
}
}
if (!hasMeta) {
NS_ENSURE_TRUE(AppendNewLineToString(aStr), false);
if (mDoFormat) {
NS_ENSURE_TRUE(AppendIndentation(aStr), false);
}
NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING("<meta http-equiv=\"content-type\""), aStr), false);
NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING(" content=\"text/html; charset="), aStr), false);
NS_ENSURE_TRUE(AppendToString(NS_ConvertASCIItoUTF16(mCharset), aStr), false);
if (mIsHTMLSerializer) {
NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING("\">"), aStr), false);
} else {
NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING("\" />"), aStr), false);
}
}
}
return true;
}
void
nsXHTMLContentSerializer::AfterElementEnd(nsIContent * aContent,
nsAString& aStr)
{
NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
// this method is not called by nsHTMLContentSerializer
// so we don't have to check HTML element, just XHTML
if (aContent->IsHTMLElement(nsGkAtoms::body)) {
--mInBody;
}
}
NS_IMETHODIMP
nsXHTMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument,
nsAString& aStr)
{
if (!mBodyOnly)
return nsXMLContentSerializer::AppendDocumentStart(aDocument, aStr);
return NS_OK;
}
bool
nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent,
bool & aForceFormat,
nsAString& aStr,
nsresult& aResult)
{
aResult = NS_OK;
// The _moz_dirty attribute is emitted by the editor to
// indicate that this element should be pretty printed
// even if we're not in pretty printing mode
aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
if (aContent->IsHTMLElement(nsGkAtoms::br) &&
(mFlags & nsIDocumentEncoder::OutputNoFormattingInPre) &&
PreLevel() > 0) {
aResult = AppendNewLineToString(aStr) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
return false;
}
if (aContent->IsHTMLElement(nsGkAtoms::body)) {
++mInBody;
}
return true;
}
bool
nsXHTMLContentSerializer::CheckElementEnd(mozilla::dom::Element* aElement,
bool& aForceFormat,
nsAString& aStr)
{
NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
if (mIsCopying && aElement->IsHTMLElement(nsGkAtoms::ol)) {
NS_ASSERTION((!mOLStateStack.IsEmpty()), "Cannot have an empty OL Stack");
/* Though at this point we must always have an state to be deleted as all
the OL opening tags are supposed to push an olState object to the stack*/
if (!mOLStateStack.IsEmpty()) {
mOLStateStack.RemoveElementAt(mOLStateStack.Length() -1);
}
}
bool dummyFormat;
return nsXMLContentSerializer::CheckElementEnd(aElement, dummyFormat, aStr);
}
bool
nsXHTMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr,
nsAString& aOutputStr)
{
if (mBodyOnly && !mInBody) {
return true;
}
if (mDisableEntityEncoding) {
return aOutputStr.Append(aStr, mozilla::fallible);
}
return nsXMLContentSerializer::AppendAndTranslateEntities(aStr, aOutputStr);
}
bool
nsXHTMLContentSerializer::IsShorthandAttr(const nsIAtom* aAttrName,
const nsIAtom* aElementName)
{
// checked
if ((aAttrName == nsGkAtoms::checked) &&
(aElementName == nsGkAtoms::input)) {
return true;
}
// compact
if ((aAttrName == nsGkAtoms::compact) &&
(aElementName == nsGkAtoms::dir ||
aElementName == nsGkAtoms::dl ||
aElementName == nsGkAtoms::menu ||
aElementName == nsGkAtoms::ol ||
aElementName == nsGkAtoms::ul)) {
return true;
}
// declare
if ((aAttrName == nsGkAtoms::declare) &&
(aElementName == nsGkAtoms::object)) {
return true;
}
// defer
if ((aAttrName == nsGkAtoms::defer) &&
(aElementName == nsGkAtoms::script)) {
return true;
}
// disabled
if ((aAttrName == nsGkAtoms::disabled) &&
(aElementName == nsGkAtoms::button ||
aElementName == nsGkAtoms::input ||
aElementName == nsGkAtoms::optgroup ||
aElementName == nsGkAtoms::option ||
aElementName == nsGkAtoms::select ||
aElementName == nsGkAtoms::textarea)) {
return true;
}
// ismap
if ((aAttrName == nsGkAtoms::ismap) &&
(aElementName == nsGkAtoms::img ||
aElementName == nsGkAtoms::input)) {
return true;
}
// multiple
if ((aAttrName == nsGkAtoms::multiple) &&
(aElementName == nsGkAtoms::select)) {
return true;
}
// noresize
if ((aAttrName == nsGkAtoms::noresize) &&
(aElementName == nsGkAtoms::frame)) {
return true;
}
// noshade
if ((aAttrName == nsGkAtoms::noshade) &&
(aElementName == nsGkAtoms::hr)) {
return true;
}
// nowrap
if ((aAttrName == nsGkAtoms::nowrap) &&
(aElementName == nsGkAtoms::td ||
aElementName == nsGkAtoms::th)) {
return true;
}
// readonly
if ((aAttrName == nsGkAtoms::readonly) &&
(aElementName == nsGkAtoms::input ||
aElementName == nsGkAtoms::textarea)) {
return true;
}
// selected
if ((aAttrName == nsGkAtoms::selected) &&
(aElementName == nsGkAtoms::option)) {
return true;
}
// autoplay and controls
if ((aElementName == nsGkAtoms::video || aElementName == nsGkAtoms::audio) &&
(aAttrName == nsGkAtoms::autoplay || aAttrName == nsGkAtoms::muted ||
aAttrName == nsGkAtoms::controls)) {
return true;
}
return false;
}
bool
nsXHTMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID, nsIAtom* aName)
{
if (aNamespaceID != kNameSpaceID_XHTML) {
return mAddSpace;
}
if (aName == nsGkAtoms::title ||
aName == nsGkAtoms::meta ||
aName == nsGkAtoms::link ||
aName == nsGkAtoms::style ||
aName == nsGkAtoms::select ||
aName == nsGkAtoms::option ||
aName == nsGkAtoms::script ||
aName == nsGkAtoms::html) {
return true;
}
else {
nsIParserService* parserService = nsContentUtils::GetParserService();
if (parserService) {
bool res;
parserService->
IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res);
return res;
}
}
return mAddSpace;
}
bool
nsXHTMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID, nsIAtom* aName)
{
if (aNamespaceID != kNameSpaceID_XHTML) {
return false;
}
if ((aName == nsGkAtoms::html) ||
(aName == nsGkAtoms::head) ||
(aName == nsGkAtoms::body) ||
(aName == nsGkAtoms::ul) ||
(aName == nsGkAtoms::ol) ||
(aName == nsGkAtoms::dl) ||
(aName == nsGkAtoms::table) ||
(aName == nsGkAtoms::tbody) ||
(aName == nsGkAtoms::tr) ||
(aName == nsGkAtoms::br) ||
(aName == nsGkAtoms::meta) ||
(aName == nsGkAtoms::link) ||
(aName == nsGkAtoms::script) ||
(aName == nsGkAtoms::select) ||
(aName == nsGkAtoms::map) ||
(aName == nsGkAtoms::area) ||
(aName == nsGkAtoms::style)) {
return true;
}
return false;
}
bool
nsXHTMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID, nsIAtom* aName)
{
if (aNamespaceID != kNameSpaceID_XHTML) {
return false;
}
if ((aName == nsGkAtoms::html) ||
(aName == nsGkAtoms::head) ||
(aName == nsGkAtoms::body) ||
(aName == nsGkAtoms::ul) ||
(aName == nsGkAtoms::ol) ||
(aName == nsGkAtoms::dl) ||
(aName == nsGkAtoms::select) ||
(aName == nsGkAtoms::table) ||
(aName == nsGkAtoms::tbody)) {
return true;
}
return false;
}
bool
nsXHTMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID, nsIAtom* aName)
{
if (aNamespaceID != kNameSpaceID_XHTML) {
return false;
}
if ((aName == nsGkAtoms::html) ||
(aName == nsGkAtoms::head) ||
(aName == nsGkAtoms::body) ||
(aName == nsGkAtoms::tr) ||
(aName == nsGkAtoms::th) ||
(aName == nsGkAtoms::td) ||
(aName == nsGkAtoms::pre) ||
(aName == nsGkAtoms::title) ||
(aName == nsGkAtoms::li) ||
(aName == nsGkAtoms::dt) ||
(aName == nsGkAtoms::dd) ||
(aName == nsGkAtoms::blockquote) ||
(aName == nsGkAtoms::select) ||
(aName == nsGkAtoms::option) ||
(aName == nsGkAtoms::p) ||
(aName == nsGkAtoms::map) ||
(aName == nsGkAtoms::div)) {
return true;
}
else {
nsIParserService* parserService = nsContentUtils::GetParserService();
if (parserService) {
bool res;
parserService->
IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res);
return res;
}
}
return false;
}
void
nsXHTMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode)
{
if (!ShouldMaintainPreLevel() ||
!aNode->IsHTMLElement()) {
return;
}
if (IsElementPreformatted(aNode) ||
aNode->IsAnyOfHTMLElements(nsGkAtoms::script,
nsGkAtoms::style,
nsGkAtoms::noscript,
nsGkAtoms::noframes)) {
PreLevel()++;
}
}
void
nsXHTMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode)
{
if (!ShouldMaintainPreLevel() ||
!aNode->IsHTMLElement()) {
return;
}
if (IsElementPreformatted(aNode) ||
aNode->IsAnyOfHTMLElements(nsGkAtoms::script,
nsGkAtoms::style,
nsGkAtoms::noscript,
nsGkAtoms::noframes)) {
--PreLevel();
}
}
bool
nsXHTMLContentSerializer::IsElementPreformatted(nsIContent* aNode)
{
MOZ_ASSERT(ShouldMaintainPreLevel(), "We should not be calling this needlessly");
if (!aNode->IsElement()) {
return false;
}
RefPtr<nsStyleContext> styleContext =
nsComputedDOMStyle::GetStyleContextForElementNoFlush(aNode->AsElement(),
nullptr, nullptr);
if (styleContext) {
const nsStyleText* textStyle = styleContext->StyleText();
return textStyle->WhiteSpaceOrNewlineIsSignificant();
}
return false;
}
bool
nsXHTMLContentSerializer::SerializeLIValueAttribute(nsIContent* aElement,
nsAString& aStr)
{
// We are copying and we are at the "first" LI node of OL in selected range.
// It may not be the first LI child of OL but it's first in the selected range.
// Note that we get into this condition only once per a OL.
bool found = false;
nsCOMPtr<nsIDOMNode> currNode = do_QueryInterface(aElement);
nsAutoString valueStr;
olState state (0, false);
if (!mOLStateStack.IsEmpty()) {
state = mOLStateStack[mOLStateStack.Length()-1];
// isFirstListItem should be true only before the serialization of the
// first item in the list.
state.isFirstListItem = false;
mOLStateStack[mOLStateStack.Length()-1] = state;
}
int32_t startVal = state.startVal;
int32_t offset = 0;
// Traverse previous siblings until we find one with "value" attribute.
// offset keeps track of how many previous siblings we had tocurrNode traverse.
while (currNode && !found) {
nsCOMPtr<nsIDOMElement> currElement = do_QueryInterface(currNode);
// currElement may be null if it were a text node.
if (currElement) {
nsAutoString tagName;
currElement->GetTagName(tagName);
if (tagName.LowerCaseEqualsLiteral("li")) {
currElement->GetAttribute(NS_LITERAL_STRING("value"), valueStr);
if (valueStr.IsEmpty())
offset++;
else {
found = true;
nsresult rv = NS_OK;
startVal = valueStr.ToInteger(&rv);
}
}
}
nsCOMPtr<nsIDOMNode> tmp;
currNode->GetPreviousSibling(getter_AddRefs(tmp));
currNode.swap(tmp);
}
// If LI was not having "value", Set the "value" attribute for it.
// Note that We are at the first LI in the selected range of OL.
if (offset == 0 && found) {
// offset = 0 => LI itself has the value attribute and we did not need to traverse back.
// Just serialize value attribute like other tags.
NS_ENSURE_TRUE(SerializeAttr(EmptyString(), NS_LITERAL_STRING("value"),
valueStr, aStr, false), false);
}
else if (offset == 1 && !found) {
/*(offset = 1 && !found) means either LI is the first child node of OL
and LI is not having "value" attribute.
In that case we would not like to set "value" attribute to reduce the changes.
*/
//do nothing...
}
else if (offset > 0) {
// Set value attribute.
nsAutoString valueStr;
//As serializer needs to use this valueAttr we are creating here,
valueStr.AppendInt(startVal + offset);
NS_ENSURE_TRUE(SerializeAttr(EmptyString(), NS_LITERAL_STRING("value"),
valueStr, aStr, false), false);
}
return true;
}
bool
nsXHTMLContentSerializer::IsFirstChildOfOL(nsIContent* aElement)
{
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
nsAutoString parentName;
nsCOMPtr<nsIDOMNode> parentNode;
node->GetParentNode(getter_AddRefs(parentNode));
if (parentNode)
parentNode->GetNodeName(parentName);
else
return false;
if (parentName.LowerCaseEqualsLiteral("ol")) {
if (!mOLStateStack.IsEmpty()) {
olState state = mOLStateStack[mOLStateStack.Length()-1];
if (state.isFirstListItem)
return true;
}
return false;
}
else
return false;
}
bool
nsXHTMLContentSerializer::HasNoChildren(nsIContent * aContent) {
for (nsIContent* child = aContent->GetFirstChild();
child;
child = child->GetNextSibling()) {
if (!child->IsNodeOfType(nsINode::eTEXT))
return false;
if (child->TextLength())
return false;
}
return true;
}