Bug 574089 - Optimize ::GetInnerHTML, r=jst

--HG--
extra : rebase_source : 3ed1549eaa96d3ff3fe6bf2bb2562dd9e68fcc17
This commit is contained in:
Olli Pettay 2010-06-26 23:39:56 +03:00
parent fb86d46cf9
commit f48d7a4a08
10 changed files with 191 additions and 60 deletions

View File

@ -63,6 +63,7 @@
#include "nsSMILAnimationController.h"
#endif // MOZ_SMIL
#include "nsIScriptGlobalObject.h"
#include "nsIDocumentEncoder.h"
class nsIContent;
class nsPresContext;
@ -116,10 +117,9 @@ class Element;
} // namespace mozilla
// fbcd570b-dbfa-479b-9bd0-02312129c044
#define NS_IDOCUMENT_IID \
{ 0xfbcd570b, 0xdbfa, 0x479b, \
{ 0x9b, 0xd0, 0x02, 0x31, 0x21, 0x29, 0xc0, 0x44 } }
{ 0x1d8bd3d4, 0x6f6d, 0x49fe, \
{ 0xaf, 0xda, 0xc9, 0x4a, 0xef, 0x8f, 0xcf, 0x1f } }
// Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
@ -1115,6 +1115,16 @@ public:
// Do nothing.
}
already_AddRefed<nsIDocumentEncoder> GetCachedEncoder()
{
return mCachedEncoder.forget();
}
void SetCachedEncoder(nsIDocumentEncoder* aEncoder)
{
mCachedEncoder = aEncoder;
}
// In case of failure, the document really can't initialize the frame loader.
virtual nsresult InitializeFrameLoader(nsFrameLoader* aLoader) = 0;
// In case of failure, the caller must handle the error, for example by
@ -1422,6 +1432,17 @@ protected:
return GetRootElement();
}
void SetContentTypeInternal(const nsACString& aType)
{
mCachedEncoder = nsnull;
mContentType = aType;
}
nsCString GetContentTypeInternal() const
{
return mContentType;
}
nsCOMPtr<nsIURI> mDocumentURI;
nsCOMPtr<nsIURI> mDocumentBaseURI;
@ -1530,7 +1551,9 @@ protected:
PRUint32 mBidiOptions;
nsCString mContentLanguage;
private:
nsCString mContentType;
protected:
// The document's security info
nsCOMPtr<nsISupports> mSecurityInfo;
@ -1560,6 +1583,8 @@ protected:
// Weak reference to mScriptGlobalObject QI:d to nsPIDOMWindow,
// updated on every set of mSecriptGlobalObject.
nsPIDOMWindow *mWindow;
nsCOMPtr<nsIDocumentEncoder> mCachedEncoder;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)

View File

