Mozilla/mozilla/content/xul/templates/src/nsXULTemplateBuilder.cpp
rjc%netscape.com de84938fce XUL templates now look for any attribute (other than "uri") which equals "..." and substitutes the URI of the attached node.
git-svn-id: svn://10.0.0.236/trunk@34614 18797224-902f-48f8-a5cc-f745e15eee43
1999-06-10 19:44:59 +00:00

2315 lines
74 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; 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 nsIRDFDocument implementation that builds a tree widget XUL
content model that is to be used with a tree control.
TO DO
1) Get a real namespace for XUL. This should go in a
header file somewhere.
2) Can we do sorting as an insertion sort? This is especially
important in the case where content streams in; e.g., gets added
in the OnAssert() method as opposed to the CreateContents()
method.
*/
#include "nsCOMPtr.h"
#include "nsCRT.h"
#include "nsIAtom.h"
#include "nsIContent.h"
#include "nsIDOMElement.h"
#include "nsIDOMElementObserver.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeObserver.h"
#include "nsIDocument.h"
#include "nsINameSpaceManager.h"
#include "nsIRDFContentModelBuilder.h"
#include "nsIRDFCompositeDataSource.h"
#include "nsIRDFDocument.h"
#include "nsIRDFNode.h"
#include "nsIRDFObserver.h"
#include "nsIRDFService.h"
#include "nsIServiceManager.h"
#include "nsINameSpaceManager.h"
#include "nsIServiceManager.h"
#include "nsISupportsArray.h"
#include "nsIURL.h"
#include "nsLayoutCID.h"
#include "nsRDFCID.h"
#include "nsRDFContentUtils.h"
#include "nsString.h"
#include "nsXPIDLString.h"
#include "rdf.h"
#include "rdfutil.h"
#include "nsITimer.h"
#include "nsITimerCallback.h"
#include "nsIDOMXULElement.h"
#include "nsVoidArray.h"
#include "nsIXULSortService.h"
#include "nsIHTMLElementFactory.h"
#include "nsIHTMLContent.h"
#include "nsRDFGenericBuilder.h"
#define XUL_TEMPLATES 1
////////////////////////////////////////////////////////////////////////
static NS_DEFINE_IID(kIContentIID, NS_ICONTENT_IID);
static NS_DEFINE_IID(kIDocumentIID, NS_IDOCUMENT_IID);
static NS_DEFINE_IID(kINameSpaceManagerIID, NS_INAMESPACEMANAGER_IID);
static NS_DEFINE_IID(kIRDFResourceIID, NS_IRDFRESOURCE_IID);
static NS_DEFINE_IID(kIRDFLiteralIID, NS_IRDFLITERAL_IID);
static NS_DEFINE_IID(kIRDFContentModelBuilderIID, NS_IRDFCONTENTMODELBUILDER_IID);
static NS_DEFINE_IID(kIRDFObserverIID, NS_IRDFOBSERVER_IID);
static NS_DEFINE_IID(kIRDFServiceIID, NS_IRDFSERVICE_IID);
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID);
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
static NS_DEFINE_IID(kXULSortServiceCID, NS_XULSORTSERVICE_CID);
static NS_DEFINE_IID(kIXULSortServiceIID, NS_IXULSORTSERVICE_IID);
static NS_DEFINE_CID(kHTMLElementFactoryCID, NS_HTML_ELEMENT_FACTORY_CID);
static NS_DEFINE_CID(kIHTMLElementFactoryIID, NS_IHTML_ELEMENT_FACTORY_IID);
////////////////////////////////////////////////////////////////////////
nsrefcnt RDFGenericBuilderImpl::gRefCnt = 0;
nsIXULSortService* RDFGenericBuilderImpl::XULSortService = nsnull;
nsIAtom* RDFGenericBuilderImpl::kContainerAtom;
nsIAtom* RDFGenericBuilderImpl::kIsContainerAtom;
nsIAtom* RDFGenericBuilderImpl::kXULContentsGeneratedAtom;
nsIAtom* RDFGenericBuilderImpl::kItemContentsGeneratedAtom;
nsIAtom* RDFGenericBuilderImpl::kIdAtom;
nsIAtom* RDFGenericBuilderImpl::kOpenAtom;
nsIAtom* RDFGenericBuilderImpl::kResourceAtom;
nsIAtom* RDFGenericBuilderImpl::kURIAtom;
nsIAtom* RDFGenericBuilderImpl::kContainmentAtom;
nsIAtom* RDFGenericBuilderImpl::kNaturalOrderPosAtom;
nsIAtom* RDFGenericBuilderImpl::kIgnoreAtom;
nsIAtom* RDFGenericBuilderImpl::kSubcontainmentAtom;
nsIAtom* RDFGenericBuilderImpl::kRootcontainmentAtom;
nsIAtom* RDFGenericBuilderImpl::kTreeTemplateAtom;
nsIAtom* RDFGenericBuilderImpl::kRuleAtom;
nsIAtom* RDFGenericBuilderImpl::kTreeContentsGeneratedAtom;
nsIAtom* RDFGenericBuilderImpl::kTextAtom;
nsIAtom* RDFGenericBuilderImpl::kPropertyAtom;
nsIAtom* RDFGenericBuilderImpl::kInstanceOfAtom;
PRInt32 RDFGenericBuilderImpl::kNameSpaceID_RDF;
PRInt32 RDFGenericBuilderImpl::kNameSpaceID_XUL;
nsIRDFService* RDFGenericBuilderImpl::gRDFService;
nsIRDFContainerUtils* RDFGenericBuilderImpl::gRDFContainerUtils;
nsINameSpaceManager* RDFGenericBuilderImpl::gNameSpaceManager;
nsIRDFResource* RDFGenericBuilderImpl::kNC_Title;
nsIRDFResource* RDFGenericBuilderImpl::kNC_child;
nsIRDFResource* RDFGenericBuilderImpl::kNC_Column;
nsIRDFResource* RDFGenericBuilderImpl::kNC_Folder;
nsIRDFResource* RDFGenericBuilderImpl::kRDF_child;
////////////////////////////////////////////////////////////////////////
RDFGenericBuilderImpl::RDFGenericBuilderImpl(void)
: mDocument(nsnull),
mDB(nsnull),
mRoot(nsnull),
mTimer(nsnull)
{
NS_INIT_REFCNT();
if (gRefCnt == 0) {
kContainerAtom = NS_NewAtom("container");
kIsContainerAtom = NS_NewAtom("iscontainer");
kXULContentsGeneratedAtom = NS_NewAtom("xulcontentsgenerated");
kItemContentsGeneratedAtom = NS_NewAtom("itemcontentsgenerated");
kTreeContentsGeneratedAtom = NS_NewAtom("treecontentsgenerated");
kIdAtom = NS_NewAtom("id");
kOpenAtom = NS_NewAtom("open");
kResourceAtom = NS_NewAtom("resource");
kURIAtom = NS_NewAtom("uri");
kContainmentAtom = NS_NewAtom("containment");
kIgnoreAtom = NS_NewAtom("ignore");
kNaturalOrderPosAtom = NS_NewAtom("pos");
kSubcontainmentAtom = NS_NewAtom("subcontainment");
kRootcontainmentAtom = NS_NewAtom("rootcontainment");
kTreeTemplateAtom = NS_NewAtom("template");
kRuleAtom = NS_NewAtom("rule");
kTextAtom = NS_NewAtom("text");
kPropertyAtom = NS_NewAtom("property");
kInstanceOfAtom = NS_NewAtom("instanceOf");
nsresult rv;
// Register the XUL and RDF namespaces: these'll just retrieve
// the IDs if they've already been registered by someone else.
if (NS_SUCCEEDED(rv = nsComponentManager::CreateInstance(kNameSpaceManagerCID,
nsnull,
kINameSpaceManagerIID,
(void**) &gNameSpaceManager))) {
// XXX This is sure to change. Copied from mozilla/layout/xul/content/src/nsXULAtoms.cpp
static const char kXULNameSpaceURI[]
= "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
static const char kRDFNameSpaceURI[]
= RDF_NAMESPACE_URI;
rv = gNameSpaceManager->RegisterNameSpace(kXULNameSpaceURI, kNameSpaceID_XUL);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to register XUL namespace");
rv = gNameSpaceManager->RegisterNameSpace(kRDFNameSpaceURI, kNameSpaceID_RDF);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to register RDF namespace");
}
else {
NS_ERROR("couldn't create namepsace manager");
}
// Initialize the global shared reference to the service
// manager and get some shared resource objects.
rv = nsServiceManager::GetService(kRDFServiceCID,
kIRDFServiceIID,
(nsISupports**) &gRDFService);
if (NS_SUCCEEDED(rv)) {
gRDFService->GetResource(NC_NAMESPACE_URI "Title", &kNC_Title);
gRDFService->GetResource(NC_NAMESPACE_URI "child", &kNC_child);
gRDFService->GetResource(NC_NAMESPACE_URI "Column", &kNC_Column);
gRDFService->GetResource(NC_NAMESPACE_URI "Folder", &kNC_Folder);
gRDFService->GetResource(RDF_NAMESPACE_URI "child", &kRDF_child);
}
rv = nsServiceManager::GetService(kRDFContainerUtilsCID,
nsIRDFContainerUtils::GetIID(),
(nsISupports**) &gRDFContainerUtils);
rv = nsServiceManager::GetService(kXULSortServiceCID,
kIXULSortServiceIID,
(nsISupports**) &XULSortService);
}
++gRefCnt;
}
RDFGenericBuilderImpl::~RDFGenericBuilderImpl(void)
{
NS_IF_RELEASE(mRoot);
if (mDB) {
mDB->RemoveObserver(this);
NS_RELEASE(mDB);
}
// NS_IF_RELEASE(mDocument) not refcounted
--gRefCnt;
if (gRefCnt == 0) {
NS_RELEASE(kContainerAtom);
NS_RELEASE(kIsContainerAtom);
NS_RELEASE(kXULContentsGeneratedAtom);
NS_RELEASE(kItemContentsGeneratedAtom);
NS_RELEASE(kIdAtom);
NS_RELEASE(kOpenAtom);
NS_RELEASE(kResourceAtom);
NS_RELEASE(kURIAtom);
NS_RELEASE(kContainmentAtom);
NS_RELEASE(kIgnoreAtom);
NS_RELEASE(kNaturalOrderPosAtom);
NS_RELEASE(kSubcontainmentAtom);
NS_RELEASE(kRootcontainmentAtom);
NS_RELEASE(kTreeTemplateAtom);
NS_RELEASE(kRuleAtom);
NS_RELEASE(kTreeContentsGeneratedAtom);
NS_RELEASE(kTextAtom);
NS_RELEASE(kPropertyAtom);
NS_RELEASE(kInstanceOfAtom);
NS_RELEASE(kNC_Title);
NS_RELEASE(kNC_Column);
nsServiceManager::ReleaseService(kRDFServiceCID, gRDFService);
nsServiceManager::ReleaseService(kRDFContainerUtilsCID, gRDFContainerUtils);
nsServiceManager::ReleaseService(kXULSortServiceCID, XULSortService);
NS_RELEASE(gNameSpaceManager);
}
if (mTimer)
{
mTimer->Cancel();
NS_RELEASE(mTimer);
mTimer = nsnull;
}
}
////////////////////////////////////////////////////////////////////////
NS_IMPL_ADDREF(RDFGenericBuilderImpl);
NS_IMPL_RELEASE(RDFGenericBuilderImpl);
NS_IMETHODIMP
RDFGenericBuilderImpl::QueryInterface(REFNSIID iid, void** aResult)
{
NS_PRECONDITION(aResult != nsnull, "null ptr");
if (! aResult)
return NS_ERROR_NULL_POINTER;
if (iid.Equals(kIRDFContentModelBuilderIID) ||
iid.Equals(kISupportsIID)) {
*aResult = NS_STATIC_CAST(nsIRDFContentModelBuilder*, this);
}
else if (iid.Equals(kIRDFObserverIID)) {
*aResult = NS_STATIC_CAST(nsIRDFObserver*, this);
}
else if (iid.Equals(nsIDOMNodeObserver::GetIID())) {
*aResult = NS_STATIC_CAST(nsIDOMNodeObserver*, this);
}
else if (iid.Equals(nsIDOMElementObserver::GetIID())) {
*aResult = NS_STATIC_CAST(nsIDOMElementObserver*, this);
}
else {
*aResult = nsnull;
return NS_NOINTERFACE;
}
NS_ADDREF(this);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
// nsIRDFContentModelBuilder methods
NS_IMETHODIMP
RDFGenericBuilderImpl::SetDocument(nsIRDFDocument* aDocument)
{
// note: null now allowed, it indicates document going away
mDocument = aDocument; // not refcounted
if (aDocument)
{
if (nsnull == mTimer)
{
NS_VERIFY(NS_SUCCEEDED(NS_NewTimer(&mTimer)), "couldn't get timer");
if (mTimer)
{
mTimer->Init(this, /* PR_TRUE, */ 1L);
}
}
}
else
{
if (mTimer)
{
mTimer->Cancel();
NS_RELEASE(mTimer);
mTimer = nsnull;
}
}
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::SetDataBase(nsIRDFCompositeDataSource* aDataBase)
{
NS_PRECONDITION(aDataBase != nsnull, "null ptr");
if (! aDataBase)
return NS_ERROR_NULL_POINTER;
NS_PRECONDITION(mDB == nsnull, "already initialized");
if (mDB)
return NS_ERROR_ALREADY_INITIALIZED;
mDB = aDataBase;
NS_ADDREF(mDB);
mDB->AddObserver(this);
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::GetDataBase(nsIRDFCompositeDataSource** aDataBase)
{
NS_PRECONDITION(aDataBase != nsnull, "null ptr");
if (! aDataBase)
return NS_ERROR_NULL_POINTER;
*aDataBase = mDB;
NS_ADDREF(mDB);
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::CreateRootContent(nsIRDFResource* aResource)
{
// Create a structure that looks like this:
//
// <html:document>
// <html:body>
// <xul:rootwidget>
// </xul:rootwidget>
// </html:body>
// </html:document>
//
// Eventually, we should be able to do away with the HTML tags and
// just have it create XUL in some other context.
nsresult rv;
nsCOMPtr<nsIAtom> tag;
nsCOMPtr<nsIDocument> doc;
nsCOMPtr<nsIAtom> rootAtom;
if (NS_FAILED(rv = GetRootWidgetAtom(getter_AddRefs(rootAtom)))) {
return rv;
}
if (NS_FAILED(rv = mDocument->QueryInterface(kIDocumentIID,
(void**) getter_AddRefs(doc))))
return rv;
// Create the DOCUMENT element
if ((tag = dont_AddRef(NS_NewAtom("document"))) == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIContent> root;
if (NS_FAILED(rv = NS_NewRDFElement(kNameSpaceID_HTML, /* XXX */
tag,
getter_AddRefs(root))))
return rv;
doc->SetRootContent(NS_STATIC_CAST(nsIContent*, root));
// Create the BODY element
nsCOMPtr<nsIContent> body;
if ((tag = dont_AddRef(NS_NewAtom("body"))) == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
if (NS_FAILED(rv = NS_NewRDFElement(kNameSpaceID_HTML, /* XXX */
tag,
getter_AddRefs(body))))
return rv;
// Attach the BODY to the DOCUMENT
if (NS_FAILED(rv = root->AppendChildTo(body, PR_FALSE)))
return rv;
// Create the xul:rootwidget element, and indicate that children should
// be recursively generated on demand
nsCOMPtr<nsIContent> widget;
if (NS_FAILED(rv = CreateElement(kNameSpaceID_XUL,
rootAtom,
aResource,
getter_AddRefs(widget))))
return rv;
if (NS_FAILED(rv = widget->SetAttribute(kNameSpaceID_RDF, kContainerAtom, "true", PR_FALSE)))
return rv;
// Attach the ROOTWIDGET to the BODY
if (NS_FAILED(rv = body->AppendChildTo(widget, PR_FALSE)))
return rv;
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::SetRootContent(nsIContent* aElement)
{
NS_IF_RELEASE(mRoot);
mRoot = aElement;
NS_IF_ADDREF(mRoot);
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::CreateContents(nsIContent* aElement)
{
nsresult rv;
// First, make sure that the element is in the right widget -- ours.
if (!IsElementInWidget(aElement))
return NS_OK;
// Next, see if the item is even "open". If not, then just
// pretend it doesn't have _any_ contents. We check this _before_
// checking the contents-generated attribute so that we don't
// eagerly set contents-generated on a closed node.
if (!IsOpen(aElement))
return NS_OK;
// Now see if someone has marked the element's contents as being
// generated: this prevents a re-entrant call from triggering
// another generation.
nsAutoString attrValue;
if (NS_FAILED(rv = aElement->GetAttribute(kNameSpaceID_None,
kItemContentsGeneratedAtom,
attrValue))) {
NS_ERROR("unable to test contents-generated attribute");
return rv;
}
if ((rv == NS_CONTENT_ATTR_HAS_VALUE) && (attrValue.EqualsIgnoreCase("true")))
return NS_OK;
// Now mark the element's contents as being generated so that
// any re-entrant calls don't trigger an infinite recursion.
if (NS_FAILED(rv = aElement->SetAttribute(kNameSpaceID_None,
kItemContentsGeneratedAtom,
"true",
PR_FALSE))) {
NS_ERROR("unable to set contents-generated attribute");
return rv;
}
// Get the element's resource so that we can generate cell
// values. We could QI for the nsIRDFResource here, but doing this
// via the nsIContent interface allows us to support generic nodes
// that might get added in by DOM calls.
nsCOMPtr<nsIRDFResource> resource;
if (NS_FAILED(rv = nsRDFContentUtils::GetElementRefResource(aElement, getter_AddRefs(resource)))) {
NS_ERROR("unable to get element resource");
return rv;
}
// XXX Eventually, we may want to factor this into one method that
// handles RDF containers (RDF:Bag, et al.) and another that
// handles multi-attributes. For performance...
// Create a cursor that'll enumerate all of the outbound arcs
nsCOMPtr<nsISimpleEnumerator> properties;
rv = mDB->ArcLabelsOut(resource, getter_AddRefs(properties));
if (NS_FAILED(rv)) return rv;
// rjc - sort
nsISupportsArray *tempArray;
if (NS_FAILED(rv = NS_NewISupportsArray(&tempArray)))
return(rv);
while (1) {
PRBool hasMore;
rv = properties->HasMoreElements(&hasMore);
if (NS_FAILED(rv)) return rv;
if (! hasMore)
break;
nsCOMPtr<nsISupports> isupports;
rv = properties->GetNext(getter_AddRefs(isupports));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
// If it's not a widget item property, then it doesn't specify an
// object that is member of the current container element;
// rather, it specifies a "simple" property of the container
// element. Skip it.
if (!IsContainmentProperty(aElement, property))
continue;
// Create a second cursor that'll enumerate all of the values
// for all of the arcs.
nsCOMPtr<nsISimpleEnumerator> targets;
rv = mDB->GetTargets(resource, property, PR_TRUE, getter_AddRefs(targets));
if (NS_FAILED(rv)) return rv;
while (1) {
PRBool hasMoreElements;
rv = targets->HasMoreElements(&hasMoreElements);
if (NS_FAILED(rv)) return rv;
if (! hasMoreElements)
break;
nsCOMPtr<nsISupports> isupportsNext;
rv = targets->GetNext(getter_AddRefs(isupportsNext));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIRDFResource> valueResource = do_QueryInterface(isupportsNext);
if (valueResource && IsContainmentProperty(aElement, property)) {
/* XXX hack: always append value resource 1st!
due to sort callback implementation */
tempArray->AppendElement(valueResource);
tempArray->AppendElement(property);
}
else {
nsCOMPtr<nsIRDFNode> value = do_QueryInterface(isupportsNext);
rv = SetWidgetAttribute(aElement, property, value);
if (NS_FAILED(rv)) return rv;
}
}
}
PRUint32 cnt = 0;
rv = tempArray->Count(&cnt);
NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed");
unsigned long numElements = cnt;
if (numElements > 0)
{
nsIRDFResource ** flatArray = new nsIRDFResource *[numElements];
if (flatArray)
{
// flatten array of resources, sort them, then add as item elements
unsigned long loop;
for (loop=0; loop<numElements; loop++)
flatArray[loop] = (nsIRDFResource *)tempArray->ElementAt(loop);
if (nsnull != XULSortService)
{
XULSortService->OpenContainer(mDB, aElement, flatArray, numElements/2, 2*sizeof(nsIRDFResource *));
}
for (loop=0; loop<numElements; loop+=2)
{
#ifdef XUL_TEMPLATES
rv = CreateWidgetItem(aElement, flatArray[loop+1], flatArray[loop], loop+1);
#else
rv = AddWidgetItem(aElement, flatArray[loop+1], flatArray[loop], loop+1);
#endif
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create widget item");
}
for (int i = numElements - 1; i >=0; i--)
{
NS_IF_RELEASE(flatArray[i]);
}
delete [] flatArray;
}
}
for (int i = numElements - 1; i >= 0; i--)
{
tempArray->RemoveElementAt(i);
}
NS_IF_RELEASE(tempArray);
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::CreateElement(PRInt32 aNameSpaceID,
nsIAtom* aTag,
nsIRDFResource* aResource,
nsIContent** aResult)
{
nsresult rv;
nsCOMPtr<nsIContent> result;
rv = NS_NewRDFElement(aNameSpaceID, aTag, getter_AddRefs(result));
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create new RDFElement");
if (NS_FAILED(rv)) return rv;
nsXPIDLCString uri;
rv = aResource->GetValue( getter_Copies(uri) );
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource URI");
if (NS_FAILED(rv)) return rv;
rv = result->SetAttribute(kNameSpaceID_None, kIdAtom, (const char*) uri, PR_FALSE);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to set id attribute");
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDocument> doc( do_QueryInterface(mDocument) );
rv = result->SetDocument(doc, PR_FALSE);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to set element's document");
if (NS_FAILED(rv)) return rv;
*aResult = result;
NS_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::SetAllAttributesOnElement(nsIContent *aNode, nsIRDFResource *res)
{
// get all arcs out, and if not a containment property, set attribute
nsresult rv = NS_OK;
PRBool markAsContainer = PR_FALSE;
nsCOMPtr<nsISimpleEnumerator> arcs;
if (NS_SUCCEEDED(rv = mDB->ArcLabelsOut(res, getter_AddRefs(arcs))))
{
PRBool hasMore = PR_TRUE;
while(hasMore)
{
rv = arcs->HasMoreElements(&hasMore);
if (NS_FAILED(rv)) break;
if (hasMore == PR_FALSE) break;
nsCOMPtr<nsISupports> isupports;
if (NS_FAILED(rv = arcs->GetNext(getter_AddRefs(isupports)))) break;
nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
// Ignore properties that are used to indicate "tree-ness"
if (IsContainmentProperty(aNode, property))
{
markAsContainer = PR_TRUE;
continue;
}
// Ignore any properties set in ignore attribute
if (IsIgnoredProperty(aNode, property))
{
continue;
}
PRInt32 nameSpaceID;
nsCOMPtr<nsIAtom> tag;
if (NS_FAILED(rv = mDocument->SplitProperty(property, &nameSpaceID, getter_AddRefs(tag))))
break;
nsCOMPtr<nsIRDFNode> value;
if (NS_FAILED(rv = mDB->GetTarget(res, property, PR_TRUE, getter_AddRefs(value))))
break;
if (rv == NS_RDF_NO_VALUE)
continue;
nsAutoString s;
if (NS_SUCCEEDED(rv = nsRDFContentUtils::GetTextForNode(value, s)))
{
aNode->SetAttribute(nameSpaceID, tag, s, PR_FALSE);
}
}
}
if (markAsContainer == PR_TRUE)
{
// mark this as a "container" so that we know to
// recursively generate kids if they're asked for.
rv = aNode->SetAttribute(kNameSpaceID_RDF, kContainerAtom, "true", PR_FALSE);
}
return(rv);
}
NS_IMETHODIMP
RDFGenericBuilderImpl::IsTemplateRuleMatch(nsIRDFResource *aNode, nsIContent *aRule, PRBool *matchingRuleFound)
{
nsresult rv = NS_OK;
PRInt32 count;
*matchingRuleFound = PR_FALSE;
if (NS_FAILED(rv = aRule->GetAttributeCount(count)))
{
return(rv);
}
*matchingRuleFound = PR_TRUE;
for (PRInt32 loop=0; loop<count; loop++)
{
PRInt32 attribNameSpaceID;
nsCOMPtr<nsIAtom> attribAtom;
if (NS_FAILED(rv = aRule->GetAttributeNameAt(loop, attribNameSpaceID, *getter_AddRefs(attribAtom))))
{
*matchingRuleFound = PR_FALSE;
break;
}
nsAutoString attribValue;
if (NS_FAILED(rv = aRule->GetAttribute(attribNameSpaceID, attribAtom, attribValue)))
{
*matchingRuleFound = PR_FALSE;
break;
}
#ifdef DEBUG
nsAutoString nsName;
attribAtom->ToString(nsName);
char *debugName = nsName.ToNewCString();
if (debugName)
{
delete [] debugName;
debugName = nsnull;
}
#endif
// Note: some attributes must be skipped on XUL template rule subtree
// never compare against rdf:container attribute
if ((attribAtom.get() == kContainerAtom) && (attribNameSpaceID == kNameSpaceID_RDF))
continue;
// never compare against rdf:property attribute
else if ((attribAtom.get() == kPropertyAtom) && (attribNameSpaceID == kNameSpaceID_RDF))
continue;
// never compare against rdf:instanceOf attribute
else if ((attribAtom.get() == kInstanceOfAtom) && (attribNameSpaceID == kNameSpaceID_RDF))
continue;
// never compare against {}:id attribute
else if ((attribAtom.get() == kIdAtom) && (attribNameSpaceID == kNameSpaceID_None))
continue;
// never compare against {}:rootcontainment attribute
else if ((attribAtom.get() == kRootcontainmentAtom) && (attribNameSpaceID == kNameSpaceID_None))
continue;
// never compare against {}:subcontainment attribute
else if ((attribAtom.get() == kSubcontainmentAtom) && (attribNameSpaceID == kNameSpaceID_None))
continue;
// never compare against {}:xulcontentsgenerated attribute
else if ((attribAtom.get() == kXULContentsGeneratedAtom) && (attribNameSpaceID == kNameSpaceID_None))
continue;
// never compare against {}:itemcontentsgenerated attribute (bogus)
else if ((attribAtom.get() == kItemContentsGeneratedAtom) && (attribNameSpaceID == kNameSpaceID_None))
continue;
// never compare against {}:treecontentsgenerated attribute (bogus)
else if ((attribAtom.get() == kTreeContentsGeneratedAtom) && (attribNameSpaceID == kNameSpaceID_None))
continue;
else if ((attribNameSpaceID == kNameSpaceID_None) && (attribAtom.get() == kIsContainerAtom))
{
// check and see if aNode is a container
PRBool containerFlag = IsContainer(aRule, aNode);
if (containerFlag && (!attribValue.EqualsIgnoreCase("true")))
{
*matchingRuleFound = PR_FALSE;
break;
}
else if (!containerFlag && (!attribValue.EqualsIgnoreCase("false")))
{
*matchingRuleFound = PR_FALSE;
break;
}
}
else
{
nsCOMPtr<nsIRDFResource> attribAtomResource;
if (NS_FAILED(rv = GetResource(attribNameSpaceID, attribAtom, getter_AddRefs(attribAtomResource))))
{
*matchingRuleFound = PR_FALSE;
break;
}
nsCOMPtr<nsIRDFNode> aResult;
if (NS_FAILED(rv = mDB->GetTarget(aNode, attribAtomResource, PR_TRUE, getter_AddRefs(aResult))))
{
*matchingRuleFound = PR_FALSE;
break;
}
nsAutoString resultStr;
if (NS_FAILED(rv = nsRDFContentUtils::GetTextForNode(aResult, resultStr)))
{
*matchingRuleFound = PR_FALSE;
break;
}
if (!attribValue.Equals(resultStr))
{
*matchingRuleFound = PR_FALSE;
break;
}
}
}
return(rv);
}
NS_IMETHODIMP
RDFGenericBuilderImpl::FindTemplateForResource(nsIRDFResource *aNode, nsIContent **theTemplate)
{
nsresult rv;
PRInt32 count;
nsCOMPtr<nsIContent> aTemplate;
*theTemplate = nsnull;
if (NS_FAILED(rv = mRoot->ChildCount(count))) return(rv);
for (PRInt32 loop=0; loop<count; loop++)
{
if (NS_FAILED(rv = mRoot->ChildAt(loop, *getter_AddRefs(aTemplate))))
continue;
PRInt32 nameSpaceID;
if (NS_FAILED(rv = aTemplate->GetNameSpaceID(nameSpaceID)))
continue;
if (nameSpaceID != kNameSpaceID_XUL)
continue;
nsCOMPtr<nsIAtom> tag;
if (NS_FAILED(rv = aTemplate->GetTag(*getter_AddRefs(tag))))
continue;
if (tag.get() != kTreeTemplateAtom)
continue;
/*
// check for debugging
nsAutoString debugValue;
if (NS_SUCCEEDED(rv = aTemplate->GetAttribute(kNameSpaceID_None, kDebugAtom, debugValue)))
{
debugValue = "true";
}
*/
// found a template; check against any (optional) rules
PRInt32 numRuleChildren, numRulesFound = 0;
if (NS_FAILED(rv = aTemplate->ChildCount(numRuleChildren)))
continue;
for (PRInt32 ruleLoop=0; ruleLoop<numRuleChildren; ruleLoop++)
{
nsCOMPtr<nsIContent> aRule;
if (NS_FAILED(rv = aTemplate->ChildAt(ruleLoop, *getter_AddRefs(aRule))))
continue;
PRInt32 ruleNameSpaceID;
if (NS_FAILED(rv = aRule->GetNameSpaceID(ruleNameSpaceID)))
continue;
if (ruleNameSpaceID != kNameSpaceID_XUL)
continue;
nsCOMPtr<nsIAtom> ruleTag;
if (NS_SUCCEEDED(rv = aRule->GetTag(*getter_AddRefs(ruleTag))))
{
if (ruleTag.get() == kRuleAtom)
{
++numRulesFound;
PRBool matchingRuleFound = PR_FALSE;
if (NS_SUCCEEDED(rv = IsTemplateRuleMatch(aNode, aRule,
&matchingRuleFound)))
{
if (matchingRuleFound == PR_TRUE)
{
// found a matching rule, use it as the template
*theTemplate = aRule;
NS_ADDREF(*theTemplate);
return(NS_OK);
}
}
}
}
}
if (numRulesFound == 0)
{
// if no rules are specified in the template, just use it
*theTemplate = aTemplate;
NS_ADDREF(*theTemplate);
break;
}
}
return(rv);
}
NS_IMETHODIMP
RDFGenericBuilderImpl::PopulateWidgetItemSubtree(nsIContent *aTemplateRoot, nsIContent *aTemplate,
nsIContent *treeChildren, nsIContent* aElement, nsIRDFResource* aProperty,
nsIRDFResource* aValue, PRInt32 aNaturalOrderPos)
{
PRInt32 count;
nsresult rv;
if (NS_FAILED(rv = aTemplate->ChildCount(count))) return(rv);
for (PRInt32 loop=0; loop<count; loop++)
{
nsCOMPtr<nsIContent> aTemplateKid;
if (NS_FAILED(rv = aTemplate->ChildAt(loop, *getter_AddRefs(aTemplateKid))))
continue;
PRInt32 nameSpaceID;
if (NS_FAILED(rv = aTemplateKid->GetNameSpaceID(nameSpaceID)))
continue;
// check whether this item is a containment item for aValue
PRBool isContainmentElement = PR_FALSE;
if (nameSpaceID == kNameSpaceID_XUL)
{
nsAutoString idValue;
if (NS_SUCCEEDED(rv = aTemplateKid->GetAttribute(kNameSpaceID_None,
kURIAtom, idValue)) && (rv == NS_CONTENT_ATTR_HAS_VALUE))
{
if (idValue.EqualsIgnoreCase("..."))
{
isContainmentElement = PR_TRUE;
}
}
}
nsCOMPtr<nsIAtom> tag;
if (NS_SUCCEEDED(rv = aTemplateKid->GetTag(*getter_AddRefs(tag))))
{
nsCOMPtr<nsIContent> treeGrandchild;
if (isContainmentElement == PR_TRUE)
{
if (NS_FAILED(rv = CreateElement(nameSpaceID,
tag, aValue, getter_AddRefs(treeGrandchild))))
continue;
if (NS_FAILED(rv = SetAllAttributesOnElement(treeGrandchild, aValue)))
continue;
}
else if ((tag.get() == kTextAtom) && (nameSpaceID == kNameSpaceID_XUL))
{
// <xul:text rdf:resource="..."> is replaced by text of the
// actual value of the rdf:resource attribute for the given node
nsAutoString attrValue;
if (NS_FAILED(rv = aTemplateKid->GetAttribute(kNameSpaceID_RDF,
kResourceAtom, attrValue)))
continue;
if (rv != NS_CONTENT_ATTR_HAS_VALUE)
continue;
if (attrValue.Length() < 1)
continue;
char *prop = attrValue.ToNewCString();
if (nsnull == prop)
continue;
nsCOMPtr<nsIRDFResource> propRes;
if (NS_FAILED(rv = gRDFService->GetResource(prop, getter_AddRefs(propRes))))
continue;
nsCOMPtr<nsIRDFNode> aResult;
if (NS_FAILED(rv = mDB->GetTarget(aValue,
propRes, PR_TRUE, getter_AddRefs(aResult))))
continue;
rv = nsRDFContentUtils::AttachTextNode(treeChildren, aResult);
delete [] prop;
prop = nsnull;
}
else
{
if (nameSpaceID == kNameSpaceID_HTML)
{
nsIHTMLElementFactory *mHTMLElementFactory;
rv = nsComponentManager::CreateInstance(kHTMLElementFactoryCID,
nsnull, kIHTMLElementFactoryIID, (void**) &mHTMLElementFactory);
if (NS_FAILED(rv))
continue;
nsCOMPtr<nsIHTMLContent> element;
nsAutoString tagName(tag->GetUnicode());
rv = mHTMLElementFactory->CreateInstanceByTag(tagName, getter_AddRefs(element));
if (NS_FAILED(rv))
continue;
nsCOMPtr<nsIDocument> doc;
if (NS_FAILED(rv = mDocument->QueryInterface(kIDocumentIID, getter_AddRefs(doc))))
continue;
// XXX To do
// Make sure our ID is set. Unlike XUL elements, we want to make sure
// that our ID is relative if possible.
if (NS_FAILED(rv = element->SetDocument(doc, PR_FALSE)))
continue;
treeGrandchild = do_QueryInterface(element);
NS_RELEASE(mHTMLElementFactory);
}
else
{
if (NS_FAILED(rv = NS_NewRDFElement(nameSpaceID, tag,
getter_AddRefs(treeGrandchild))))
continue;
nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDocument);
if (! doc)
continue;
treeGrandchild->SetDocument(doc, PR_FALSE);
}
}
if (treeGrandchild && treeGrandchild.get())
{
// copy all attributes from template to new node
PRInt32 numAttribs;
if (NS_SUCCEEDED(rv = aTemplateKid->GetAttributeCount(numAttribs)) &&
(rv == NS_CONTENT_ATTR_HAS_VALUE))
{
for (PRInt32 aLoop=0; aLoop<numAttribs; aLoop++)
{
PRInt32 attribNameSpaceID;
nsCOMPtr<nsIAtom> attribName;
if (NS_SUCCEEDED(rv = aTemplateKid->GetAttributeNameAt(aLoop,
attribNameSpaceID, *getter_AddRefs(attribName))))
{
// only copy attributes that aren't already set on the node
nsAutoString val;
if (NS_SUCCEEDED(rv = treeGrandchild->GetAttribute(attribNameSpaceID,
attribName, val)))
{
if (rv == NS_CONTENT_ATTR_HAS_VALUE) continue;
}
// never copy rdf:container attribute
if ((attribName.get() == kContainerAtom) &&
(attribNameSpaceID == kNameSpaceID_RDF))
continue;
// never copy rdf:property attribute
else if ((attribName.get() == kPropertyAtom) &&
(attribNameSpaceID == kNameSpaceID_RDF))
continue;
// never copy rdf:instanceOf attribute
else if ((attribName.get() == kInstanceOfAtom) &&
(attribNameSpaceID == kNameSpaceID_RDF))
continue;
// never copy {}:rootcontainment attribute
else if ((attribName.get() == kRootcontainmentAtom) &&
(attribNameSpaceID == kNameSpaceID_None))
continue;
// never copy {}:subcontainment attribute
else if ((attribName.get() == kSubcontainmentAtom) &&
(attribNameSpaceID == kNameSpaceID_None))
continue;
// never copy {}:ID attribute
else if ((attribName.get() == kIdAtom) &&
(attribNameSpaceID == kNameSpaceID_None))
continue;
// never copy {}:uri attribute
else if ((attribName.get() == kURIAtom) &&
(attribNameSpaceID == kNameSpaceID_None))
continue;
// never copy {}:itemcontentsgenerated attribute (bogus)
else if ((attribName.get() == kItemContentsGeneratedAtom) &&
(attribNameSpaceID == kNameSpaceID_None))
continue;
// never copy {}:treecontentsgenerated attribute (bogus)
else if ((attribName.get() == kTreeContentsGeneratedAtom) &&
(attribNameSpaceID == kNameSpaceID_None))
continue;
nsAutoString attribValue;
if (NS_SUCCEEDED(rv = aTemplateKid->GetAttribute(attribNameSpaceID,
attribName,attribValue)) && (rv == NS_CONTENT_ATTR_HAS_VALUE))
{
if (attribValue.Find("rdf:") == 0)
{
// found an attribute which wants to bind its value
// to RDF so look it up in the graph
attribValue.Cut(0,4);
char *prop = attribValue.ToNewCString();
if (nsnull == prop) continue;
attribValue = "";
nsCOMPtr<nsIRDFResource> propRes;
rv = gRDFService->GetResource(prop, getter_AddRefs(propRes));
delete [] prop;
if (NS_FAILED(rv)) continue;
nsCOMPtr<nsIRDFNode> valueNode;
if (NS_FAILED(rv = mDB->GetTarget(aValue, propRes, PR_TRUE,
getter_AddRefs(valueNode))) || (rv == NS_RDF_NO_VALUE))
continue;
nsCOMPtr<nsIRDFLiteral> literalValue = do_QueryInterface(valueNode);
if (!literalValue)
continue;
PRUnichar *uniVal;
if (NS_FAILED(rv = literalValue->GetValue(&uniVal)))
continue;
attribValue = uniVal;
}
else if (attribValue.EqualsIgnoreCase("..."))
{
char *uri = nsnull;
aValue->GetValue(&uri);
attribValue=uri;
}
treeGrandchild->SetAttribute(attribNameSpaceID,
attribName, attribValue, PR_FALSE);
}
}
}
}
// set natural order hint
if (aNaturalOrderPos > 0)
{
nsAutoString pos, zero("0000");
pos.Append(aNaturalOrderPos, 10);
if (pos.Length() < 4)
{
pos.Insert(zero, 0, 4-pos.Length());
}
treeGrandchild->SetAttribute(kNameSpaceID_None, kNaturalOrderPosAtom, pos, PR_FALSE);
}
// recurse into template subtree
PRInt32 numTemplateKids;
if (NS_SUCCEEDED(rv = aTemplateKid->ChildCount(numTemplateKids)))
{
if (numTemplateKids > 0)
{
rv = PopulateWidgetItemSubtree(aTemplateRoot, aTemplateKid, treeGrandchild,
aElement, aProperty, aValue, aNaturalOrderPos);
}
}
// Note: add into tree, but only sort if its a containment element!
if ((nsnull != XULSortService) && (isContainmentElement == PR_TRUE))
{
if (NS_FAILED(rv = XULSortService->InsertContainerNode(treeChildren, treeGrandchild)))
{
treeChildren->AppendChildTo(treeGrandchild, PR_TRUE);
}
}
else
{
treeChildren->AppendChildTo(treeGrandchild, PR_TRUE);
}
// If item says its "open", then recurve now and build up its children
nsAutoString openState;
if (NS_SUCCEEDED(rv = treeGrandchild->GetAttribute(kNameSpaceID_None, kOpenAtom, openState))
&& (rv == NS_CONTENT_ATTR_HAS_VALUE) && (openState.EqualsIgnoreCase("true")))
{
OpenWidgetItem(treeGrandchild);
}
}
}
}
return(rv);
}
NS_IMETHODIMP
RDFGenericBuilderImpl::CreateWidgetItem(nsIContent *aElement, nsIRDFResource *aProperty,
nsIRDFResource *aValue, PRInt32 aNaturalOrderPos)
{
nsCOMPtr<nsIContent> aTemplate;
nsresult rv;
if (NS_SUCCEEDED(rv = FindTemplateForResource(aValue, getter_AddRefs(aTemplate))) &&
(aTemplate))
{
nsCOMPtr<nsIContent> children = do_QueryInterface(aElement);
PRBool isRoot = PR_FALSE;
// if template specifies a rootcontainment attribute, get it
// and determine if we are building children off of the root
nsAutoString rootAttrValue;
if (NS_SUCCEEDED(rv = aTemplate->GetAttribute(kNameSpaceID_None,
kRootcontainmentAtom, rootAttrValue)) && (rv == NS_CONTENT_ATTR_HAS_VALUE))
{
nsCOMPtr<nsIAtom> rootAtom = NS_NewAtom(rootAttrValue);
if (rootAtom)
{
PRInt32 nameSpaceID;
if (NS_SUCCEEDED(rv = aElement->GetNameSpaceID(nameSpaceID)))
{
if (nameSpaceID == kNameSpaceID_XUL)
{
nsCOMPtr<nsIAtom> tag;
if (NS_SUCCEEDED(rv = aElement->GetTag(*getter_AddRefs(tag))))
{
if (tag == rootAtom)
{
isRoot = PR_TRUE;
}
}
}
}
}
}
// if template specifies a subcontainment attribute, get it
// and make sure aElement contains it (if not the root),
// otherwise use aElement
if (isRoot == PR_FALSE)
{
nsAutoString attrValue;
if (NS_SUCCEEDED(rv = aTemplate->GetAttribute(kNameSpaceID_None,
kSubcontainmentAtom, attrValue)) && (rv == NS_CONTENT_ATTR_HAS_VALUE))
{
nsCOMPtr<nsIAtom> childrenAtom = NS_NewAtom(attrValue);
if (childrenAtom)
{
if (NS_SUCCEEDED(rv = EnsureElementHasGenericChild(aElement,
kNameSpaceID_XUL, childrenAtom, getter_AddRefs(children))))
{
}
}
}
}
rv = PopulateWidgetItemSubtree(aTemplate, aTemplate, children,
aElement, aProperty, aValue, aNaturalOrderPos);
}
else
{
rv = AddWidgetItem(aElement, aProperty, aValue, aNaturalOrderPos);
}
return(rv);
}
NS_IMETHODIMP
RDFGenericBuilderImpl::OnAssert(nsIRDFResource* aSubject,
nsIRDFResource* aPredicate,
nsIRDFNode* aObject)
{
NS_PRECONDITION(mDocument != nsnull, "not initialized");
if (! mDocument)
return NS_ERROR_NOT_INITIALIZED;
nsresult rv;
nsCOMPtr<nsISupportsArray> elements;
if (NS_FAILED(rv = NS_NewISupportsArray(getter_AddRefs(elements)))) {
NS_ERROR("unable to create new ISupportsArray");
return rv;
}
// Find all the elements in the content model that correspond to
// aSubject: for each, we'll try to build XUL children if
// appropriate.
if (NS_FAILED(rv = mDocument->GetElementsForResource(aSubject, elements))) {
NS_ERROR("unable to retrieve elements from resource");
return rv;
}
PRUint32 cnt = 0;
rv = elements->Count(&cnt);
NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed");
for (PRInt32 i = cnt - 1; i >= 0; --i) {
nsCOMPtr<nsIContent> element( do_QueryInterface(elements->ElementAt(i)) );
// XXX somehow figure out if building XUL kids on this
// particular element makes any sense whatsoever.
// We'll start by making sure that the element at least has
// the same parent has the content model builder's root
if (!IsElementInWidget(element))
continue;
nsCOMPtr<nsIRDFResource> resource;
if (NS_SUCCEEDED(aObject->QueryInterface(kIRDFResourceIID,
(void**) getter_AddRefs(resource)))
&& IsContainmentProperty(element, aPredicate)) {
// Okay, the object _is_ a resource, and the predicate is
// a containment property. So this'll be a new item in the widget
// control.
// But if the contents of aElement _haven't_ yet been
// generated, then just ignore the assertion. We do this
// because we know that _eventually_ the contents will be
// generated (via CreateContents()) when somebody asks for
// them later.
nsAutoString contentsGenerated;
if (NS_FAILED(rv = element->GetAttribute(kNameSpaceID_None,
kItemContentsGeneratedAtom,
contentsGenerated))) {
NS_ERROR("severe problem trying to get attribute");
return rv;
}
if ((rv == NS_CONTENT_ATTR_HAS_VALUE) &&
contentsGenerated.EqualsIgnoreCase("true")) {
// Okay, it's a "live" element, so go ahead and append the new
// child to this node.
#ifdef XUL_TEMPLATES
rv = CreateWidgetItem(element, aPredicate, resource, 0);
#else
rv = AddWidgetItem(element, aPredicate, resource, 0);
#endif
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create widget item");
if (NS_FAILED(rv)) return rv;
}
}
else {
// Either the object of the assertion is not a resource,
// or the object is a resource and the predicate is not a
// tree property. So this won't be a new row in the
// table. See if we can use it to set a cell value on the
// current element.
nsCOMPtr<nsIRDFNode> target;
rv = mDB->GetTarget(aSubject, aPredicate, PR_TRUE, getter_AddRefs(target));
if (NS_FAILED(rv)) return rv;
return SetWidgetAttribute(element, aPredicate, target);
}
}
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::OnUnassert(nsIRDFResource* aSubject,
nsIRDFResource* aPredicate,
nsIRDFNode* aObject)
{
NS_PRECONDITION(mDocument != nsnull, "not initialized");
if (! mDocument)
return NS_ERROR_NOT_INITIALIZED;
nsresult rv;
nsCOMPtr<nsISupportsArray> elements;
if (NS_FAILED(rv = NS_NewISupportsArray(getter_AddRefs(elements)))) {
NS_ERROR("unable to create new ISupportsArray");
return rv;
}
// Find all the elements in the content model that correspond to
// aSubject: for each, we'll try to build XUL children if
// appropriate.
if (NS_FAILED(rv = mDocument->GetElementsForResource(aSubject, elements))) {
NS_ERROR("unable to retrieve elements from resource");
return rv;
}
PRUint32 cnt = 0;
rv = elements->Count(&cnt);
NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed");
for (PRInt32 i = cnt - 1; i >= 0; --i) {
nsCOMPtr<nsIContent> element( do_QueryInterface(elements->ElementAt(i)) );
// XXX somehow figure out if building XUL kids on this
// particular element makes any sense whatsoever.
// We'll start by making sure that the element at least has
// the same parent has the content model builder's root
if (!IsElementInWidget(element))
continue;
nsCOMPtr<nsIRDFResource> resource;
if (NS_SUCCEEDED(aObject->QueryInterface(kIRDFResourceIID,
(void**) getter_AddRefs(resource)))
&& IsContainmentProperty(element, aPredicate)) {
// Okay, the object _is_ a resource, and the predicate is
// a containment property. So this'll be a new item in the widget
// control.
// But if the contents of aElement _haven't_ yet been
// generated, then just ignore the unassertion: nothing is
// in the content model to remove.
nsAutoString contentsGenerated;
if (NS_FAILED(rv = element->GetAttribute(kNameSpaceID_None,
kItemContentsGeneratedAtom,
contentsGenerated))) {
NS_ERROR("severe problem trying to get attribute");
return rv;
}
if ((rv == NS_CONTENT_ATTR_HAS_VALUE) &&
contentsGenerated.EqualsIgnoreCase("true")) {
// Okay, it's a "live" element, so go ahead and append the new
// child to this node.
if (NS_FAILED(rv = RemoveWidgetItem(element, aPredicate, resource))) {
NS_ERROR("unable to create new widget item");
return rv;
}
}
}
else {
// Either the object of the assertion is not a resource,
// or the object is a resource and the predicate is not a
// tree property. So this won't be a new row in the
// table. See if we can use it to set a cell value on the
// current element.
nsCOMPtr<nsIRDFNode> target;
rv = mDB->GetTarget(aSubject, aPredicate, PR_TRUE, getter_AddRefs(target));
if (NS_FAILED(rv)) return rv;
if (rv == NS_RDF_NO_VALUE) {
return UnsetWidgetAttribute(element, aPredicate, aObject);
}
else {
return SetWidgetAttribute(element, aPredicate, target);
}
}
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
// nsIDOMNodeObserver interface
//
// XXX Any of these methods that can't be implemented in a generic
// way should become pure virtual on this class.
//
NS_IMETHODIMP
RDFGenericBuilderImpl::OnSetNodeValue(nsIDOMNode* aNode, const nsString& aValue)
{
NS_NOTYETIMPLEMENTED("write me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::OnInsertBefore(nsIDOMNode* aParent, nsIDOMNode* aNewChild, nsIDOMNode* aRefChild)
{
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::OnReplaceChild(nsIDOMNode* aParent, nsIDOMNode* aNewChild, nsIDOMNode* aOldChild)
{
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::OnRemoveChild(nsIDOMNode* aParent, nsIDOMNode* aOldChild)
{
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::OnAppendChild(nsIDOMNode* aParent, nsIDOMNode* aNewChild)
{
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
// nsIDOMElementObserver interface
NS_IMETHODIMP
RDFGenericBuilderImpl::OnSetAttribute(nsIDOMElement* aElement, const nsString& aName, const nsString& aValue)
{
nsresult rv;
nsCOMPtr<nsIRDFResource> resource;
if (NS_FAILED(rv = GetDOMNodeResource(aElement, getter_AddRefs(resource)))) {
// XXX it's not a resource element, so there's no assertions
// we need to make on the back-end. Should we just do the
// update?
return NS_OK;
}
// Get the nsIContent interface, it's a bit more utilitarian
nsCOMPtr<nsIContent> element( do_QueryInterface(aElement) );
if (! element) {
NS_ERROR("element doesn't support nsIContent");
return NS_ERROR_UNEXPECTED;
}
// Make sure that the element is in the widget. XXX Even this may be
// a bit too promiscuous: an element may also be a XUL element...
if (!IsElementInWidget(element))
return NS_OK;
// Split the element into its namespace and tag components
PRInt32 elementNameSpaceID;
if (NS_FAILED(rv = element->GetNameSpaceID(elementNameSpaceID))) {
NS_ERROR("unable to get element namespace ID");
return rv;
}
nsCOMPtr<nsIAtom> elementNameAtom;
if (NS_FAILED(rv = element->GetTag( *getter_AddRefs(elementNameAtom) ))) {
NS_ERROR("unable to get element tag");
return rv;
}
// Split the property name into its namespace and tag components
PRInt32 attrNameSpaceID;
nsCOMPtr<nsIAtom> attrNameAtom;
if (NS_FAILED(rv = element->ParseAttributeString(aName, *getter_AddRefs(attrNameAtom), attrNameSpaceID))) {
NS_ERROR("unable to parse attribute string");
return rv;
}
// Now do the work to change the attribute. There are a couple of
// special cases that we need to check for here...
if ((elementNameSpaceID == kNameSpaceID_XUL) &&
IsItemOrFolder(element) && // XXX IsItemOrFolder(): is this what we really mean?
(attrNameSpaceID == kNameSpaceID_None) &&
(attrNameAtom.get() == kOpenAtom)) {
// We are (possibly) changing the value of the "open"
// attribute. This may require us to generate or destroy
// content in the widget. See what the old value was...
nsAutoString attrValue;
if (NS_FAILED(rv = element->GetAttribute(kNameSpaceID_None, kOpenAtom, attrValue))) {
NS_ERROR("unable to get current open state");
return rv;
}
if ((rv == NS_CONTENT_ATTR_NO_VALUE) || (rv == NS_CONTENT_ATTR_NOT_THERE) ||
((rv == NS_CONTENT_ATTR_HAS_VALUE) && (! attrValue.EqualsIgnoreCase(aValue))) ||
PR_TRUE // XXX just always allow this to fire.
) {
// Okay, it's really changing.
// This is a "transient" property, so we _don't_ go to the
// RDF graph to set it.
if (NS_FAILED(rv = element->SetAttribute(kNameSpaceID_None, kOpenAtom, aValue, PR_TRUE))) {
NS_ERROR("unable to update attribute on content node");
return rv;
}
if (aValue.EqualsIgnoreCase("true")) {
rv = OpenWidgetItem(element);
}
else {
rv = CloseWidgetItem(element);
}
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to open/close tree item");
return rv;
}
}
else if ((elementNameSpaceID == kNameSpaceID_XUL) &&
(IsItemOrFolder(element) || // XXX IsItemOrFolder(): is this what we really mean?
IsWidgetInsertionRootElement(element)) &&
(attrNameSpaceID == kNameSpaceID_None) &&
(attrNameAtom.get() == kIdAtom)) {
// We are (possibly) changing the actual identity of the
// element; e.g., re-rooting an item in the tree.
nsCOMPtr<nsIRDFResource> newResource;
if (NS_FAILED(rv = gRDFService->GetUnicodeResource(aValue.GetUnicode(), getter_AddRefs(newResource)))) {
NS_ERROR("unable to get new resource");
return rv;
}
#if 0 // XXX we're fighting with the XUL builder, so just _always_ let this through.
// Didn't change. So bail!
if (resource == newResource)
return NS_OK;
#endif
// Allright, it really is changing. So blow away the old
// content node and insert a new one with the new ID in
// its place.
nsCOMPtr<nsIContent> parent;
if (NS_FAILED(rv = element->GetParent(*getter_AddRefs(parent)))) {
NS_ERROR("unable to get element's parent");
return rv;
}
PRInt32 elementIndex;
if (NS_FAILED(rv = parent->IndexOf(element, elementIndex))) {
NS_ERROR("unable to get element's index within parent");
return rv;
}
if (NS_FAILED(rv = parent->RemoveChildAt(elementIndex, PR_TRUE))) {
NS_ERROR("unable to remove element");
return rv;
}
nsCOMPtr<nsIContent> newElement;
if (NS_FAILED(rv = CreateElement(elementNameSpaceID,
elementNameAtom,
newResource,
getter_AddRefs(newElement)))) {
NS_ERROR("unable to create new element");
return rv;
}
// Attach transient properties to the new element.
//
// XXX all I really care about right this minute is the
// "open" state. We could put this stuff in a table and
// drive it that way.
nsAutoString attrValue;
if (NS_FAILED(rv = element->GetAttribute(kNameSpaceID_None, kOpenAtom, attrValue))) {
NS_ERROR("unable to get open state of old element");
return rv;
}
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
if (NS_FAILED(rv = newElement->SetAttribute(kNameSpaceID_None, kOpenAtom, attrValue, PR_FALSE))) {
NS_ERROR("unable to set open state of new element");
return rv;
}
}
// Mark as a container so the contents get regenerated
if (NS_FAILED(rv = newElement->SetAttribute(kNameSpaceID_RDF,
kContainerAtom,
"true",
PR_FALSE))) {
NS_ERROR("unable to mark as a container");
return rv;
}
// Now insert the new element into the parent. This should
// trigger a reflow and cause the contents to be regenerated.
if (NS_FAILED(rv = parent->InsertChildAt(newElement, elementIndex, PR_TRUE))) {
NS_ERROR("unable to add new element to the parent");
return rv;
}
return NS_OK;
}
// If we get here, it's a "vanilla" property: push its value into the graph.
if (kNameSpaceID_Unknown == attrNameSpaceID) {
attrNameSpaceID = kNameSpaceID_None; // ignore unknown prefix XXX is this correct?
}
nsCOMPtr<nsIRDFResource> property;
if (NS_FAILED(rv = GetResource(attrNameSpaceID, attrNameAtom, getter_AddRefs(property)))) {
NS_ERROR("unable to construct resource");
return rv;
}
// Unassert the old value, if there was one.
nsAutoString oldValue;
if (NS_CONTENT_ATTR_HAS_VALUE == element->GetAttribute(attrNameSpaceID, attrNameAtom, oldValue)) {
nsCOMPtr<nsIRDFLiteral> value;
if (NS_FAILED(rv = gRDFService->GetLiteral(oldValue.GetUnicode(), getter_AddRefs(value)))) {
NS_ERROR("unable to construct literal");
return rv;
}
rv = mDB->Unassert(resource, property, value);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to unassert old property value");
}
// Assert the new value
{
nsCOMPtr<nsIRDFLiteral> value;
if (NS_FAILED(rv = gRDFService->GetLiteral(aValue.GetUnicode(), getter_AddRefs(value)))) {
NS_ERROR("unable to construct literal");
return rv;
}
rv = mDB->Assert(resource, property, value, PR_TRUE);
NS_ASSERTION(rv == NS_RDF_ASSERTION_ACCEPTED, "unable to assert new property value");
if (NS_FAILED(rv)) return rv;
}
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::OnRemoveAttribute(nsIDOMElement* aElement, const nsString& aName)
{
nsresult rv;
nsCOMPtr<nsIRDFResource> resource;
if (NS_FAILED(rv = GetDOMNodeResource(aElement, getter_AddRefs(resource)))) {
// XXX it's not a resource element, so there's no assertions
// we need to make on the back-end. Should we just do the
// update?
return NS_OK;
}
// Get the nsIContent interface, it's a bit more utilitarian
nsCOMPtr<nsIContent> element( do_QueryInterface(aElement) );
if (! element) {
NS_ERROR("element doesn't support nsIContent");
return NS_ERROR_UNEXPECTED;
}
// Make sure that the element is in the widget. XXX Even this may be
// a bit too promiscuous: an element may also be a XUL element...
if (!IsElementInWidget(element))
return NS_OK;
// Split the element into its namespace and tag components
PRInt32 elementNameSpaceID;
if (NS_FAILED(rv = element->GetNameSpaceID(elementNameSpaceID))) {
NS_ERROR("unable to get element namespace ID");
return rv;
}
nsCOMPtr<nsIAtom> elementNameAtom;
if (NS_FAILED(rv = element->GetTag( *getter_AddRefs(elementNameAtom) ))) {
NS_ERROR("unable to get element tag");
return rv;
}
// Split the property name into its namespace and tag components
PRInt32 attrNameSpaceID;
nsCOMPtr<nsIAtom> attrNameAtom;
if (NS_FAILED(rv = element->ParseAttributeString(aName, *getter_AddRefs(attrNameAtom), attrNameSpaceID))) {
NS_ERROR("unable to parse attribute string");
return rv;
}
if ((elementNameSpaceID == kNameSpaceID_XUL) &&
IsItemOrFolder(element) && // XXX Is this what we really mean?
(attrNameSpaceID == kNameSpaceID_None) &&
(attrNameAtom.get() == kOpenAtom)) {
// We are removing the value of the "open" attribute. This may
// require us to destroy content from the tree.
// XXX should we check for existence of the attribute first?
if (NS_FAILED(rv = element->UnsetAttribute(kNameSpaceID_None, kOpenAtom, PR_TRUE))) {
NS_ERROR("unable to attribute on update content node");
return rv;
}
if (NS_FAILED(rv = CloseWidgetItem(element))) {
NS_ERROR("unable to close widget item");
return rv;
}
}
else {
// It's a "vanilla" property: push its value into the graph.
nsCOMPtr<nsIRDFResource> property;
if (kNameSpaceID_Unknown == attrNameSpaceID) {
attrNameSpaceID = kNameSpaceID_None; // ignore unknown prefix XXX is this correct?
}
if (NS_FAILED(rv = GetResource(attrNameSpaceID, attrNameAtom, getter_AddRefs(property)))) {
NS_ERROR("unable to construct resource");
return rv;
}
// Unassert the old value, if there was one.
nsAutoString oldValue;
if (NS_CONTENT_ATTR_HAS_VALUE == element->GetAttribute(attrNameSpaceID, attrNameAtom, oldValue)) {
nsCOMPtr<nsIRDFLiteral> value;
if (NS_FAILED(rv = gRDFService->GetLiteral(oldValue.GetUnicode(), getter_AddRefs(value)))) {
NS_ERROR("unable to construct literal");
return rv;
}
rv = mDB->Unassert(resource, property, value);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to unassert old property value");
}
}
return NS_OK;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::OnSetAttributeNode(nsIDOMElement* aElement, nsIDOMAttr* aNewAttr)
{
NS_NOTYETIMPLEMENTED("write me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RDFGenericBuilderImpl::OnRemoveAttributeNode(nsIDOMElement* aElement, nsIDOMAttr* aOldAttr)
{
NS_NOTYETIMPLEMENTED("write me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
////////////////////////////////////////////////////////////////////////
// Implementation methods
nsresult
RDFGenericBuilderImpl::EnsureElementHasGenericChild(nsIContent* parent,
PRInt32 nameSpaceID,
nsIAtom* tag,
nsIContent** result)
{
nsresult rv;
rv = nsRDFContentUtils::FindChildByTag(parent, nameSpaceID, tag, result);
if (NS_FAILED(rv))
return rv;
if (rv == NS_RDF_NO_VALUE) {
// we need to construct a new child element.
nsCOMPtr<nsIContent> element;
if (NS_FAILED(rv = NS_NewRDFElement(nameSpaceID, tag, getter_AddRefs(element))))
return rv;
if (NS_FAILED(rv = parent->AppendChildTo(element, PR_FALSE)))
return rv;
*result = element;
NS_ADDREF(*result);
}
return NS_OK;
}
nsresult
RDFGenericBuilderImpl::FindWidgetRootElement(nsIContent* aElement,
nsIContent** aWidgetElement)
{
nsresult rv;
nsCOMPtr<nsIAtom> rootAtom;
if (NS_FAILED(rv = GetRootWidgetAtom(getter_AddRefs(rootAtom)))) {
return rv;
}
// walk up the tree until you find rootAtom
nsCOMPtr<nsIContent> element(do_QueryInterface(aElement));
while (element) {
PRInt32 nameSpaceID;
if (NS_FAILED(rv = element->GetNameSpaceID(nameSpaceID)))
return rv; // XXX fatal
if (nameSpaceID == kNameSpaceID_XUL) {
nsCOMPtr<nsIAtom> tag;
if (NS_FAILED(rv = element->GetTag(*getter_AddRefs(tag))))
return rv;
if (tag == rootAtom) {
*aWidgetElement = element;
NS_ADDREF(*aWidgetElement);
return NS_OK;
}
}
// up to the parent...
nsCOMPtr<nsIContent> parent;
element->GetParent(*getter_AddRefs(parent));
element = parent;
}
//NS_ERROR("must not've started from within the XUL widget");
return NS_ERROR_FAILURE;
}
PRBool
RDFGenericBuilderImpl::IsItemOrFolder(nsIContent* aElement)
{
// XXX It seems like this should be a pure virtual method that
// subclasses implement?
// XXX All of the callers of this method actually only seem to
// care if the element is a "folder".
// Returns PR_TRUE if the element is an "item" or a "folder" in
// the widget.
nsresult rv;
nsCOMPtr<nsIAtom> itemAtom;
nsCOMPtr<nsIAtom> folderAtom;
if (NS_FAILED(rv = GetWidgetItemAtom(getter_AddRefs(itemAtom))) ||
NS_FAILED(rv = GetWidgetFolderAtom(getter_AddRefs(folderAtom)))) {
return rv;
}
// "element" must be itemAtom
nsCOMPtr<nsIAtom> tag;
if (NS_FAILED(rv = aElement->GetTag(*getter_AddRefs(tag))))
return PR_FALSE;
if (tag != itemAtom && tag != folderAtom)
return PR_FALSE;
return PR_TRUE;
}
PRBool
RDFGenericBuilderImpl::IsWidgetInsertionRootElement(nsIContent* element)
{
// Returns PR_TRUE if the element is the root point to insert new items.
nsresult rv;
nsCOMPtr<nsIAtom> rootAtom;
if (NS_FAILED(rv = GetInsertionRootAtom(getter_AddRefs(rootAtom)))) {
return rv;
}
PRInt32 nameSpaceID;
if (NS_FAILED(rv = element->GetNameSpaceID(nameSpaceID))) {
NS_ERROR("unable to get namespace ID");
return PR_FALSE;
}
if (nameSpaceID != kNameSpaceID_XUL)
return PR_FALSE; // not a XUL element
nsCOMPtr<nsIAtom> elementTag;
if (NS_FAILED(rv = element->GetTag(*getter_AddRefs(elementTag)))) {
NS_ERROR("unable to get tag");
return PR_FALSE;
}
if (elementTag != rootAtom)
return PR_FALSE; // not the place to insert a child
return PR_TRUE;
}
PRBool
RDFGenericBuilderImpl::IsContainmentProperty(nsIContent* aElement, nsIRDFResource* aProperty)
{
// XXX is this okay to _always_ treat ordinal properties as tree
// properties? Probably not...
nsresult rv;
PRBool isOrdinal;
rv = gRDFContainerUtils->IsOrdinalProperty(aProperty, &isOrdinal);
if (NS_FAILED(rv))
return PR_FALSE;
if (isOrdinal)
return PR_TRUE;
nsXPIDLCString propertyURI;
if (NS_FAILED(rv = aProperty->GetValue( getter_Copies(propertyURI) ))) {
NS_ERROR("unable to get property URI");
return PR_FALSE;
}
nsAutoString uri;
// Walk up the content tree looking for the "rdf:containment"
// attribute, so we can determine if the specified property
// actually defines containment.
nsCOMPtr<nsIContent> element( dont_QueryInterface(aElement) );
while (element) {
// Never ever ask an HTML element about non-HTML attributes.
PRInt32 nameSpaceID;
if (NS_FAILED(rv = element->GetNameSpaceID(nameSpaceID))) {
NS_ERROR("unable to get element namespace");
return PR_FALSE;
}
if (nameSpaceID != kNameSpaceID_HTML) {
// Is this the "containment" property?
if (NS_FAILED(rv = element->GetAttribute(kNameSpaceID_None, kContainmentAtom, uri))) {
NS_ERROR("unable to get attribute value");
return PR_FALSE;
}
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
// Okay we've found the locally-scoped tree properties,
// a whitespace-separated list of property URIs. So we
// definitively know whether this is a tree property or
// not.
if (uri.Find(propertyURI) >= 0)
return PR_TRUE;
else
return PR_FALSE;
}
}
nsCOMPtr<nsIContent> parent;
element->GetParent(*getter_AddRefs(parent));
element = parent;
}
// If we get here, we didn't find any tree property: so now
// defaults start to kick in.
#define TREE_PROPERTY_HACK
#if defined(TREE_PROPERTY_HACK)
if ((aProperty == kNC_child) ||
(aProperty == kNC_Folder) ||
(aProperty == kRDF_child)) {
return PR_TRUE;
}
else
#endif // defined(TREE_PROPERTY_HACK)
return PR_FALSE;
}
PRBool
RDFGenericBuilderImpl::IsIgnoredProperty(nsIContent* aElement, nsIRDFResource* aProperty)
{
nsresult rv;
nsXPIDLCString propertyURI;
rv = aProperty->GetValue( getter_Copies(propertyURI) );
if (NS_FAILED(rv)) return rv;
nsAutoString uri;
// Walk up the content tree looking for the "rdf:ignore"
// attribute, so we can determine if the specified property should
// be ignored.
nsCOMPtr<nsIContent> element( dont_QueryInterface(aElement) );
while (element) {
PRInt32 nameSpaceID;
rv = element->GetNameSpaceID(nameSpaceID);
if (NS_FAILED(rv)) return rv;
// Never ever ask an HTML element about non-HTML attributes
if (nameSpaceID != kNameSpaceID_HTML) {
rv = element->GetAttribute(kNameSpaceID_None, kIgnoreAtom, uri);
if (NS_FAILED(rv)) return rv;
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
// Okay, we've found the locally-scoped ignore
// properties, which is a whitespace-separated list of
// property URIs. So we definitively know whether this
// property should be ignored or not.
if (uri.Find(propertyURI) >= 0)
return PR_TRUE;
else
return PR_FALSE;
}
}
nsCOMPtr<nsIContent> parent;
element->GetParent(*getter_AddRefs(parent));
element = parent;
}
// Walked _all_ the way to the top and couldn't find anything to
// ignore.
return PR_FALSE;
}
PRBool
RDFGenericBuilderImpl::IsContainer(nsIContent* aElement, nsIRDFResource* aResource)
{
// Look at all of the arcs extending _out_ of the resource: if any
// of them are that "containment" property, then we know we'll
// have children.
nsCOMPtr<nsISimpleEnumerator> arcs;
nsresult rv;
rv = mDB->ArcLabelsOut(aResource, getter_AddRefs(arcs));
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get arcs out");
if (NS_FAILED(rv))
return PR_FALSE;
while (1) {
PRBool hasMore;
rv = arcs->HasMoreElements(&hasMore);
NS_ASSERTION(NS_SUCCEEDED(rv), "severe error advancing cursor");
if (NS_FAILED(rv))
return PR_FALSE;
if (! hasMore)
break;
nsCOMPtr<nsISupports> isupports;
rv = arcs->GetNext(getter_AddRefs(isupports));
if (NS_FAILED(rv))
return PR_FALSE;
nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
if (! IsContainmentProperty(aElement, property))
continue;
// see if it has a value
nsCOMPtr<nsIRDFNode> value;
rv = mDB->GetTarget(aResource, property, PR_TRUE, getter_AddRefs(value));
if (NS_FAILED(rv))
return PR_FALSE;
if (value)
return PR_TRUE;
}
return PR_FALSE;
}
PRBool
RDFGenericBuilderImpl::IsOpen(nsIContent* aElement)
{
nsresult rv;
// needs to be a the valid insertion root or an item to begin with.
PRInt32 nameSpaceID;
if (NS_FAILED(rv = aElement->GetNameSpaceID(nameSpaceID))) {
NS_ERROR("unable to get namespace ID");
return PR_FALSE;
}
if (nameSpaceID != kNameSpaceID_XUL)
return PR_FALSE;
nsCOMPtr<nsIAtom> tag;
if (NS_FAILED(rv = aElement->GetTag( *getter_AddRefs(tag) ))) {
NS_ERROR("unable to get element's tag");
return PR_FALSE;
}
nsCOMPtr<nsIAtom> insertionAtom;
if (NS_FAILED(rv = GetInsertionRootAtom(getter_AddRefs(insertionAtom)))) {
return rv;
}
nsCOMPtr<nsIAtom> folderAtom;
if (NS_FAILED(rv = GetWidgetFolderAtom(getter_AddRefs(folderAtom)))) {
return rv;
}
// The insertion root is _always_ open.
if (tag == insertionAtom)
return PR_TRUE;
// If it's not a widget folder item, then it's not open.
if (tag != folderAtom)
return PR_FALSE;
nsAutoString value;
if (NS_FAILED(rv = aElement->GetAttribute(kNameSpaceID_None, kOpenAtom, value))) {
NS_ERROR("unable to get open attribute");
return rv;
}
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
if (value.EqualsIgnoreCase("true"))
return PR_TRUE;
}
return PR_FALSE;
}
PRBool
RDFGenericBuilderImpl::IsElementInWidget(nsIContent* aElement)
{
// Make sure that we're actually creating content for the tree
// content model that we've been assigned to deal with.
// Walk up the parent chain from us to the root and
// see what we find.
if (aElement == mRoot)
return PR_TRUE;
// walk up the tree until you find rootAtom
nsCOMPtr<nsIContent> element(do_QueryInterface(aElement));
nsCOMPtr<nsIContent> parent;
element->GetParent(*getter_AddRefs(parent));
element = parent;
while (element) {
if (element.get() == mRoot)
return PR_TRUE;
// up to the parent...
nsCOMPtr<nsIContent> parent;
element->GetParent(*getter_AddRefs(parent));
element = parent;
}
return PR_FALSE;
}
nsresult
RDFGenericBuilderImpl::GetDOMNodeResource(nsIDOMNode* aNode, nsIRDFResource** aResource)
{
nsresult rv;
// Given an nsIDOMNode that presumably has been created as a proxy
// for an RDF resource, pull the RDF resource information out of
// it.
nsCOMPtr<nsIContent> element;
if (NS_FAILED(rv = aNode->QueryInterface(kIContentIID, getter_AddRefs(element) ))) {
NS_ERROR("DOM element doesn't support nsIContent");
return rv;
}
return nsRDFContentUtils::GetElementRefResource(element, aResource);
}
nsresult
RDFGenericBuilderImpl::GetResource(PRInt32 aNameSpaceID,
nsIAtom* aNameAtom,
nsIRDFResource** aResource)
{
NS_PRECONDITION(aNameAtom != nsnull, "null ptr");
if (! aNameAtom)
return NS_ERROR_NULL_POINTER;
// XXX should we allow nodes with no namespace???
NS_PRECONDITION(aNameSpaceID != kNameSpaceID_Unknown, "no namespace");
if (aNameSpaceID == kNameSpaceID_Unknown)
return NS_ERROR_UNEXPECTED;
// construct a fully-qualified URI from the namespace/tag pair.
nsAutoString uri;
gNameSpaceManager->GetNameSpaceURI(aNameSpaceID, uri);
// XXX check to see if we need to insert a '/' or a '#'
nsAutoString tag(aNameAtom->GetUnicode());
if (0 < uri.Length() && uri.Last() != '#' && uri.Last() != '/' && tag.First() != '#')
uri.Append('#');
uri.Append(tag);
nsresult rv = gRDFService->GetUnicodeResource(uri.GetUnicode(), aResource);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource");
return rv;
}
nsresult
RDFGenericBuilderImpl::OpenWidgetItem(nsIContent* aElement)
{
return CreateContents(aElement);
}
nsresult
RDFGenericBuilderImpl::CloseWidgetItem(nsIContent* aElement)
{
nsresult rv;
// Find the tag that contains the children so that we can remove all of
// the children.
nsCOMPtr<nsIAtom> parentAtom;
if (NS_FAILED(rv = GetItemAtomThatContainsTheChildren(getter_AddRefs(parentAtom)))) {
return rv;
}
nsCOMPtr<nsIContent> parentNode;
nsCOMPtr<nsIAtom> tag;
if (NS_FAILED(rv = aElement->GetTag(*getter_AddRefs(tag))))
return rv; // XXX fatal
if (tag == parentAtom) {
parentNode = dont_QueryInterface(aElement);
rv = NS_OK;
}
else {
rv = nsRDFContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, parentAtom, getter_AddRefs(parentNode));
}
if (rv == NS_RDF_NO_VALUE) {
// No tag; must've already been closed
return NS_OK;
}
if (NS_FAILED(rv)) {
NS_ERROR("severe error retrieving parent node for removal");
return rv;
}
PRInt32 count;
if (NS_FAILED(rv = parentNode->ChildCount(count))) {
NS_ERROR("unable to get count of the parent's children");
return rv;
}
while (--count >= 0) {
// XXX This is a bit gross. We need to recursively remove any
// subtrees before we remove the current subtree.
nsIContent* child;
if (NS_SUCCEEDED(rv = parentNode->ChildAt(count, child))) {
rv = CloseWidgetItem(child);
NS_RELEASE(child);
}
rv = parentNode->RemoveChildAt(count, PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "error removing child");
}
// Clear the contents-generated attribute so that the next time we
// come back, we'll regenerate the kids we just killed.
if (NS_FAILED(rv = aElement->UnsetAttribute(kNameSpaceID_None,
kItemContentsGeneratedAtom,
PR_FALSE))) {
NS_ERROR("unable to clear contents-generated attribute");
return rv;
}
return NS_OK;
}