[Chrome FastLoad]

Implement XUL Prototype Document and Prototype Element serialization/deserialization scheme for FastLoad of chrome documents.
r=brendan, sr=hyatt


git-svn-id: svn://10.0.0.236/trunk@120648 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
ben%netscape.com 2002-05-03 03:04:34 +00:00
parent 521dde6f78
commit 989514f6c4
13 changed files with 1164 additions and 764 deletions

View File

@ -246,6 +246,8 @@ Shutdown(nsIModule* aSelf)
nsXULContentUtils::Finish();
nsXULAtoms::ReleaseAtoms();
nsXULElement::ReleaseGlobals();
nsXULPrototypeElement::ReleaseGlobals();
nsXULPrototypeScript::ReleaseGlobals();
#endif
#ifdef MOZ_SVG

View File

@ -55,8 +55,6 @@
#include "nsXBLProtoImplMethod.h"
#include "nsXBLProtoImplField.h"
static NS_DEFINE_CID(kCSSParserCID, NS_CSSPARSER_CID);
nsresult
NS_NewXBLContentSink(nsIXMLContentSink** aResult,
nsIDocument* aDoc,

View File

@ -23,6 +23,7 @@
* Peter Annema <disttsc@bart.nl>
* Brendan Eich <brendan@mozilla.org>
* Mike Shaver <shaver@mozilla.org>
* Ben Goodger <ben@netscape.com>
*
* This Original Code has been modified by IBM Corporation.
* Modifications made by IBM described herein are
@ -81,6 +82,7 @@
#include "nsIDocument.h"
#include "nsIEventListenerManager.h"
#include "nsIEventStateManager.h"
#include "nsIFastLoadService.h"
#include "nsIHTMLContentContainer.h"
#include "nsIHTMLStyleSheet.h"
#include "nsIStyleContext.h"
@ -152,6 +154,9 @@
class nsIWebShell;
// Global object maintenance
nsINodeInfoManager* nsXULPrototypeElement::sNodeInfoManager = nsnull;
nsICSSParser* nsXULPrototypeElement::sCSSParser = nsnull;
nsIXULPrototypeCache* nsXULPrototypeScript::sXULPrototypeCache = nsnull;
nsIXBLService * nsXULElement::gXBLService = nsnull;
// XXX This is sure to change. Copied from mozilla/layout/xul/content/src/nsXULAtoms.cpp
@ -445,25 +450,6 @@ PRUint32 nsXULPrototypeAttribute::gNumCacheSets;
PRUint32 nsXULPrototypeAttribute::gNumCacheFills;
#endif
//----------------------------------------------------------------------
// nsXULNode
// XXXbe temporary, make 'em pure when all subclasses implement
nsresult
nsXULPrototypeNode::Serialize(nsIObjectOutputStream* aStream,
nsIScriptContext* aContext)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsXULPrototypeNode::Deserialize(nsIObjectInputStream* aStream,
nsIScriptContext* aContext)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
//----------------------------------------------------------------------
// nsXULElement
//
@ -4932,38 +4918,182 @@ nsXULPrototypeElement::Serialize(nsIObjectOutputStream* aStream,
{
nsresult rv;
#if 0
// XXXbe partial deserializer is not ready for this yet
rv = aStream->Write32(PRUint32(mNumChildren));
if (NS_FAILED(rv)) return rv;
#endif
// Write basic prototype data
rv = aStream->Write32(mType);
rv |= aStream->Write32(mLineNo);
// XXXbe check for failure once all elements have been taught to serialize
for (PRInt32 i = 0; i < mNumChildren; i++) {
rv = mChildren[i]->Serialize(aStream, aContext);
NS_ASSERTION(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_IMPLEMENTED,
"can't serialize!");
// Write Node Info
nsAutoString namespaceURI;
rv |= mNodeInfo->GetNamespaceURI(namespaceURI);
rv |= aStream->WriteWStringZ(namespaceURI.get());
nsAutoString qualifiedName;
rv |= mNodeInfo->GetQualifiedName(qualifiedName);
rv |= aStream->WriteWStringZ(qualifiedName.get());
// Write Attributes
rv |= aStream->Write32(mNumAttributes);
nsAutoString attributeValue, attributeNamespaceURI, attributeName;
PRInt32 i;
for (i = 0; i < mNumAttributes; ++i) {
rv |= mAttributes[i].mNodeInfo->GetNamespaceURI(attributeNamespaceURI);
rv |= aStream->WriteWStringZ(attributeNamespaceURI.get());
rv |= mAttributes[i].mNodeInfo->GetQualifiedName(attributeName);
rv |= aStream->WriteWStringZ(attributeName.get());
rv |= mAttributes[i].mValue.GetValue(attributeValue);
rv |= aStream->WriteWStringZ(attributeValue.get());
}
return NS_OK;
// Now write children
rv |= aStream->Write32(PRUint32(mNumChildren));
for (i = 0; i < mNumChildren; i++) {
nsXULPrototypeNode* child = mChildren[i];
switch (child->mType) {
case eType_Element:
case eType_Text:
rv |= child->Serialize(aStream, aContext);
break;
case eType_Script:
rv |= aStream->Write32(child->mType);
nsXULPrototypeScript* script = NS_STATIC_CAST(nsXULPrototypeScript*, child);
if (script) {
rv |= aStream->WriteBoolean(script->mOutOfLine);
if (! script->mOutOfLine)
rv |= script->Serialize(aStream, aContext);
else
rv |= aStream->WriteCompoundObject(script->mSrcURI,
NS_GET_IID(nsIURI),
PR_TRUE);
}
break;
}
}
return rv;
}
nsresult
nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
nsIScriptContext* aContext)
nsIScriptContext* aContext,
nsIURI* aDocumentURI)
{
nsresult rv;
PRUint32 numChildren;
rv = aStream->Read32(&numChildren);
if (NS_FAILED(rv)) return rv;
PRUint32 number;
rv = aStream->Read32(&number);
mLineNo = (PRInt32)number;
// XXXbe temporary until we stop parsing tags when deserializing
NS_ASSERTION(PRInt32(numChildren) == mNumChildren, "XUL/FastLoad mismatch");
// Read Node Info
nsXPIDLString namespaceURI;
rv |= aStream->ReadWStringZ(getter_Copies(namespaceURI));
nsXPIDLString qualifiedName;
rv |= aStream->ReadWStringZ(getter_Copies(qualifiedName));
// XXXbe check for failure once all elements have been taught to serialize
for (PRInt32 i = 0; i < mNumChildren; i++)
(void) mChildren[i]->Deserialize(aStream, aContext);
return NS_OK;
nsINodeInfoManager* nimgr = GetNodeInfoManager();
rv |= nimgr->GetNodeInfo(qualifiedName, namespaceURI, *getter_AddRefs(mNodeInfo));
// Read Attributes
rv |= aStream->Read32(&number);
mNumAttributes = PRInt32(number);
PRInt32 i;
if (mNumAttributes > 0) {
mAttributes = new nsXULPrototypeAttribute[mNumAttributes];
if (! mAttributes)
return NS_ERROR_OUT_OF_MEMORY;
nsXPIDLString attributeValue, attributeNamespaceURI, attributeName;
for (i = 0; i < mNumAttributes; ++i) {
rv |= aStream->ReadWStringZ(getter_Copies(attributeNamespaceURI));
rv |= aStream->ReadWStringZ(getter_Copies(attributeName));
rv |= nimgr->GetNodeInfo(attributeName, attributeNamespaceURI,
*getter_AddRefs(mAttributes[i].mNodeInfo));
rv |= aStream->ReadWStringZ(getter_Copies(attributeValue));
mAttributes[i].mValue.SetValue(attributeValue);
}
// Compute the element's class list if the element has a 'class' attribute.
nsAutoString value;
if (NS_SUCCEEDED(GetAttr(kNameSpaceID_None, nsXULAtoms::clazz, value)))
rv |= nsClassList::ParseClasses(&mClassList, value);
// Parse the element's 'style' attribute
if (NS_SUCCEEDED(GetAttr(kNameSpaceID_None, nsXULAtoms::style, value))) {
nsICSSParser* parser = GetCSSParser();
rv |= parser->ParseStyleAttribute(value, aDocumentURI,
getter_AddRefs(mInlineStyleRule));
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to parse style rule");
}
}
rv |= aStream->Read32(&number);
mNumChildren = PRInt32(number);
if (mNumChildren > 0) {
mChildren = new nsXULPrototypeNode*[mNumChildren];
if (! mChildren)
return NS_ERROR_OUT_OF_MEMORY;
for (i = 0; i < mNumChildren; i++) {
rv |= aStream->Read32(&number);
Type childType = (Type)number;
nsXULPrototypeNode* child = nsnull;
switch (childType) {
case eType_Element:
child = new nsXULPrototypeElement(-1);
if (! child)
return NS_ERROR_OUT_OF_MEMORY;
child->mType = childType;
rv |= child->Deserialize(aStream, aContext, aDocumentURI);
break;
case eType_Text:
child = new nsXULPrototypeText(-1);
if (! child)
return NS_ERROR_OUT_OF_MEMORY;
child->mType = childType;
rv |= child->Deserialize(aStream, aContext, aDocumentURI);
break;
case eType_Script:
// language version obtained during deserialization.
child = new nsXULPrototypeScript(-1, nsnull);
if (! child)
return NS_ERROR_OUT_OF_MEMORY;
child->mType = childType;
nsXULPrototypeScript* script = NS_STATIC_CAST(nsXULPrototypeScript*, child);
if (script) {
rv |= aStream->ReadBoolean(&script->mOutOfLine);
if (! script->mOutOfLine)
rv |= script->Deserialize(aStream, aContext, aDocumentURI);
else {
rv |= aStream->ReadObject(PR_TRUE, getter_AddRefs(script->mSrcURI));
rv |= script->DeserializeOutOfLineScript(aStream, aContext);
}
}
break;
}
mChildren[i] = child;
}
}
return rv;
}
@ -4989,6 +5119,7 @@ nsXULPrototypeElement::GetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, nsAString&
nsXULPrototypeScript::nsXULPrototypeScript(PRInt32 aLineNo, const char *aVersion)
: nsXULPrototypeNode(eType_Script, aLineNo),
mSrcLoading(PR_FALSE),
mOutOfLine(PR_TRUE),
mSrcLoadWaiters(nsnull),
mJSObject(nsnull),
mLangVersion(aVersion)
@ -5011,16 +5142,11 @@ nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream,
{
nsresult rv;
// Write basic prototype data
aStream->Write32(mLineNo);
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nsnull || !mJSObject,
"script source still loading when serializing?!");
#if 0
// XXXbe redundant while we're still parsing XUL instead of deserializing it
// XXXbe also, we should serialize mType and mLineNo first.
rv = NS_WriteOptionalCompoundObject(aStream, mSrcURI, NS_GET_IID(nsIURI),
PR_TRUE);
if (NS_FAILED(rv)) return rv;
#endif
JSContext* cx = NS_REINTERPRET_CAST(JSContext*,
aContext->GetNativeContext());
@ -5072,21 +5198,20 @@ nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream,
nsresult
nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
nsIScriptContext* aContext)
nsIScriptContext* aContext,
nsIURI* aDocumentURI)
{
NS_TIMELINE_MARK_FUNCTION("chrome js deserialize");
nsresult rv;
// Read basic prototype data
PRUint32 number;
aStream->Read32(&number);
mLineNo = (PRInt32)number;
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nsnull || !mJSObject,
"prototype script not well-initialized when deserializing?!");
#if 0
// XXXbe redundant while we're still parsing XUL instead of deserializing it
// XXXbe also, we should deserialize mType and mLineNo first.
rv = NS_ReadOptionalObject(aStream, PR_TRUE, getter_AddRefs(mSrcURI));
if (NS_FAILED(rv)) return rv;
#endif
PRUint32 size;
rv = aStream->Read32(&size);
if (NS_FAILED(rv)) return rv;
@ -5157,8 +5282,106 @@ nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
return NS_OK;
}
// XXXbe temporary, goes away when we serialize entire XUL prototype document
#include "nsIFastLoadService.h"
nsresult
nsXULPrototypeScript::DeserializeOutOfLineScript(nsIObjectInputStream* aInput,
nsIScriptContext* aContext)
{
nsIXULPrototypeCache* cache = GetXULCache();
nsCOMPtr<nsIFastLoadService> fastLoadService;
cache->GetFastLoadService(getter_AddRefs(fastLoadService));
nsCOMPtr<nsIObjectInputStream> objectInput;
if (fastLoadService)
fastLoadService->GetInputStream(getter_AddRefs(objectInput));
if (objectInput) {
PRBool useXULCache = PR_TRUE;
if (mSrcURI) {
// NB: we must check the XUL script cache early, to avoid
// multiple deserialization attempts for a given script, which
// would exhaust the multiplexed stream containing the singly
// serialized script. Note that nsXULDocument::LoadScript
// checks the XUL script cache too, in order to handle the
// serialization case.
//
// We need do this only for <script src='strres.js'> and the
// like, i.e., out-of-line scripts that are included by several
// different XUL documents multiplexed in the FastLoad file.
cache->GetEnabled(&useXULCache);
if (useXULCache) {
cache->GetScript(mSrcURI, NS_REINTERPRET_CAST(void**, &mJSObject));
}
}
if (! mJSObject) {
// Keep track of FastLoad failure via rv, so we can
// AbortFastLoads if things look bad.
nsresult rv = NS_OK;
nsCOMPtr<nsIURI> oldURI;
if (mSrcURI) {
nsCAutoString spec;
mSrcURI->GetAsciiSpec(spec);
rv = fastLoadService->StartMuxedDocument(mSrcURI, spec.get(),
nsIFastLoadService::NS_FASTLOAD_READ);
if (NS_SUCCEEDED(rv))
rv = fastLoadService->SelectMuxedDocument(mSrcURI, getter_AddRefs(oldURI));
} else {
// An inline script: check FastLoad multiplexing direction
// and skip Deserialize if we're not reading from a
// muxed stream to get inline objects that are contained in
// the current document.
PRInt32 direction;
fastLoadService->GetDirection(&direction);
if (direction != nsIFastLoadService::NS_FASTLOAD_READ)
rv = NS_ERROR_NOT_AVAILABLE;
}
// Don't reflect errors into rv: mJSObject will be null
// after any error, which suffices to cause the script to
// be reloaded (from the src= URI, if any) and recompiled.
// We're better off slow-loading than bailing out due to a
// FastLoad error.
if (NS_SUCCEEDED(rv))
rv = Deserialize(objectInput, aContext, nsnull);
if (NS_SUCCEEDED(rv) && mSrcURI) {
rv = fastLoadService->EndMuxedDocument(mSrcURI);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIURI> tempURI;
rv = fastLoadService->SelectMuxedDocument(oldURI, getter_AddRefs(tempURI));
NS_ASSERTION(NS_SUCCEEDED(rv) && (!tempURI || tempURI == mSrcURI),
"not currently deserializing into the script we thought we were!");
}
}
if (NS_SUCCEEDED(rv)) {
if (useXULCache && mSrcURI) {
PRBool isChrome = PR_FALSE;
mSrcURI->SchemeIs("chrome", &isChrome);
if (isChrome) {
nsIXULPrototypeCache* cache = GetXULCache();
cache->PutScript(mSrcURI, NS_REINTERPRET_CAST(void*, mJSObject));
}
}
} else {
// If mSrcURI is not in the FastLoad multiplex,
// rv will be NS_ERROR_NOT_AVAILABLE and we'll try to
// update the FastLoad file to hold a serialization of
// this script, once it has finished loading.
if (rv != NS_ERROR_NOT_AVAILABLE)
cache->AbortFastLoads();
}
}
}
return NS_OK;
}
nsresult
nsXULPrototypeScript::Compile(const PRUnichar* aText,
@ -5229,7 +5452,7 @@ nsXULPrototypeScript::Compile(const PRUnichar* aText,
(void**)&mJSObject);
if (NS_FAILED(rv)) return rv;
if (mJSObject) {
// Root the compiled prototype script object.
JSContext* cx = NS_REINTERPRET_CAST(JSContext*,
@ -5237,18 +5460,64 @@ nsXULPrototypeScript::Compile(const PRUnichar* aText,
if (!cx)
return NS_ERROR_UNEXPECTED;
// XXXbe temporary, until we serialize/deserialize everything from the
// nsXULPrototypeDocument on down...
nsCOMPtr<nsIFastLoadService> fastLoadService(do_GetFastLoadService());
nsCOMPtr<nsIObjectOutputStream> objectOutput;
fastLoadService->GetOutputStream(getter_AddRefs(objectOutput));
if (objectOutput) {
rv = Serialize(objectOutput, context);
if (NS_FAILED(rv))
nsXULDocument::AbortFastLoads();
if (mOutOfLine) {
nsCOMPtr<nsIFastLoadService> fastLoadService;
nsIXULPrototypeCache* cache = GetXULCache();
cache->GetFastLoadService(getter_AddRefs(fastLoadService));
if (fastLoadService) {
nsCOMPtr<nsIObjectOutputStream> objectOutput;
fastLoadService->GetOutputStream(getter_AddRefs(objectOutput));
if (objectOutput) {
rv = Serialize(objectOutput, context);
if (NS_FAILED(rv))
cache->AbortFastLoads();
}
}
}
}
return NS_OK;
}
//----------------------------------------------------------------------
//
// nsXULPrototypeText
//
nsresult
nsXULPrototypeText::Serialize(nsIObjectOutputStream* aStream,
nsIScriptContext* aContext)
{
nsresult rv;
// Write basic prototype data
rv = aStream->Write32(mType);
rv |= aStream->Write32(mLineNo);
rv |= aStream->WriteWStringZ(mValue.get());
return rv;
}
nsresult
nsXULPrototypeText::Deserialize(nsIObjectInputStream* aStream,
nsIScriptContext* aContext,
nsIURI* aDocumentURI)
{
nsresult rv;
// Write basic prototype data
PRUint32 number;
rv = aStream->Read32(&number);
mLineNo = (PRInt32)number;
PRUnichar* str = nsnull;
rv |= aStream->ReadWStringZ(&str);
mValue.Adopt(str);
return rv;
}

View File

@ -23,6 +23,7 @@
* Chris Waterson <waterson@netscape.com>
* Peter Annema <disttsc@bart.nl>
* Mike Shaver <shaver@mozilla.org>
* Ben Goodger <ben@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -54,6 +55,7 @@
#include "nsIAtom.h"
#include "nsINodeInfo.h"
#include "nsIControllers.h"
#include "nsICSSParser.h"
#include "nsIDOMElement.h"
#include "nsIDOMEventReceiver.h"
#include "nsIDOMXULElement.h"
@ -69,12 +71,14 @@
#include "nsIXBLBinding.h"
#include "nsIURI.h"
#include "nsIXULContent.h"
#include "nsIXULPrototypeCache.h"
#include "nsIXULTemplateBuilder.h"
#include "nsIBoxObject.h"
#include "nsXULAttributes.h"
#include "nsIChromeEventHandler.h"
#include "nsXULAttributeValue.h"
#include "nsIXBLService.h"
#include "nsLayoutCID.h"
#include "nsGenericElement.h" // for nsCheapVoidArray
@ -94,6 +98,8 @@ class nsIWebShell;
class nsIObjectInputStream;
class nsIObjectOutputStream;
static NS_DEFINE_CID(kCSSParserCID, NS_CSSPARSER_CID);
////////////////////////////////////////////////////////////////////////
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
@ -195,9 +201,10 @@ public:
virtual ~nsXULPrototypeNode() {}
virtual nsresult Serialize(nsIObjectOutputStream* aStream,
nsIScriptContext* aContext);
nsIScriptContext* aContext) = 0;
virtual nsresult Deserialize(nsIObjectInputStream* aStream,
nsIScriptContext* aContext);
nsIScriptContext* aContext,
nsIURI* aDocumentURI) = 0;
void AddRef() { mRefCnt++; };
void Release()
@ -249,7 +256,8 @@ public:
virtual nsresult Serialize(nsIObjectOutputStream* aStream,
nsIScriptContext* aContext);
virtual nsresult Deserialize(nsIObjectInputStream* aStream,
nsIScriptContext* aContext);
nsIScriptContext* aContext,
nsIURI* aDocumentURI);
PRInt32 mNumChildren;
nsXULPrototypeNode** mChildren; // [OWNER]
@ -263,6 +271,36 @@ public:
nsClassList* mClassList;
nsresult GetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, nsAString& aValue);
static void ReleaseGlobals()
{
NS_IF_RELEASE(sNodeInfoManager);
NS_IF_RELEASE(sCSSParser);
}
protected:
static nsINodeInfoManager* GetNodeInfoManager()
{
if (!sNodeInfoManager) {
CallCreateInstance(NS_NODEINFOMANAGER_CONTRACTID, &sNodeInfoManager);
nsCOMPtr<nsINameSpaceManager> nsmgr;
NS_NewNameSpaceManager(getter_AddRefs(nsmgr));
sNodeInfoManager->Init(nsnull, nsmgr);
}
return sNodeInfoManager;
}
static nsINodeInfoManager* sNodeInfoManager;
static nsICSSParser* GetCSSParser()
{
if (!sCSSParser)
CallCreateInstance(kCSSParserCID, &sCSSParser);
return sCSSParser;
}
static nsICSSParser* sCSSParser;
};
struct JSRuntime;
@ -278,7 +316,10 @@ public:
virtual nsresult Serialize(nsIObjectOutputStream* aStream,
nsIScriptContext* aContext);
virtual nsresult Deserialize(nsIObjectInputStream* aStream,
nsIScriptContext* aContext);
nsIScriptContext* aContext,
nsIURI* aDocumentURI);
virtual nsresult DeserializeOutOfLineScript(nsIObjectInputStream* aInput,
nsIScriptContext* aContext);
nsresult Compile(const PRUnichar* aText, PRInt32 aTextLength,
nsIURI* aURI, PRInt32 aLineNo,
@ -287,9 +328,25 @@ public:
nsCOMPtr<nsIURI> mSrcURI;
PRBool mSrcLoading;
PRBool mOutOfLine;
nsXULDocument* mSrcLoadWaiters; // [OWNER] but not COMPtr
JSObject* mJSObject;
const char* mLangVersion;
static void ReleaseGlobals()
{
NS_IF_RELEASE(sXULPrototypeCache);
}
protected:
static nsIXULPrototypeCache* GetXULCache()
{
if (!sXULPrototypeCache)
CallGetService("@mozilla.org/xul/xul-prototype-cache;1", &sXULPrototypeCache);
return sXULPrototypeCache;
}
static nsIXULPrototypeCache* sXULPrototypeCache;
};
class nsXULPrototypeText : public nsXULPrototypeNode
@ -306,6 +363,12 @@ public:
MOZ_COUNT_DTOR(nsXULPrototypeText);
}
virtual nsresult Serialize(nsIObjectOutputStream* aStream,
nsIScriptContext* aContext);
virtual nsresult Deserialize(nsIObjectInputStream* aStream,
nsIScriptContext* aContext,
nsIURI* aDocumentURI);
nsString mValue;
};

