Files
Mozilla/mozilla/rdf/content/src/nsRDFDocument.cpp
1999-01-22 06:48:25 +00:00

2003 lines
58 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/*
An implementation for the nsIRDFDocument interface. This
implementation serves as the basis for generating an NGLayout
content model.
TO DO
1) Figure out how to get rid of the DummyListener hack.
*/
#include "nsIArena.h"
#include "nsICollection.h"
#include "nsIContent.h"
#include "nsICSSParser.h"
#include "nsICSSStyleSheet.h"
#include "nsIDTD.h"
#include "nsIDocument.h"
#include "nsIDocumentObserver.h"
#include "nsIEnumerator.h"
#include "nsIHTMLStyleSheet.h"
#include "nsINameSpaceManager.h"
#include "nsIParser.h"
#include "nsIPresContext.h"
#include "nsIPresShell.h"
#include "nsIRDFCompositeDataSource.h"
#include "nsIRDFContent.h"
#include "nsIRDFContentModelBuilder.h"
#include "nsIRDFCursor.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFDocument.h"
#include "nsIRDFNode.h"
#include "nsIRDFObserver.h"
#include "nsIRDFService.h"
#include "nsIRDFXMLDataSource.h"
#include "nsIScriptContextOwner.h"
#include "nsIServiceManager.h"
#include "nsIStreamListener.h"
#include "nsIStyleSet.h"
#include "nsIStyleSheet.h"
#include "nsISupportsArray.h"
#include "nsIURL.h"
#include "nsIURLGroup.h"
#include "nsIWebShell.h"
#include "nsLayoutCID.h"
#include "nsParserCIID.h"
#include "nsRDFCID.h"
#include "nsRDFContentSink.h"
#include "nsVoidArray.h"
#include "plhash.h"
#include "rdfutil.h"
////////////////////////////////////////////////////////////////////////
static NS_DEFINE_IID(kICSSParserIID, NS_ICSS_PARSER_IID); // XXX grr..
static NS_DEFINE_IID(kICollectionIID, NS_ICOLLECTION_IID);
static NS_DEFINE_IID(kIContentIID, NS_ICONTENT_IID);
static NS_DEFINE_IID(kIDTDIID, NS_IDTD_IID);
static NS_DEFINE_IID(kIDocumentIID, NS_IDOCUMENT_IID);
static NS_DEFINE_IID(kIHTMLStyleSheetIID, NS_IHTML_STYLE_SHEET_IID);
static NS_DEFINE_IID(kINameSpaceManagerIID, NS_INAMESPACEMANAGER_IID);
static NS_DEFINE_IID(kIParserIID, NS_IPARSER_IID);
static NS_DEFINE_IID(kIPresShellIID, NS_IPRESSHELL_IID);
static NS_DEFINE_IID(kIRDFCompositeDataSourceIID, NS_IRDFCOMPOSITEDATASOURCE_IID);
static NS_DEFINE_IID(kIRDFDataSourceIID, NS_IRDFDATASOURCE_IID);
static NS_DEFINE_IID(kIRDFDocumentIID, NS_IRDFDOCUMENT_IID);
static NS_DEFINE_IID(kIRDFLiteralIID, NS_IRDFLITERAL_IID);
static NS_DEFINE_IID(kIRDFResourceIID, NS_IRDFRESOURCE_IID);
static NS_DEFINE_IID(kIRDFServiceIID, NS_IRDFSERVICE_IID);
static NS_DEFINE_IID(kIRDFXMLDataSourceIID, NS_IRDFXMLDATASOURCE_IID);
static NS_DEFINE_IID(kIStreamListenerIID, NS_ISTREAMLISTENER_IID);
static NS_DEFINE_IID(kIStreamObserverIID, NS_ISTREAMOBSERVER_IID);
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kIWebShellIID, NS_IWEB_SHELL_IID);
static NS_DEFINE_IID(kIXMLDocumentIID, NS_IXMLDOCUMENT_IID);
static NS_DEFINE_CID(kCSSParserCID, NS_CSSPARSER_CID);
static NS_DEFINE_CID(kHTMLStyleSheetCID, NS_HTMLSTYLESHEET_CID);
static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID);
static NS_DEFINE_CID(kParserCID, NS_PARSER_IID); // XXX
static NS_DEFINE_CID(kPresShellCID, NS_PRESSHELL_CID);
static NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID);
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kRDFXMLDataSourceCID, NS_RDFXMLDATASOURCE_CID);
static NS_DEFINE_CID(kRDFCompositeDataSourceCID, NS_RDFCOMPOSITEDATASOURCE_CID);
static NS_DEFINE_CID(kRangeListCID, NS_RANGELIST_CID);
static NS_DEFINE_CID(kWellFormedDTDCID, NS_WELLFORMEDDTD_CID);
////////////////////////////////////////////////////////////////////////
static PLHashNumber
rdf_HashPointer(const void* key)
{
return (PLHashNumber) key;
}
////////////////////////////////////////////////////////////////////////
class RDFDocumentImpl : public nsIDocument,
public nsIRDFDocument,
public nsIRDFObserver,
public nsIRDFXMLDataSourceObserver
{
public:
RDFDocumentImpl();
virtual ~RDFDocumentImpl();
// nsISupports interface
NS_DECL_ISUPPORTS
// nsIDocument interface
virtual nsIArena* GetArena();
NS_IMETHOD StartDocumentLoad(nsIURL *aUrl,
nsIContentViewerContainer* aContainer,
nsIStreamListener **aDocListener,
const char* aCommand);
virtual const nsString* GetDocumentTitle() const;
virtual nsIURL* GetDocumentURL() const;
virtual nsIURLGroup* GetDocumentURLGroup() const;
NS_IMETHOD GetBaseURL(nsIURL*& aURL) const;
virtual nsCharSetID GetDocumentCharacterSet() const;
virtual void SetDocumentCharacterSet(nsCharSetID aCharSetID);
virtual nsresult CreateShell(nsIPresContext* aContext,
nsIViewManager* aViewManager,
nsIStyleSet* aStyleSet,
nsIPresShell** aInstancePtrResult);
virtual PRBool DeleteShell(nsIPresShell* aShell);
virtual PRInt32 GetNumberOfShells();
virtual nsIPresShell* GetShellAt(PRInt32 aIndex);
virtual nsIDocument* GetParentDocument();
virtual void SetParentDocument(nsIDocument* aParent);
virtual void AddSubDocument(nsIDocument* aSubDoc);
virtual PRInt32 GetNumberOfSubDocuments();
virtual nsIDocument* GetSubDocumentAt(PRInt32 aIndex);
virtual nsIContent* GetRootContent();
virtual void SetRootContent(nsIContent* aRoot);
virtual PRInt32 GetNumberOfStyleSheets();
virtual nsIStyleSheet* GetStyleSheetAt(PRInt32 aIndex);
virtual void AddStyleSheet(nsIStyleSheet* aSheet);
virtual void SetStyleSheetDisabledState(nsIStyleSheet* aSheet,
PRBool mDisabled);
virtual nsIScriptContextOwner *GetScriptContextOwner();
virtual void SetScriptContextOwner(nsIScriptContextOwner *aScriptContextOwner);
NS_IMETHOD GetNameSpaceManager(nsINameSpaceManager*& aManager);
virtual void AddObserver(nsIDocumentObserver* aObserver);
virtual PRBool RemoveObserver(nsIDocumentObserver* aObserver);
NS_IMETHOD BeginLoad();
NS_IMETHOD EndLoad();
NS_IMETHOD ContentChanged(nsIContent* aContent,
nsISupports* aSubContent);
NS_IMETHOD AttributeChanged(nsIContent* aChild,
nsIAtom* aAttribute,
PRInt32 aHint); // See nsStyleConsts fot hint values
NS_IMETHOD ContentAppended(nsIContent* aContainer,
PRInt32 aNewIndexInContainer);
NS_IMETHOD ContentInserted(nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer);
NS_IMETHOD ContentReplaced(nsIContent* aContainer,
nsIContent* aOldChild,
nsIContent* aNewChild,
PRInt32 aIndexInContainer);
NS_IMETHOD ContentRemoved(nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer);
NS_IMETHOD StyleRuleChanged(nsIStyleSheet* aStyleSheet,
nsIStyleRule* aStyleRule,
PRInt32 aHint); // See nsStyleConsts fot hint values
NS_IMETHOD StyleRuleAdded(nsIStyleSheet* aStyleSheet,
nsIStyleRule* aStyleRule);
NS_IMETHOD StyleRuleRemoved(nsIStyleSheet* aStyleSheet,
nsIStyleRule* aStyleRule);
NS_IMETHOD GetSelection(nsICollection** aSelection);
NS_IMETHOD SelectAll();
NS_IMETHOD FindNext(const nsString &aSearchStr, PRBool aMatchCase, PRBool aSearchDown, PRBool &aIsFound);
virtual void CreateXIF(nsString & aBuffer, nsISelection* aSelection);
virtual void ToXIF(nsXIFConverter& aConverter, nsIDOMNode* aNode);
virtual void BeginConvertToXIF(nsXIFConverter& aConverter, nsIDOMNode* aNode);
virtual void ConvertChildrenToXIF(nsXIFConverter& aConverter, nsIDOMNode* aNode);
virtual void FinishConvertToXIF(nsXIFConverter& aConverter, nsIDOMNode* aNode);
virtual PRBool IsInRange(const nsIContent *aStartContent, const nsIContent* aEndContent, const nsIContent* aContent) const;
virtual PRBool IsBefore(const nsIContent *aNewContent, const nsIContent* aCurrentContent) const;
virtual PRBool IsInSelection(nsISelection* aSelection, const nsIContent *aContent) const;
virtual nsIContent* GetPrevContent(const nsIContent *aContent) const;
virtual nsIContent* GetNextContent(const nsIContent *aContent) const;
virtual void SetDisplaySelection(PRBool aToggle);
virtual PRBool GetDisplaySelection() const;
NS_IMETHOD HandleDOMEvent(nsIPresContext& aPresContext,
nsEvent* aEvent,
nsIDOMEvent** aDOMEvent,
PRUint32 aFlags,
nsEventStatus& aEventStatus);
// nsIXMLDocument interface
NS_IMETHOD PrologElementAt(PRUint32 aOffset, nsIContent** aContent);
NS_IMETHOD PrologCount(PRUint32* aCount);
NS_IMETHOD AppendToProlog(nsIContent* aContent);
NS_IMETHOD EpilogElementAt(PRUint32 aOffset, nsIContent** aContent);
NS_IMETHOD EpilogCount(PRUint32* aCount);
NS_IMETHOD AppendToEpilog(nsIContent* aContent);
// nsIRDFDocument interface
NS_IMETHOD Init(nsIRDFContentModelBuilder* aBuilder);
NS_IMETHOD SetRootResource(nsIRDFResource* resource);
NS_IMETHOD GetDataBase(nsIRDFCompositeDataSource*& result);
NS_IMETHOD CreateChildren(nsIRDFContent* element);
NS_IMETHOD AddTreeProperty(nsIRDFResource* resource);
NS_IMETHOD RemoveTreeProperty(nsIRDFResource* resource);
NS_IMETHOD IsTreeProperty(nsIRDFResource* aProperty, PRBool* aResult) const;
NS_IMETHOD MapResource(nsIRDFResource* aResource, nsIRDFContent* aContent);
NS_IMETHOD UnMapResource(nsIRDFResource* aResource, nsIRDFContent* aContent);
NS_IMETHOD SplitProperty(nsIRDFResource* aResource, PRInt32* aNameSpaceID, nsIAtom** aTag);
// nsIRDFObserver interface
NS_IMETHOD OnAssert(nsIRDFResource* subject,
nsIRDFResource* predicate,
nsIRDFNode* object);
NS_IMETHOD OnUnassert(nsIRDFResource* subject,
nsIRDFResource* predicate,
nsIRDFNode* object);
// nsIRDFXMLDataSourceObserver interface
NS_IMETHOD OnBeginLoad(nsIRDFXMLDataSource* aDataSource);
NS_IMETHOD OnInterrupt(nsIRDFXMLDataSource* aDataSource);
NS_IMETHOD OnResume(nsIRDFXMLDataSource* aDataSource);
NS_IMETHOD OnEndLoad(nsIRDFXMLDataSource* aDataSource);
NS_IMETHOD OnRootResourceFound(nsIRDFXMLDataSource* aDataSource, nsIRDFResource* aResource);
NS_IMETHOD OnCSSStyleSheetAdded(nsIRDFXMLDataSource* aDataSource, nsIURL* aStyleSheetURI);
NS_IMETHOD OnNamedDataSourceAdded(nsIRDFXMLDataSource* aDataSource, const char* aNamedDataSourceURI);
// Implementation methods
nsresult StartLayout(void);
protected:
nsIContent*
FindContent(const nsIContent* aStartNode,
const nsIContent* aTest1,
const nsIContent* aTest2) const;
nsresult
LoadCSSStyleSheet(nsIURL* url);
nsresult
AddNamedDataSource(const char* uri);
nsIArena* mArena;
nsVoidArray mObservers;
nsAutoString mDocumentTitle;
nsIURL* mDocumentURL;
nsIURLGroup* mDocumentURLGroup;
nsIContent* mRootContent;
nsIDocument* mParentDocument;
nsIScriptContextOwner* mScriptContextOwner;
nsCharSetID mCharSetID;
nsVoidArray mStyleSheets;
nsICollection* mSelection;
PRBool mDisplaySelection;
nsVoidArray mPresShells;
nsINameSpaceManager* mNameSpaceManager;
nsIStyleSheet* mAttrStyleSheet;
nsIRDFCompositeDataSource* mDB;
nsIRDFService* mRDFService;
nsISupportsArray* mTreeProperties;
nsIRDFContentModelBuilder* mBuilder;
PLHashTable* mResources;
nsIRDFDataSource* mLocalDataSource;
nsIRDFXMLDataSource* mDocumentDataSource;
};
////////////////////////////////////////////////////////////////////////
// DummyListener
//
// This is a _total_ hack that is used to get stuff to draw right
// when a second copy is loaded. I need to talk to Guha about what
// the expected behavior should be...
//
class DummyListener : public nsIStreamListener
{
private:
RDFDocumentImpl* mRDFDocument;
PRBool mWasNotifiedOnce; // XXX why do we get _two_ OnStart/StopBinding() calls?
public:
DummyListener(RDFDocumentImpl* aRDFDocument)
: mRDFDocument(aRDFDocument),
mWasNotifiedOnce(PR_FALSE)
{
NS_INIT_REFCNT();
NS_ADDREF(mRDFDocument);
}
virtual ~DummyListener(void) {
NS_RELEASE(mRDFDocument);
}
// nsISupports interface
NS_DECL_ISUPPORTS
// nsIStreamObserver interface
NS_IMETHOD
OnStartBinding(nsIURL* aURL, const char *aContentType) {
if (! mWasNotifiedOnce) {
mRDFDocument->BeginLoad();
mRDFDocument->StartLayout();
}
return NS_OK;
}
NS_IMETHOD
OnProgress(nsIURL* aURL, PRUint32 aProgress, PRUint32 aProgressMax) {
return NS_OK;
}
NS_IMETHOD OnStatus(nsIURL* aURL, const PRUnichar* aMsg) {
return NS_OK;
}
NS_IMETHOD OnStopBinding(nsIURL* aURL, nsresult aStatus, const PRUnichar* aMsg) {
if (! mWasNotifiedOnce) {
mRDFDocument->EndLoad();
mWasNotifiedOnce = PR_TRUE;
}
return NS_OK;
}
// nsIStreamListener interface
NS_IMETHOD
GetBindInfo(nsIURL* aURL, nsStreamBindingInfo* aInfo) {
aInfo->seekable = PR_FALSE;
return NS_OK;
}
NS_IMETHOD
OnDataAvailable(nsIURL* aURL, nsIInputStream *aIStream, PRUint32 aLength) {
return NS_OK;
}
};
NS_IMPL_ADDREF(DummyListener);
NS_IMPL_RELEASE(DummyListener);
NS_IMETHODIMP
DummyListener::QueryInterface(REFNSIID aIID, void** aResult)
{
NS_PRECONDITION(aResult != nsnull, "null ptr");
if (! aResult)
return NS_ERROR_NULL_POINTER;
if (aIID.Equals(kIStreamListenerIID) ||
aIID.Equals(kIStreamObserverIID) ||
aIID.Equals(kISupportsIID)) {
*aResult = NS_STATIC_CAST(nsIStreamListener*, this);
NS_ADDREF(this);
return NS_OK;
}
else {
*aResult = nsnull;
return NS_NOINTERFACE;
}
}
////////////////////////////////////////////////////////////////////////
// ctors & dtors
RDFDocumentImpl::RDFDocumentImpl(void)
: mArena(nsnull),
mDocumentURL(nsnull),
mDocumentURLGroup(nsnull),
mRootContent(nsnull),
mParentDocument(nsnull),
mScriptContextOwner(nsnull),
mSelection(nsnull),
mDisplaySelection(PR_FALSE),
mNameSpaceManager(nsnull),
mAttrStyleSheet(nsnull),
mDB(nsnull),
mRDFService(nsnull),
mTreeProperties(nsnull),
mBuilder(nsnull),
mResources(nsnull),
mLocalDataSource(nsnull),
mDocumentDataSource(nsnull)
{
NS_INIT_REFCNT();
nsresult rv;
// construct a selection object
if (NS_FAILED(rv = nsRepository::CreateInstance(kRangeListCID,
nsnull,
kICollectionIID,
(void**) &mSelection)))
PR_ASSERT(0);
}
RDFDocumentImpl::~RDFDocumentImpl()
{
if (mDocumentDataSource) {
mDocumentDataSource->RemoveXMLStreamObserver(this);
NS_RELEASE(mDocumentDataSource);
}
NS_IF_RELEASE(mLocalDataSource);
if (mResources)
PL_HashTableDestroy(mResources);
if (mRDFService) {
nsServiceManager::ReleaseService(kRDFServiceCID, mRDFService);
mRDFService = nsnull;
}
// mParentDocument is never refcounted
NS_IF_RELEASE(mBuilder);
NS_IF_RELEASE(mSelection);
NS_IF_RELEASE(mScriptContextOwner);
NS_IF_RELEASE(mAttrStyleSheet);
NS_IF_RELEASE(mTreeProperties);
NS_IF_RELEASE(mRootContent);
if (mDB) {
mDB->RemoveObserver(this);
NS_RELEASE(mDB);
}
NS_IF_RELEASE(mDocumentURLGroup);
NS_IF_RELEASE(mDocumentURL);
NS_IF_RELEASE(mArena);
NS_IF_RELEASE(mNameSpaceManager);
}
////////////////////////////////////////////////////////////////////////
// nsISupports interface
NS_IMETHODIMP
RDFDocumentImpl::QueryInterface(REFNSIID iid, void** result)
{
if (! result)
return NS_ERROR_NULL_POINTER;
*result = nsnull;
if (iid.Equals(kIDocumentIID) ||
iid.Equals(kISupportsIID)) {
*result = NS_STATIC_CAST(nsIDocument*, this);
NS_ADDREF(this);
return NS_OK;
}
else if (iid.Equals(kIRDFDocumentIID) ||
iid.Equals(kIXMLDocumentIID)) {
*result = NS_STATIC_CAST(nsIRDFDocument*, this);
NS_ADDREF(this);
return NS_OK;
}
return NS_NOINTERFACE;
}
NS_IMPL_ADDREF(RDFDocumentImpl);
NS_IMPL_RELEASE(RDFDocumentImpl);
////////////////////////////////////////////////////////////////////////
// nsIDocument interface
nsIArena*
RDFDocumentImpl::GetArena()
{
NS_IF_ADDREF(mArena);
return mArena;
}
NS_IMETHODIMP
RDFDocumentImpl::StartDocumentLoad(nsIURL *aURL,
nsIContentViewerContainer* aContainer,
nsIStreamListener **aDocListener,
const char* aCommand)
{
nsresult rv;
// Delete references to style sheets - this should be done in superclass...
PRInt32 index = mStyleSheets.Count();
while (--index >= 0) {
nsIStyleSheet* sheet = (nsIStyleSheet*) mStyleSheets.ElementAt(index);
sheet->SetOwningDocument(nsnull);
NS_RELEASE(sheet);
}
mStyleSheets.Clear();
NS_IF_RELEASE(mDocumentURL);
NS_IF_RELEASE(mDocumentURLGroup);
mDocumentTitle.Truncate();
mDocumentURL = aURL;
NS_ADDREF(aURL);
(void)aURL->GetURLGroup(&mDocumentURLGroup);
// Create an HTML style sheet for the HTML content.
nsIHTMLStyleSheet* sheet;
if (NS_SUCCEEDED(rv = nsRepository::CreateInstance(kHTMLStyleSheetCID,
nsnull,
kIHTMLStyleSheetIID,
(void**) &sheet))) {
if (NS_SUCCEEDED(rv = sheet->Init(aURL, this))) {
mAttrStyleSheet = sheet;
NS_ADDREF(mAttrStyleSheet);
AddStyleSheet(mAttrStyleSheet);
}
NS_RELEASE(sheet);
}
if (NS_FAILED(rv))
return rv;
// Create a "scratch" in-memory data store to associate with the
// document to be a catch-all for any doc-specific info that we
// need to store (e.g., current sort order, etc.)
if (NS_FAILED(rv = nsRepository::CreateInstance(kRDFInMemoryDataSourceCID,
nsnull,
kIRDFDataSourceIID,
(void**) &mLocalDataSource)))
return rv;
if (NS_FAILED(rv = mDB->AddDataSource(mLocalDataSource)))
return rv;
const char* uri;
if (NS_FAILED(rv = aURL->GetSpec(&uri)))
return rv;
// Now load the actual XML/RDF document data source.
nsIRDFDataSource* ds;
if (NS_SUCCEEDED(rv = mRDFService->GetDataSource(uri, &ds))) {
if (NS_FAILED(rv = ds->QueryInterface(kIRDFXMLDataSourceIID, (void**) &mDocumentDataSource))) {
NS_RELEASE(ds);
return rv;
}
NS_RELEASE(ds);
if (NS_FAILED(rv = mDB->AddDataSource(mDocumentDataSource)))
return rv;
// we found the data source already loaded locally. Load it's
// style sheets and attempt to include any named data sources
// that it references into this document.
nsIURL** styleSheetURLs;
PRInt32 count;
if (NS_SUCCEEDED(rv = mDocumentDataSource->GetCSSStyleSheetURLs(&styleSheetURLs, &count))) {
for (PRInt32 i = 0; i < count; ++i) {
if (NS_FAILED(rv = LoadCSSStyleSheet(styleSheetURLs[i]))) {
NS_ASSERTION(PR_FALSE, "couldn't load style sheet");
}
}
}
const char* const* namedDataSourceURIs;
if (NS_SUCCEEDED(rv = mDocumentDataSource->GetNamedDataSourceURIs(&namedDataSourceURIs, &count))) {
for (PRInt32 i = 0; i < count; ++i) {
if (NS_FAILED(rv = AddNamedDataSource(namedDataSourceURIs[i]))) {
#ifdef DEBUG
printf("error adding named data source %s\n", namedDataSourceURIs[i]);
#endif
}
}
}
nsIRDFResource* root;
if (NS_SUCCEEDED(rv = mDocumentDataSource->GetRootResource(&root))) {
SetRootResource(root);
StartLayout();
}
// XXX Allright, this is an atrocious hack. Basically, we
// construct a dummy listener object so that we can load the
// URL, which allows us to receive StartLayout() and EndLoad()
// calls asynchronously. If we don't do this, then there is no
// way (that I could figure out) to force the content model to
// be traversed so that a document is laid out again.
//
// Looking at the "big picture" here, the real problem is that
// the "registered" data sources mechanism is really just a
// cache of stream data sources. It's not really clear what
// should happen when somebody opens a second document on the
// same source. You'd kinda like both to refer to the same
// thing, so changes to one are immediately reflected in the
// other. On the other hand, you'd also like to be able to
// _unload_ and reload a content model, say by doing
// "shift+reload". _So_, that kinda ties into the real cache,
// etc. etc.
//
// What I guess I'm saying is, maybe it doesn't make that much
// sense to register stream data sources when they're
// created...I dunno...
if (aDocListener) {
nsIStreamListener* lsnr = new DummyListener(this);
if (! lsnr)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(lsnr);
*aDocListener = lsnr;
if (NS_FAILED(rv = NS_OpenURL(aURL, lsnr)))
return rv;
}
}
else if (NS_SUCCEEDED(rv = nsRepository::CreateInstance(kRDFXMLDataSourceCID,
nsnull,
kIRDFXMLDataSourceIID,
(void**) &mDocumentDataSource))) {
if (NS_FAILED(rv = mDB->AddDataSource(mDocumentDataSource)))
return rv;
// We need to construct a new stream and load it. The stream
// will automagically register itself as a named data source,
// so if subsequent docs ask for it, they'll get the real
// deal. In the meantime, add us as an
// nsIRDFXMLDataSourceObserver so that we'll be notified when we
// need to load style sheets, etc.
nsIRDFXMLDataSource* doc;
if (NS_SUCCEEDED(rv = mDocumentDataSource->QueryInterface(kIRDFXMLDataSourceIID, (void**) &doc))) {
doc->AddXMLStreamObserver(this);
NS_RELEASE(doc);
}
if (NS_FAILED(rv = mDocumentDataSource->Init(uri)))
return rv;
if (aDocListener) {
*aDocListener = nsnull;
}
}
else {
// an error occurred
PR_ASSERT(0);
}
return NS_OK;
}
const nsString*
RDFDocumentImpl::GetDocumentTitle() const
{
return &mDocumentTitle;
}
nsIURL*
RDFDocumentImpl::GetDocumentURL() const
{
NS_IF_ADDREF(mDocumentURL);
return mDocumentURL;
}
nsIURLGroup*
RDFDocumentImpl::GetDocumentURLGroup() const
{
NS_IF_ADDREF(mDocumentURLGroup);
return mDocumentURLGroup;
}
NS_IMETHODIMP
RDFDocumentImpl::GetBaseURL(nsIURL*& aURL) const
{
NS_IF_ADDREF(mDocumentURL);
aURL = mDocumentURL;
return NS_OK;
}
nsCharSetID
RDFDocumentImpl::GetDocumentCharacterSet() const
{
return mCharSetID;
}
void
RDFDocumentImpl::SetDocumentCharacterSet(nsCharSetID aCharSetID)
{
mCharSetID = aCharSetID;
}
nsresult
RDFDocumentImpl::CreateShell(nsIPresContext* aContext,
nsIViewManager* aViewManager,
nsIStyleSet* aStyleSet,
nsIPresShell** aInstancePtrResult)
{
NS_PRECONDITION(aInstancePtrResult, "null ptr");
if (! aInstancePtrResult)
return NS_ERROR_NULL_POINTER;
nsresult rv;
nsIPresShell* shell;
if (NS_FAILED(rv = nsRepository::CreateInstance(kPresShellCID,
nsnull,
kIPresShellIID,
(void**) &shell)))
return rv;
if (NS_FAILED(rv = shell->Init(this, aContext, aViewManager, aStyleSet))) {
NS_RELEASE(shell);
return rv;
}
mPresShells.AppendElement(shell);
*aInstancePtrResult = shell; // addref implicit
return NS_OK;
}
PRBool
RDFDocumentImpl::DeleteShell(nsIPresShell* aShell)
{
return mPresShells.RemoveElement(aShell);
}
PRInt32
RDFDocumentImpl::GetNumberOfShells()
{
return mPresShells.Count();
}
nsIPresShell*
RDFDocumentImpl::GetShellAt(PRInt32 aIndex)
{
nsIPresShell* shell = NS_STATIC_CAST(nsIPresShell*, mPresShells[aIndex]);
NS_IF_ADDREF(shell);
return shell;
}
nsIDocument*
RDFDocumentImpl::GetParentDocument()
{
NS_IF_ADDREF(mParentDocument);
return mParentDocument;
}
void
RDFDocumentImpl::SetParentDocument(nsIDocument* aParent)
{
// Note that we do *not* AddRef our parent because that would
// create a circular reference.
mParentDocument = aParent;
}
void
RDFDocumentImpl::AddSubDocument(nsIDocument* aSubDoc)
{
// we don't do subdocs.
PR_ASSERT(0);
}
PRInt32
RDFDocumentImpl::GetNumberOfSubDocuments()
{
return 0;
}
nsIDocument*
RDFDocumentImpl::GetSubDocumentAt(PRInt32 aIndex)
{
// we don't do subdocs.
PR_ASSERT(0);
return nsnull;
}
nsIContent*
RDFDocumentImpl::GetRootContent()
{
NS_IF_ADDREF(mRootContent);
return mRootContent;
}
void
RDFDocumentImpl::SetRootContent(nsIContent* aRoot)
{
if (mRootContent) {
mRootContent->SetDocument(nsnull, PR_TRUE);
NS_RELEASE(mRootContent);
}
mRootContent = aRoot;
if (mRootContent) {
mRootContent->SetDocument(this, PR_TRUE);
NS_ADDREF(mRootContent);
}
}
PRInt32
RDFDocumentImpl::GetNumberOfStyleSheets()
{
return mStyleSheets.Count();
}
nsIStyleSheet*
RDFDocumentImpl::GetStyleSheetAt(PRInt32 aIndex)
{
nsIStyleSheet* sheet = NS_STATIC_CAST(nsIStyleSheet*, mStyleSheets[aIndex]);
NS_IF_ADDREF(sheet);
return sheet;
}
void
RDFDocumentImpl::AddStyleSheet(nsIStyleSheet* aSheet)
{
NS_PRECONDITION(aSheet, "null arg");
if (!aSheet)
return;
mStyleSheets.AppendElement(aSheet);
NS_ADDREF(aSheet);
aSheet->SetOwningDocument(this);
PRBool enabled;
aSheet->GetEnabled(enabled);
if (enabled) {
PRInt32 count, index;
count = mPresShells.Count();
for (index = 0; index < count; index++) {
nsIPresShell* shell = NS_STATIC_CAST(nsIPresShell*, mPresShells[index]);
nsIStyleSet* set = shell->GetStyleSet();
if (nsnull != set) {
set->InsertDocStyleSheetBefore(aSheet, nsnull); // put it first
NS_RELEASE(set);
}
}
// XXX should observers be notified for disabled sheets??? I think not, but I could be wrong
count = mObservers.Count();
for (index = 0; index < count; index++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers.ElementAt(index);
observer->StyleSheetAdded(this, aSheet);
}
}
}
void
RDFDocumentImpl::SetStyleSheetDisabledState(nsIStyleSheet* aSheet,
PRBool aDisabled)
{
NS_PRECONDITION(nsnull != aSheet, "null arg");
PRInt32 count;
PRInt32 index = mStyleSheets.IndexOf((void *)aSheet);
// If we're actually in the document style sheet list
if (-1 != index) {
count = mPresShells.Count();
PRInt32 index;
for (index = 0; index < count; index++) {
nsIPresShell* shell = (nsIPresShell*)mPresShells.ElementAt(index);
nsIStyleSet* set = shell->GetStyleSet();
if (nsnull != set) {
if (aDisabled) {
set->RemoveDocStyleSheet(aSheet);
}
else {
set->InsertDocStyleSheetBefore(aSheet, nsnull); // put it first
}
NS_RELEASE(set);
}
}
}
count = mObservers.Count();
for (index = 0; index < count; index++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers.ElementAt(index);
observer->StyleSheetDisabledStateChanged(this, aSheet, aDisabled);
}
}
nsIScriptContextOwner *
RDFDocumentImpl::GetScriptContextOwner()
{
NS_IF_ADDREF(mScriptContextOwner);
return mScriptContextOwner;
}
void
RDFDocumentImpl::SetScriptContextOwner(nsIScriptContextOwner *aScriptContextOwner)
{
// XXX HACK ALERT! If the script context owner is null, the document
// will soon be going away. So tell our content that to lose its
// reference to the document. This has to be done before we
// actually set the script context owner to null so that the
// content elements can remove references to their script objects.
if (!aScriptContextOwner && mRootContent)
mRootContent->SetDocument(nsnull, PR_TRUE);
NS_IF_RELEASE(mScriptContextOwner);
mScriptContextOwner = aScriptContextOwner;
NS_IF_ADDREF(mScriptContextOwner);
}
NS_IMETHODIMP
RDFDocumentImpl::GetNameSpaceManager(nsINameSpaceManager*& aManager)
{
aManager = mNameSpaceManager;
NS_IF_ADDREF(aManager);
return NS_OK;
}
// Note: We don't hold a reference to the document observer; we assume
// that it has a live reference to the document.
void
RDFDocumentImpl::AddObserver(nsIDocumentObserver* aObserver)
{
// XXX Make sure the observer isn't already in the list
if (mObservers.IndexOf(aObserver) == -1) {
mObservers.AppendElement(aObserver);
}
}
PRBool
RDFDocumentImpl::RemoveObserver(nsIDocumentObserver* aObserver)
{
return mObservers.RemoveElement(aObserver);
}
NS_IMETHODIMP
RDFDocumentImpl::BeginLoad()
{
PRInt32 i, count = mObservers.Count();
for (i = 0; i < count; i++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*) mObservers[i];
observer->BeginLoad(this);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::EndLoad()
{
PRInt32 i, count = mObservers.Count();
for (i = 0; i < count; i++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*) mObservers[i];
observer->EndLoad(this);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::ContentChanged(nsIContent* aContent,
nsISupports* aSubContent)
{
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i];
observer->ContentChanged(this, aContent, aSubContent);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::AttributeChanged(nsIContent* aChild,
nsIAtom* aAttribute,
PRInt32 aHint)
{
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i];
observer->AttributeChanged(this, aChild, aAttribute, aHint);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::ContentAppended(nsIContent* aContainer,
PRInt32 aNewIndexInContainer)
{
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i];
observer->ContentAppended(this, aContainer, aNewIndexInContainer);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::ContentInserted(nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
{
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i];
observer->ContentInserted(this, aContainer, aChild, aIndexInContainer);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::ContentReplaced(nsIContent* aContainer,
nsIContent* aOldChild,
nsIContent* aNewChild,
PRInt32 aIndexInContainer)
{
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i];
observer->ContentReplaced(this, aContainer, aOldChild, aNewChild,
aIndexInContainer);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::ContentRemoved(nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
{
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i];
observer->ContentRemoved(this, aContainer,
aChild, aIndexInContainer);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::StyleRuleChanged(nsIStyleSheet* aStyleSheet,
nsIStyleRule* aStyleRule,
PRInt32 aHint)
{
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i];
observer->StyleRuleChanged(this, aStyleSheet, aStyleRule, aHint);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::StyleRuleAdded(nsIStyleSheet* aStyleSheet,
nsIStyleRule* aStyleRule)
{
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i];
observer->StyleRuleAdded(this, aStyleSheet, aStyleRule);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::StyleRuleRemoved(nsIStyleSheet* aStyleSheet,
nsIStyleRule* aStyleRule)
{
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i];
observer->StyleRuleRemoved(this, aStyleSheet, aStyleRule);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::GetSelection(nsICollection** aSelection)
{
if (!mSelection) {
PR_ASSERT(0);
*aSelection = nsnull;
return NS_ERROR_NOT_INITIALIZED;
}
NS_ADDREF(mSelection);
*aSelection = mSelection;
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::SelectAll()
{
nsIContent * start = nsnull;
nsIContent * end = nsnull;
nsIContent * body = nsnull;
nsString bodyStr("BODY");
PRInt32 i, n;
mRootContent->ChildCount(n);
for (i=0;i<n;i++) {
nsIContent * child;
mRootContent->ChildAt(i, child);
PRBool isSynthetic;
child->IsSynthetic(isSynthetic);
if (!isSynthetic) {
nsIAtom * atom;
child->GetTag(atom);
if (bodyStr.EqualsIgnoreCase(atom)) {
body = child;
break;
}
}
NS_RELEASE(child);
}
if (body == nsnull) {
return NS_ERROR_FAILURE;
}
start = body;
// Find Very first Piece of Content
for (;;) {
start->ChildCount(n);
if (n <= 0) {
break;
}
nsIContent * child = start;
child->ChildAt(0, start);
NS_RELEASE(child);
}
end = body;
// Last piece of Content
for (;;) {
end->ChildCount(n);
if (n <= 0) {
break;
}
nsIContent * child = end;
child->ChildAt(n-1, end);
NS_RELEASE(child);
}
//NS_RELEASE(start);
//NS_RELEASE(end);
#if 0 // XXX nsSelectionRange is in another DLL
nsSelectionRange * range = mSelection->GetRange();
nsSelectionPoint * startPnt = range->GetStartPoint();
nsSelectionPoint * endPnt = range->GetEndPoint();
startPnt->SetPoint(start, -1, PR_TRUE);
endPnt->SetPoint(end, -1, PR_FALSE);
#endif
SetDisplaySelection(PR_TRUE);
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::FindNext(const nsString &aSearchStr, PRBool aMatchCase, PRBool aSearchDown, PRBool &aIsFound)
{
aIsFound = PR_FALSE;
return NS_ERROR_FAILURE;
}
void
RDFDocumentImpl::CreateXIF(nsString & aBuffer, nsISelection* aSelection)
{
PR_ASSERT(0);
}
void
RDFDocumentImpl::ToXIF(nsXIFConverter& aConverter, nsIDOMNode* aNode)
{
PR_ASSERT(0);
}
void
RDFDocumentImpl::BeginConvertToXIF(nsXIFConverter& aConverter, nsIDOMNode* aNode)
{
PR_ASSERT(0);
}
void
RDFDocumentImpl::ConvertChildrenToXIF(nsXIFConverter& aConverter, nsIDOMNode* aNode)
{
PR_ASSERT(0);
}
void
RDFDocumentImpl::FinishConvertToXIF(nsXIFConverter& aConverter, nsIDOMNode* aNode)
{
PR_ASSERT(0);
}
PRBool
RDFDocumentImpl::IsInRange(const nsIContent *aStartContent, const nsIContent* aEndContent, const nsIContent* aContent) const
{
PRBool result;
if (aStartContent == aEndContent) {
return PRBool(aContent == aStartContent);
}
else if (aStartContent == aContent || aEndContent == aContent) {
result = PR_TRUE;
}
else {
result = IsBefore(aStartContent,aContent);
if (result)
result = IsBefore(aContent, aEndContent);
}
return result;
}
PRBool
RDFDocumentImpl::IsBefore(const nsIContent *aNewContent, const nsIContent* aCurrentContent) const
{
PRBool result = PR_FALSE;
if (nsnull != aNewContent && nsnull != aCurrentContent && aNewContent != aCurrentContent) {
nsIContent* test = FindContent(mRootContent, aNewContent, aCurrentContent);
if (test == aNewContent)
result = PR_TRUE;
NS_RELEASE(test);
}
return result;
}
PRBool
RDFDocumentImpl::IsInSelection(nsISelection* aSelection, const nsIContent *aContent) const
{
PRBool result = PR_FALSE;
if (mSelection != nsnull) {
#if 0 // XXX can't include this because nsSelectionPoint is in another DLL.
nsSelectionRange* range = mSelection->GetRange();
if (range != nsnull) {
nsSelectionPoint* startPoint = range->GetStartPoint();
nsSelectionPoint* endPoint = range->GetEndPoint();
nsIContent* startContent = startPoint->GetContent();
nsIContent* endContent = endPoint->GetContent();
result = IsInRange(startContent, endContent, aContent);
NS_IF_RELEASE(startContent);
NS_IF_RELEASE(endContent);
}
#endif
}
return result;
}
nsIContent*
RDFDocumentImpl::GetPrevContent(const nsIContent *aContent) const
{
nsIContent* result = nsnull;
// Look at previous sibling
if (nsnull != aContent) {
nsIContent* parent;
aContent->GetParent(parent);
if (parent != nsnull && parent != mRootContent) {
PRInt32 index;
parent->IndexOf((nsIContent*)aContent, index);
if (index > 0)
parent->ChildAt(index-1, result);
else
result = GetPrevContent(parent);
}
NS_IF_RELEASE(parent);
}
return result;
}
nsIContent*
RDFDocumentImpl::GetNextContent(const nsIContent *aContent) const
{
nsIContent* result = nsnull;
if (nsnull != aContent) {
// Look at next sibling
nsIContent* parent;
aContent->GetParent(parent);
if (parent != nsnull && parent != mRootContent) {
PRInt32 index;
parent->IndexOf((nsIContent*)aContent, index);
PRInt32 count;
parent->ChildCount(count);
if (index+1 < count) {
parent->ChildAt(index+1, result);
// Get first child down the tree
for (;;) {
PRInt32 n;
result->ChildCount(n);
if (n <= 0)
break;
nsIContent * old = result;
old->ChildAt(0, result);
NS_RELEASE(old);
result->ChildCount(n);
}
} else {
result = GetNextContent(parent);
}
}
NS_IF_RELEASE(parent);
}
return result;
}
void
RDFDocumentImpl::SetDisplaySelection(PRBool aToggle)
{
mDisplaySelection = aToggle;
}
PRBool
RDFDocumentImpl::GetDisplaySelection() const
{
return mDisplaySelection;
}
NS_IMETHODIMP
RDFDocumentImpl::HandleDOMEvent(nsIPresContext& aPresContext,
nsEvent* aEvent,
nsIDOMEvent** aDOMEvent,
PRUint32 aFlags,
nsEventStatus& aEventStatus)
{
PR_ASSERT(0);
return NS_ERROR_NOT_IMPLEMENTED;
}
////////////////////////////////////////////////////////////////////////
// nsIXMLDocument interface
NS_IMETHODIMP
RDFDocumentImpl::PrologElementAt(PRUint32 aOffset, nsIContent** aContent)
{
PR_ASSERT(0);
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RDFDocumentImpl::PrologCount(PRUint32* aCount)
{
PR_ASSERT(0);
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RDFDocumentImpl::AppendToProlog(nsIContent* aContent)
{
PR_ASSERT(0);
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RDFDocumentImpl::EpilogElementAt(PRUint32 aOffset, nsIContent** aContent)
{
PR_ASSERT(0);
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RDFDocumentImpl::EpilogCount(PRUint32* aCount)
{
PR_ASSERT(0);
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RDFDocumentImpl::AppendToEpilog(nsIContent* aContent)
{
PR_ASSERT(0);
return NS_ERROR_NOT_IMPLEMENTED;
}
////////////////////////////////////////////////////////////////////////
// nsIRDFDocument interface
NS_IMETHODIMP
RDFDocumentImpl::Init(nsIRDFContentModelBuilder* aBuilder)
{
NS_PRECONDITION(aBuilder != nsnull, "null ptr");
if (! aBuilder)
return NS_ERROR_NULL_POINTER;
nsresult rv;
NS_ADDREF(aBuilder);
mBuilder = aBuilder;
if (NS_FAILED(rv = NS_NewHeapArena(&mArena, nsnull)))
return rv;
if (NS_FAILED(rv = nsRepository::CreateInstance(kNameSpaceManagerCID,
nsnull,
kINameSpaceManagerIID,
(void**) &mNameSpaceManager)))
return rv;
static PRInt32 kInitialResourceTableSize = 1023;
if ((mResources = PL_NewHashTable(kInitialResourceTableSize,
rdf_HashPointer,
PL_CompareValues,
PL_CompareValues,
nsnull,
nsnull)) == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
if (NS_FAILED(rv = nsRepository::CreateInstance(kRDFCompositeDataSourceCID,
nsnull,
kIRDFCompositeDataSourceIID,
(void**) &mDB)))
return rv;
if (NS_FAILED(rv = mDB->AddObserver(this)))
return rv;
if (NS_FAILED(rv = nsServiceManager::GetService(kRDFServiceCID,
kIRDFServiceIID,
(nsISupports**) &mRDFService)))
return rv;
// The builder may try to ask us for our DB, so do this last...
if (NS_FAILED(rv = mBuilder->SetDocument(this)))
return rv;
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::SetRootResource(nsIRDFResource* aResource)
{
NS_ASSERTION(mBuilder != nsnull, "not initialized");
if (! mBuilder)
return NS_ERROR_NOT_INITIALIZED;
nsresult rv = mBuilder->CreateRoot(aResource);
return rv;
}
NS_IMETHODIMP
RDFDocumentImpl::GetDataBase(nsIRDFCompositeDataSource*& result)
{
NS_PRECONDITION(mDB != nsnull, "not initialized");
if (! mDB)
return NS_ERROR_NOT_INITIALIZED;
result = mDB;
NS_ADDREF(result);
return NS_OK;
}
// XXX It may make sense to factor the implementation of this method
// into the content model builder: maybe the content model builder can
// do a more efficient implementation?
NS_IMETHODIMP
RDFDocumentImpl::CreateChildren(nsIRDFContent* element)
{
nsresult rv;
NS_ASSERTION(mDB, "not initialized");
if (! mDB)
return NS_ERROR_NOT_INITIALIZED;
NS_ASSERTION(mRDFService, "not initialized");
if (! mRDFService)
return NS_ERROR_NOT_INITIALIZED;
nsIRDFResource* resource = nsnull;
nsIRDFArcsOutCursor* properties = nsnull;
if (NS_FAILED(rv = element->GetResource(resource)))
goto done;
// Create a cursor that'll enumerate all of the outbound arcs
//
// XXX This is really horrendous and could use a good does of
// special casing. For example, we should treat RDF containers
// specially and _not_ enumerate each of the arcs.
if (NS_FAILED(rv = mDB->ArcLabelsOut(resource, &properties)))
goto done;
while (NS_SUCCEEDED(rv = properties->Advance())) {
nsIRDFResource* property = nsnull;
if (NS_FAILED(rv = properties->GetValue((nsIRDFNode**)&property)))
break;
#ifdef DEBUG
{
const char* s;
property->GetValue(&s);
}
#endif
// Create a second cursor that'll enumerate all of the values
// for all of the arcs.
nsIRDFAssertionCursor* assertions;
if (NS_FAILED(rv = mDB->GetTargets(resource, property, PR_TRUE, &assertions))) {
NS_RELEASE(property);
break;
}
while (NS_SUCCEEDED(rv = assertions->Advance())) {
nsIRDFNode* value;
if (NS_SUCCEEDED(rv = assertions->GetValue((nsIRDFNode**)&value))) {
// At this point, the specific RDFDocumentImpl
// implementations will create an appropriate child
// element (or elements).
rv = mBuilder->OnAssert(element, property, value);
NS_RELEASE(value);
}
if (NS_FAILED(rv))
break;
}
NS_RELEASE(assertions);
NS_RELEASE(property);
}
if (rv == NS_ERROR_RDF_CURSOR_EMPTY)
// This is a normal return code from nsIRDFCursor::Advance()
rv = NS_OK;
done:
NS_IF_RELEASE(resource);
NS_IF_RELEASE(properties);
return rv;
}
NS_IMETHODIMP
RDFDocumentImpl::AddTreeProperty(nsIRDFResource* resource)
{
nsresult rv;
if (! mTreeProperties) {
if (NS_FAILED(rv = NS_NewISupportsArray(&mTreeProperties)))
return rv;
}
// ensure uniqueness
if (mTreeProperties->IndexOf(resource) != -1) {
PR_ASSERT(0);
return NS_OK;
}
mTreeProperties->AppendElement(resource);
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::RemoveTreeProperty(nsIRDFResource* resource)
{
if (! mTreeProperties) {
// XXX no properties have ever been inserted!
PR_ASSERT(0);
return NS_OK;
}
if (! mTreeProperties->RemoveElement(resource)) {
// XXX that specific property has never been inserted!
PR_ASSERT(0);
return NS_OK;
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::IsTreeProperty(nsIRDFResource* aProperty, PRBool* aResult) const
{
#define TREE_PROPERTY_HACK
#if defined(TREE_PROPERTY_HACK)
const char* p;
aProperty->GetValue(&p);
nsAutoString s(p);
if (s.Equals(NC_NAMESPACE_URI "child") ||
s.Equals(NC_NAMESPACE_URI "Folder")) {
*aResult = PR_TRUE;
return NS_OK;
}
#endif // defined(TREE_PROPERTY_HACK)
if (rdf_IsOrdinalProperty(aProperty)) {
*aResult = PR_TRUE;
return NS_OK;
}
if (! mTreeProperties) {
*aResult = PR_FALSE;
return NS_OK;
}
if (mTreeProperties->IndexOf(aProperty) == -1) {
*aResult = PR_FALSE;
return NS_OK;
}
// XXX return "true" by default???
*aResult = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::MapResource(nsIRDFResource* aResource, nsIRDFContent* aContent)
{
// XXX busted: a resource might be located in multiple content
// elements in the tree. This needs to map to a set, not a single
// instance.
PL_HashTableAdd(mResources, aResource, aContent);
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::UnMapResource(nsIRDFResource* aResource, nsIRDFContent* aContent)
{
// XXX busted: a resource might be located in multiple content
// elements in the tree. This needs to map to a set, not a single
// instance.
PL_HashTableRemove(mResources, aResource);
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::SplitProperty(nsIRDFResource* aProperty,
PRInt32* aNameSpaceID,
nsIAtom** aTag)
{
NS_PRECONDITION(aProperty != nsnull, "null ptr");
if (! aProperty)
return NS_ERROR_NULL_POINTER;
// XXX okay, this is a total hack. for this to work right, we need
// to remember what the namespace and the tag were when the
// property was created, which we don't do...
const char* p;
aProperty->GetValue(&p);
nsAutoString uri(p);
PRInt32 index;
if ((index = uri.RFind('#')) < 0) {
if ((index = uri.RFind('/')) < 0) {
NS_ASSERTION(PR_FALSE, "make this smarter!");
}
}
nsAutoString tag;
PRInt32 count = uri.Length() - (index + 1);
uri.Right(tag, count);
uri.Cut(index + 1, count);
tag.ToUpperCase();
*aTag = NS_NewAtom(tag);
nsresult rv = mNameSpaceManager->GetNameSpaceID(uri, *aNameSpaceID);
NS_ASSERTION(NS_SUCCEEDED(rv) && *aNameSpaceID != kNameSpaceID_Unknown, "unknown namespace");
return rv;
}
////////////////////////////////////////////////////////////////////////
// nsIRDFObserver methods
NS_IMETHODIMP
RDFDocumentImpl::OnAssert(nsIRDFResource* subject,
nsIRDFResource* predicate,
nsIRDFNode* object)
{
NS_PRECONDITION(mResources != nsnull, "not initialized");
if (! mResources)
return NS_ERROR_NOT_INITIALIZED;
nsIRDFContent* element = (nsIRDFContent*) PL_HashTableLookup(mResources, subject);
if (! element)
// it's not in the tree: we're done!
return NS_OK;
PRBool hasLiveKids;
if (NS_SUCCEEDED(element->ChildrenHaveBeenGenerated(hasLiveKids)) && hasLiveKids) {
// that is, if the children have _already_ been generated,
// manually add this new one. Otherwise, just ignore it: it'll
// get generated when someone asks the content model for it.
mBuilder->OnAssert(element, predicate, object);
}
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::OnUnassert(nsIRDFResource* subject,
nsIRDFResource* predicate,
nsIRDFNode* object)
{
NS_PRECONDITION(mResources != nsnull, "not initialized");
if (! mResources)
return NS_ERROR_NOT_INITIALIZED;
nsIRDFContent* element = (nsIRDFContent*) PL_HashTableLookup(mResources, subject);
if (! element)
// it's not in the tree: we're done!
return NS_OK;
PRBool hasLiveKids;
if (NS_SUCCEEDED(element->ChildrenHaveBeenGenerated(hasLiveKids)) && hasLiveKids) {
// that is, if the children have _already_ been generated,
// manually remove this one. Otherwise, just ignore it: it'll
// never get generated anyway...
mBuilder->OnUnassert(element, predicate, object);
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
// nsIRDFXMLDataSourceObserver interface
NS_IMETHODIMP
RDFDocumentImpl::OnBeginLoad(nsIRDFXMLDataSource* aDataSource)
{
return BeginLoad();
}
NS_IMETHODIMP
RDFDocumentImpl::OnInterrupt(nsIRDFXMLDataSource* aDataSource)
{
// flow any content that we have up until now.
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::OnResume(nsIRDFXMLDataSource* aDataSource)
{
return NS_OK;
}
NS_IMETHODIMP
RDFDocumentImpl::OnEndLoad(nsIRDFXMLDataSource* aDataSource)
{
return EndLoad();
}
NS_IMETHODIMP
RDFDocumentImpl::OnRootResourceFound(nsIRDFXMLDataSource* aDataSource, nsIRDFResource* aResource)
{
nsresult rv;
if (NS_SUCCEEDED(rv = SetRootResource(aResource))) {
rv = StartLayout();
}
return rv;
}
NS_IMETHODIMP
RDFDocumentImpl::OnCSSStyleSheetAdded(nsIRDFXMLDataSource* aDataSource, nsIURL* aStyleSheetURL)
{
return LoadCSSStyleSheet(aStyleSheetURL);
}
NS_IMETHODIMP
RDFDocumentImpl::OnNamedDataSourceAdded(nsIRDFXMLDataSource* aDataSource, const char* aNamedDataSourceURI)
{
return AddNamedDataSource(aNamedDataSourceURI);
}
////////////////////////////////////////////////////////////////////////
// Implementation methods
nsIContent*
RDFDocumentImpl::FindContent(const nsIContent* aStartNode,
const nsIContent* aTest1,
const nsIContent* aTest2) const
{
PRInt32 count;
aStartNode->ChildCount(count);
PRInt32 index;
for(index = 0; index < count;index++) {
nsIContent* child;
aStartNode->ChildAt(index, child);
nsIContent* content = FindContent(child,aTest1,aTest2);
if (content != nsnull) {
NS_IF_RELEASE(child);
return content;
}
if (child == aTest1 || child == aTest2) {
NS_IF_RELEASE(content);
return child;
}
NS_IF_RELEASE(child);
NS_IF_RELEASE(content);
}
return nsnull;
}
nsresult
RDFDocumentImpl::LoadCSSStyleSheet(nsIURL* url)
{
nsresult rv;
nsIInputStream* iin;
rv = NS_OpenURL(url, &iin);
if (NS_OK != rv) {
NS_RELEASE(url);
return rv;
}
nsIUnicharInputStream* uin = nsnull;
rv = NS_NewConverterStream(&uin, nsnull, iin);
NS_RELEASE(iin);
if (NS_OK != rv) {
NS_RELEASE(url);
return rv;
}
nsICSSParser* parser;
rv = nsRepository::CreateInstance(kCSSParserCID,
nsnull,
kICSSParserIID,
(void**) &parser);
if (NS_SUCCEEDED(rv)) {
nsICSSStyleSheet* sheet = nsnull;
// XXX note: we are ignoring rv until the error code stuff in the
// input routines is converted to use nsresult's
parser->SetCaseSensitive(PR_TRUE);
parser->Parse(uin, url, sheet);
if (nsnull != sheet) {
AddStyleSheet(sheet);
NS_RELEASE(sheet);
rv = NS_OK;
} else {
rv = NS_ERROR_OUT_OF_MEMORY;/* XXX */
}
NS_RELEASE(parser);
}
NS_RELEASE(uin);
NS_RELEASE(url);
return rv;
}
nsresult
RDFDocumentImpl::AddNamedDataSource(const char* uri)
{
nsresult rv;
nsIRDFDataSource* ds = nsnull;
if (NS_FAILED(rv = mRDFService->GetDataSource(uri, &ds)))
goto done;
if (NS_FAILED(rv = mDB->AddDataSource(ds)))
goto done;
done:
NS_IF_RELEASE(ds);
return rv;
}
nsresult
RDFDocumentImpl::StartLayout(void)
{
PRInt32 count = GetNumberOfShells();
for (PRInt32 i = 0; i < count; i++) {
nsIPresShell* shell = GetShellAt(i);
if (nsnull == shell)
continue;
// Resize-reflow this time
nsIPresContext* cx = shell->GetPresContext();
nsRect r;
cx->GetVisibleArea(r);
shell->InitialReflow(r.width, r.height);
NS_RELEASE(cx);
// Now trigger a refresh
nsIViewManager* vm = shell->GetViewManager();
if (nsnull != vm) {
vm->EnableRefresh();
NS_RELEASE(vm);
}
// Start observing the document _after_ we do the initial
// reflow. Otherwise, we'll get into an trouble trying to
// creat kids before the root frame is established.
shell->BeginObservingDocument();
NS_RELEASE(shell);
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
nsresult
NS_NewRDFDocument(nsIRDFDocument** result)
{
NS_PRECONDITION(result != nsnull, "null ptr");
if (! result)
return NS_ERROR_NULL_POINTER;
RDFDocumentImpl* doc = new RDFDocumentImpl();
if (! doc)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(doc);
*result = doc;
return NS_OK;
}