Files
palemoon27/dom/base/nsXHTMLContentSerializer.cpp
T
roytam1 8a0a002cf2 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1148708: Add missing 'override' annotations in DocAccessibleChild.h. rs=ehsan (d606358545)
- Bug 1210408 - make nsMaiInterfaceAction work with proxies, r=tbsaunde (f7c819c6ae)
- Bug 1210407 - teach nsMaiInterfaceTable to use proxies, r=tbsaunde (4ca4f10b5f)
- bug 1185157 make sure we don't send an event to a destroyed ipc document r=billm (23acf53f75)
- bug 1214864 - make SetCarretOffset() async r=davidb (e3079e9b2d)
- missing of Bug 1139972 - IPC Proxy for charAt, r=tbsaunde (e9593ed752)
- bug 1191598 - Pass MOZ_CURRENT_PROJECT in environment when running post-build automation steps for universal mac builds. r=gps (fc342c6ced)
- Bug 1164596 - Add mach android-emulator command; r=ahal (afeb9b27d1)
- Bug 1223149 - Add basic usage documentation for mach build; r=glandium (bfb802d175)
- Bug 1182301 - Improve 'mach build' notifications. r=gps (2c65a122d1)
- Bug 1184696 - Add clobber targets to |mach clobber|; Ability to clobber compiled python files, r=gps (35d8be292e)
- Bug 1117958 - Allow any debugging options to the run or gtest mach subcommands to automatically enable debugging. r=gps (32f986af4b)
- Bug 1180081 - Properly rebuild gtest/libxul before running gtests. r=gps (80db9a3d49)
- Bug 1171647, part 1 - Define a new function to convert the mode to a string. r=njn (61ad16f5ba)
- Bug 1171647, part 2 - Remove redundant assertion for dark matter mode. r=njn (b5ac9519f3)
- Bug 1058178, part 1 - Implement DMD heap scanning mode. r=njn (60e1079536)
- Bug 1058178, part 2 - Implement address clamping analysis for DMD scan logs. r=njn (45c0326b93)
- Bug 1102388 - Fix DMD static constructor ordering dependency. r=mccr8 (59b87897a1)
- Bug 1128705 - Don't redefine PAGE_SIZE in DMD if it's already defined. r=erahm (49216348ee)
- Bug 1179042 - Add a script for analyzing memory blocks using a heap scan DMD log. r=njn DONTBUILD (1c08d2d66e)
- Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat (1c999d139b)
- Bug 1158772 - fix non-idiomatic memset call in nsDeque.cpp; r=erahm (de6b555245)
- Bug 1199400 - Part 1: Use CheckedInt when growing nsDeque capacity. r=froydnj (dfdf6814a3)
- Bug 1199400 - Part 0: Remove unused nsDequeIterator. r=froydnj (38d69d7f47)
- Bug 1199400 - Part 2: Add tests for possible nsDeque corner cases. r=froydnj (931baff195)
- Bug 1201997 - Part 1 - Converted compiled test to gtest for nsDeque class. Added tests to test untested methods. r=froydn (e893916651)
- Bug 1201997 - Part 2 - Removing unused methods from the nsDeque class. r=froydn (41595a90ac)
- Bug 1201997 - Part 3 - Make internally used methods private. r=froydn (8cd3afd96f)
- Bug 1201997 - Part 4 - Change size and offset variables to size_t.r=froydn (73eabc8d60)
- Bug 1215140 P1 Add an nsIConsoleReportCollector interface to support navigation channel logging. r=bz (8a41535e2b)
- Bug 1215140 P2 Make HttpBaseChannel implement nsIConsoleReportCollector. r=bz (75fca301f2)
- Bug 1197679 - If nsUnknownDecoder is involved in e10s DivertToParent can break. r=jduell (5d94a12504)
- Bug 1178991 - smartptr for http converter r=hurley (8e7fbc8443)
- bug 366559 - patch 2, fix nsHTTPCompressConv indentation r=bagder (ba762da587)
- bug 366559 - patch 3, fix nsHTTPCompressConv bracing style r=bagder (54195ab451)
- bits of  bug 366559 - patch 7, content-encoding brotli for http (f0b4051022)
- Bug 1205112 - Make PushEvent.data nullable. r=mt,smaug (775db32856)
- Bug 1193414 - SharedWorkers thread should be kept alive also when the SharedWorker object is CCed, r=khuey (b77ea8125c)
- Bug 1206520: Add about:config prefs to enable throwing on asm.js validation failures; r=bz (c42126665d)
- Bug 1193414 - Telemetry for SharedWorker spawning. r=bkelly (77984b7bcc)
- Bug 1205676 - Enable WPT service-worker/unregister-then-register-new-script.https.html in e10s, r=nsm (ec24939cf6)
- Bug 1193133 - Throw when calling postMessage from a Service Worker dom object with no global. r=bkelly (526dcacfab)
- Bug 1181871 P1 Only enforce Cache Context shared data destruction on target thread after init. r=ehsan (cdbf3ed3a8)
- Bug 1181871 P2 Fix ServiceWorkerManager usage of stack-based ErrorResult. r=ehsan (c449195d90)
- minor cleanup and missing bit of 1198230 (02f459db05)
- Bug 1143717 - Implement the ServiceWorkerMessageEvent interface. r=baku (027b3465f2)
- fix misspatch (708eee4e84)
- Bug 1188545 - Disentangle service workers from shared workers and refactor event dispatching code into a separate class. r=nsm,mrbkap (fb5b5341c9)
- Bug 1205228 - Change PackagedAppVerifier to notify the verification result asynchronously. r=valentin. (9edda0fa00)
- Bug 1178518 - Packaged App Utils. r=valentin (f60f3b7a93)
- Bug 1213150 - Part 1: Add a nsContentUtils::IsNonSubresourceRequest helper; r=jdm (b509cc3cc9)
- Bug 1213150 - Part 2: Rework ShouldPrepareForIntercept() in terms of subresource requests; r=jdm (2e92fe8780)
- Bug 1213150 - Part 3: Remove nsIInterceptedChannel.isNavigation; r=jdm (becf1cc12f)
- Bug 1213150 follow-up: fix build bustage (8d73d6ca73)
- Bug 1198394 - Part 1: Allow interception of HSTS upgraded connections in non-e10s mode; r=mcmanus (f504c5be08)
- Bug 1198394 - Part 2: Add a test for interception of HSTS upgraded connections; r=jdm (054e984eef)
- Bug 1187011 - Don't allow response body with null body status. r=bkelly (b1860741d1)
- missing bit of 1140788 (29d319712e)
- Bug 1213436 - Reject core dumps with node IDs that don't fit in an IEEE 754 double; r=sfink (3c1f6fdda0)
- Bug 1211006 - Add Debugger.Source.prototype.canonicalId; r=ejpbruel (eef7b79fce)
- Bug 1199218 - Implement JS::ubi::Node::size for js::LazyScript referents; r=sfink (098a48d240)
- Bug 1220031 - Add JS::ubi::Node::scriptFilename; r=sfink (6b824ae680)
- Bug 1143575. Remove unused MediaQueue::Empty. r=cpearce (de737f3433)
- Bug 1209933 - Make sure all parent runtime pointers are the topmost parent, r=billm. (fe824d967d)
- Bug 1197012 - Fix ThrowTypeError in Notification. r=mccr8 (0b1a097526)
- Bug 1197893 - Check the number of arguments for ThrowTypeError() and ThrowRangeError() at compile time. r=peterv (d98c7d78a0)
- Bug 1142083 - Add test for IDN Unicode domain redirect. r=mcmanus (0c8961fe17)
- Bug 1187159 - Add mochitest for loading packaged apps (iframe+fetch+mozapp) r=jduell (ce90ea561b)
- Bug 1186290 - Notify TabParent to switch process when loading a signed package. r=honzab, r=kanru. (c58a14554a)
- fix (15e2df75eb)
- Bug 1206124 P1 Fix "same-origin" CORS credentials in FetchDriver. r=ehsan (fae1bb6ab3)
- Bug 1206124 P2 Test fetch() with credentials and redirects. r=ehsan (ffc6254112)
- Bug 1211751: Remove nsIChannelEventSink-forwarding from EventSource and FetchDriver. It's never needed. r=smaug (adafe5737a)
- Bug 1212433 Fail fetch() calls that require preflight and also redirect. r=sicking a=abillings (c0d6742b9e)
- Bug 1193128 - Fix base64 decoding when fetching data URIs. r=baku (80bafa291a)
- Bug 1195167 part 1: Let necko handle all protocols. r=bkelly (bb932b0ada)
- Bug 1195167 part 2: Remove redundant aCORSFlag argument and instead use mCORSFlagEverSet. r=bkelly (beadafcad0)
- Bug 1195167 part 3: Remove more scheme-specific handling from FetchDriver. r=bkelly (d00b38db9e)
- Bug 1195167 part 4: Remove FetchDriver::BasicFetch since it is empty. r=bkelly (c5ed097267)
- Bug 1210413 P2 Test CORS credentials on cross-origin redirects. r=sicking a=dveditz (b4eeb8aac0)
- Bug 1210413 P1 Propagate new channel load flags from child to parent on redirect. r=jduell a=dveditz (8b329af4fa)
- Bug 1195167 part 5: Make FetchDriver use AsyncOpen2. r=bkelly (cc217c4cc1)
- Bug 1195167 part 6: Some code simplification since necko handles fetch recursion. r=bkelly (f3b6da2262)
- Bug 1195167: Followup to fix test which I forgot to change (81e7439a2e)
- Bug 1215746: Remove RequestMode::Cors_with_forced_preflight. r=bkelly (0336e812b6)
- Bug 1211000: Move CORS preflight logic from nsCORSListenerProxy to nsCORSPreflightListener. r=ehsan (bf2f71cf22)
- missing bit of Bug 1211443 - Drop scheduled update if decoder initialization isn't done yet. r=jya (f6bc074e33)
- Bug 1182571: Fix nsILoadInfo->GetContentPolicyType API to be less ambigious. Audit and fix all users of it. r=ckerschb (5af6fa7442)
- fix (e40c8e7625)
- Bug 1173811 - Part 1: Propagate the response URL to intercepted channels when necessary (non-e10s). r=mayhemer,bkelly (26f4f13c28)
- Bug 1173811 - Part 2: Propagate the response URL to intercepted channels when necessary (e10s). r=mayhemer,bkelly (a603fe1df2)
- Bug 1154309 - Add New Resource Timing Fields r=bz,hurley (1d14eb6bef)
- Bug 1175685 - add OriginAttribute to LoadInfo. r=jonas, r=ckerschb, r=michal (a5d18bb637)
- Bug 1175685 - add OriginAttribute to LoadInfo. r=jonas, r=ckerschb, r=michal (fb07d2c8aa)
- Bug 1212904 P1 Add a LoadTainting enumeration. r=jduell (a1db8a3e99)
- Bug 1212904 P2 Add LoadTainting information to nsILoadInfo. r=jduell (2482e5e334)
- Bug 1221151 - use [infallible] in nsILoadInfo.idl instead of manual %{C++ blocks; r=jduell (aae73129b6)
- Bug 1045891 - CSP 2 child-src implementation r=ckerschb (792920aeb9)
- Bug 1219931 - CSP: Don't allow removing a policy (r=sicking) (9daaab4186)
- Bug 1208661 - Dump client-side layer textures. r=BenWa (1f2d17d515)
2022-10-17 11:08:37 +08:00

1013 lines
31 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::AppendEndOfElementStart(nsIContent *aOriginalElement,
nsIAtom * aName,
int32_t aNamespaceID,
nsAString& aStr)
{
// this method is not called by nsHTMLContentSerializer
// so we don't have to check HTML element, just XHTML
NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
if (kNameSpaceID_XHTML != aNamespaceID) {
return nsXMLContentSerializer::AppendEndOfElementStart(aOriginalElement, aName,
aNamespaceID, aStr);
}
nsIContent* content = aOriginalElement;
// for non empty elements, even if they are not a container, we always
// serialize their content, because the XHTML element could contain non XHTML
// nodes useful in some context, like in an XSLT stylesheet
if (HasNoChildren(content)) {
nsIParserService* parserService = nsContentUtils::GetParserService();
if (parserService) {
bool isContainer;
parserService->
IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(aName),
isContainer);
if (!isContainer) {
// for backward compatibility with HTML 4 user agents
// only non-container HTML elements can be closed immediatly,
// and a space is added before />
return AppendToString(NS_LITERAL_STRING(" />"), aStr);
}
}
}
return AppendToString(kGreaterThan, aStr);
}
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) {
AppendNewLineToString(aStr);
return false;
}
if (aContent->IsHTMLElement(nsGkAtoms::body)) {
++mInBody;
}
return true;
}
bool
nsXHTMLContentSerializer::CheckElementEnd(nsIContent * aContent,
bool & aForceFormat,
nsAString& aStr)
{
NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
// this method is not called by nsHTMLContentSerializer
// so we don't have to check HTML element, just XHTML
if (aContent->IsHTMLElement()) {
if (mIsCopying && aContent->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);
}
}
if (HasNoChildren(aContent)) {
nsIParserService* parserService = nsContentUtils::GetParserService();
if (parserService) {
bool isContainer;
parserService->
IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(
aContent->NodeInfo()->NameAtom()),
isContainer);
if (!isContainer) {
// non-container HTML elements are already closed,
// see AppendEndOfElementStart
return false;
}
}
}
// for backward compatibility with old HTML user agents,
// empty elements should have an ending tag, so we mustn't call
// nsXMLContentSerializer::CheckElementEnd
return true;
}
bool dummyFormat;
return nsXMLContentSerializer::CheckElementEnd(aContent, 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;
}