View File

@ -161,11 +161,6 @@ public:
*/
NS_IMETHOD OnPrototypeLoadDone() = 0;
/**
* Callback from the content sink upon resumption from the parser.
*/
NS_IMETHOD OnResumeContentSink() = 0;
/**
* Notify the XUL document that it's being hidden
*/

View File

@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <ben@netscape.com>
*
*
* Alternatively, the contents of this file may be used under the terms of
@ -49,9 +50,11 @@
class nsICSSStyleSheet;
class nsIURI;
class nsIXULPrototypeDocument;
class nsIXULDocument;
class nsCString;
class nsIDocument;
class nsIXBLDocumentInfo;
class nsIFastLoadService;
// {3A0A0FC1-8349-11d3-BE47-00104BDE6048}
#define NS_XULPROTOTYPECACHE_CID \
@ -105,10 +108,34 @@ public:
* Stop the FastLoad process abruptly, removing the FastLoad file.
*/
NS_IMETHOD AbortFastLoads() = 0;
/**
* Retrieve the FastLoad service
*/
NS_IMETHOD GetFastLoadService(nsIFastLoadService** aResult) = 0;
/**
* Remove a XULDocument from the set of loading documents
*/
NS_IMETHOD RemoveFromFastLoadSet(nsIURI* aDocumentURI) = 0;
/**
* Write Prototype Document to FastLoad file
*/
NS_IMETHOD WritePrototype(nsIXULPrototypeDocument* aDocument) = 0;
};
extern NS_IMETHODIMP
NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult);
const char XUL_FASTLOAD_FILE_BASENAME[] = "XUL";
#define XUL_FASTLOAD_FILE_VERSION (0xfeedbeef - 4)
#define XUL_SERIALIZATION_BUFFER_SIZE (64 * 1024)
#define XUL_DESERIALIZATION_BUFFER_SIZE (8 * 1024)
#endif // nsIXULPrototypeCache_h__