@ -44,6 +44,13 @@ interface nsISelection;
interface nsIDOMNode;
interface nsIOutputStream;
%{ C++
class nsINode;
class nsIDocument;
%}
[ptr] native nsINodePtr(nsINode);
[ptr] native nsIDocumentPtr(nsIDocument);
[scriptable, uuid(c0da5b87-0ba7-4d7c-8cb3-fcb02af4253d)]
interface nsIDocumentEncoderNodeFixup : nsISupports
{
@ -61,7 +68,7 @@ interface nsIDocumentEncoderNodeFixup : nsISupports
nsIDOMNode fixupNode(in nsIDOMNode aNode, out boolean aSerializeCloneKids);
};
[scriptable, uuid(794a81f6-bde6-4f76-9f5e-0ea0911a2d9f)]
[scriptable, uuid(7222bdf1-c2b9-41f1-a40a-a3d65283a95b)]
interface nsIDocumentEncoder : nsISupports
{
// Output methods flag bits. There are a frightening number of these,
@ -238,6 +245,9 @@ interface nsIDocumentEncoder : nsISupports
void init(in nsIDOMDocument aDocument,
in AString aMimeType,
in unsigned long aFlags);
[noscript] void nativeInit(in nsIDocumentPtr aDocument,
in AString aMimeType,
in unsigned long aFlags);
/**
* If the selection is set to a non-null value, then the
@ -270,6 +280,7 @@ interface nsIDocumentEncoder : nsISupports
* @param aContainer The node which child nodes will be encoded.
*/
void setContainerNode(in nsIDOMNode aContainer);
[noscript] void setNativeContainerNode(in nsINodePtr aContainer);
/**
* Documents typically have an intrinsic character set,

View File

@ -1393,7 +1393,7 @@ nsDOMImplementation::Init(nsIURI* aDocumentURI, nsIURI* aBaseURI,
nsDocument::nsDocument(const char* aContentType)
: nsIDocument()
{
mContentType = aContentType;
SetContentTypeInternal(nsDependentCString(aContentType));
#ifdef PR_LOGGING
if (!gDocumentLeakPRLog)
@ -1688,6 +1688,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstBaseNodeWithHref)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDOMImplementation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOriginalDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCachedEncoder)
// Traverse all our nsCOMArrays.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mStyleSheets)
@ -1731,6 +1732,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFirstBaseNodeWithHref)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDOMImplementation)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOriginalDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCachedEncoder)
NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA
@ -1947,7 +1949,7 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
mLastModified.Truncate();
// XXXbz I guess we're assuming that the caller will either pass in
// a channel with a useful type or call SetContentType?
mContentType.Truncate();
SetContentTypeInternal(EmptyCString());
mContentLanguage.Truncate();
mBaseTarget.Truncate();
mReferrer.Truncate();
@ -2155,7 +2157,7 @@ nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
contentType.EndReading(end);
semicolon = start;
FindCharInReadable(';', semicolon, end);
mContentType = Substring(start, semicolon);
SetContentTypeInternal(Substring(start, semicolon));
}
RetrieveRelevantHeaders(aChannel);
@ -2436,7 +2438,7 @@ nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache)
NS_IMETHODIMP
nsDocument::GetContentType(nsAString& aContentType)
{
CopyUTF8toUTF16(mContentType, aContentType);
CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
return NS_OK;
}
@ -2444,11 +2446,11 @@ nsDocument::GetContentType(nsAString& aContentType)
void
nsDocument::SetContentType(const nsAString& aContentType)
{
NS_ASSERTION(mContentType.IsEmpty() ||
mContentType.Equals(NS_ConvertUTF16toUTF8(aContentType)),
NS_ASSERTION(GetContentTypeInternal().IsEmpty() ||
GetContentTypeInternal().Equals(NS_ConvertUTF16toUTF8(aContentType)),
"Do you really want to change the content-type?");
CopyUTF16toUTF8(aContentType, mContentType);
SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType));
}
/* Return true if the document is in the focused top-level window, and is an
@ -7305,7 +7307,7 @@ nsDocument::CloneDocHelper(nsDocument* clone) const
clone->mCompatMode = mCompatMode;
clone->mBidiOptions = mBidiOptions;
clone->mContentLanguage = mContentLanguage;
clone->mContentType = mContentType;
clone->SetContentTypeInternal(GetContentTypeInternal());
clone->mSecurityInfo = mSecurityInfo;
// State from nsDocument

View File

@ -81,6 +81,7 @@
#include "nsReadableUtils.h"
#include "nsTArray.h"
#include "nsIFrame.h"
#include "nsStringBuffer.h"
nsresult NS_NewDomSelection(nsISelection **aDomSelection);
@ -95,8 +96,8 @@ public:
nsDocumentEncoder();
virtual ~nsDocumentEncoder();
NS_DECL_ISUPPORTS
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(nsDocumentEncoder)
NS_DECL_NSIDOCUMENTENCODER
protected:
@ -175,17 +176,36 @@ protected:
PRPackedBool mHaltRangeHint;
PRPackedBool mIsCopying; // Set to PR_TRUE only while copying
PRPackedBool mNodeIsContainer;
nsStringBuffer* mCachedBuffer;
};
NS_IMPL_ADDREF(nsDocumentEncoder)
NS_IMPL_RELEASE(nsDocumentEncoder)
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocumentEncoder)
NS_INTERFACE_MAP_BEGIN(nsDocumentEncoder)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocumentEncoder)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDocumentEncoder)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocumentEncoder)
NS_INTERFACE_MAP_ENTRY(nsIDocumentEncoder)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
nsDocumentEncoder::nsDocumentEncoder()
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocumentEncoder)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSelection)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRange)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNode)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCommonParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocumentEncoder)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSelection)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRange)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNode)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCommonParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
nsDocumentEncoder::nsDocumentEncoder() : mCachedBuffer(nsnull)
{
Initialize();
mMimeType.AssignLiteral("text/plain");
@ -202,10 +222,14 @@ void nsDocumentEncoder::Initialize()
mEndRootIndex = 0;
mHaltRangeHint = PR_FALSE;
mNodeIsContainer = PR_FALSE;
mSerializer = nsnull;
}
nsDocumentEncoder::~nsDocumentEncoder()
{
if (mCachedBuffer) {
mCachedBuffer->Release();
}
}
NS_IMETHODIMP
@ -216,10 +240,23 @@ nsDocumentEncoder::Init(nsIDOMDocument* aDocument,
if (!aDocument)
return NS_ERROR_INVALID_ARG;
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
return NativeInit(doc, aMimeType, aFlags);
}
NS_IMETHODIMP
nsDocumentEncoder::NativeInit(nsIDocument* aDocument,
const nsAString& aMimeType,
PRUint32 aFlags)
{
if (!aDocument)
return NS_ERROR_INVALID_ARG;
Initialize();
mDocument = do_QueryInterface(aDocument);
NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
mDocument = aDocument;
mMimeType = aMimeType;
@ -266,6 +303,14 @@ nsDocumentEncoder::SetContainerNode(nsIDOMNode *aContainer)
return NS_OK;
}
NS_IMETHODIMP
nsDocumentEncoder::SetNativeContainerNode(nsINode* aContainer)
{
mNodeIsContainer = PR_TRUE;
mNode = aContainer;
return NS_OK;
}
NS_IMETHODIMP
nsDocumentEncoder::SetCharset(const nsACString& aCharset)
{
@ -928,11 +973,26 @@ nsDocumentEncoder::EncodeToString(nsAString& aOutputString)
aOutputString.Truncate();
nsCAutoString progId(NS_CONTENTSERIALIZER_CONTRACTID_PREFIX);
AppendUTF16toUTF8(mMimeType, progId);
nsString output;
static const size_t bufferSize = 2048;
if (!mCachedBuffer) {
mCachedBuffer = nsStringBuffer::Alloc(bufferSize);
}
NS_ASSERTION(!mCachedBuffer->IsReadonly(),
"DocumentEncoder shouldn't keep reference to non-readonly buffer!");
static_cast<PRUnichar*>(mCachedBuffer->Data())[0] = PRUnichar(0);
mCachedBuffer->ToString(0, output, PR_TRUE);
// output owns the buffer now!
mCachedBuffer = nsnull;
mSerializer = do_CreateInstance(progId.get());
NS_ENSURE_TRUE(mSerializer, NS_ERROR_NOT_IMPLEMENTED);
if (!mSerializer) {
nsCAutoString progId(NS_CONTENTSERIALIZER_CONTRACTID_PREFIX);
AppendUTF16toUTF8(mMimeType, progId);
mSerializer = do_CreateInstance(progId.get());
NS_ENSURE_TRUE(mSerializer, NS_ERROR_NOT_IMPLEMENTED);
}
nsresult rv = NS_OK;
@ -966,47 +1026,59 @@ nsDocumentEncoder::EncodeToString(nsAString& aOutputString)
if (node != prevNode) {
if (prevNode) {
nsCOMPtr<nsINode> p = do_QueryInterface(prevNode);
rv = SerializeNodeEnd(p, aOutputString);
rv = SerializeNodeEnd(p, output);
NS_ENSURE_SUCCESS(rv, rv);
prevNode = nsnull;
}
nsCOMPtr<nsIContent> content = do_QueryInterface(node);
if (content && content->Tag() == nsGkAtoms::tr) {
nsCOMPtr<nsINode> n = do_QueryInterface(node);
rv = SerializeNodeStart(n, 0, -1, aOutputString);
rv = SerializeNodeStart(n, 0, -1, output);
NS_ENSURE_SUCCESS(rv, rv);
prevNode = node;
}
}
nsCOMPtr<nsIRange> r = do_QueryInterface(range);
rv = SerializeRangeToString(r, aOutputString);
rv = SerializeRangeToString(r, output);
NS_ENSURE_SUCCESS(rv, rv);
}
if (prevNode) {
nsCOMPtr<nsINode> p = do_QueryInterface(prevNode);
rv = SerializeNodeEnd(p, aOutputString);
rv = SerializeNodeEnd(p, output);
NS_ENSURE_SUCCESS(rv, rv);
}
mSelection = nsnull;
} else if (mRange) {
rv = SerializeRangeToString(mRange, aOutputString);
rv = SerializeRangeToString(mRange, output);
mRange = nsnull;
} else if (mNode) {
rv = SerializeToStringRecursive(mNode, aOutputString, mNodeIsContainer);
rv = SerializeToStringRecursive(mNode, output, mNodeIsContainer);
mNode = nsnull;
} else {
rv = mSerializer->AppendDocumentStart(mDocument, aOutputString);
rv = mSerializer->AppendDocumentStart(mDocument, output);
if (NS_SUCCEEDED(rv)) {
rv = SerializeToStringRecursive(mDocument, aOutputString, PR_FALSE);
rv = SerializeToStringRecursive(mDocument, output, PR_FALSE);
}
}
NS_ENSURE_SUCCESS(rv, rv);
rv = mSerializer->Flush(aOutputString);
rv = mSerializer->Flush(output);
if (NS_SUCCEEDED(rv)) {
aOutputString.Append(output.get(), output.Length());
}
mCachedBuffer = nsStringBuffer::FromString(output);
// Try to cache the buffer.
if (mCachedBuffer && mCachedBuffer->StorageSize() == bufferSize &&
!mCachedBuffer->IsReadonly()) {
mCachedBuffer->AddRef();
} else {
mCachedBuffer = nsnull;
}
return rv;
}

View File

@ -101,6 +101,8 @@ nsXHTMLContentSerializer::Init(PRUint32 aFlags, PRUint32 aWrapColumn,
const char* aCharSet, PRBool aIsCopying,
PRBool aRewriteEncodingDeclaration)
{
mInBody = 0;
// 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

View File

@ -111,6 +111,15 @@ nsXMLContentSerializer::Init(PRUint32 aFlags, PRUint32 aWrapColumn,
const char* aCharSet, PRBool aIsCopying,
PRBool aRewriteEncodingDeclaration)
{
mPrefixIndex = 0;
mColPos = 0;
mIndentOverflow = 0;
mIsIndentationAddedOnCurrentLine = PR_FALSE;
mInAttribute = PR_FALSE;
mAddNewlineForRootNode = PR_FALSE;
mAddSpace = PR_FALSE;
mMayIgnoreLineBreakSequence = PR_FALSE;
mCharset = aCharSet;
mFlags = aFlags;

View File

@ -655,13 +655,11 @@ nsGenericHTMLElement::GetInnerHTML(nsAString& aInnerHTML)
{
aInnerHTML.Truncate();
nsCOMPtr<nsIDocument> doc = GetOwnerDoc();
nsIDocument* doc = GetOwnerDoc();
if (!doc) {
return NS_OK; // We rely on the document for doing HTML conversion
}
nsCOMPtr<nsIDOMNode> thisNode(do_QueryInterface(static_cast<nsIContent *>
(this)));
nsresult rv = NS_OK;
nsAutoString contentType;
@ -670,13 +668,15 @@ nsGenericHTMLElement::GetInnerHTML(nsAString& aInnerHTML)
} else {
doc->GetContentType(contentType);
}
nsCOMPtr<nsIDocumentEncoder> docEncoder;
docEncoder =
do_CreateInstance(PromiseFlatCString(
nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
if (!docEncoder) {
docEncoder =
do_CreateInstance(PromiseFlatCString(
nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
NS_ConvertUTF16toUTF8(contentType)
).get());
}
if (!(docEncoder || doc->IsHTML())) {
// This could be some type for which we create a synthetic document. Try
// again as XML
@ -686,17 +686,19 @@ nsGenericHTMLElement::GetInnerHTML(nsAString& aInnerHTML)
NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
rv = docEncoder->Init(domDoc, contentType,
nsIDocumentEncoder::OutputEncodeBasicEntities |
// Output DOM-standard newlines
nsIDocumentEncoder::OutputLFLineBreak |
// Don't do linebreaking that's not present in the source
nsIDocumentEncoder::OutputRaw);
rv = docEncoder->NativeInit(doc, contentType,
nsIDocumentEncoder::OutputEncodeBasicEntities |
// Output DOM-standard newlines
nsIDocumentEncoder::OutputLFLineBreak |
// Don't do linebreaking that's not present in
// the source
nsIDocumentEncoder::OutputRaw);
NS_ENSURE_SUCCESS(rv, rv);
docEncoder->SetContainerNode(thisNode);
return docEncoder->EncodeToString(aInnerHTML);
docEncoder->SetNativeContainerNode(this);
rv = docEncoder->EncodeToString(aInnerHTML);
doc->SetCachedEncoder(docEncoder);
return rv;
}
nsresult

View File

@ -342,7 +342,7 @@ nsHTMLDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
// Make the content type default to "text/html", we are a HTML
// document, after all. Once we start getting data, this may be
// changed.
mContentType = "text/html";
SetContentTypeInternal(nsDependentCString("text/html"));
}
nsStyleSet::sheetType
@ -2035,7 +2035,7 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace)
}
// This will be propagated to the parser when someone actually calls write()
mContentType = aContentType;
SetContentTypeInternal(aContentType);
mWriteState = eDocumentOpened;
@ -2134,7 +2134,7 @@ nsHTMLDocument::Close()
++mWriteLevel;
rv = mParser->Parse(EmptyString(), mParser->GetRootContextKey(),
mContentType, PR_TRUE);
GetContentTypeInternal(), PR_TRUE);
--mWriteLevel;
// XXX Make sure that all the document.written content is
@ -2234,11 +2234,11 @@ nsHTMLDocument::WriteCommon(const nsAString& aText,
// why pay that price when we don't need to?
if (aNewlineTerminate) {
rv = mParser->Parse(aText + new_line,
key, mContentType,
key, GetContentTypeInternal(),
(mWriteState == eNotWriting || (mWriteLevel > 1)));
} else {
rv = mParser->Parse(aText,
key, mContentType,
key, GetContentTypeInternal(),
(mWriteState == eNotWriting || (mWriteLevel > 1)));
}

View File

@ -165,8 +165,10 @@ class nsStringBuffer
* however, string length is always measured in storage units
* (2-byte units for wide strings).
*/
NS_COM void ToString(PRUint32 len, nsAString &str);
NS_COM void ToString(PRUint32 len, nsACString &str);
NS_COM void ToString(PRUint32 len, nsAString &str,
PRBool aMoveOwnership = PR_FALSE);
NS_COM void ToString(PRUint32 len, nsACString &str,
PRBool aMoveOwnership = PR_FALSE);
};
#endif /* !defined(nsStringBuffer_h__ */

View File

@ -264,7 +264,8 @@ nsStringBuffer::FromString(const nsACString& str)
}
void
nsStringBuffer::ToString(PRUint32 len, nsAString &str)
nsStringBuffer::ToString(PRUint32 len, nsAString &str,
PRBool aMoveOwnership)
{
PRUnichar* data = static_cast<PRUnichar*>(Data());
@ -275,12 +276,15 @@ nsStringBuffer::ToString(PRUint32 len, nsAString &str)
PRUint32 flags = accessor->flags();
flags = (flags & 0xFFFF0000) | nsSubstring::F_SHARED | nsSubstring::F_TERMINATED;
AddRef();
if (!aMoveOwnership) {
AddRef();
}
accessor->set(data, len, flags);
}
void
nsStringBuffer::ToString(PRUint32 len, nsACString &str)
nsStringBuffer::ToString(PRUint32 len, nsACString &str,
PRBool aMoveOwnership)
{
char* data = static_cast<char*>(Data());
@ -291,7 +295,9 @@ nsStringBuffer::ToString(PRUint32 len, nsACString &str)
PRUint32 flags = accessor->flags();
flags = (flags & 0xFFFF0000) | nsCSubstring::F_SHARED | nsCSubstring::F_TERMINATED;
AddRef();
if (!aMoveOwnership) {
AddRef();
}
accessor->set(data, len, flags);
}