View File

@ -56,6 +56,7 @@ class nsString;
class nsVoidArray;
class nsXULPrototypeElement;
class nsIXULDocument;
class nsIScriptGlobalObject;
// {187A63D0-8337-11d3-BE47-00104BDE6048}
#define NS_IXULPROTOTYPEDOCUMENT_IID \

View File

@ -52,6 +52,9 @@ REQUIRES = xpcom \
DEFINES=-D_IMPL_NS_HTML -DWIN32_LEAN_AND_MEAN
EXPORTS = \
$(NULL)
CPP_OBJS= \
.\$(OBJDIR)\nsElementMap.obj \
.\$(OBJDIR)\nsXULCommandDispatcher.obj \

View File

@ -42,10 +42,7 @@
/*
An implementation for a Gecko-style content sink that knows how
to build an RDF content model from XUL.
For each container tag, an RDF Sequence object is created that
contains the order set of children of that container.
to build a content model from XUL.
For more information on XUL, see http://www.mozilla.org/xpfe
@ -74,8 +71,6 @@
#include "nsINodeInfo.h"
#include "nsIParser.h"
#include "nsIPresShell.h"
#include "nsIRDFCompositeDataSource.h"
#include "nsIRDFService.h"
#include "nsIScriptContext.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptGlobalObjectOwner.h"
@ -120,7 +115,6 @@ static PRLogModuleInfo* gLog;
static const char kXULNameSpaceURI[] = XUL_NAMESPACE_URI;
static NS_DEFINE_CID(kCSSParserCID, NS_CSSPARSER_CID);
static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID);
static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID);
@ -553,10 +547,6 @@ XULContentSinkImpl::WillInterrupt(void)
NS_IMETHODIMP
XULContentSinkImpl::WillResume(void)
{
nsCOMPtr<nsIXULDocument> xuldoc(do_QueryReferent(mDocument));
if (xuldoc)
xuldoc->OnResumeContentSink();
// XXX Notify the webshell, if necessary
return NS_OK;
}
@ -951,6 +941,8 @@ XULContentSinkImpl::HandleEndElement(const PRUnichar *aName)
if (! script->mSrcURI && ! script->mJSObject) {
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
script->mOutOfLine = PR_FALSE;
rv = script->Compile(mText, mTextLength, mDocumentURL,
script->mLineNo, doc, mPrototype);
}
@ -1487,109 +1479,6 @@ XULContentSinkImpl::OpenScript(const PRUnichar** aAttributes,
}
}
// XXXbe temporary, until we serialize/deserialize everything from
// the nsXULPrototypeDocument on down...
nsCOMPtr<nsIFastLoadService> fastLoadService;
nsCOMPtr<nsIObjectInputStream> objectInput;
nsXULDocument::GetFastLoadService(getter_AddRefs(fastLoadService));
if (fastLoadService)
fastLoadService->GetInputStream(getter_AddRefs(objectInput));
if (objectInput) {
PRBool useXULCache = PR_TRUE;
if (script->mSrcURI) {
// NB: we must check the XUL script cache early, to avoid
// multiple deserialization attempts for a given script, which
// would exhaust the multiplexed stream containing the singly
// serialized script. Note that nsXULDocument::LoadScript
// checks the XUL script cache too, in order to handle the
// serialization case.
//
// We need do this only for <script src='strres.js'> and the
// like, i.e., out-of-line scripts that are included by several
// different XUL documents multiplexed in the FastLoad file.
gXULCache->GetEnabled(&useXULCache);
if (useXULCache) {
gXULCache->GetScript(script->mSrcURI,
NS_REINTERPRET_CAST(void**, &script->mJSObject));
}
}
if (! script->mJSObject) {
nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner(do_QueryInterface(mPrototype));
nsCOMPtr<nsIScriptGlobalObject> globalObject;
globalOwner->GetScriptGlobalObject(getter_AddRefs(globalObject));
NS_ASSERTION(globalObject != nsnull, "no prototype global object!");
nsCOMPtr<nsIScriptContext> scriptContext;
globalObject->GetContext(getter_AddRefs(scriptContext));
NS_ASSERTION(scriptContext != nsnull,
"no prototype script context!");
// Keep track of FastLoad failure via rv2, so we can
// AbortFastLoads if things look bad.
nsresult rv2 = NS_OK;
if (script->mSrcURI) {
nsCAutoString spec;
script->mSrcURI->GetAsciiSpec(spec);
rv2 = fastLoadService->StartMuxedDocument(script->mSrcURI,
spec.get(),
nsIFastLoadService::NS_FASTLOAD_READ);
if (NS_SUCCEEDED(rv2))
rv2 = fastLoadService->SelectMuxedDocument(script->mSrcURI);
} else {
// An inline script: check FastLoad multiplexing direction
// and skip script->Deserialize if we're not reading from a
// muxed stream to get inline objects that are contained in
// the current document.
PRInt32 direction;
fastLoadService->GetDirection(&direction);
if (direction != nsIFastLoadService::NS_FASTLOAD_READ)
rv2 = NS_ERROR_NOT_AVAILABLE;
}
// Don't reflect errors into rv: mJSObject will be null
// after any error, which suffices to cause the script to
// be reloaded (from the src= URI, if any) and recompiled.
// We're better off slow-loading than bailing out due to a
// FastLoad error.
if (NS_SUCCEEDED(rv2))
rv2 = script->Deserialize(objectInput, scriptContext);
if (NS_SUCCEEDED(rv2) && script->mSrcURI) {
rv2 = fastLoadService->EndMuxedDocument(script->mSrcURI);
if (NS_SUCCEEDED(rv2)) {
// We must reselect the including XUL document now,
// in case the next script in it is an inline one
// whose data comes from its stream, not from the
// now-exhausted document that was multiplexed for
// script->mSrcURI. Inline scripts are compiled in
// XULContentSinkImpl::CloseContainer.
rv2 = fastLoadService->SelectMuxedDocument(mDocumentURL);
}
}
if (NS_SUCCEEDED(rv2)) {
if (useXULCache && script->mSrcURI) {
PRBool isChrome = PR_FALSE;
script->mSrcURI->SchemeIs("chrome", &isChrome);
if (isChrome) {
gXULCache->PutScript(script->mSrcURI,
NS_REINTERPRET_CAST(void*, script->mJSObject));
}
}
} else {
// If script->mSrcURI is not in the FastLoad multiplex,
// rv2 will be NS_ERROR_NOT_AVAILABLE and we'll try to
// update the FastLoad file to hold a serialization of
// this script, once it has finished loading.
if (rv2 != NS_ERROR_NOT_AVAILABLE)
nsXULDocument::AbortFastLoads();
}
}
}
nsVoidArray* children;
rv = mContextStack.GetTopChildren(&children);
if (NS_FAILED(rv)) {

View File

@ -48,7 +48,7 @@
Notes
-----
1. We do some monkey business in the document observer methods to
1. We do some monkey business in the document observer methods to`
keep the element map in sync for HTML elements. Why don't we just
do it for _all_ elements? Well, in the case of XUL elements,
which may be lazily created during frame construction, the
@ -156,8 +156,6 @@
#include "nsMimeTypes.h"
#include "nsISelectionController.h"
#include "nsContentUtils.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIFastLoadFileControl.h"
#include "nsIFastLoadService.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
@ -193,7 +191,7 @@ static NS_DEFINE_IID(kIParserIID, NS_IPARSER_IID);
static PRBool IsChromeURI(nsIURI* aURI)
{
// why is this check a member function of nsXULDocument? -gagan
PRBool isChrome=PR_FALSE;
PRBool isChrome = PR_FALSE;
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome)
return PR_TRUE;
return PR_FALSE;
@ -422,9 +420,8 @@ nsXULDocument::nsXULDocument(void)
mNextSrcLoadWaiter(nsnull),
mDisplaySelection(PR_FALSE),
mIsPopup(PR_FALSE),
mIsFastLoad(PR_FALSE),
mApplyingPersistedAttrs(PR_FALSE),
mNextFastLoad(nsnull),
mIsWritingFastLoad(PR_FALSE),
mBoxObjectTable(nsnull),
mTemplateBuilderTable(nsnull),
mResolutionPhase(nsForwardReference::eStart),
@ -449,10 +446,6 @@ nsXULDocument::nsXULDocument(void)
mSubDocuments = nsnull;
}
nsIFastLoadService* nsXULDocument::gFastLoadService = nsnull;
nsIFile* nsXULDocument::gFastLoadFile = nsnull;
nsXULDocument* nsXULDocument::gFastLoadList = nsnull;
nsXULDocument::~nsXULDocument()
{
NS_ASSERTION(mNextSrcLoadWaiter == nsnull,
@ -531,20 +524,21 @@ nsXULDocument::~nsXULDocument()
}
if (gXULCache) {
// Remove the current document here from the FastLoad table in
// case the document did not make it past StartLayout in
// ResumeWalk. The FastLoad table must be clear of entries so
// that the FastLoad file footer can be properly written.
gXULCache->RemoveFromFastLoadSet(mDocumentURL);
nsServiceManager::ReleaseService(kXULPrototypeCacheCID, gXULCache);
gXULCache = nsnull;
}
NS_IF_RELEASE(gFastLoadService); // don't need ReleaseService nowadays!
NS_IF_RELEASE(gFastLoadFile);
}
if (mNodeInfoManager) {
mNodeInfoManager->DropDocumentReference();
}
if (mIsFastLoad)
RemoveFromFastLoadList();
}
@ -732,26 +726,28 @@ nsXULDocument::StartDocumentLoad(const char* aCommand,
rv = PrepareStyleSheets(mDocumentURL);
if (NS_FAILED(rv)) return rv;
// Get the content type, if possible, to see if it's a cached XUL
// load. We explicitly ignore failure at this point, because
// certain hacks (cough, the directory viewer) need to be able to
// StartDocumentLoad() before the channel's content type has been
// detected.
nsCAutoString contentType;
aChannel->GetContentType(contentType);
if (contentType.Equals(NS_LITERAL_CSTRING("mozilla.application/cached-xul"))) {
// Look in the chrome cache: we've got this puppy loaded
// already.
nsCOMPtr<nsIXULPrototypeDocument> proto;
rv = gXULCache->GetPrototype(mDocumentURL, getter_AddRefs(proto));
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(proto != nsnull, "no prototype on cached load");
if (! proto)
return NS_ERROR_UNEXPECTED;
// Look in the chrome cache: we've got this puppy loaded
// already.
nsCOMPtr<nsIXULPrototypeDocument> proto;
gXULCache->GetPrototype(mDocumentURL, getter_AddRefs(proto));
// Same comment as nsChromeProtocolHandler::NewChannel and
// nsXULDocument::ResumeWalk
// - Ben Goodger
//
// We don't abort on failure here because there are too many valid
// cases that can return failure, and the null-ness of |proto| is enough
// to trigger the fail-safe parse-from-disk solution. Example failure cases
// (for reference) include:
//
// NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the FastLoad cache,
// parse from disk
// other: the FastLoad cache file, XUL.mfl, could not be found, probably
// due to being accessed before a profile has been selected (e.g.
// loading chrome for the profile manager itself). This must be
// parsed from disk.
if (proto) {
// If we're racing with another document to load proto, wait till the
// load has finished loading before trying to add cloned style sheets.
// nsXULDocument::EndLoad will call proto->NotifyLoadDone, which will
@ -784,20 +780,16 @@ nsXULDocument::StartDocumentLoad(const char* aCommand,
gXULCache->GetEnabled(&useXULCache);
PRBool fillXULCache = (useXULCache && IsChromeURI(mDocumentURL));
// Try to open a FastLoad file for reading, or create one for writing.
// If one exists and looks valid, the nsIFastLoadService will purvey
// a non-null input stream via its GetInputStream method, and we will
// deserialize saved objects from that stream. Otherwise, we'll write
// to the output stream returned by GetOutputStream.
if (fillXULCache && nsCRT::strcmp(aCommand, "view-source") != 0)
StartFastLoad();
// It's just a vanilla document load. Create a parser to deal
// with the stream n' stuff.
nsCOMPtr<nsIParser> parser;
rv = PrepareToLoad(aContainer, aCommand, aChannel, aLoadGroup, getter_AddRefs(parser));
if (NS_FAILED(rv)) return rv;
mIsWritingFastLoad = PR_TRUE;
nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener");
if (NS_FAILED(rv)) return rv;
@ -1744,6 +1736,7 @@ nsXULDocument::EndLoad()
// Whack the prototype document into the cache so that the next
// time somebody asks for it, they don't need to load it by hand.
nsCOMPtr<nsIURI> uri;
rv = mCurrentPrototype->GetURI(getter_AddRefs(uri));
if (NS_FAILED(rv)) return rv;
@ -1752,8 +1745,16 @@ nsXULDocument::EndLoad()
PRBool useXULCache;
gXULCache->GetEnabled(&useXULCache);
// If the current prototype is an overlay document (non-master prototype)
// and we're filling the FastLoad disk cache, tell the cache we're done
// loading it, and write the prototype.
if (useXULCache && mIsWritingFastLoad &&
mMasterPrototype != mCurrentPrototype)
gXULCache->WritePrototype(mCurrentPrototype);
nsCOMPtr<nsIXULChromeRegistry> reg(do_GetService(kChromeRegistryCID, &rv));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsISupportsArray> sheets;
reg->GetStyleSheets(uri, getter_AddRefs(sheets));
@ -1784,11 +1785,6 @@ nsXULDocument::EndLoad()
rv = mCurrentPrototype->NotifyLoadDone();
if (NS_FAILED(rv)) return rv;
if (mIsFastLoad) {
rv = gFastLoadService->EndMuxedDocument(uri);
if (NS_FAILED(rv))
AbortFastLoads();
}
}
// Now walk the prototype to build content.
@ -1826,23 +1822,6 @@ nsXULDocument::OnPrototypeLoadDone()
}
NS_IMETHODIMP
nsXULDocument::OnResumeContentSink()
{
if (mIsFastLoad) {
nsresult rv;
nsCOMPtr<nsIURI> uri;
rv = mCurrentPrototype->GetURI(getter_AddRefs(uri));
if (NS_FAILED(rv)) return rv;
rv = gFastLoadService->SelectMuxedDocument(uri);
if (NS_FAILED(rv))
AbortFastLoads();
}
return NS_OK;
}
PR_STATIC_CALLBACK(PRBool)
ClearPresentationStuff(nsHashKey *aKey, void *aData, void* aClosure)
{
@ -2154,6 +2133,7 @@ nsXULDocument::ExecuteOnBroadcastHandlerFor(nsIContent* aBroadcaster,
return NS_OK;
}
NS_IMETHODIMP
nsXULDocument::AttributeChanged(nsIContent* aElement,
PRInt32 aNameSpaceID,
@ -4917,411 +4897,6 @@ nsXULDocument::CreateElement(nsINodeInfo *aNodeInfo, nsIContent** aResult)
}
const char XUL_FASTLOAD_FILE_BASENAME[] = "XUL";
// XXXbe move to nsXULPrototypeDocument.cpp in the nsISerializable section.
// We'll increment (or maybe decrement, for easier deciphering) this maigc
// number as we flesh out the FastLoad file to include more and more data
// induced by the master prototype document.
#define XUL_FASTLOAD_FILE_VERSION (0xfeedbeef - 3)
#define XUL_SERIALIZATION_BUFFER_SIZE (64 * 1024)
#define XUL_DESERIALIZATION_BUFFER_SIZE (8 * 1024)
class nsXULFastLoadFileIO : public nsIFastLoadFileIO
{
public:
nsXULFastLoadFileIO(nsIFile* aFile)
: mFile(aFile) {
NS_INIT_REFCNT();
MOZ_COUNT_CTOR(nsXULFastLoadFileIO);
}
virtual ~nsXULFastLoadFileIO() {
MOZ_COUNT_DTOR(nsXULFastLoadFileIO);
}
NS_DECL_ISUPPORTS
NS_DECL_NSIFASTLOADFILEIO
nsCOMPtr<nsIFile> mFile;
nsCOMPtr<nsIInputStream> mInputStream;
nsCOMPtr<nsIOutputStream> mOutputStream;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsXULFastLoadFileIO, nsIFastLoadFileIO)
MOZ_DECL_CTOR_COUNTER(nsXULFastLoadFileIO)
NS_IMETHODIMP
nsXULFastLoadFileIO::GetInputStream(nsIInputStream** aResult)
{
if (! mInputStream) {
nsresult rv;
nsCOMPtr<nsIInputStream> fileInput;
rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInput), mFile);
if (NS_FAILED(rv)) return rv;
rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream),
fileInput,
XUL_DESERIALIZATION_BUFFER_SIZE);
if (NS_FAILED(rv)) return rv;
}
NS_ADDREF(*aResult = mInputStream);
return NS_OK;
}
NS_IMETHODIMP
nsXULFastLoadFileIO::GetOutputStream(nsIOutputStream** aResult)
{
if (! mOutputStream) {
PRInt32 ioFlags = PR_WRONLY;
if (! mInputStream)
ioFlags |= PR_CREATE_FILE | PR_TRUNCATE;
nsresult rv;
nsCOMPtr<nsIOutputStream> fileOutput;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOutput), mFile,
ioFlags, 0644);
if (NS_FAILED(rv)) return rv;
rv = NS_NewBufferedOutputStream(getter_AddRefs(mOutputStream),
fileOutput,
XUL_SERIALIZATION_BUFFER_SIZE);
if (NS_FAILED(rv)) return rv;
}
NS_ADDREF(*aResult = mOutputStream);
return NS_OK;
}
static PRBool gDisableXULFastLoad = PR_FALSE; // enabled by default
static PRBool gChecksumXULFastLoadFile = PR_TRUE; // XXXbe too paranoid
static const char kDisableXULFastLoadPref[] = "nglayout.debug.disable_xul_fastload";
static const char kChecksumXULFastLoadFilePref[] = "nglayout.debug.checksum_xul_fastload_file";
PR_STATIC_CALLBACK(int)
FastLoadPrefChangedCallback(const char* aPref, void* aClosure)
{
nsCOMPtr<nsIPref> prefs = do_GetService(NS_PREF_CONTRACTID);
if (prefs) {
PRBool wasEnabled = !gDisableXULFastLoad;
prefs->GetBoolPref(kDisableXULFastLoadPref, &gDisableXULFastLoad);
if (wasEnabled && gDisableXULFastLoad)
nsXULDocument::AbortFastLoads();
prefs->GetBoolPref(kChecksumXULFastLoadFilePref, &gChecksumXULFastLoadFile);
}
return 0;
}
nsresult
nsXULDocument::StartFastLoad()
{
nsresult rv;
// Test gFastLoadList to decide whether this is the first nsXULDocument
// participating in FastLoad. If gFastLoadList is non-null, this document
// must not be first, but it can join the FastLoad process. Examples of
// multiple master documents participating include hiddenWindow.xul and
// navigator.xul on the Mac, and multiple-app-component (e.g., mailnews
// and browser) startup due to command-line arguments.
//
// XXXbe we should attempt to update the FastLoad file after startup!
//
// XXXbe we do not yet use nsFastLoadPtrs, but once we do, we must keep
// the FastLoad input stream open for the life of the app.
if (gFastLoadList) {
mIsFastLoad = PR_TRUE;
mNextFastLoad = gFastLoadList;
gFastLoadList = this;
return NS_OK;
}
NS_ASSERTION(!gFastLoadService,
"gFastLoadList null but gFastLoadService non-null!");
nsCOMPtr<nsIPref> prefs(do_GetService(NS_PREF_CONTRACTID));
if (prefs) {
prefs->GetBoolPref(kDisableXULFastLoadPref, &gDisableXULFastLoad);
prefs->GetBoolPref(kChecksumXULFastLoadFilePref, &gChecksumXULFastLoadFile);
prefs->RegisterCallback(kDisableXULFastLoadPref,
FastLoadPrefChangedCallback,
nsnull);
prefs->RegisterCallback(kChecksumXULFastLoadFilePref,
FastLoadPrefChangedCallback,
nsnull);
if (gDisableXULFastLoad)
return NS_ERROR_NOT_AVAILABLE;
}
// Get the chrome directory to validate against the one stored in the
// FastLoad file, or to store there if we're generating a new file.
nsCOMPtr<nsIFile> chromeDir;
rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR, getter_AddRefs(chromeDir));
if (NS_FAILED(rv))
return rv;
nsCAutoString chromePath;
rv = chromeDir->GetPath(chromePath);
if (NS_FAILED(rv))
return rv;
// Use a local to refer to the service till we're sure we succeeded, then
// commit to gFastLoadService. Same for gFastLoadFile, which is used to
// delete the FastLoad file on abort.
nsCOMPtr<nsIFastLoadService> fastLoadService(do_GetFastLoadService());
if (! fastLoadService)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIFile> file;
rv = fastLoadService->NewFastLoadFile(XUL_FASTLOAD_FILE_BASENAME,
getter_AddRefs(file));
if (NS_FAILED(rv)) return rv;
// Give the FastLoad service an object by which it can get or create a
// file output stream given an input stream on the same file.
nsXULFastLoadFileIO* xio = new nsXULFastLoadFileIO(file);
nsCOMPtr<nsIFastLoadFileIO> io = NS_STATIC_CAST(nsIFastLoadFileIO*, xio);
if (! io)
return NS_ERROR_OUT_OF_MEMORY;
fastLoadService->SetFileIO(io);
// Try to read an existent FastLoad file.
PRBool exists = PR_FALSE;
if (NS_SUCCEEDED(file->Exists(&exists)) && exists) {
nsCOMPtr<nsIInputStream> input;
rv = io->GetInputStream(getter_AddRefs(input));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIObjectInputStream> objectInput;
rv = fastLoadService->NewInputStream(input, getter_AddRefs(objectInput));
if (NS_SUCCEEDED(rv)) {
if (gChecksumXULFastLoadFile) {
nsCOMPtr<nsIFastLoadReadControl>
readControl(do_QueryInterface(objectInput));
if (readControl) {
// Verify checksum, using the fastLoadService's checksum
// cache to avoid computing more than once per session.
PRUint32 checksum;
rv = readControl->GetChecksum(&checksum);
if (NS_SUCCEEDED(rv)) {
PRUint32 verified;
rv = fastLoadService->ComputeChecksum(file,
readControl,
&verified);
if (NS_SUCCEEDED(rv) && verified != checksum) {
#ifdef DEBUG
printf("bad FastLoad file checksum\n");
#endif
rv = NS_ERROR_FAILURE;
}
}
}
}
if (NS_SUCCEEDED(rv)) {
// XXXbe get version number, scripts only for now -- bump
// version later when rest of prototype document header is
// serialized
PRUint32 version;
rv = objectInput->Read32(&version);
if (NS_SUCCEEDED(rv)) {
if (version != XUL_FASTLOAD_FILE_VERSION) {
#ifdef DEBUG
printf("bad FastLoad file version\n");
#endif
rv = NS_ERROR_UNEXPECTED;
} else {
nsXPIDLCString fileChromePath;
rv = objectInput->ReadStringZ(
getter_Copies(fileChromePath));
if (NS_SUCCEEDED(rv) &&
!fileChromePath.Equals(chromePath)) {
rv = NS_ERROR_UNEXPECTED;
}
}
}
}
}
if (NS_SUCCEEDED(rv)) {
fastLoadService->SetInputStream(objectInput);
} else {
// NB: we must close before attempting to remove, for non-Unix OSes
// that can't do open-unlink.
if (objectInput)
objectInput->Close();
else
input->Close();
xio->mInputStream = nsnull;
#ifdef DEBUG
file->MoveTo(nsnull, NS_LITERAL_CSTRING("Invalid.mfasl"));
#else
file->Remove(PR_FALSE);
#endif
exists = PR_FALSE;
}
}
// FastLoad file not found, or invalid: write a new one.
if (! exists) {
nsCOMPtr<nsIOutputStream> output;
rv = io->GetOutputStream(getter_AddRefs(output));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIObjectOutputStream> objectOutput;
rv = fastLoadService->NewOutputStream(output,
getter_AddRefs(objectOutput));
if (NS_SUCCEEDED(rv)) {
rv = objectOutput->Write32(XUL_FASTLOAD_FILE_VERSION);
if (NS_SUCCEEDED(rv))
rv = objectOutput->WriteStringZ(chromePath.get());
}
// Remove here even though some errors above will lead to a FastLoad
// file invalidation. Other errors (failure to note the dependency on
// installed-chrome.txt, e.g.) will not cause invalidation, and we may
// as well tidy up now.
if (NS_FAILED(rv)) {
if (objectOutput)
objectOutput->Close();
else
output->Close();
xio->mOutputStream = nsnull;
file->Remove(PR_FALSE);
return rv;
}
fastLoadService->SetOutputStream(objectOutput);
}
// If this fails, some weird reentrancy or multi-threading has occurred.
NS_ASSERTION(!gFastLoadService && !gFastLoadFile && !gFastLoadList,
"something's very wrong!");
// Success! Insert this nsXULDocument into the (empty) gFastLoadList,
// and commit locals to globals.
mIsFastLoad = PR_TRUE;
gFastLoadList = this;
NS_ADDREF(gFastLoadService = fastLoadService);
NS_ADDREF(gFastLoadFile = file);
return NS_OK;
}
nsresult
nsXULDocument::EndFastLoad()
{
nsresult rv = NS_OK, rv2 = NS_OK;
// Exclude all non-chrome loads and XUL cache hits right away.
if (! mIsFastLoad)
return rv;
// Remove this document from the global FastLoad list. We use the list's
// emptiness instead of a counter, to decide when the FastLoad process has
// finally completed. We use a singly-linked list because there won't be
// more than a handful of master XUL documents racing, worst case.
mIsFastLoad = PR_FALSE;
RemoveFromFastLoadList();
// Fetch the current input (if FastLoad file existed) or output (if we're
// creating the FastLoad file during this app startup) stream.
nsCOMPtr<nsIObjectInputStream> objectInput;
nsCOMPtr<nsIObjectOutputStream> objectOutput;
gFastLoadService->GetInputStream(getter_AddRefs(objectInput));
gFastLoadService->GetOutputStream(getter_AddRefs(objectOutput));
if (objectOutput) {
#if 0
// XXXbe for now, write scripts as we sink content...
rv = objectOutput->WriteObject(mMasterPrototype, PR_TRUE);
#endif
// If this is the last of one or more XUL master documents loaded
// together at app startup, close the FastLoad service's singleton
// output stream now.
//
// NB: we must close input after output, in case the output stream
// implementation needs to read from the input stream, to compute a
// FastLoad file checksum. In that case, the implementation used
// nsIFastLoadFileIO to get the corresponding input stream for this
// output stream.
if (! gFastLoadList) {
gFastLoadService->SetOutputStream(nsnull);
rv = objectOutput->Close();
if (NS_SUCCEEDED(rv) && gChecksumXULFastLoadFile) {
rv = gFastLoadService->CacheChecksum(gFastLoadFile,
objectOutput);
}
}
}
if (objectInput) {
// If this is the last of one or more XUL master documents loaded
// together at app startup, close the FastLoad service's singleton
// input stream now.
if (! gFastLoadList) {
gFastLoadService->SetInputStream(nsnull);
rv2 = objectInput->Close();
}
}
// If the list is empty now, the FastLoad process is done.
if (! gFastLoadList) {
NS_RELEASE(gFastLoadService);
NS_RELEASE(gFastLoadFile);
}
return NS_FAILED(rv) ? rv : rv2;
}
nsresult
nsXULDocument::AbortFastLoads()
{
#ifdef DEBUG_brendan
NS_BREAK();
#endif
// Save a strong ref to the FastLoad file, so we can remove it after we
// close open streams to it.
nsCOMPtr<nsIFile> file = gFastLoadFile;
// End all pseudo-concurrent XUL document FastLoads, which will close any
// i/o streams open on the FastLoad file.
while (gFastLoadList)
gFastLoadList->EndFastLoad();
// Now rename or remove the file.
if (file) {
#ifdef DEBUG
file->MoveTo(nsnull, NS_LITERAL_CSTRING("Aborted.mfasl"));
#else
file->Remove(PR_FALSE);
#endif
}
// Flush the XUL cache for good measure, in case we cached a bogus/downrev
// script, somehow.
if (gXULCache)
gXULCache->Flush();
return NS_OK;
}
nsresult
nsXULDocument::PrepareToLoad(nsISupports* aContainer,
const char* aCommand,
@ -5364,30 +4939,6 @@ nsXULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
rv = mCurrentPrototype->SetURI(aURI);
if (NS_FAILED(rv)) return rv;
// If we're reading or writing a FastLoad file, tell the FastLoad
// service to start multiplexing data from aURI, associating it in
// the file with the URL's string. Each time the parser resumes
// sinking content, it "selects" the memorized document from the
// FastLoad multiplexor, using the nsIURI* as a fast identifier.
if (mIsFastLoad) {
nsCAutoString urlspec;
rv = aURI->GetAsciiSpec(urlspec);
if (NS_FAILED(rv)) return rv;
// If StartMuxedDocument returns NS_ERROR_NOT_AVAILABLE, then
// we must be reading the file, and urlspec was not associated
// with any multiplexed stream in it. The FastLoad service
// will therefore arrange to update the file, writing new data
// at the end while old (available) data continues to be read
// from the pre-existing part of the file.
rv = gFastLoadService->StartMuxedDocument(aURI, urlspec.get(),
nsIFastLoadService::NS_FASTLOAD_READ |
nsIFastLoadService::NS_FASTLOAD_WRITE);
NS_ASSERTION(rv != NS_ERROR_NOT_AVAILABLE, "only reading FastLoad?!");
if (NS_FAILED(rv))
AbortFastLoads();
}
// Create a XUL content sink, a parser, and kick off a load for
// the overlay.
nsCOMPtr<nsIXULContentSink> sink;
@ -6012,9 +5563,24 @@ nsXULDocument::ResumeWalk()
#endif
// Look in the prototype cache for the prototype document with
// the specified overlay URI.
rv = gXULCache->GetPrototype(uri, getter_AddRefs(mCurrentPrototype));
if (NS_FAILED(rv)) return rv;
gXULCache->GetPrototype(uri, getter_AddRefs(mCurrentPrototype));
// Same comment as nsChromeProtocolHandler::NewChannel and
// nsXULDocument::StartDocumentLoad
// - Ben Goodger
//
// We don't abort on failure here because there are too many valid
// cases that can return failure, and the null-ness of |proto| is enough
// to trigger the fail-safe parse-from-disk solution. Example failure cases
// (for reference) include:
//
// NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the FastLoad cache,
// parse from disk
// other: the FastLoad cache file, XUL.mfl, could not be found, probably
// due to being accessed before a profile has been selected (e.g.
// loading chrome for the profile manager itself). This must be
// parsed from disk.
PRBool cache;
gXULCache->GetEnabled(&cache);
@ -6050,6 +5616,8 @@ nsXULDocument::ResumeWalk()
rv = PrepareToLoadPrototype(uri, "view", nsnull, getter_AddRefs(parser));
if (NS_FAILED(rv)) return rv;
mIsWritingFastLoad = PR_TRUE;
nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser);
if (! listener)
return NS_ERROR_UNEXPECTED;
@ -6100,10 +5668,11 @@ nsXULDocument::ResumeWalk()
StartLayout();
// Since we've bothered to load and parse all this fancy XUL, let's try to
// save a condensed serialization of it for faster loading next time. We
// do this after StartLayout() in case we want to serialize frames.
EndFastLoad();
PRBool useXULCache;
gXULCache->GetEnabled(&useXULCache);
if (useXULCache && mIsWritingFastLoad)
gXULCache->WritePrototype(mMasterPrototype);
for (PRInt32 i = mObservers.Count() - 1; i >= 0; --i) {
nsIDocumentObserver* observer = (nsIDocumentObserver*) mObservers[i];
@ -6122,10 +5691,10 @@ nsXULDocument::ResumeWalk()
mPlaceHolderRequest = nsnull;
}
}
return rv;
}
nsresult
nsXULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, PRBool* aBlock)
{
@ -6250,27 +5819,33 @@ nsXULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
// before serializing script data under scriptProto->Compile, and
// End muxing afterward.
nsCOMPtr<nsIURI> uri = scriptProto->mSrcURI;
if (mIsFastLoad) {
nsCOMPtr<nsIFastLoadService> fastLoadService;
gXULCache->GetFastLoadService(getter_AddRefs(fastLoadService));
if (fastLoadService) {
nsCAutoString urispec;
uri->GetAsciiSpec(urispec);
rv = gFastLoadService->StartMuxedDocument(uri, urispec.get(),
nsIFastLoadService::NS_FASTLOAD_WRITE);
rv = fastLoadService->StartMuxedDocument(uri, urispec.get(),
nsIFastLoadService::NS_FASTLOAD_WRITE);
NS_ASSERTION(rv != NS_ERROR_NOT_AVAILABLE, "reading FastLoad?!");
if (NS_SUCCEEDED(rv))
gFastLoadService->SelectMuxedDocument(uri);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIURI> oldURI;
fastLoadService->SelectMuxedDocument(uri, getter_AddRefs(oldURI));
}
}
nsString stringStr; stringStr.AssignWithConversion(string, stringLen);
rv = scriptProto->Compile(stringStr.get(), stringLen, uri, 1, this,
mMasterPrototype);
mCurrentPrototype);
// End muxing the .js file into the FastLoad file. We don't Abort
// the FastLoad process here, when writing, as we do when reading.
// XXXbe maybe we should...
// NB: we don't need to Select mDocumentURL again, because scripts
// load after their including prototype document has fully loaded.
if (mIsFastLoad)
gFastLoadService->EndMuxedDocument(uri);
if (fastLoadService)
fastLoadService->EndMuxedDocument(uri);
aStatus = rv;
if (NS_SUCCEEDED(rv) && scriptProto->mJSObject) {

View File

@ -368,7 +368,6 @@ public:
NS_IMETHOD SetTemplateBuilderFor(nsIContent* aContent, nsIXULTemplateBuilder* aBuilder);
NS_IMETHOD GetTemplateBuilderFor(nsIContent* aContent, nsIXULTemplateBuilder** aResult);
NS_IMETHOD OnPrototypeLoadDone();
NS_IMETHOD OnResumeContentSink();
NS_IMETHOD OnHide();
// nsIDOMEventCapturer interface
@ -430,16 +429,6 @@ public:
PRInt32 aNamespaceID,
nsRDFDOMNodeList* aElements);
static nsresult
GetFastLoadService(nsIFastLoadService** aResult)
{
NS_IF_ADDREF(*aResult = gFastLoadService);
return NS_OK;
}
static nsresult
AbortFastLoads();
protected:
// Implementation methods
friend nsresult
@ -476,27 +465,6 @@ protected:
nsresult CreateElement(nsINodeInfo *aNodeInfo, nsIContent** aResult);
nsresult StartFastLoad();
nsresult EndFastLoad();
static nsIFastLoadService* gFastLoadService;
static nsIFile* gFastLoadFile;
static PRBool gFastLoadDone;
static nsXULDocument* gFastLoadList;
void RemoveFromFastLoadList() {
nsXULDocument** docp = &gFastLoadList;
nsXULDocument* doc;
while ((doc = *docp) != nsnull) {
if (doc == this) {
*docp = doc->mNextFastLoad;
doc->mNextFastLoad = nsnull;
break;
}
docp = &doc->mNextFastLoad;
}
}
nsresult PrepareToLoad(nsISupports* aContainer,
const char* aCommand,
nsIChannel* aChannel,
@ -599,7 +567,7 @@ protected:
PRPackedBool mIsPopup;
PRPackedBool mIsFastLoad;
PRPackedBool mApplyingPersistedAttrs;
nsXULDocument* mNextFastLoad;
PRPackedBool mIsWritingFastLoad;
nsCOMPtr<nsIDOMXULCommandDispatcher> mCommandDispatcher; // [OWNER] of the focus tracker
nsCOMPtr<nsIBindingManager> mBindingManager; // [OWNER] of all bindings

View File

@ -21,6 +21,8 @@
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
* Brendan Eich <brendan@mozilla.org>
* Ben Goodger <ben@netscape.com>
*
*
* Alternatively, the contents of this file may be used under the terms of
@ -48,7 +50,9 @@
#include "nsICSSStyleSheet.h"
#include "nsIXULPrototypeCache.h"
#include "nsIXULPrototypeDocument.h"
#include "nsIXULDocument.h"
#include "nsIURI.h"
#include "nsIURL.h"
#include "nsHashtable.h"
#include "nsXPIDLString.h"
#include "plstr.h"
@ -58,6 +62,16 @@
#include "nsIServiceManager.h"
#include "nsXULDocument.h"
#include "nsIFastLoadService.h"
#include "nsIFastLoadFileControl.h"
#include "nsIFile.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
#include "nsNetUtil.h"
#include "nsAppDirectoryServiceDefs.h"
class nsXULPrototypeCache : public nsIXULPrototypeCache
{
public:
@ -86,6 +100,9 @@ public:
NS_IMETHOD GetEnabled(PRBool* aIsEnabled);
NS_IMETHOD AbortFastLoads();
NS_IMETHOD GetFastLoadService(nsIFastLoadService** aResult);
NS_IMETHOD RemoveFromFastLoadSet(nsIURI* aDocumentURI);
NS_IMETHOD WritePrototype(nsIXULPrototypeDocument* aPrototypeDocument);
protected:
friend NS_IMETHODIMP
@ -123,6 +140,18 @@ protected:
return new nsIURIKey(mKey);
}
};
///////////////////////////////////////////////////////////////////////////
// FastLoad
nsSupportsHashtable mFastLoadURITable;
static nsIFastLoadService* gFastLoadService;
static nsIFile* gFastLoadFile;
// Bootstrap FastLoad Service
nsresult StartFastLoad(nsIURI* aDocumentURI);
nsresult StartFastLoadingURI(nsIURI* aURI, PRInt32 aDirectionFlags);
};
static PRBool gDisableXULCache = PR_FALSE; // enabled by default
@ -153,6 +182,9 @@ DisableXULCacheChangedCallback(const char* aPref, void* aClosure)
//----------------------------------------------------------------------
nsIFastLoadService* nsXULPrototypeCache::gFastLoadService = nsnull;
nsIFile* nsXULPrototypeCache::gFastLoadFile = nsnull;
nsXULPrototypeCache::nsXULPrototypeCache()
{
NS_INIT_REFCNT();
@ -162,6 +194,9 @@ nsXULPrototypeCache::nsXULPrototypeCache()
nsXULPrototypeCache::~nsXULPrototypeCache()
{
FlushScripts();
NS_IF_RELEASE(gFastLoadService); // don't need ReleaseService nowadays!
NS_IF_RELEASE(gFastLoadFile);
}
@ -202,11 +237,46 @@ NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult)
NS_IMETHODIMP
nsXULPrototypeCache::GetPrototype(nsIURI* aURI, nsIXULPrototypeDocument** _result)
{
nsresult rv = NS_OK;
nsIURIKey key(aURI);
*_result = NS_STATIC_CAST(nsIXULPrototypeDocument*, mPrototypeTable.Get(&key));
return NS_OK;
}
if (! *_result) {
// No prototype in XUL memory cache. Spin up FastLoad Service and
// look in FastLoad file.
rv = StartFastLoad(aURI);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIObjectInputStream> objectInput;
gFastLoadService->GetInputStream(getter_AddRefs(objectInput));
rv = StartFastLoadingURI(aURI, nsIFastLoadService::NS_FASTLOAD_READ);
if (NS_SUCCEEDED (rv)) {
nsCOMPtr<nsIURI> oldURI;
gFastLoadService->SelectMuxedDocument(aURI, getter_AddRefs(oldURI));
// Create a new prototype document.
nsCOMPtr<nsIXULPrototypeDocument> protoDoc;
rv = NS_NewXULPrototypeDocument(nsnull,
NS_GET_IID(nsIXULPrototypeDocument),
getter_AddRefs(protoDoc));
if (NS_FAILED(rv)) return rv;
rv = protoDoc->Read(objectInput);
if (NS_SUCCEEDED(rv))
NS_ADDREF(*_result = protoDoc);
gFastLoadService->EndMuxedDocument(aURI);
RemoveFromFastLoadSet(aURI);
PutPrototype(protoDoc);
}
}
}
return rv;
}
NS_IMETHODIMP
nsXULPrototypeCache::PutPrototype(nsIXULPrototypeDocument* aDocument)
@ -442,8 +512,477 @@ nsXULPrototypeCache::GetEnabled(PRBool* aIsEnabled)
}
NS_IMETHODIMP
nsXULPrototypeCache::GetFastLoadService(nsIFastLoadService** aResult)
{
NS_IF_ADDREF(*aResult = gFastLoadService);
return NS_OK;
}
static PRBool gDisableXULFastLoad = PR_FALSE; // enabled by default
static PRBool gChecksumXULFastLoadFile = PR_TRUE; // XXXbe too paranoid
NS_IMETHODIMP
nsXULPrototypeCache::AbortFastLoads()
{
return nsXULDocument::AbortFastLoads();
#ifdef DEBUG_brendan
NS_BREAK();
#endif
// Save a strong ref to the FastLoad file, so we can remove it after we
// close open streams to it.
nsCOMPtr<nsIFile> file = gFastLoadFile;
// Now rename or remove the file.
if (file) {
#ifdef DEBUG
file->MoveTo(nsnull, NS_LITERAL_CSTRING("Aborted.mfasl"));
#else
file->Remove(PR_FALSE);
#endif
}
// Flush the XUL cache for good measure, in case we cached a bogus/downrev
// script, somehow.
Flush();
// Clear the FastLoad set
mFastLoadURITable.Reset();
if (! gFastLoadService)
return NS_OK;
// Fetch the current input (if FastLoad file existed) or output (if we're
// creating the FastLoad file during this app startup) stream.
nsCOMPtr<nsIObjectInputStream> objectInput;
nsCOMPtr<nsIObjectOutputStream> objectOutput;
gFastLoadService->GetInputStream(getter_AddRefs(objectInput));
gFastLoadService->GetOutputStream(getter_AddRefs(objectOutput));
if (objectOutput) {
gFastLoadService->SetOutputStream(nsnull);
if (NS_SUCCEEDED(objectOutput->Close()) && gChecksumXULFastLoadFile)
gFastLoadService->CacheChecksum(gFastLoadFile,
objectOutput);
}
if (objectInput) {
// If this is the last of one or more XUL master documents loaded
// together at app startup, close the FastLoad service's singleton
// input stream now.
gFastLoadService->SetInputStream(nsnull);
objectInput->Close();
}
// If the list is empty now, the FastLoad process is done.
NS_RELEASE(gFastLoadService);
NS_RELEASE(gFastLoadFile);
return NS_OK;
}
NS_IMETHODIMP
nsXULPrototypeCache::RemoveFromFastLoadSet(nsIURI* aURI)
{
nsIURIKey key(aURI);
mFastLoadURITable.Remove(&key);
return NS_OK;
}
static const char kDisableXULFastLoadPref[] = "nglayout.debug.disable_xul_fastload";
static const char kChecksumXULFastLoadFilePref[] = "nglayout.debug.checksum_xul_fastload_file";
NS_IMETHODIMP
nsXULPrototypeCache::WritePrototype(nsIXULPrototypeDocument* aPrototypeDocument)
{
nsresult rv = NS_OK, rv2 = NS_OK;
// We're here before the FastLoad service has been initialized, probably because
// of the profile manager. Bail quietly, don't worry, we'll be back later.
if (! gFastLoadService)
return NS_OK;
// Fetch the current input (if FastLoad file existed) or output (if we're
// creating the FastLoad file during this app startup) stream.
nsCOMPtr<nsIObjectInputStream> objectInput;
nsCOMPtr<nsIObjectOutputStream> objectOutput;
gFastLoadService->GetInputStream(getter_AddRefs(objectInput));
gFastLoadService->GetOutputStream(getter_AddRefs(objectOutput));
nsCOMPtr<nsIURI> protoURI;
aPrototypeDocument->GetURI(getter_AddRefs(protoURI));
// Remove this document from the FastLoad table. We use the table's
// emptiness instead of a counter to decide when the FastLoad process
// has completed. When complete, we can write footer details to the
// FastLoad file.
RemoveFromFastLoadSet(protoURI);
PRInt32 count = mFastLoadURITable.Count();
if (objectOutput) {
rv = StartFastLoadingURI(protoURI, nsIFastLoadService::NS_FASTLOAD_WRITE);
if (NS_SUCCEEDED (rv)) {
// Re-select the URL of the current prototype, as out-of-line script loads
// may have changed
nsCOMPtr<nsIURI> oldURI;
gFastLoadService->SelectMuxedDocument(protoURI, getter_AddRefs(oldURI));
aPrototypeDocument->Write(objectOutput);
gFastLoadService->EndMuxedDocument(protoURI);
}
// If this is the last of one or more XUL master documents loaded
// together at app startup, close the FastLoad service's singleton
// output stream now.
//
// NB: we must close input after output, in case the output stream
// implementation needs to read from the input stream, to compute a
// FastLoad file checksum. In that case, the implementation used
// nsIFastLoadFileIO to get the corresponding input stream for this
// output stream.
if (count == 0) {
gFastLoadService->SetOutputStream(nsnull);
rv = objectOutput->Close();
if (NS_SUCCEEDED(rv) && gChecksumXULFastLoadFile) {
rv = gFastLoadService->CacheChecksum(gFastLoadFile,
objectOutput);
}
}
}
if (objectInput) {
// If this is the last of one or more XUL master documents loaded
// together at app startup, close the FastLoad service's singleton
// input stream now.
if (count == 0) {
gFastLoadService->SetInputStream(nsnull);
rv2 = objectInput->Close();
}
}
// If the list is empty now, the FastLoad process is done.
if (count == 0) {
NS_RELEASE(gFastLoadService);
NS_RELEASE(gFastLoadFile);
}
return NS_FAILED(rv) ? rv : rv2;
}
nsresult
nsXULPrototypeCache::StartFastLoadingURI(nsIURI* aURI, PRInt32 aDirectionFlags)
{
nsresult rv;
nsCAutoString urlspec;
rv = aURI->GetAsciiSpec(urlspec);
if (NS_FAILED(rv)) return rv;
// If StartMuxedDocument returns NS_ERROR_NOT_AVAILABLE, then
// we must be reading the file, and urlspec was not associated
// with any multiplexed stream in it. The FastLoad service
// will therefore arrange to update the file, writing new data
// at the end while old (available) data continues to be read
// from the pre-existing part of the file.
return gFastLoadService->StartMuxedDocument(aURI, urlspec.get(), aDirectionFlags);
}
PR_STATIC_CALLBACK(int)
FastLoadPrefChangedCallback(const char* aPref, void* aClosure)
{
nsCOMPtr<nsIPref> prefs = do_GetService(NS_PREF_CONTRACTID);
if (prefs) {
PRBool wasEnabled = !gDisableXULFastLoad;
prefs->GetBoolPref(kDisableXULFastLoadPref, &gDisableXULFastLoad);
if (wasEnabled && gDisableXULFastLoad) {
static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID);
nsCOMPtr<nsIXULPrototypeCache> cache(do_GetService(kXULPrototypeCacheCID));
if (cache)
cache->AbortFastLoads();
}
prefs->GetBoolPref(kChecksumXULFastLoadFilePref, &gChecksumXULFastLoadFile);
}
return 0;
}
class nsXULFastLoadFileIO : public nsIFastLoadFileIO
{
public:
nsXULFastLoadFileIO(nsIFile* aFile)
: mFile(aFile) {
NS_INIT_REFCNT();
MOZ_COUNT_CTOR(nsXULFastLoadFileIO);
}
virtual ~nsXULFastLoadFileIO() {
MOZ_COUNT_DTOR(nsXULFastLoadFileIO);
}
NS_DECL_ISUPPORTS
NS_DECL_NSIFASTLOADFILEIO
nsCOMPtr<nsIFile> mFile;
nsCOMPtr<nsIInputStream> mInputStream;
nsCOMPtr<nsIOutputStream> mOutputStream;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsXULFastLoadFileIO, nsIFastLoadFileIO)
MOZ_DECL_CTOR_COUNTER(nsXULFastLoadFileIO)
NS_IMETHODIMP
nsXULFastLoadFileIO::GetInputStream(nsIInputStream** aResult)
{
if (! mInputStream) {
nsresult rv;
nsCOMPtr<nsIInputStream> fileInput;
rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInput), mFile);
if (NS_FAILED(rv)) return rv;
rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream),
fileInput,
XUL_DESERIALIZATION_BUFFER_SIZE);
if (NS_FAILED(rv)) return rv;
}
NS_ADDREF(*aResult = mInputStream);
return NS_OK;
}
NS_IMETHODIMP
nsXULFastLoadFileIO::GetOutputStream(nsIOutputStream** aResult)
{
if (! mOutputStream) {
PRInt32 ioFlags = PR_WRONLY;
if (! mInputStream)
ioFlags |= PR_CREATE_FILE | PR_TRUNCATE;
nsresult rv;
nsCOMPtr<nsIOutputStream> fileOutput;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOutput), mFile,
ioFlags, 0644);
if (NS_FAILED(rv)) return rv;
rv = NS_NewBufferedOutputStream(getter_AddRefs(mOutputStream),
fileOutput,
XUL_SERIALIZATION_BUFFER_SIZE);
if (NS_FAILED(rv)) return rv;
}
NS_ADDREF(*aResult = mOutputStream);
return NS_OK;
}
nsresult
nsXULPrototypeCache::StartFastLoad(nsIURI* aURI)
{
nsresult rv;
PRBool isChrome = PR_FALSE;
nsCAutoString path;
aURI->GetPath(path);
PRInt32 length = path.Length();
const nsACString& extn = Substring(path, path.Length()-4, 4);
if (! extn.Equals(NS_LITERAL_CSTRING(".xul")))
return NS_ERROR_NOT_AVAILABLE;
nsIURIKey key(aURI);
// Test gFastLoadList to decide whether this is the first nsXULDocument
// participating in FastLoad. If gFastLoadList is non-null, this document
// must not be first, but it can join the FastLoad process. Examples of
// multiple master documents participating include hiddenWindow.xul and
// navigator.xul on the Mac, and multiple-app-component (e.g., mailnews
// and browser) startup due to command-line arguments.
//
// XXXbe we should attempt to update the FastLoad file after startup!
//
// XXXbe we do not yet use nsFastLoadPtrs, but once we do, we must keep
// the FastLoad input stream open for the life of the app.
if (gFastLoadService && gFastLoadFile) {
mFastLoadURITable.Put(&key, aURI);
return NS_OK;
}
// Use a local to refer to the service till we're sure we succeeded, then
// commit to gFastLoadService. Same for gFastLoadFile, which is used to
// delete the FastLoad file on abort.
nsCOMPtr<nsIFastLoadService> fastLoadService(do_GetFastLoadService());
if (! fastLoadService)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIPref> prefs(do_GetService(NS_PREF_CONTRACTID));
if (prefs) {
prefs->GetBoolPref(kDisableXULFastLoadPref, &gDisableXULFastLoad);
prefs->GetBoolPref(kChecksumXULFastLoadFilePref, &gChecksumXULFastLoadFile);
prefs->RegisterCallback(kDisableXULFastLoadPref,
FastLoadPrefChangedCallback,
nsnull);
prefs->RegisterCallback(kChecksumXULFastLoadFilePref,
FastLoadPrefChangedCallback,
nsnull);
if (gDisableXULFastLoad)
return NS_ERROR_NOT_AVAILABLE;
}
// Get the chrome directory to validate against the one stored in the
// FastLoad file, or to store there if we're generating a new file.
nsCOMPtr<nsIFile> chromeDir;
rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR, getter_AddRefs(chromeDir));
if (NS_FAILED(rv))
return rv;
nsCAutoString chromePath;
rv = chromeDir->GetPath(chromePath);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIFile> file;
rv = fastLoadService->NewFastLoadFile(XUL_FASTLOAD_FILE_BASENAME,
getter_AddRefs(file));
if (NS_FAILED(rv)) return rv;
// Give the FastLoad service an object by which it can get or create a
// file output stream given an input stream on the same file.
nsXULFastLoadFileIO* xio = new nsXULFastLoadFileIO(file);
nsCOMPtr<nsIFastLoadFileIO> io = NS_STATIC_CAST(nsIFastLoadFileIO*, xio);
if (! io)
return NS_ERROR_OUT_OF_MEMORY;
fastLoadService->SetFileIO(io);
// Try to read an existent FastLoad file.
PRBool exists = PR_FALSE;
if (NS_SUCCEEDED(file->Exists(&exists)) && exists) {
nsCOMPtr<nsIInputStream> input;
rv = io->GetInputStream(getter_AddRefs(input));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIObjectInputStream> objectInput;
rv = fastLoadService->NewInputStream(input, getter_AddRefs(objectInput));
if (NS_SUCCEEDED(rv)) {
if (gChecksumXULFastLoadFile) {
nsCOMPtr<nsIFastLoadReadControl>
readControl(do_QueryInterface(objectInput));
if (readControl) {
// Verify checksum, using the fastLoadService's checksum
// cache to avoid computing more than once per session.
PRUint32 checksum;
rv = readControl->GetChecksum(&checksum);
if (NS_SUCCEEDED(rv)) {
PRUint32 verified;
rv = fastLoadService->ComputeChecksum(file,
readControl,
&verified);
if (NS_SUCCEEDED(rv) && verified != checksum) {
#ifdef DEBUG
printf("bad FastLoad file checksum\n");
#endif
rv = NS_ERROR_FAILURE;
}
}
}
}
if (NS_SUCCEEDED(rv)) {
// XXXbe get version number, scripts only for now -- bump
// version later when rest of prototype document header is
// serialized
PRUint32 version;
rv = objectInput->Read32(&version);
if (NS_SUCCEEDED(rv)) {
if (version != XUL_FASTLOAD_FILE_VERSION) {
#ifdef DEBUG
printf("bad FastLoad file version\n");
#endif
rv = NS_ERROR_UNEXPECTED;
} else {
nsXPIDLCString fileChromePath;
rv = objectInput->ReadStringZ(
getter_Copies(fileChromePath));
if (NS_SUCCEEDED(rv) &&
!fileChromePath.Equals(chromePath)) {
rv = NS_ERROR_UNEXPECTED;
}
}
}
}
}
if (NS_SUCCEEDED(rv)) {
fastLoadService->SetInputStream(objectInput);
} else {
// NB: we must close before attempting to remove, for non-Unix OSes
// that can't do open-unlink.
if (objectInput)
objectInput->Close();
else
input->Close();
xio->mInputStream = nsnull;
#ifdef DEBUG
file->MoveTo(nsnull, NS_LITERAL_CSTRING("Invalid.mfasl"));
#else
file->Remove(PR_FALSE);
#endif
exists = PR_FALSE;
}
}
// FastLoad file not found, or invalid: write a new one.
if (! exists) {
nsCOMPtr<nsIOutputStream> output;
rv = io->GetOutputStream(getter_AddRefs(output));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIObjectOutputStream> objectOutput;
rv = fastLoadService->NewOutputStream(output,
getter_AddRefs(objectOutput));
if (NS_SUCCEEDED(rv)) {
rv = objectOutput->Write32(XUL_FASTLOAD_FILE_VERSION);
if (NS_SUCCEEDED(rv))
rv = objectOutput->WriteStringZ(chromePath.get());
}
// Remove here even though some errors above will lead to a FastLoad
// file invalidation. Other errors (failure to note the dependency on
// installed-chrome.txt, e.g.) will not cause invalidation, and we may
// as well tidy up now.
if (NS_FAILED(rv)) {
if (objectOutput)
objectOutput->Close();
else
output->Close();
xio->mOutputStream = nsnull;
file->Remove(PR_FALSE);
return rv;
}
fastLoadService->SetOutputStream(objectOutput);
}
// Success! Insert this URI into the mFastLoadURITable
// and commit locals to globals.
mFastLoadURITable.Put(&key, aURI);
NS_ADDREF(gFastLoadService = fastLoadService);
NS_ADDREF(gFastLoadFile = file);
return NS_OK;
}

View File

@ -22,6 +22,7 @@
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
* L. David Baron <dbaron@fas.harvard.edu>
* Ben Goodger <ben@netscape.com>
*
*
* Alternatively, the contents of this file may be used under the terms of
@ -286,8 +287,6 @@ NS_NewXULPrototypeDocument(nsISupports* aOuter, REFNSIID aIID, void** aResult)
// nsISerializable methods
//
#define XUL_FAST_LOAD_VERSION 0
NS_IMETHODIMP
nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream)
{
@ -295,16 +294,67 @@ nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream)
PRUint32 version;
rv = aStream->Read32(&version);
if (NS_FAILED(rv)) return rv;
if (version != XUL_FAST_LOAD_VERSION)
if (version != XUL_FASTLOAD_FILE_VERSION)
return NS_ERROR_FAILURE;
rv = aStream->ReadObject(PR_TRUE, getter_AddRefs(mURI));
if (NS_FAILED(rv)) return rv;
rv |= aStream->ReadObject(PR_TRUE, getter_AddRefs(mURI));
// XXXbe more to come
return NS_OK;
PRUint32 referenceCount;
nsCOMPtr<nsIURI> referenceURI;
PRUint32 i;
// nsISupportsArray mStyleSheetReferences
rv |= aStream->Read32(&referenceCount);
if (NS_FAILED(rv)) return rv;
for (i = 0; i < referenceCount; ++i) {
rv |= aStream->ReadObject(PR_TRUE, getter_AddRefs(referenceURI));
mStyleSheetReferences->AppendElement(referenceURI);
}
// nsISupportsArray mOverlayReferences
rv |= aStream->Read32(&referenceCount);
for (i = 0; i < referenceCount; ++i) {
rv |= aStream->ReadObject(PR_TRUE, getter_AddRefs(referenceURI));
mOverlayReferences->AppendElement(referenceURI);
}
// nsIPrincipal mDocumentPrincipal
nsCOMPtr<nsIScriptSecurityManager> securityManager =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
if (! securityManager)
return NS_ERROR_FAILURE;
rv |= securityManager->GetCodebasePrincipal(mURI, getter_AddRefs(mDocumentPrincipal));
rv |= NS_ReadOptionalObject(aStream, PR_TRUE, getter_AddRefs(mDocumentPrincipal));
// nsIScriptGlobalObject mGlobalObject
mGlobalObject = new nsXULPDGlobalObject();
if (! mGlobalObject)
return NS_ERROR_OUT_OF_MEMORY;
rv |= mGlobalObject->SetGlobalObjectOwner(this); // does not refcount
mRoot = new nsXULPrototypeElement(-1);
if (! mRoot)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIScriptContext> scriptContext;
rv |= mGlobalObject->GetContext(getter_AddRefs(scriptContext));
NS_ASSERTION(scriptContext != nsnull,
"no prototype script context!");
PRUint32 type;
rv |= aStream->Read32(&type);
if ((nsXULPrototypeNode::Type)type != nsXULPrototypeNode::eType_Element)
return NS_ERROR_FAILURE;
rv |= mRoot->Deserialize(aStream, scriptContext, mURI);
rv |= NotifyLoadDone();
return rv;
}
@ -313,27 +363,49 @@ nsXULPrototypeDocument::Write(nsIObjectOutputStream* aStream)
{
nsresult rv;
rv = aStream->Write32(XUL_FAST_LOAD_VERSION);
if (NS_FAILED(rv)) return rv;
rv = aStream->Write32(XUL_FASTLOAD_FILE_VERSION);
rv |= aStream->WriteCompoundObject(mURI, NS_GET_IID(nsIURI), PR_TRUE);
PRUint32 referenceCount;
nsCOMPtr<nsIURI> referenceURI;
rv = aStream->WriteCompoundObject(mURI, NS_GET_IID(nsIURI), PR_TRUE);
if (NS_FAILED(rv)) return rv;
PRUint32 i;
// nsISupportsArray mStyleSheetReferences
mStyleSheetReferences->Count(&referenceCount);
rv |= aStream->Write32(referenceCount);
for (i = 0; i < referenceCount; ++i) {
mStyleSheetReferences->QueryElementAt(i, NS_GET_IID(nsIURI), getter_AddRefs(referenceURI));
rv |= aStream->WriteCompoundObject(referenceURI, NS_GET_IID(nsIURI), PR_TRUE);
}
// nsISupportsArray mOverlayReferences
mOverlayReferences->Count(&referenceCount);
rv |= aStream->Write32(referenceCount);
for (i = 0; i < referenceCount; ++i) {
mOverlayReferences->QueryElementAt(i, NS_GET_IID(nsIURI), getter_AddRefs(referenceURI));
rv |= aStream->WriteCompoundObject(referenceURI, NS_GET_IID(nsIURI), PR_TRUE);
}
// nsIPrincipal mDocumentPrincipal
rv |= NS_WriteOptionalObject(aStream, mDocumentPrincipal, PR_TRUE);
// Now serialize the document contents
nsCOMPtr<nsIScriptGlobalObject> globalObject;
rv |= GetScriptGlobalObject(getter_AddRefs(globalObject));
nsCOMPtr<nsIScriptContext> scriptContext;
rv = mGlobalObject->GetContext(getter_AddRefs(scriptContext));
if (NS_FAILED(rv)) return rv;
#if 0
rv = mRoot->Serialize(aStream, scriptContext);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsISupportsArray> mStyleSheetReferences;
nsCOMPtr<nsISupportsArray> mOverlayReferences;
nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
nsCOMPtr<nsIScriptGlobalObject> mGlobalObject;
#endif
return NS_OK;
rv |= globalObject->GetContext(getter_AddRefs(scriptContext));
if (mRoot)
rv |= mRoot->Serialize(aStream, scriptContext);
return rv;
}
@ -418,7 +490,6 @@ nsXULPrototypeDocument::GetOverlayReferences(nsISupportsArray** aResult)
}
NS_IMETHODIMP
nsXULPrototypeDocument::GetHeaderData(nsIAtom* aField, nsAString& aData) const
{