Bug 1459748 - Fork RDF to comm-central. rs=bustage-fix
authorJorg K <jorgk@jorgk.com>
Tue, 08 May 2018 18:19:56 +0200
changeset 31189 13e2fda05aa3793cde3c1afb06472de6d391e88a
parent 31188 b00e830b9e5e3a006102519c34b8fc8e862e9cdf
child 31190 0719cec3b02019d20a742a39900fd36752188a23
push id2249
push userclokep@gmail.com
push dateTue, 26 Jun 2018 01:48:25 +0000
treeherdercomm-beta@93c72f9a5f79 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbustage-fix
bugs1459748
Bug 1459748 - Fork RDF to comm-central. rs=bustage-fix
mailnews/mailnews.mozbuild
rdf/base/moz.build
rdf/base/nsCompositeDataSource.cpp
rdf/base/nsContainerEnumerator.cpp
rdf/base/nsDefaultResourceFactory.cpp
rdf/base/nsIRDFCompositeDataSource.idl
rdf/base/nsIRDFContainer.idl
rdf/base/nsIRDFContainerUtils.idl
rdf/base/nsIRDFContentSink.h
rdf/base/nsIRDFDataSource.idl
rdf/base/nsIRDFDelegateFactory.idl
rdf/base/nsIRDFInMemoryDataSource.idl
rdf/base/nsIRDFInferDataSource.idl
rdf/base/nsIRDFLiteral.idl
rdf/base/nsIRDFNode.idl
rdf/base/nsIRDFObserver.idl
rdf/base/nsIRDFPropagatableDataSource.idl
rdf/base/nsIRDFPurgeableDataSource.idl
rdf/base/nsIRDFRemoteDataSource.idl
rdf/base/nsIRDFResource.idl
rdf/base/nsIRDFService.idl
rdf/base/nsIRDFXMLParser.idl
rdf/base/nsIRDFXMLSerializer.idl
rdf/base/nsIRDFXMLSink.idl
rdf/base/nsIRDFXMLSource.idl
rdf/base/nsInMemoryDataSource.cpp
rdf/base/nsNameSpaceMap.cpp
rdf/base/nsNameSpaceMap.h
rdf/base/nsRDFBaseDataSources.h
rdf/base/nsRDFContainer.cpp
rdf/base/nsRDFContainerUtils.cpp
rdf/base/nsRDFContentSink.cpp
rdf/base/nsRDFResource.cpp
rdf/base/nsRDFResource.h
rdf/base/nsRDFService.cpp
rdf/base/nsRDFService.h
rdf/base/nsRDFXMLDataSource.cpp
rdf/base/nsRDFXMLParser.cpp
rdf/base/nsRDFXMLParser.h
rdf/base/nsRDFXMLSerializer.cpp
rdf/base/nsRDFXMLSerializer.h
rdf/base/rdf.h
rdf/base/rdfIDataSource.idl
rdf/base/rdfITripleVisitor.idl
rdf/base/rdfutil.cpp
rdf/base/rdfutil.h
rdf/build/moz.build
rdf/build/nsRDFCID.h
rdf/build/nsRDFModule.cpp
rdf/datasource/moz.build
rdf/datasource/nsILocalStore.h
rdf/datasource/nsIRDFFTP.h
rdf/datasource/nsLocalStore.cpp
rdf/datasource/nsRDFBuiltInDataSources.h
rdf/moz.build
--- a/mailnews/mailnews.mozbuild
+++ b/mailnews/mailnews.mozbuild
@@ -7,10 +7,11 @@ if CONFIG['MOZ_LDAP_XPCOM']:
     DIRS += [
         '/%s/ldap' % CONFIG['commreltopsrcdir'],
         '/%s/ldap/xpcom' % CONFIG['commreltopsrcdir'],
     ]
 
 DIRS += [
     '/%s/mailnews' % CONFIG['commreltopsrcdir'],
     '/%s/db' % CONFIG['commreltopsrcdir'],
+    '/%s/rdf' % CONFIG['commreltopsrcdir'],
 ]
 
new file mode 100644
--- /dev/null
+++ b/rdf/base/moz.build
@@ -0,0 +1,59 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+    'nsIRDFCompositeDataSource.idl',
+    'nsIRDFContainer.idl',
+    'nsIRDFContainerUtils.idl',
+    'nsIRDFDataSource.idl',
+    'nsIRDFDelegateFactory.idl',
+    'nsIRDFInferDataSource.idl',
+    'nsIRDFInMemoryDataSource.idl',
+    'nsIRDFLiteral.idl',
+    'nsIRDFNode.idl',
+    'nsIRDFObserver.idl',
+    'nsIRDFPropagatableDataSource.idl',
+    'nsIRDFPurgeableDataSource.idl',
+    'nsIRDFRemoteDataSource.idl',
+    'nsIRDFResource.idl',
+    'nsIRDFService.idl',
+    'nsIRDFXMLParser.idl',
+    'nsIRDFXMLSerializer.idl',
+    'nsIRDFXMLSink.idl',
+    'nsIRDFXMLSource.idl',
+    'rdfIDataSource.idl',
+    'rdfITripleVisitor.idl',
+]
+
+XPIDL_MODULE = 'rdf'
+
+EXPORTS += [
+    'nsIRDFContentSink.h',
+    'nsRDFResource.h',
+    'rdf.h',
+]
+
+UNIFIED_SOURCES += [
+    'nsCompositeDataSource.cpp',
+    'nsContainerEnumerator.cpp',
+    'nsDefaultResourceFactory.cpp',
+    'nsInMemoryDataSource.cpp',
+    'nsNameSpaceMap.cpp',
+    'nsRDFContainer.cpp',
+    'nsRDFContainerUtils.cpp',
+    'nsRDFContentSink.cpp',
+    'nsRDFResource.cpp',
+    'nsRDFService.cpp',
+    'nsRDFXMLDataSource.cpp',
+    'nsRDFXMLParser.cpp',
+    'nsRDFXMLSerializer.cpp',
+    'rdfutil.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
+    CXXFLAGS += ['-Wno-error=shadow']
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsCompositeDataSource.cpp
@@ -0,0 +1,1357 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  A simple composite data source implementation. A composit data
+  source is just a strategy for combining individual data sources into
+  a collective graph.
+
+
+  1) A composite data source holds a sequence of data sources. The set
+     of data sources can be specified during creation of the
+     database. Data sources can also be added/deleted from a database
+     later.
+
+  2) The aggregation mechanism is based on simple super-positioning of
+     the graphs from the datasources. If there is a conflict (i.e.,
+     data source A has a true arc from foo to bar while data source B
+     has a false arc from foo to bar), the data source that it earlier
+     in the sequence wins.
+
+     The implementation below doesn't really do this and needs to be
+     fixed.
+
+*/
+
+#include "xpcom-config.h"
+#include "nsCOMPtr.h"
+#include "nsIComponentManager.h"
+#include "nsIRDFCompositeDataSource.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFObserver.h"
+#include "nsIRDFRemoteDataSource.h"
+#include "nsTArray.h"
+#include "nsCOMArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsString.h"
+#include "rdf.h"
+#include "nsCycleCollectionParticipant.h"
+
+#include "nsEnumeratorUtils.h"
+
+#include "mozilla/Logging.h"
+#include <stdio.h>
+mozilla::LazyLogModule nsRDFLog("RDF");
+
+//----------------------------------------------------------------------
+//
+// CompositeDataSourceImpl
+//
+
+class CompositeEnumeratorImpl;
+class CompositeArcsInOutEnumeratorImpl;
+class CompositeAssertionEnumeratorImpl;
+
+class CompositeDataSourceImpl : public nsIRDFCompositeDataSource,
+                                public nsIRDFObserver
+{
+public:
+    CompositeDataSourceImpl(void);
+    explicit CompositeDataSourceImpl(char** dataSources);
+
+    // nsISupports interface
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CompositeDataSourceImpl,
+                                             nsIRDFCompositeDataSource)
+
+    // nsIRDFDataSource interface
+    NS_DECL_NSIRDFDATASOURCE
+
+    // nsIRDFCompositeDataSource interface
+    NS_DECL_NSIRDFCOMPOSITEDATASOURCE
+
+    // nsIRDFObserver interface
+    NS_DECL_NSIRDFOBSERVER
+
+    bool HasAssertionN(int n, nsIRDFResource* source,
+                            nsIRDFResource* property,
+                            nsIRDFNode* target,
+                            bool tv);
+
+protected:
+    nsCOMArray<nsIRDFObserver> mObservers;
+    nsCOMArray<nsIRDFDataSource> mDataSources;
+
+    bool        mAllowNegativeAssertions;
+    bool        mCoalesceDuplicateArcs;
+    int32_t     mUpdateBatchNest;
+
+    virtual ~CompositeDataSourceImpl() {}
+
+    friend class CompositeEnumeratorImpl;
+    friend class CompositeArcsInOutEnumeratorImpl;
+    friend class CompositeAssertionEnumeratorImpl;
+};
+
+//----------------------------------------------------------------------
+//
+// CompositeEnumeratorImpl
+//
+
+class CompositeEnumeratorImpl : public nsISimpleEnumerator
+{
+    // nsISupports
+    NS_DECL_ISUPPORTS
+
+    // nsISimpleEnumerator interface
+    NS_DECL_NSISIMPLEENUMERATOR
+
+    // pure abstract methods to be overridden
+    virtual nsresult
+    GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult) = 0;
+
+    virtual nsresult
+    HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult) = 0;
+
+protected:
+    CompositeEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
+                            bool aAllowNegativeAssertions,
+                            bool aCoalesceDuplicateArcs);
+
+    virtual ~CompositeEnumeratorImpl();
+
+    CompositeDataSourceImpl* mCompositeDataSource;
+
+    nsISimpleEnumerator* mCurrent;
+    nsIRDFNode*  mResult;
+    int32_t      mNext;
+    AutoTArray<nsCOMPtr<nsIRDFNode>, 8>  mAlreadyReturned;
+    bool mAllowNegativeAssertions;
+    bool mCoalesceDuplicateArcs;
+};
+
+
+CompositeEnumeratorImpl::CompositeEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
+                                                 bool aAllowNegativeAssertions,
+                                                 bool aCoalesceDuplicateArcs)
+    : mCompositeDataSource(aCompositeDataSource),
+      mCurrent(nullptr),
+      mResult(nullptr),
+	  mNext(0),
+      mAllowNegativeAssertions(aAllowNegativeAssertions),
+      mCoalesceDuplicateArcs(aCoalesceDuplicateArcs)
+{
+    NS_ADDREF(mCompositeDataSource);
+}
+
+
+CompositeEnumeratorImpl::~CompositeEnumeratorImpl(void)
+{
+    NS_IF_RELEASE(mCurrent);
+    NS_IF_RELEASE(mResult);
+    NS_RELEASE(mCompositeDataSource);
+}
+
+NS_IMPL_ADDREF(CompositeEnumeratorImpl)
+NS_IMPL_RELEASE(CompositeEnumeratorImpl)
+NS_IMPL_QUERY_INTERFACE(CompositeEnumeratorImpl, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+CompositeEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    // If we've already queued up a next target, then yep, there are
+    // more elements.
+    if (mResult) {
+        *aResult = true;
+        return NS_OK;
+    }
+
+    // Otherwise, we'll need to find a next target, switching cursors
+    // if necessary.
+    for ( ; mNext < mCompositeDataSource->mDataSources.Count(); ++mNext) {
+        if (! mCurrent) {
+            // We don't have a current enumerator, so create a new one on
+            // the next data source.
+            nsIRDFDataSource* datasource =
+                mCompositeDataSource->mDataSources[mNext];
+
+            rv = GetEnumerator(datasource, &mCurrent);
+            if (NS_FAILED(rv)) return rv;
+            if (rv == NS_RDF_NO_VALUE)
+                continue;
+
+            NS_ASSERTION(mCurrent != nullptr, "you're always supposed to return an enumerator from GetEnumerator, punk.");
+            if (! mCurrent)
+                continue;
+        }
+
+        do {
+            int32_t i;
+
+            bool hasMore;
+            rv = mCurrent->HasMoreElements(&hasMore);
+            if (NS_FAILED(rv)) return rv;
+
+            // Is the current enumerator depleted?
+            if (! hasMore) {
+                NS_RELEASE(mCurrent);
+                break;
+            }
+
+            // Even if the current enumerator has more elements, we still
+            // need to check that the current element isn't masked by
+            // a negation in an earlier data source.
+
+            // "Peek" ahead and pull out the next target.
+            nsCOMPtr<nsISupports> result;
+            rv = mCurrent->GetNext(getter_AddRefs(result));
+            if (NS_FAILED(rv)) return rv;
+
+            rv = result->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) &mResult);
+            if (NS_FAILED(rv)) return rv;
+
+            if (mAllowNegativeAssertions)
+            {
+                // See if any previous data source negates this
+                bool hasNegation = false;
+                for (i = mNext - 1; i >= 0; --i)
+                {
+                    nsIRDFDataSource* datasource =
+                        mCompositeDataSource->mDataSources[i];
+
+                    rv = HasNegation(datasource, mResult, &hasNegation);
+                    if (NS_FAILED(rv)) return rv;
+
+                    if (hasNegation)
+                        break;
+                }
+
+                // if so, we've gotta keep looking
+                if (hasNegation)
+                {
+                    NS_RELEASE(mResult);
+                    continue;
+                }
+            }
+
+            if (mCoalesceDuplicateArcs)
+            {
+                // Now see if we've returned it once already.
+                // XXX N.B. performance here...may want to hash if things get large?
+                bool alreadyReturned = false;
+                for (i = mAlreadyReturned.Length() - 1; i >= 0; --i)
+                {
+                    if (mAlreadyReturned[i] == mResult)
+                    {
+                        alreadyReturned = true;
+                        break;
+                    }
+                }
+                if (alreadyReturned)
+                {
+                    NS_RELEASE(mResult);
+                    continue;
+                }
+            }
+
+            // If we get here, then we've really found one. It'll
+            // remain cached in mResult until GetNext() sucks it out.
+            *aResult = true;
+
+            // Remember that we returned it, so we don't return duplicates.
+
+            // XXX I wonder if we should make unique-checking be
+            // optional. This could get to be pretty expensive (this
+            // implementation turns iteration into O(n^2)).
+
+            if (mCoalesceDuplicateArcs)
+            {
+                mAlreadyReturned.AppendElement(mResult);
+            }
+
+            return NS_OK;
+        } while (1);
+    }
+
+    // if we get here, there aren't any elements left.
+    *aResult = false;
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+    nsresult rv;
+
+    bool hasMore;
+    rv = HasMoreElements(&hasMore);
+    if (NS_FAILED(rv)) return rv;
+
+    if (! hasMore)
+        return NS_ERROR_UNEXPECTED;
+
+    // Don't AddRef: we "transfer" ownership to the caller
+    *aResult = mResult;
+    mResult = nullptr;
+
+    return NS_OK;
+}
+
+//----------------------------------------------------------------------
+//
+// CompositeArcsInOutEnumeratorImpl
+//
+//
+
+class CompositeArcsInOutEnumeratorImpl : public CompositeEnumeratorImpl
+{
+public:
+    enum Type { eArcsIn, eArcsOut };
+
+    virtual ~CompositeArcsInOutEnumeratorImpl();
+
+    virtual nsresult
+    GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult) override;
+
+    virtual nsresult
+    HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult) override;
+
+    CompositeArcsInOutEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
+                                     nsIRDFNode* aNode,
+                                     Type aType,
+                                     bool aAllowNegativeAssertions,
+                                     bool aCoalesceDuplicateArcs);
+
+private:
+    nsIRDFNode* mNode;
+    Type        mType;
+};
+
+
+CompositeArcsInOutEnumeratorImpl::CompositeArcsInOutEnumeratorImpl(
+                CompositeDataSourceImpl* aCompositeDataSource,
+                nsIRDFNode* aNode,
+                Type aType,
+                bool aAllowNegativeAssertions,
+                bool aCoalesceDuplicateArcs)
+    : CompositeEnumeratorImpl(aCompositeDataSource, aAllowNegativeAssertions, aCoalesceDuplicateArcs),
+      mNode(aNode),
+      mType(aType)
+{
+    NS_ADDREF(mNode);
+}
+
+CompositeArcsInOutEnumeratorImpl::~CompositeArcsInOutEnumeratorImpl()
+{
+    NS_RELEASE(mNode);
+}
+
+
+nsresult
+CompositeArcsInOutEnumeratorImpl::GetEnumerator(
+                 nsIRDFDataSource* aDataSource,
+                 nsISimpleEnumerator** aResult)
+{
+    if (mType == eArcsIn) {
+        return aDataSource->ArcLabelsIn(mNode, aResult);
+    }
+    else {
+        nsCOMPtr<nsIRDFResource> resource( do_QueryInterface(mNode) );
+        return aDataSource->ArcLabelsOut(resource, aResult);
+    }
+}
+
+nsresult
+CompositeArcsInOutEnumeratorImpl::HasNegation(
+                 nsIRDFDataSource* aDataSource,
+                 nsIRDFNode* aNode,
+                 bool* aResult)
+{
+    *aResult = false;
+    return NS_OK;
+}
+
+
+//----------------------------------------------------------------------
+//
+// CompositeAssertionEnumeratorImpl
+//
+
+class CompositeAssertionEnumeratorImpl : public CompositeEnumeratorImpl
+{
+public:
+    virtual nsresult
+    GetEnumerator(nsIRDFDataSource* aDataSource, nsISimpleEnumerator** aResult) override;
+
+    virtual nsresult
+    HasNegation(nsIRDFDataSource* aDataSource, nsIRDFNode* aNode, bool* aResult) override;
+
+    CompositeAssertionEnumeratorImpl(CompositeDataSourceImpl* aCompositeDataSource,
+                                     nsIRDFResource* aSource,
+                                     nsIRDFResource* aProperty,
+                                     nsIRDFNode* aTarget,
+                                     bool aTruthValue,
+                                     bool aAllowNegativeAssertions,
+                                     bool aCoalesceDuplicateArcs);
+
+    virtual ~CompositeAssertionEnumeratorImpl();
+
+private:
+    nsIRDFResource* mSource;
+    nsIRDFResource* mProperty;
+    nsIRDFNode*     mTarget;
+    bool            mTruthValue;
+};
+
+
+CompositeAssertionEnumeratorImpl::CompositeAssertionEnumeratorImpl(
+                  CompositeDataSourceImpl* aCompositeDataSource,
+                  nsIRDFResource* aSource,
+                  nsIRDFResource* aProperty,
+                  nsIRDFNode* aTarget,
+                  bool aTruthValue,
+                  bool aAllowNegativeAssertions,
+                  bool aCoalesceDuplicateArcs)
+    : CompositeEnumeratorImpl(aCompositeDataSource, aAllowNegativeAssertions, aCoalesceDuplicateArcs),
+      mSource(aSource),
+      mProperty(aProperty),
+      mTarget(aTarget),
+      mTruthValue(aTruthValue)
+{
+    NS_IF_ADDREF(mSource);
+    NS_ADDREF(mProperty); // always must be specified
+    NS_IF_ADDREF(mTarget);
+}
+
+CompositeAssertionEnumeratorImpl::~CompositeAssertionEnumeratorImpl()
+{
+    NS_IF_RELEASE(mSource);
+    NS_RELEASE(mProperty);
+    NS_IF_RELEASE(mTarget);
+}
+
+
+nsresult
+CompositeAssertionEnumeratorImpl::GetEnumerator(
+                 nsIRDFDataSource* aDataSource,
+                 nsISimpleEnumerator** aResult)
+{
+    if (mSource) {
+        return aDataSource->GetTargets(mSource, mProperty, mTruthValue, aResult);
+    }
+    else {
+        return aDataSource->GetSources(mProperty, mTarget, mTruthValue, aResult);
+    }
+}
+
+nsresult
+CompositeAssertionEnumeratorImpl::HasNegation(
+                 nsIRDFDataSource* aDataSource,
+                 nsIRDFNode* aNode,
+                 bool* aResult)
+{
+    if (mSource) {
+        return aDataSource->HasAssertion(mSource, mProperty, aNode, !mTruthValue, aResult);
+    }
+    else {
+        nsCOMPtr<nsIRDFResource> source( do_QueryInterface(aNode) );
+        return aDataSource->HasAssertion(source, mProperty, mTarget, !mTruthValue, aResult);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_NewRDFCompositeDataSource(nsIRDFCompositeDataSource** result)
+{
+    CompositeDataSourceImpl* db = new CompositeDataSourceImpl();
+    if (! db)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    *result = db;
+    NS_ADDREF(*result);
+    return NS_OK;
+}
+
+
+CompositeDataSourceImpl::CompositeDataSourceImpl(void)
+	: mAllowNegativeAssertions(true),
+	  mCoalesceDuplicateArcs(true),
+      mUpdateBatchNest(0)
+{
+}
+
+//----------------------------------------------------------------------
+//
+// nsISupports interface
+//
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(CompositeDataSourceImpl)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CompositeDataSourceImpl)
+    uint32_t i, count = tmp->mDataSources.Count();
+    for (i = count; i > 0; --i) {
+        tmp->mDataSources[i - 1]->RemoveObserver(tmp);
+        tmp->mDataSources.RemoveObjectAt(i - 1);
+    }
+    NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CompositeDataSourceImpl)
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers)
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDataSources)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(CompositeDataSourceImpl)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(CompositeDataSourceImpl)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CompositeDataSourceImpl)
+    NS_INTERFACE_MAP_ENTRY(nsIRDFCompositeDataSource)
+    NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
+    NS_INTERFACE_MAP_ENTRY(nsIRDFObserver)
+    NS_INTERFACE_MAP_ENTRY(nsIRDFCompositeDataSource)
+    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRDFCompositeDataSource)
+NS_INTERFACE_MAP_END
+
+
+//----------------------------------------------------------------------
+//
+// nsIRDFDataSource interface
+//
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetURI(nsACString& aURI)
+{
+    aURI.SetIsVoid(true);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetSource(nsIRDFResource* property,
+                                   nsIRDFNode* target,
+                                   bool tv,
+                                   nsIRDFResource** source)
+{
+	if (!mAllowNegativeAssertions && !tv)
+		return(NS_RDF_NO_VALUE);
+
+    int32_t count = mDataSources.Count();
+    for (int32_t i = 0; i < count; ++i) {
+        nsresult rv;
+        rv = mDataSources[i]->GetSource(property, target, tv, source);
+        if (NS_FAILED(rv)) return rv;
+
+        if (rv == NS_RDF_NO_VALUE)
+            continue;
+
+        if (!mAllowNegativeAssertions) return(NS_OK);
+
+        // okay, found it. make sure we don't have the opposite
+        // asserted in a more local data source
+        if (!HasAssertionN(count-1, *source, property, target, !tv))
+            return NS_OK;
+
+        NS_RELEASE(*source);
+        return NS_RDF_NO_VALUE;
+    }
+    return NS_RDF_NO_VALUE;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetSources(nsIRDFResource* aProperty,
+                                    nsIRDFNode* aTarget,
+                                    bool aTruthValue,
+                                    nsISimpleEnumerator** aResult)
+{
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aTarget != nullptr, "null ptr");
+    if (! aTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    if (! mAllowNegativeAssertions && ! aTruthValue)
+        return(NS_RDF_NO_VALUE);
+
+    *aResult = new CompositeAssertionEnumeratorImpl(this, nullptr, aProperty,
+                                                    aTarget, aTruthValue,
+                                                    mAllowNegativeAssertions,
+                                                    mCoalesceDuplicateArcs);
+
+    if (! *aResult)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(*aResult);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetTarget(nsIRDFResource* aSource,
+                                   nsIRDFResource* aProperty,
+                                   bool aTruthValue,
+                                   nsIRDFNode** aResult)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    if (! mAllowNegativeAssertions && ! aTruthValue)
+        return(NS_RDF_NO_VALUE);
+
+    int32_t count = mDataSources.Count();
+    for (int32_t i = 0; i < count; ++i) {
+        nsresult rv;
+        rv = mDataSources[i]->GetTarget(aSource, aProperty, aTruthValue,
+                                        aResult);
+        if (NS_FAILED(rv))
+            return rv;
+
+        if (rv == NS_OK) {
+            // okay, found it. make sure we don't have the opposite
+            // asserted in an earlier data source
+
+            if (mAllowNegativeAssertions) {
+                if (HasAssertionN(count-1, aSource, aProperty, *aResult, !aTruthValue)) {
+                    // whoops, it's been negated.
+                    NS_RELEASE(*aResult);
+                    return NS_RDF_NO_VALUE;
+                }
+            }
+            return NS_OK;
+        }
+    }
+
+    // Otherwise, we couldn't find it at all.
+    return NS_RDF_NO_VALUE;
+}
+
+bool
+CompositeDataSourceImpl::HasAssertionN(int n,
+                                       nsIRDFResource* aSource,
+                                       nsIRDFResource* aProperty,
+                                       nsIRDFNode* aTarget,
+                                       bool aTruthValue)
+{
+    nsresult rv;
+    for (int32_t m = 0; m < n; ++m) {
+        bool result;
+        rv = mDataSources[m]->HasAssertion(aSource, aProperty, aTarget,
+                                           aTruthValue, &result);
+        if (NS_FAILED(rv))
+            return false;
+
+        // found it!
+        if (result)
+            return true;
+    }
+    return false;
+}
+
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetTargets(nsIRDFResource* aSource,
+                                    nsIRDFResource* aProperty,
+                                    bool aTruthValue,
+                                    nsISimpleEnumerator** aResult)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    if (! mAllowNegativeAssertions && ! aTruthValue)
+        return(NS_RDF_NO_VALUE);
+
+    *aResult =
+        new CompositeAssertionEnumeratorImpl(this,
+                                             aSource, aProperty, nullptr,
+                                             aTruthValue,
+                                             mAllowNegativeAssertions,
+                                             mCoalesceDuplicateArcs);
+
+    if (! *aResult)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(*aResult);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::Assert(nsIRDFResource* aSource,
+                                nsIRDFResource* aProperty,
+                                nsIRDFNode* aTarget,
+                                bool aTruthValue)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aTarget != nullptr, "null ptr");
+    if (! aTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    if (! mAllowNegativeAssertions && ! aTruthValue)
+        return(NS_RDF_ASSERTION_REJECTED);
+
+    nsresult rv;
+
+    // XXX Need to add back the stuff for unblocking ...
+
+    // We iterate backwards from the last data source which was added
+    // ("the most remote") to the first ("the most local"), trying to
+    // apply the assertion in each.
+    for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+        rv = mDataSources[i]->Assert(aSource, aProperty, aTarget, aTruthValue);
+        if (NS_RDF_ASSERTION_ACCEPTED == rv)
+            return rv;
+
+        if (NS_FAILED(rv))
+            return rv;
+    }
+
+    // nobody wanted to accept it
+    return NS_RDF_ASSERTION_REJECTED;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::Unassert(nsIRDFResource* aSource,
+                                  nsIRDFResource* aProperty,
+                                  nsIRDFNode* aTarget)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aTarget != nullptr, "null ptr");
+    if (! aTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    // Iterate through each of the datasources, starting with "the
+    // most local" and moving to "the most remote". If _any_ of the
+    // datasources have the assertion, attempt to unassert it.
+    bool unasserted = true;
+    int32_t i;
+    int32_t count = mDataSources.Count();
+    for (i = 0; i < count; ++i) {
+        nsIRDFDataSource* ds = mDataSources[i];
+
+        bool hasAssertion;
+        rv = ds->HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion);
+        if (NS_FAILED(rv)) return rv;
+
+        if (hasAssertion) {
+            rv = ds->Unassert(aSource, aProperty, aTarget);
+            if (NS_FAILED(rv)) return rv;
+
+            if (rv != NS_RDF_ASSERTION_ACCEPTED) {
+                unasserted = false;
+                break;
+            }
+        }
+    }
+
+    // Either none of the datasources had it, or they were all willing
+    // to let it be unasserted.
+    if (unasserted)
+        return NS_RDF_ASSERTION_ACCEPTED;
+
+    // If we get here, one of the datasources already had the
+    // assertion, and was adamant about not letting us remove
+    // it. Iterate from the "most local" to the "most remote"
+    // attempting to assert the negation...
+    for (i = 0; i < count; ++i) {
+        rv = mDataSources[i]->Assert(aSource, aProperty, aTarget, false);
+        if (NS_FAILED(rv)) return rv;
+
+        // Did it take?
+        if (rv == NS_RDF_ASSERTION_ACCEPTED)
+            return rv;
+    }
+
+    // Couln't get anyone to accept the negation, either.
+    return NS_RDF_ASSERTION_REJECTED;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::Change(nsIRDFResource* aSource,
+                                nsIRDFResource* aProperty,
+                                nsIRDFNode* aOldTarget,
+                                nsIRDFNode* aNewTarget)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aOldTarget != nullptr, "null ptr");
+    if (! aOldTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aNewTarget != nullptr, "null ptr");
+    if (! aNewTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    // XXX So we're assuming that a datasource _must_ accept the
+    // atomic change; i.e., we can't split it up across two
+    // datasources. That sucks.
+
+    // We iterate backwards from the last data source which was added
+    // ("the most remote") to the first ("the most local"), trying to
+    // apply the change in each.
+    for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+        rv = mDataSources[i]->Change(aSource, aProperty, aOldTarget, aNewTarget);
+        if (NS_RDF_ASSERTION_ACCEPTED == rv)
+            return rv;
+
+        if (NS_FAILED(rv))
+            return rv;
+    }
+
+    // nobody wanted to accept it
+    return NS_RDF_ASSERTION_REJECTED;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::Move(nsIRDFResource* aOldSource,
+                              nsIRDFResource* aNewSource,
+                              nsIRDFResource* aProperty,
+                              nsIRDFNode* aTarget)
+{
+    NS_PRECONDITION(aOldSource != nullptr, "null ptr");
+    if (! aOldSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aNewSource != nullptr, "null ptr");
+    if (! aNewSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aTarget != nullptr, "null ptr");
+    if (! aTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    // XXX So we're assuming that a datasource _must_ accept the
+    // atomic move; i.e., we can't split it up across two
+    // datasources. That sucks.
+
+    // We iterate backwards from the last data source which was added
+    // ("the most remote") to the first ("the most local"), trying to
+    // apply the assertion in each.
+    for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+        rv = mDataSources[i]->Move(aOldSource, aNewSource, aProperty, aTarget);
+        if (NS_RDF_ASSERTION_ACCEPTED == rv)
+            return rv;
+
+        if (NS_FAILED(rv))
+            return rv;
+    }
+
+    // nobody wanted to accept it
+    return NS_RDF_ASSERTION_REJECTED;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::HasAssertion(nsIRDFResource* aSource,
+                                      nsIRDFResource* aProperty,
+                                      nsIRDFNode* aTarget,
+                                      bool aTruthValue,
+                                      bool* aResult)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    if (! mAllowNegativeAssertions && ! aTruthValue)
+    {
+        *aResult = false;
+        return(NS_OK);
+    }
+
+    nsresult rv;
+
+    // Otherwise, look through all the data sources to see if anyone
+    // has the positive...
+    int32_t count = mDataSources.Count();
+    for (int32_t i = 0; i < count; ++i) {
+        nsIRDFDataSource* datasource = mDataSources[i];
+        rv = datasource->HasAssertion(aSource, aProperty, aTarget, aTruthValue, aResult);
+        if (NS_FAILED(rv)) return rv;
+
+        if (*aResult)
+            return NS_OK;
+
+        if (mAllowNegativeAssertions)
+        {
+            bool hasNegation;
+            rv = datasource->HasAssertion(aSource, aProperty, aTarget, !aTruthValue, &hasNegation);
+            if (NS_FAILED(rv)) return rv;
+
+            if (hasNegation)
+            {
+                *aResult = false;
+                return NS_OK;
+            }
+        }
+    }
+
+    // If we get here, nobody had the assertion at all
+    *aResult = false;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::AddObserver(nsIRDFObserver* aObserver)
+{
+    NS_PRECONDITION(aObserver != nullptr, "null ptr");
+    if (! aObserver)
+        return NS_ERROR_NULL_POINTER;
+
+    // XXX ensure uniqueness?
+    mObservers.AppendObject(aObserver);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::RemoveObserver(nsIRDFObserver* aObserver)
+{
+    NS_PRECONDITION(aObserver != nullptr, "null ptr");
+    if (! aObserver)
+        return NS_ERROR_NULL_POINTER;
+
+    mObservers.RemoveObject(aObserver);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
+{
+    nsresult rv;
+    *result = false;
+    int32_t count = mDataSources.Count();
+    for (int32_t i = 0; i < count; ++i) {
+        rv = mDataSources[i]->HasArcIn(aNode, aArc, result);
+        if (NS_FAILED(rv)) return rv;
+        if (*result)
+            return NS_OK;
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *result)
+{
+    nsresult rv;
+    *result = false;
+    int32_t count = mDataSources.Count();
+    for (int32_t i = 0; i < count; ++i) {
+        rv = mDataSources[i]->HasArcOut(aSource, aArc, result);
+        if (NS_FAILED(rv)) return rv;
+        if (*result)
+            return NS_OK;
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::ArcLabelsIn(nsIRDFNode* aTarget, nsISimpleEnumerator** aResult)
+{
+    NS_PRECONDITION(aTarget != nullptr, "null ptr");
+    if (! aTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    nsISimpleEnumerator* result =
+        new CompositeArcsInOutEnumeratorImpl(this, aTarget,
+                                             CompositeArcsInOutEnumeratorImpl::eArcsIn,
+                                             mAllowNegativeAssertions,
+                                             mCoalesceDuplicateArcs);
+
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(result);
+    *aResult = result;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::ArcLabelsOut(nsIRDFResource* aSource,
+                                      nsISimpleEnumerator** aResult)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    nsISimpleEnumerator* result =
+        new CompositeArcsInOutEnumeratorImpl(this, aSource,
+                                             CompositeArcsInOutEnumeratorImpl::eArcsOut,
+                                             mAllowNegativeAssertions,
+                                             mCoalesceDuplicateArcs);
+
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(result);
+    *aResult = result;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetAllResources(nsISimpleEnumerator** aResult)
+{
+    MOZ_ASSERT_UNREACHABLE("CompositeDataSourceImpl::GetAllResources");
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetAllCmds(nsIRDFResource* source,
+                                    nsISimpleEnumerator/*<nsIRDFResource>*/** result)
+{
+    nsresult rv;
+    nsCOMPtr<nsISimpleEnumerator> set;
+
+    for (int32_t i = 0; i < mDataSources.Count(); i++)
+    {
+        nsCOMPtr<nsISimpleEnumerator> dsCmds;
+
+        rv = mDataSources[i]->GetAllCmds(source, getter_AddRefs(dsCmds));
+        if (NS_SUCCEEDED(rv))
+        {
+            nsCOMPtr<nsISimpleEnumerator> tmp;
+            rv = NS_NewUnionEnumerator(getter_AddRefs(tmp), set, dsCmds);
+            set.swap(tmp);
+            if (NS_FAILED(rv)) return(rv);
+        }
+    }
+
+    set.forget(result);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::IsCommandEnabled(nsISupports/* nsIRDFResource container */* aSources,
+                                          nsIRDFResource*   aCommand,
+                                          nsISupports/* nsIRDFResource container */* aArguments,
+                                          bool* aResult)
+{
+    nsresult rv;
+    for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+        bool enabled = true;
+        rv = mDataSources[i]->IsCommandEnabled(aSources, aCommand, aArguments, &enabled);
+        if (NS_FAILED(rv) && (rv != NS_ERROR_NOT_IMPLEMENTED))
+        {
+            return(rv);
+        }
+
+        if (! enabled) {
+            *aResult = false;
+            return(NS_OK);
+        }
+    }
+    *aResult = true;
+    return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::DoCommand(nsISupports/* nsIRDFResource container */* aSources,
+                                   nsIRDFResource*   aCommand,
+                                   nsISupports/* nsIRDFResource container */* aArguments)
+{
+    for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+        nsresult rv = mDataSources[i]->DoCommand(aSources, aCommand, aArguments);
+        if (NS_FAILED(rv) && (rv != NS_ERROR_NOT_IMPLEMENTED))
+        {
+            return(rv);   // all datasources must succeed
+        }
+    }
+    return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::BeginUpdateBatch()
+{
+    for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+        mDataSources[i]->BeginUpdateBatch();
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::EndUpdateBatch()
+{
+    for (int32_t i = mDataSources.Count() - 1; i >= 0; --i) {
+        mDataSources[i]->EndUpdateBatch();
+    }
+    return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFCompositeDataSource methods
+// XXX rvg We should make this take an additional argument specifying where
+// in the sequence of data sources (of the db), the new data source should
+// fit in. Right now, the new datasource gets stuck at the end.
+// need to add the observers of the CompositeDataSourceImpl to the new data source.
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetAllowNegativeAssertions(bool *aAllowNegativeAssertions)
+{
+	*aAllowNegativeAssertions = mAllowNegativeAssertions;
+	return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::SetAllowNegativeAssertions(bool aAllowNegativeAssertions)
+{
+	mAllowNegativeAssertions = aAllowNegativeAssertions;
+	return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetCoalesceDuplicateArcs(bool *aCoalesceDuplicateArcs)
+{
+	*aCoalesceDuplicateArcs = mCoalesceDuplicateArcs;
+	return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::SetCoalesceDuplicateArcs(bool aCoalesceDuplicateArcs)
+{
+	mCoalesceDuplicateArcs = aCoalesceDuplicateArcs;
+	return(NS_OK);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::AddDataSource(nsIRDFDataSource* aDataSource)
+{
+    NS_ASSERTION(aDataSource != nullptr, "null ptr");
+    if (! aDataSource)
+        return NS_ERROR_NULL_POINTER;
+
+    mDataSources.AppendObject(aDataSource);
+    aDataSource->AddObserver(this);
+    return NS_OK;
+}
+
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::RemoveDataSource(nsIRDFDataSource* aDataSource)
+{
+    NS_ASSERTION(aDataSource != nullptr, "null ptr");
+    if (! aDataSource)
+        return NS_ERROR_NULL_POINTER;
+
+
+    if (mDataSources.IndexOf(aDataSource) >= 0) {
+        aDataSource->RemoveObserver(this);
+        mDataSources.RemoveObject(aDataSource);
+    }
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::GetDataSources(nsISimpleEnumerator** _result)
+{
+    // NS_NewArrayEnumerator for an nsCOMArray takes a snapshot of the
+    // current state.
+    return NS_NewArrayEnumerator(_result, mDataSources);
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnAssert(nsIRDFDataSource* aDataSource,
+                                  nsIRDFResource* aSource,
+                                  nsIRDFResource* aProperty,
+                                  nsIRDFNode* aTarget)
+{
+    // Make sure that the assertion isn't masked by another
+    // datasource.
+    //
+    // XXX We could make this more efficient if we knew _which_
+    // datasource actually served up the OnAssert(): we could use
+    // HasAssertionN() to only search datasources _before_ the
+    // datasource that coughed up the assertion.
+	nsresult	rv = NS_OK;
+
+	if (mAllowNegativeAssertions)
+	{
+		bool hasAssertion;
+		rv = HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion);
+		if (NS_FAILED(rv)) return rv;
+
+		if (! hasAssertion)
+			return(NS_OK);
+	}
+
+    for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+        mObservers[i]->OnAssert(this, aSource, aProperty, aTarget);
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnUnassert(nsIRDFDataSource* aDataSource,
+                                    nsIRDFResource* aSource,
+                                    nsIRDFResource* aProperty,
+                                    nsIRDFNode* aTarget)
+{
+    // Make sure that the un-assertion doesn't just unmask the
+    // same assertion in a different datasource.
+    //
+    // XXX We could make this more efficient if we knew _which_
+    // datasource actually served up the OnAssert(): we could use
+    // HasAssertionN() to only search datasources _before_ the
+    // datasource that coughed up the assertion.
+    nsresult rv;
+
+	if (mAllowNegativeAssertions)
+	{
+		bool hasAssertion;
+		rv = HasAssertion(aSource, aProperty, aTarget, true, &hasAssertion);
+		if (NS_FAILED(rv)) return rv;
+
+		if (hasAssertion)
+			return NS_OK;
+	}
+
+    for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+        mObservers[i]->OnUnassert(this, aSource, aProperty, aTarget);
+    }
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnChange(nsIRDFDataSource* aDataSource,
+                                  nsIRDFResource* aSource,
+                                  nsIRDFResource* aProperty,
+                                  nsIRDFNode* aOldTarget,
+                                  nsIRDFNode* aNewTarget)
+{
+    // Make sure that the change is actually visible, and not hidden
+    // by an assertion in a different datasource.
+    //
+    // XXX Because of aggregation, this could actually mutate into a
+    // variety of OnAssert or OnChange notifications, which we'll
+    // ignore for now :-/.
+    for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+        mObservers[i]->OnChange(this, aSource, aProperty,
+                                aOldTarget, aNewTarget);
+    }
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnMove(nsIRDFDataSource* aDataSource,
+                                nsIRDFResource* aOldSource,
+                                nsIRDFResource* aNewSource,
+                                nsIRDFResource* aProperty,
+                                nsIRDFNode* aTarget)
+{
+    // Make sure that the move is actually visible, and not hidden
+    // by an assertion in a different datasource.
+    //
+    // XXX Because of aggregation, this could actually mutate into a
+    // variety of OnAssert or OnMove notifications, which we'll
+    // ignore for now :-/.
+    for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+        mObservers[i]->OnMove(this, aOldSource, aNewSource,
+                              aProperty, aTarget);
+    }
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnBeginUpdateBatch(nsIRDFDataSource* aDataSource)
+{
+    if (mUpdateBatchNest++ == 0) {
+        for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+            mObservers[i]->OnBeginUpdateBatch(this);
+        }
+    }
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CompositeDataSourceImpl::OnEndUpdateBatch(nsIRDFDataSource* aDataSource)
+{
+    NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
+    if (--mUpdateBatchNest == 0) {
+        for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
+            mObservers[i]->OnEndUpdateBatch(this);
+        }
+    }
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsContainerEnumerator.cpp
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  A simple cursor that enumerates the elements of an RDF container
+  (RDF:Bag, RDF:Seq, or RDF:Alt).
+
+  Caveats
+  -------
+
+  1. This uses an implementation-specific detail to determine the
+     index of the last element in the container; specifically, the RDF
+     utilities maintain a counter attribute on the container that
+     holds the numeric value of the next value that is to be
+     assigned. So, this cursor will bust if you use it with a bag that
+     hasn't been created using the RDF utility routines.
+
+ */
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsIRDFContainerUtils.h"
+#include "nsIRDFDataSource.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFService.h"
+#include "nsIServiceManager.h"
+#include "nsRDFCID.h"
+#include "nsString.h"
+#include "mozilla/Logging.h"
+#include "rdf.h"
+#include "rdfutil.h"
+
+////////////////////////////////////////////////////////////////////////
+
+class ContainerEnumeratorImpl : public nsISimpleEnumerator {
+private:
+    // pseudo-constants
+    static nsrefcnt              gRefCnt;
+    static nsIRDFResource*       kRDF_nextVal;
+    static nsIRDFContainerUtils* gRDFC;
+
+    nsCOMPtr<nsIRDFDataSource>      mDataSource;
+    nsCOMPtr<nsIRDFResource>        mContainer;
+    nsCOMPtr<nsIRDFResource>        mOrdinalProperty;
+
+    nsCOMPtr<nsISimpleEnumerator>   mCurrent;
+    nsCOMPtr<nsIRDFNode>            mResult;
+    int32_t mNextIndex;
+
+    virtual ~ContainerEnumeratorImpl();
+
+public:
+    ContainerEnumeratorImpl(nsIRDFDataSource* ds, nsIRDFResource* container);
+
+    nsresult Init();
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSISIMPLEENUMERATOR
+};
+
+nsrefcnt              ContainerEnumeratorImpl::gRefCnt;
+nsIRDFResource*       ContainerEnumeratorImpl::kRDF_nextVal;
+nsIRDFContainerUtils* ContainerEnumeratorImpl::gRDFC;
+
+
+ContainerEnumeratorImpl::ContainerEnumeratorImpl(nsIRDFDataSource* aDataSource,
+                                                 nsIRDFResource* aContainer)
+    : mDataSource(aDataSource),
+      mContainer(aContainer),
+      mNextIndex(1)
+{
+}
+
+nsresult
+ContainerEnumeratorImpl::Init()
+{
+    if (gRefCnt++ == 0) {
+        nsresult rv;
+
+        NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+        nsCOMPtr<nsIRDFService> rdf = do_GetService(kRDFServiceCID);
+        NS_ASSERTION(rdf != nullptr, "unable to acquire resource manager");
+        if (! rdf)
+            return NS_ERROR_FAILURE;
+
+        rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), &kRDF_nextVal);
+        NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource");
+        if (NS_FAILED(rv)) return rv;
+
+        NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
+        rv = CallGetService(kRDFContainerUtilsCID, &gRDFC);
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    return NS_OK;
+}
+
+
+ContainerEnumeratorImpl::~ContainerEnumeratorImpl()
+{
+    if (--gRefCnt == 0) {
+        NS_IF_RELEASE(kRDF_nextVal);
+        NS_IF_RELEASE(gRDFC);
+    }
+}
+
+NS_IMPL_ISUPPORTS(ContainerEnumeratorImpl, nsISimpleEnumerator)
+
+
+NS_IMETHODIMP
+ContainerEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    // If we've already queued up a next value, then we know there are more elements.
+    if (mResult) {
+        *aResult = true;
+        return NS_OK;
+    }
+
+    // Otherwise, we need to grovel
+
+    // Figure out the upper bound so we'll know when we're done. Since it's
+    // possible that we're targeting a composite datasource, we'll need to
+    // "GetTargets()" and take the maximum value of "nextVal" to know the
+    // upper bound.
+    //
+    // Remember that since nextVal is the next index that we'd assign
+    // to an element in a container, it's *one more* than count of
+    // elements in the container.
+    int32_t max = 0;
+
+    nsCOMPtr<nsISimpleEnumerator> targets;
+    rv = mDataSource->GetTargets(mContainer, kRDF_nextVal, true, getter_AddRefs(targets));
+    if (NS_FAILED(rv)) return rv;
+
+    while (1) {
+        bool hasmore;
+        targets->HasMoreElements(&hasmore);
+        if (! hasmore)
+            break;
+
+        nsCOMPtr<nsISupports> isupports;
+        targets->GetNext(getter_AddRefs(isupports));
+
+        nsCOMPtr<nsIRDFLiteral> nextValLiteral = do_QueryInterface(isupports);
+        if (! nextValLiteral)
+             continue;
+
+         const char16_t *nextValStr;
+         nextValLiteral->GetValueConst(&nextValStr);
+
+         nsresult err;
+         int32_t nextVal = nsAutoString(nextValStr).ToInteger(&err);
+
+         if (nextVal > max)
+             max = nextVal;
+    }
+
+    // Now pre-fetch our next value into mResult.
+    while (mCurrent || mNextIndex < max) {
+
+        // If mCurrent has been depleted, then conjure up a new one
+        if (! mCurrent) {
+            rv = gRDFC->IndexToOrdinalResource(mNextIndex, getter_AddRefs(mOrdinalProperty));
+            if (NS_FAILED(rv)) return rv;
+
+            rv = mDataSource->GetTargets(mContainer, mOrdinalProperty, true, getter_AddRefs(mCurrent));
+            if (NS_FAILED(rv)) return rv;
+
+            ++mNextIndex;
+        }
+
+        if (mCurrent) {
+            bool hasMore;
+            rv = mCurrent->HasMoreElements(&hasMore);
+            if (NS_FAILED(rv)) return rv;
+
+            // Is the current enumerator depleted? If so, iterate to
+            // the next index.
+            if (! hasMore) {
+                mCurrent = nullptr;
+                continue;
+            }
+
+            // "Peek" ahead and pull out the next target.
+            nsCOMPtr<nsISupports> result;
+            rv = mCurrent->GetNext(getter_AddRefs(result));
+            if (NS_FAILED(rv)) return rv;
+
+            mResult = do_QueryInterface(result, &rv);
+            if (NS_FAILED(rv)) return rv;
+
+            *aResult = true;
+            return NS_OK;
+        }
+    }
+
+    // If we get here, we ran out of elements. The cursor is empty.
+    *aResult = false;
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+ContainerEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+    nsresult rv;
+
+    bool hasMore;
+    rv = HasMoreElements(&hasMore);
+    if (NS_FAILED(rv)) return rv;
+
+    if (! hasMore)
+        return NS_ERROR_UNEXPECTED;
+
+    NS_ADDREF(*aResult = mResult);
+    mResult = nullptr;
+
+    return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_NewContainerEnumerator(nsIRDFDataSource* aDataSource,
+                          nsIRDFResource* aContainer,
+                          nsISimpleEnumerator** aResult)
+{
+    NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+    if (! aDataSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aContainer != nullptr, "null ptr");
+    if (! aContainer)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    ContainerEnumeratorImpl* result = new ContainerEnumeratorImpl(aDataSource, aContainer);
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(result);
+
+    nsresult rv = result->Init();
+    if (NS_FAILED(rv))
+        NS_RELEASE(result);
+
+    *aResult = result;
+    return rv;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsDefaultResourceFactory.cpp
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  The default resource factory implementation. This resource factory
+  produces nsIRDFResource objects for any URI prefix that is not
+  covered by some other factory.
+
+ */
+
+#include "nsRDFResource.h"
+
+nsresult
+NS_NewDefaultResource(nsIRDFResource** aResult)
+{
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    nsRDFResource* resource = new nsRDFResource();
+    if (! resource)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(resource);
+    *aResult = resource;
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFCompositeDataSource.idl
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIRDFDataSource.idl"
+
+interface nsISimpleEnumerator;
+
+/**
+ * An nsIRDFCompositeDataSource composes individual data sources, providing
+ * the illusion of a single, coherent RDF graph.
+ */
+[scriptable, uuid(96343820-307C-11D2-BC15-00805F912FE7)]
+interface nsIRDFCompositeDataSource : nsIRDFDataSource {
+
+    /**
+     *
+     * Set this value to <code>true</code> if the composite datasource
+     * may contains at least one datasource that has <em>negative</em>
+     * assertions. (This is the default.)
+     *
+     * Set this value to <code>false</code> if none of the datasources
+     * being composed contains a negative assertion. This allows the
+     * composite datasource to perform some query optimizations.
+     *
+     * By default, this value is <code>true</true>.
+     */
+    attribute boolean	allowNegativeAssertions;
+
+    /**
+     * Set to <code>true</code> if the composite datasource should
+     * take care to coalesce duplicate arcs when returning values from
+     * queries. (This is the default.)
+     *
+     * Set to <code>false</code> if the composite datasource shouldn't
+     * bother to check for duplicates. This allows the composite
+     * datasource to more efficiently answer queries.
+     *
+     * By default, this value is <code>true</code>.
+     */
+    attribute boolean	coalesceDuplicateArcs;
+
+    /**
+     * Add a datasource the the composite data source.
+     * @param aDataSource the datasource to add to composite
+     */
+    void AddDataSource(in nsIRDFDataSource aDataSource);
+
+    /**
+     * Remove a datasource from the composite data source.
+     * @param aDataSource the datasource to remove from the composite
+     */
+    void RemoveDataSource(in nsIRDFDataSource aDataSource);
+
+    /**
+     * Retrieve the datasources in the composite data source.
+     * @return an nsISimpleEnumerator that will enumerate each
+     * of the datasources in the composite
+     */
+    nsISimpleEnumerator GetDataSources();
+};
+
+%{C++
+extern nsresult
+NS_NewRDFCompositeDataSource(nsIRDFCompositeDataSource** result);
+%}
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFContainer.idl
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIRDFDataSource.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFNode.idl"
+#include "nsISimpleEnumerator.idl"
+
+// A wrapper for manipulating RDF containers
+[scriptable, uuid(D4214E90-FB94-11D2-BDD8-00104BDE6048)]
+interface nsIRDFContainer : nsISupports {
+    readonly attribute nsIRDFDataSource DataSource;
+    readonly attribute nsIRDFResource   Resource;
+
+    /**
+     * Initialize the container wrapper to the specified resource
+     * using the specified datasource for context.
+     */
+    void Init(in nsIRDFDataSource aDataSource, in nsIRDFResource aContainer);
+
+    /**
+     * Return the number of elements in the container. Note that this
+     * may not always be accurate due to aggregation.
+     */
+    long GetCount();
+
+    /**
+     * Return an enumerator that can be used to enumerate the contents
+     * of the container in ascending order.
+     */
+    nsISimpleEnumerator GetElements();
+
+    /**
+     * Append an element to the container, assigning it the next
+     * available ordinal.
+     */
+    void AppendElement(in nsIRDFNode aElement);
+
+    /**
+     * Remove the first occurence of the specified element from the
+     * container. If aRenumber is 'true', then the underlying RDF graph
+     * will be 're-numbered' to account for the removal.
+     */
+    void RemoveElement(in nsIRDFNode aElement, in boolean aRenumber);
+
+    /**
+     * Insert aElement at the specified index. If aRenumber is 'true', then
+     * the underlying RDF graph will be 're-numbered' to accomodate the new
+     * element.
+     */
+    void InsertElementAt(in nsIRDFNode aElement, in long aIndex, in boolean aRenumber);
+
+    /**
+     * Remove the element at the specified index. If aRenumber is 'true', then
+     * the underlying RDF graph will be 're-numbered' to account for the
+     * removal.
+     *
+     * @return the element that was removed.
+     */
+    nsIRDFNode RemoveElementAt(in long aIndex, in boolean aRenumber);
+
+    /**
+     * Determine the index of an element in the container.
+     *
+     * @return The index of the specified element in the container. If
+     * the element is not contained in the container, this function
+     * returns '-1'.
+     */
+    long IndexOf(in nsIRDFNode aElement);
+};
+
+%{C++
+nsresult
+NS_NewRDFContainer(nsIRDFContainer** aResult);
+
+nsresult
+NS_NewRDFContainer(nsIRDFDataSource* aDataSource,
+                   nsIRDFResource* aResource,
+                   nsIRDFContainer** aResult);
+
+/**
+ * Create a cursor on a container that enumerates its contents in
+ * order
+ */
+nsresult
+NS_NewContainerEnumerator(nsIRDFDataSource* aDataSource,
+                          nsIRDFResource* aContainer,
+                          nsISimpleEnumerator** aResult);
+
+
+%}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFContainerUtils.idl
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIRDFContainer.idl"
+#include "nsIRDFResource.idl"
+
+
+// Container utilities
+[scriptable, uuid(D4214E91-FB94-11D2-BDD8-00104BDE6048)]
+interface nsIRDFContainerUtils : nsISupports {
+    /**
+     * Returns 'true' if the property is an RDF ordinal property.
+     */
+    boolean IsOrdinalProperty(in nsIRDFResource aProperty);
+
+    /**
+     * Convert the specified index to an ordinal property.
+     */
+    nsIRDFResource IndexToOrdinalResource(in long aIndex);
+
+    /**
+     * Convert the specified ordinal property into an index
+     */
+    long OrdinalResourceToIndex(in nsIRDFResource aOrdinal);
+
+    /**
+     * Return 'true' if the specified resource is a container
+     */
+    boolean IsContainer(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+    /**
+     * Return 'true' if the specified resource is a container and it is empty
+     */
+    boolean IsEmpty(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+    /**
+     * Return 'true' if the specified resource is a bag
+     */
+    boolean IsBag(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+    /**
+     * Return 'true' if the specified resource is a sequence
+     */
+    boolean IsSeq(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+    /**
+     * Return 'true' if the specified resource is an alternation
+     */
+    boolean IsAlt(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+    /**
+     * Decorates the specified resource appropriately to make it
+     * usable as an empty bag in the specified data source.
+     */
+    nsIRDFContainer MakeBag(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+    /**
+     * Decorates the specified resource appropriately to make it
+     * usable as an empty sequence in the specified data source.
+     */
+    nsIRDFContainer MakeSeq(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+    /**
+     * Decorates the specified resource appropriately to make it
+     * usable as an empty alternation in the specified data source.
+     */
+    nsIRDFContainer MakeAlt(in nsIRDFDataSource aDataSource, in nsIRDFResource aResource);
+
+    /**
+     * Retrieve the index of element in the container. Returns -1 if
+     * the element is not in the container.
+     */
+    long indexOf(in nsIRDFDataSource aDataSource, in nsIRDFResource aContainer, in nsIRDFNode aElement);
+};
+
+%{C++
+extern nsresult
+NS_NewRDFContainerUtils(nsIRDFContainerUtils** aResult);
+%}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFContentSink.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  An RDF-specific content sink. The content sink is targeted by the
+  parser for building the RDF content model.
+
+ */
+
+#ifndef nsIRDFContentSink_h___
+#define nsIRDFContentSink_h___
+
+#include "nsIXMLContentSink.h"
+class nsIRDFDataSource;
+class nsIURI;
+
+// {3a7459d7-d723-483c-aef0-404fc48e09b8}
+#define NS_IRDFCONTENTSINK_IID \
+{ 0x3a7459d7, 0xd723, 0x483c, { 0xae, 0xf0, 0x40, 0x4f, 0xc4, 0x8e, 0x09, 0xb8 } }
+
+/**
+ * This interface represents a content sink for RDF files.
+ */
+
+class nsIRDFContentSink : public nsIXMLContentSink {
+public:
+    NS_DECLARE_STATIC_IID_ACCESSOR(NS_IRDFCONTENTSINK_IID)
+
+    /**
+     * Initialize the content sink.
+     */
+    NS_IMETHOD Init(nsIURI* aURL) = 0;
+
+    /**
+     * Set the content sink's RDF Data source
+     */
+    NS_IMETHOD SetDataSource(nsIRDFDataSource* aDataSource) = 0;
+
+    /**
+     * Retrieve the content sink's RDF data source.
+     */
+    NS_IMETHOD GetDataSource(nsIRDFDataSource*& rDataSource) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIRDFContentSink, NS_IRDFCONTENTSINK_IID)
+
+/**
+ * This constructs a content sink that can be used without a
+ * document, say, to create a stand-alone in-memory graph.
+ */
+nsresult
+NS_NewRDFContentSink(nsIRDFContentSink** aResult);
+
+#endif // nsIRDFContentSink_h___
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFDataSource.idl
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFNode.idl"
+#include "nsISimpleEnumerator.idl"
+#include "nsIRDFObserver.idl"
+
+[scriptable, uuid(0F78DA58-8321-11d2-8EAC-00805F29F370)]
+interface nsIRDFDataSource : nsISupports
+{
+    /** The "URI" of the data source. This used by the RDF service's
+     * |GetDataSource()| method to cache datasources.
+     */
+    readonly attribute ACString URI;
+
+    /** Find an RDF resource that points to a given node over the
+     * specified arc & truth value
+     *
+     * @throws NS_RDF_NO_VALUE if there is no source that leads
+     * to the target with the specified property.
+     */
+    nsIRDFResource GetSource(in nsIRDFResource aProperty,
+                             in nsIRDFNode     aTarget,
+                             in boolean        aTruthValue);
+
+    /**
+     * Find all RDF resources that point to a given node over the
+     * specified arc & truth value
+     */
+    nsISimpleEnumerator GetSources(in nsIRDFResource aProperty,
+                                   in nsIRDFNode     aTarget,
+                                   in boolean        aTruthValue);
+
+    /**
+     * Find a child of that is related to the source by the given arc
+     * arc and truth value
+     *
+     * @throws NS_RDF_NO_VALUE if there is no target accessible from the
+     * source via the specified property.
+     */
+    nsIRDFNode GetTarget(in nsIRDFResource aSource,
+                         in nsIRDFResource aProperty,
+                         in boolean        aTruthValue);
+
+    /**
+     * Find all children of that are related to the source by the given arc
+     * arc and truth value.
+     */
+    nsISimpleEnumerator GetTargets(in nsIRDFResource aSource,
+                                   in nsIRDFResource aProperty,
+                                   in boolean aTruthValue);
+
+    /**
+     * Add an assertion to the graph.
+     */
+    void Assert(in nsIRDFResource aSource, 
+                in nsIRDFResource aProperty, 
+                in nsIRDFNode     aTarget,
+                in boolean        aTruthValue);
+
+    /**
+     * Remove an assertion from the graph.
+     */
+    void Unassert(in nsIRDFResource aSource,
+                  in nsIRDFResource aProperty,
+                  in nsIRDFNode     aTarget);
+
+    /**
+     * Change an assertion from
+     *
+     *   [aSource]--[aProperty]-->[aOldTarget]
+     *
+     * to
+     * 
+     *   [aSource]--[aProperty]-->[aNewTarget]
+     */
+    void Change(in nsIRDFResource aSource,
+                in nsIRDFResource aProperty,
+                in nsIRDFNode     aOldTarget,
+                in nsIRDFNode     aNewTarget);
+
+    /**
+     * 'Move' an assertion from
+     *
+     *   [aOldSource]--[aProperty]-->[aTarget]
+     *
+     * to
+     * 
+     *   [aNewSource]--[aProperty]-->[aTarget]
+     */
+    void Move(in nsIRDFResource aOldSource,
+              in nsIRDFResource aNewSource,
+              in nsIRDFResource aProperty,
+              in nsIRDFNode     aTarget);
+
+    /**
+     * Query whether an assertion exists in this graph.
+     */
+    boolean HasAssertion(in nsIRDFResource aSource,
+                         in nsIRDFResource aProperty,
+                         in nsIRDFNode     aTarget,
+                         in boolean        aTruthValue);
+
+    /**
+     * Add an observer to this data source. If the datasource
+     * supports observers, the datasource source should hold a strong
+     * reference to the observer.
+     */
+    void AddObserver(in nsIRDFObserver aObserver);
+
+    /**
+     * Remove an observer from this data source.
+     */
+    void RemoveObserver(in nsIRDFObserver aObserver);
+
+    /**
+     * Get a cursor to iterate over all the arcs that point into a node.
+     */
+    nsISimpleEnumerator ArcLabelsIn(in nsIRDFNode aNode);
+
+    /**
+     * Get a cursor to iterate over all the arcs that originate in
+     * a resource.
+     */
+    nsISimpleEnumerator ArcLabelsOut(in nsIRDFResource aSource);
+
+    /**
+     * Retrieve all of the resources that the data source currently
+     * refers to.
+     */
+    nsISimpleEnumerator GetAllResources();
+
+    /**
+     * Returns whether a given command is enabled for a set of sources. 
+     */
+    boolean IsCommandEnabled(in nsISupports aSources,
+                             in nsIRDFResource   aCommand,
+                             in nsISupports aArguments);
+
+    /**
+     * Perform the specified command on set of sources.
+     */
+    void DoCommand(in nsISupports aSources,
+                   in nsIRDFResource   aCommand,
+                   in nsISupports aArguments);
+
+    /**
+     * Returns the set of all commands defined for a given source.
+     */
+    nsISimpleEnumerator GetAllCmds(in nsIRDFResource aSource);
+
+    /**
+     * Returns true if the specified node is pointed to by the specified arc.
+     * Equivalent to enumerating ArcLabelsIn and comparing for the specified arc.
+     */
+    boolean hasArcIn(in nsIRDFNode aNode, in nsIRDFResource aArc);
+
+    /**
+     * Returns true if the specified node has the specified outward arc.
+     * Equivalent to enumerating ArcLabelsOut and comparing for the specified arc.
+     */
+    boolean hasArcOut(in nsIRDFResource aSource, in nsIRDFResource aArc);
+
+    /**
+     * Notify observers that the datasource is about to send several
+     * notifications at once.
+     * This must be followed by calling endUpdateBatch(), otherwise
+     * viewers will get out of sync.
+     */
+    void beginUpdateBatch();
+
+    /**
+     * Notify observers that the datasource has completed issuing
+     * a notification group.
+     */
+    void endUpdateBatch();
+};
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFDelegateFactory.idl
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  An interface used for runtime pseudo-aggregation of RDF delegate
+  objects.
+
+*/
+
+#include "nsrootidl.idl"
+#include "nsISupports.idl"
+interface nsIRDFResource;
+
+/**
+ * This interface should be implemented by an XPCOM factory that
+ * is registered to handle "@mozilla.org/rdf/delegate-factory/[key]/[scheme];1"
+ * ContractIDs.
+ *
+ * The factory will be invoked to create delegate objects from
+ * nsIRDFResource::GetDelegate().
+ */
+[scriptable, uuid(A1B89470-A124-11d3-BE59-0020A6361667)]
+interface nsIRDFDelegateFactory : nsISupports
+{
+    /**
+     * Create a delegate for the specified RDF resource.
+     *
+     * The created delegate should forward AddRef() and Release()
+     * calls to the aOuter object.
+     */
+    void CreateDelegate(in nsIRDFResource aOuter,
+                        in string aKey,
+                        in nsIIDRef aIID,
+                        [retval, iid_is(aIID)] out nsQIResult aResult);
+};
+
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFInMemoryDataSource.idl
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFNode.idl"
+
+[scriptable, uuid(17C4E0AA-1DD2-11B2-8029-BF6F668DE500)]
+interface nsIRDFInMemoryDataSource : nsISupports
+{
+    void EnsureFastContainment(in nsIRDFResource aSource);
+};
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFInferDataSource.idl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIRDFDataSource.idl"
+
+/**
+ * An nsIRDFInferDataSource is implemented by a infer engine. This
+ * engine mimics assertions in addition to those in the baseDataSource
+ * according to a particular vocabulary.
+ * Infer engines have contract IDs in the form of
+ * "@mozilla.org/rdf/infer-datasource;1?engine="
+ */
+
+[scriptable, uuid(2b04860f-4017-40f6-8a57-784a1e35077a)]
+interface nsIRDFInferDataSource : nsIRDFDataSource {
+    /**
+     *
+     * The wrapped datasource.
+     *
+     * The InferDataSource contains all arcs from the wrapped
+     * datasource plus those infered by the vocabulary implemented
+     * by the InferDataSource.
+     */
+    attribute nsIRDFDataSource baseDataSource;
+};
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFLiteral.idl
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIRDFNode.idl"
+
+%{C++
+#include "nscore.h" // for char16_t
+%}
+
+[ptr] native const_octet_ptr(const uint8_t);
+
+/**
+ * A literal node in the graph, whose value is a string.
+ */
+[scriptable, uuid(E0C493D2-9542-11d2-8EB8-00805F29F370)]
+interface nsIRDFLiteral : nsIRDFNode {
+    /**
+     * The Unicode string value of the literal.
+     */
+    readonly attribute wstring Value;
+
+    /**
+     * An unscriptable version used to avoid a string copy. Meant
+     * for use as a performance optimization.
+     */
+    [noscript] void GetValueConst([shared] out wstring aConstValue);
+};
+
+/**
+ * A literal node in the graph, whose value is a date
+ */
+[scriptable, uuid(E13A24E1-C77A-11d2-80BE-006097B76B8E)]
+interface nsIRDFDate : nsIRDFNode {
+    /**
+     * The date value of the literal
+     */
+    readonly attribute PRTime Value;
+};
+
+/**
+ * A literal node in the graph, whose value is an integer
+ */
+[scriptable, uuid(E13A24E3-C77A-11d2-80BE-006097B76B8E)]
+interface nsIRDFInt : nsIRDFNode {
+    /**
+     * The integer value of the literal
+     */
+    readonly attribute long Value;
+};
+
+/**
+ * A literal node in the graph, whose value is arbitrary
+ * binary data.
+ */
+[scriptable, uuid(237f85a2-1dd2-11b2-94af-8122582fc45e)]
+interface nsIRDFBlob : nsIRDFNode {
+    /**
+     * The binary data.
+     */
+    [noscript] readonly attribute const_octet_ptr value;
+
+    /**
+     * The data's length.
+     */
+    readonly attribute long length;
+};
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFNode.idl
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+// An nsIRDFNode object is an abstract placeholder for a node in the
+// RDF data model. Its concreted implementations (e.g., nsIRDFResource
+// or nsIRDFLiteral) are the actual objects that populate the graph.
+[scriptable, uuid(0F78DA50-8321-11d2-8EAC-00805F29F370)]
+interface nsIRDFNode : nsISupports {
+    // Determine if two nodes are identical
+    boolean EqualsNode(in nsIRDFNode aNode);
+};
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFObserver.idl
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFNode.idl"
+
+interface nsIRDFDataSource;
+
+// An nsIRDFObserver object is an observer that will be notified
+// when assertions are made or removed from a datasource
+[scriptable, uuid(3CC75360-484A-11D2-BC16-00805F912FE7)]
+interface nsIRDFObserver : nsISupports {
+    /**
+     * This method is called whenever a new assertion is made
+     * in the data source
+     * @param aDataSource the datasource that is issuing
+     *   the notification.
+     * @param aSource the subject of the assertion
+     * @param aProperty the predicate of the assertion
+     * @param aTarget the object of the assertion
+     */
+    void onAssert(in nsIRDFDataSource aDataSource,
+                  in nsIRDFResource aSource,
+                  in nsIRDFResource aProperty,
+                  in nsIRDFNode     aTarget);
+
+    /**
+     * This method is called whenever an assertion is removed
+     * from the data source
+     * @param aDataSource the datasource that is issuing
+     *   the notification.
+     * @param aSource the subject of the assertion
+     * @param aProperty the predicate of the assertion
+     * @param aTarget the object of the assertion
+     */
+    void onUnassert(in nsIRDFDataSource aDataSource,
+                    in nsIRDFResource aSource,
+                    in nsIRDFResource aProperty,
+                    in nsIRDFNode     aTarget);
+
+    /**
+     * This method is called when the object of an assertion
+     * changes from one value to another.
+     * @param aDataSource the datasource that is issuing
+     *   the notification.
+     * @param aSource the subject of the assertion
+     * @param aProperty the predicate of the assertion
+     * @param aOldTarget the old object of the assertion
+     * @param aNewTarget the new object of the assertion
+     */
+    void onChange(in nsIRDFDataSource aDataSource,
+                  in nsIRDFResource aSource,
+                  in nsIRDFResource aProperty,
+                  in nsIRDFNode     aOldTarget,
+                  in nsIRDFNode     aNewTarget);
+
+    /**
+     * This method is called when the subject of an assertion
+     * changes from one value to another.
+     * @param aDataSource the datasource that is issuing
+     *   the notification.
+     * @param aOldSource the old subject of the assertion
+     * @param aNewSource the new subject of the assertion
+     * @param aProperty the predicate of the assertion
+     * @param aTarget the object of the assertion
+     */
+    void onMove(in nsIRDFDataSource aDataSource,
+                in nsIRDFResource aOldSource,
+                in nsIRDFResource aNewSource,
+                in nsIRDFResource aProperty,
+                in nsIRDFNode     aTarget);
+
+    /**
+     * This method is called when a datasource is about to
+     * send several notifications at once. The observer can
+     * use this as a cue to optimize its behavior. The observer
+     * can expect the datasource to call endUpdateBatch() when
+     * the group of notifications has completed.
+     * @param aDataSource the datasource that is going to
+     *   be issuing the notifications.
+     */
+    void onBeginUpdateBatch(in nsIRDFDataSource aDataSource);
+
+    /**
+     * This method is called when a datasource has completed
+     * issuing a notification group.
+     * @param aDataSource the datasource that has finished
+     *   issuing a group of notifications
+     */
+    void onEndUpdateBatch(in nsIRDFDataSource aDataSource);
+};
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFPropagatableDataSource.idl
@@ -0,0 +1,27 @@
+/* -*- Mode: idl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsISupports.idl"
+
+/**
+ * An nsIRDFPropagatableDataSource provides an ability to suppress
+ * synchronization notifications.
+ */
+[scriptable, uuid(5a9b4770-9fcb-4307-a12e-4b6708e78b97)]
+interface nsIRDFPropagatableDataSource: nsISupports {
+
+  /**
+   * Set this value to <code>true</code> to enable synchronization
+   * notifications.
+   *
+   * Set this value to <code>false</code> to disable synchronization
+   * notifications.
+   *
+   * By default, this value is <code>true</code>.
+   */
+  attribute boolean propagateChanges;
+
+};
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFPurgeableDataSource.idl
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFNode.idl"
+
+[scriptable, uuid(951700F0-FED0-11D2-BDD9-00104BDE6048)]
+interface nsIRDFPurgeableDataSource : nsISupports
+{
+    boolean Mark(in nsIRDFResource aSource,
+                 in nsIRDFResource aProperty,
+                 in nsIRDFNode aTarget,
+                 in boolean aTruthValue);
+
+    void Sweep();
+};
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFRemoteDataSource.idl
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * A datasource that may load asynchronously
+ */
+[scriptable, uuid(1D297320-27F7-11d3-BE01-000064657374)]
+interface nsIRDFRemoteDataSource : nsISupports
+{
+    /**
+     * This value is <code>true</code> when the datasource has
+     * fully loaded itself.
+     */
+    readonly attribute boolean loaded;
+
+    /**
+     * Specify the URI for the data source: this is the prefix
+     * that will be used to register the data source in the
+     * data source registry.
+     * @param aURI the URI to load
+     */
+    void Init(in string aURI);
+
+    /**
+     * Refresh the remote datasource, re-loading its contents
+     * from the URI.
+     *
+     * @param aBlocking If <code>true</code>, the call will block
+     * until the datasource has completely reloaded.
+     */
+    void Refresh(in boolean aBlocking);
+
+    /**
+     * Request that a data source write its contents out to 
+     * permanent storage, if applicable.
+     */
+    void Flush();
+    void FlushTo(in string aURI);
+};
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFResource.idl
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsrootidl.idl"
+#include "nsIRDFNode.idl"
+
+
+/**
+ * An nsIRDFResource is an object that has unique identity in the 
+ * RDF data model. The object's identity is determined by its URI.
+ */
+[scriptable, uuid(fb9686a7-719a-49dc-9107-10dea5739341)]
+interface nsIRDFResource : nsIRDFNode {
+    /**
+     * The single-byte string value of the resource.
+     * @note THIS IS OBSOLETE. C++ should use GetValueConst and script
+     *       should use .valueUTF8
+     */
+    readonly attribute string Value;
+
+    /**
+     * The UTF-8 URI of the resource.
+     */
+    readonly attribute AUTF8String ValueUTF8;
+
+    /**
+     * An unscriptable version used to avoid a string copy. Meant
+     * for use as a performance optimization. The string is encoded
+     * in UTF-8.
+     */
+    [noscript] void GetValueConst([shared] out string aConstValue);
+
+    /**
+     * This method is called by the nsIRDFService after constructing
+     * a resource object to initialize its URI. You would not normally
+     * call this method directly
+     */
+    void Init(in string uri);
+
+    /**
+     * Determine if the resource has the given URI.
+     */
+    boolean EqualsString(in string aURI);
+
+    /**
+     * Retrieve the "delegate" object for this resource. A resource
+     * may have several delegate objects, each of whose lifetimes is
+     * bound to the life of the resource object.
+     *
+     * This method will return the delegate for the given key after
+     * QueryInterface()-ing it to the requested IID.
+     *
+     * If no delegate exists for the specified key, this method will
+     * attempt to create one using the component manager. Specifically,
+     * it will combine aKey with the resource's URI scheme to produce
+     * a ContractID as follows:
+     *
+     *   component:/rdf/delegate-factory/[key]/[scheme]
+     *
+     * This ContractID will be used to locate a factory using the
+     * FindFactory() method of nsIComponentManager. If the nsIFactory
+     * exists, it will be used to create a "delegate factory"; that
+     * is, an object that supports nsIRDFDelegateFactory. The delegate
+     * factory will be used to construct the delegate object.
+     */
+    void GetDelegate(in string aKey, in nsIIDRef aIID,
+                     [iid_is(aIID),retval] out nsQIResult aResult);
+
+    /**
+     * Force a delegate to be "unbound" from the resource.
+     *
+     * Normally, a delegate object's lifetime will be identical to
+     * that of the resource to which it is bound; this method allows a
+     * delegate to unlink itself from an RDF resource prematurely.
+     */
+    void ReleaseDelegate(in string aKey);
+};
+
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFService.idl
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIRDFResource.idl"
+#include "nsIRDFLiteral.idl"
+#include "nsIRDFDataSource.idl"
+
+
+/**
+ * The RDF service interface. This is a singleton object which should be
+ * obtained from the <code>nsServiceManager</code>.
+ */
+[scriptable, uuid(BFD05261-834C-11d2-8EAC-00805F29F370)]
+interface nsIRDFService : nsISupports {
+    /**
+     * Construct an RDF resource from a single-byte URI. <code>nsIRDFService</code>
+     * caches resources that are in-use, so multiple calls to <code>GetResource()</code>
+     * for the same <code>uri</code> will return identical pointers. FindResource
+     * is used to find out whether there already exists a resource corresponding to that url.
+     */
+    nsIRDFResource GetResource(in AUTF8String aURI);
+
+    /**
+     * Construct an RDF resource from a Unicode URI. This is provided
+     * as a convenience method, allowing automatic, in-line C++
+     * conversion from <code>nsString</code> objects. The <code>uri</code> will
+     * be converted to a single-byte representation internally.
+     */
+    nsIRDFResource GetUnicodeResource(in AString aURI);
+
+    nsIRDFResource GetAnonymousResource();
+
+    /**
+     * Construct an RDF literal from a Unicode string.
+     */
+    nsIRDFLiteral  GetLiteral(in wstring aValue);
+
+    /**
+     * Construct an RDF literal from a PRTime.
+     */
+    nsIRDFDate     GetDateLiteral(in PRTime aValue);
+
+    /**
+     * Construct an RDF literal from an int.
+     */
+    nsIRDFInt      GetIntLiteral(in long aValue);
+
+    /**
+     * Construct an RDF literal from a data blob
+     */
+    [noscript] nsIRDFBlob getBlobLiteral(in const_octet_ptr aValue, in long aLength);
+
+    boolean IsAnonymousResource(in nsIRDFResource aResource);
+
+    /**
+     * Registers a resource with the RDF system, making it unique w.r.t.
+     * GetResource.
+     *
+     * An implementation of nsIRDFResource should call this in its
+     * Init() method if it wishes the resource to be globally unique
+     * (which is usually the case).
+     *
+     * @note that the resource will <i>not</i> be ref-counted by the
+     * RDF service: the assumption is that the resource implementation
+     * will call nsIRDFService::UnregisterResource() when the last
+     * reference to the resource is released.
+     *
+     * @note that the nsIRDFService implementation may choose to
+     * maintain a reference to the resource's URI; therefore, the
+     * resource implementation should ensure that the resource's URI
+     * (accessible via nsIRDFResource::GetValue(const char* *aURI)) is
+     * valid before calling RegisterResource(). Furthermore, the
+     * resource implementation should ensure that this pointer
+     * <i>remains</i> valid for the lifetime of the resource. (The
+     * implementation of the resource cache in nsIRDFService uses the
+     * URI maintained "internally" in the resource as a key into the
+     * cache rather than copying the resource URI itself.)
+     */
+    void RegisterResource(in nsIRDFResource aResource, in boolean aReplace);
+
+    /**
+     * Called to notify the resource manager that a resource is no
+     * longer in use. This method should only be called from the
+     * destructor of a "custom" resource implementation to notify the
+     * RDF service that the last reference to the resource has been
+     * released, so the resource is no longer valid.
+     *
+     * @note As mentioned in nsIRDFResourceFactory::CreateResource(),
+     * the RDF service will use the result of
+     * nsIRDFResource::GetValue() as a key into its cache. For this
+     * reason, you must always un-cache the resource <b>before</b>
+     * releasing the storage for the <code>const char*</code> URI.
+     */
+    void UnregisterResource(in nsIRDFResource aResource);
+
+    /**
+     * Register a <i>named data source</i>. The RDF service will call
+     * <code>nsIRDFDataSource::GetURI()</code> to determine the URI under
+     * which to register the data source.
+     *
+     * @note that the data source will <i>not</i> be refcounted by the
+     * RDF service! The assumption is that an RDF data source
+     * registers with the service once it is initialized (via
+     * <code>nsIRDFDataSource::Init()</code>), and unregisters when the
+     * last reference to the data source is released.
+     */
+    void RegisterDataSource(in nsIRDFDataSource aDataSource,
+                            in boolean          aReplace);
+
+    /**
+     * Unregister a <i>named data source</i>. The RDF service will call
+     * <code>nsIRDFDataSource::GetURI()</code> to determine the URI under which the
+     * data source was registered.
+     */
+    void UnregisterDataSource(in nsIRDFDataSource aDataSource);
+
+    /**
+     * Get the <i>named data source</i> corresponding to the URI. If a data
+     * source has been registered via <code>RegisterDataSource()</code>, that
+     * data source will be returned.
+     *
+     * If no data source is currently
+     * registered for the specified URI, and a data source <i>constructor</i>
+     * function has been registered via <code>RegisterDatasourceConstructor()</code>,
+     * the RDF service will call the constructor to attempt to construct a
+     * new data source. If construction is successful, the data source will
+     * be initialized via <code>nsIRDFDataSource::Init()</code>.
+     */
+    nsIRDFDataSource GetDataSource(in string aURI);
+
+    /**
+     * Same as GetDataSource, but if a remote/XML data source needs to be
+     * constructed, then this method will issue a <b>blocking</b> Refresh
+     * call on that data source.
+     */
+    nsIRDFDataSource GetDataSourceBlocking(in string aURI);
+};
+
+%{C++
+extern nsresult
+NS_NewRDFService(nsIRDFService** result);
+%}
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFXMLParser.idl
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIRDFDataSource.idl"
+#include "nsIStreamListener.idl"
+#include "nsIURI.idl"
+
+[scriptable, uuid(1831dd2e-1dd2-11b2-bdb3-86b7b50b70b5)]
+interface nsIRDFXMLParser : nsISupports
+{
+    /**
+     * Create a stream listener that can be used to asynchronously
+     * parse RDF/XML.
+     * @param aSink the RDF datasource the will receive the data
+     * @param aBaseURI the base URI used to resolve relative
+     *   references in the RDF/XML
+     * @return an nsIStreamListener object to handle the data
+     */
+    nsIStreamListener parseAsync(in nsIRDFDataSource aSink, in nsIURI aBaseURI);
+
+    /**
+     * Parse a string of RDF/XML
+     * @param aSink the RDF datasource that will receive the data
+     * @param aBaseURI the base URI used to resolve relative
+     *   references in the RDF/XML
+     * @param aSource a UTF8 string containing RDF/XML data.
+     */
+    void parseString(in nsIRDFDataSource aSink, in nsIURI aBaseURI, in AUTF8String aSource);
+};
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFXMLSerializer.idl
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIRDFDataSource.idl"
+
+%{C++
+class nsAtom;
+%}
+[ptr] native nsAtomPtr(nsAtom);
+
+[scriptable, uuid(8ae1fbf8-1dd2-11b2-bd21-d728069cca92)]
+interface nsIRDFXMLSerializer : nsISupports
+{
+    /**
+     * Initialize the serializer with the specified datasource.
+     * @param aDataSource the datasource from which data will be
+     *   serialized
+     */
+    void init(in nsIRDFDataSource aDataSource);
+
+    /**
+     * Add the specified namespace to the serializer.
+     * @param aPrefix the attribute namespace prefix
+     * @param aURI the namespace URI
+     */
+    [noscript] void addNameSpace(in nsAtomPtr aPrefix, in DOMString aURI);
+};
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFXMLSink.idl
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  Interfaces for the RDF/XML sink, which parses RDF/XML into
+  a graph representation.
+
+*/
+
+#include "nsISupports.idl"
+
+%{C++
+class nsAtom;
+%}
+[ptr] native nsAtomPtr(nsAtom);
+
+// XXX Until these get scriptable. See nsIRDFXMLSink::AddNameSpace()
+[ref] native nsStringRef(nsString);
+%{C++
+#include "nsStringFwd.h"
+%}
+
+interface nsIRDFXMLSink;
+
+/**
+ * An observer that is notified as progress is made on the load
+ * of an RDF/XML document in an <code>nsIRDFXMLSink</code>.
+ */
+[scriptable, uuid(EB1A5D30-AB33-11D2-8EC6-00805F29F370)]
+interface nsIRDFXMLSinkObserver : nsISupports
+{
+    /**
+     * Called when the load begins.
+     * @param aSink the RDF/XML sink on which the load is beginning.
+     */
+    void onBeginLoad(in nsIRDFXMLSink aSink);
+
+    /**
+     * Called when the load is suspended (e.g., for network quantization).
+     * @param aSink the RDF/XML sink that is being interrupted.
+     */
+    void onInterrupt(in nsIRDFXMLSink aSink);
+
+    /**
+     * Called when a suspended load is resuming.
+     * @param aSink the RDF/XML sink that is resuming.
+     */
+    void onResume(in nsIRDFXMLSink aSink);
+
+    /**
+     * Called when an RDF/XML load completes successfully.
+     * @param aSink the RDF/XML sink that has finished loading.
+     */
+    void onEndLoad(in nsIRDFXMLSink aSink);
+
+    /**
+     * Called when an error occurs during the load
+     * @param aSink the RDF/XML sink in which the error occurred
+     * @param aStatus the networking result code
+     * @param aErrorMsg an error message, if applicable
+     */
+    void onError(in nsIRDFXMLSink aSink, in nsresult aStatus, in wstring aErrorMsg);
+};
+
+
+
+/**
+ * A "sink" that receives and processes RDF/XML. This interface is used
+ * by the RDF/XML parser.
+ */
+[scriptable, uuid(EB1A5D31-AB33-11D2-8EC6-00805F29F370)]
+interface nsIRDFXMLSink : nsISupports
+{
+    /**
+     * Set to <code>true</code> if the sink is read-only and cannot
+     * be modified
+     */
+    attribute boolean readOnly;
+
+    /**
+     * Initiate the RDF/XML load.
+     */
+    void beginLoad();
+
+    /**
+     * Suspend the RDF/XML load.
+     */
+    void interrupt();
+
+    /**
+     * Resume the RDF/XML load.
+     */
+    void resume();
+
+    /**
+     * Complete the RDF/XML load.
+     */
+    void endLoad();
+
+    /**
+     * Add namespace information to the RDF/XML sink.
+     * @param aPrefix the namespace prefix
+     * @param aURI the namespace URI
+     */
+    [noscript] void addNameSpace(in nsAtomPtr aPrefix,
+                                 [const] in nsStringRef aURI);
+
+    /**
+     * Add an observer that will be notified as the RDF/XML load
+     * progresses.
+     * <p>
+     *
+     * Note that the sink will acquire a strong reference to the
+     * observer, so care should be taken to avoid cyclical references
+     * that cannot be released (i.e., if the observer holds a
+     * reference to the sink, it should be sure that it eventually
+     * clears the reference).
+     *
+     * @param aObserver the observer to add to the sink's set of
+     * load observers.
+     */
+    void addXMLSinkObserver(in nsIRDFXMLSinkObserver aObserver);
+
+    /**
+     * Remove an observer from the sink's set of observers.
+     * @param aObserver the observer to remove.
+     */
+    void removeXMLSinkObserver(in nsIRDFXMLSinkObserver aObserver);
+};
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsIRDFXMLSource.idl
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIOutputStream.idl"
+
+[scriptable, uuid(4DA56F10-99FE-11d2-8EBB-00805F29F370)]
+interface nsIRDFXMLSource : nsISupports
+{
+    /**
+     * Serialize the contents of the datasource to aStream.
+     * @param aStream the output stream the will receive the
+     *   RDF/XML. Currently, the output stream need only
+     *   implement the |write()| method.
+     */
+    void Serialize(in nsIOutputStream aStream);
+};
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsInMemoryDataSource.cpp
@@ -0,0 +1,1938 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  Implementation for an in-memory RDF data store.
+
+  TO DO
+
+  1) Instrument this code to gather space and time performance
+     characteristics.
+
+  2) Optimize lookups for datasources which have a small number
+     of properties + fanning out to a large number of targets.
+
+  3) Complete implementation of thread-safety; specifically, make
+     assertions be reference counted objects (so that a cursor can
+     still refer to an assertion that gets removed from the graph).
+
+ */
+
+#include "nsAgg.h"
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "nsArrayEnumerator.h"
+#include "nsIOutputStream.h"
+#include "nsIRDFDataSource.h"
+#include "nsIRDFLiteral.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFObserver.h"
+#include "nsIRDFInMemoryDataSource.h"
+#include "nsIRDFPropagatableDataSource.h"
+#include "nsIRDFPurgeableDataSource.h"
+#include "nsIRDFService.h"
+#include "nsIServiceManager.h"
+#include "nsCOMArray.h"
+#include "nsEnumeratorUtils.h"
+#include "nsTArray.h"
+#include "nsCRT.h"
+#include "nsRDFCID.h"
+#include "nsRDFBaseDataSources.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "rdfutil.h"
+#include "PLDHashTable.h"
+#include "plstr.h"
+#include "mozilla/Logging.h"
+#include "rdf.h"
+
+#include "rdfIDataSource.h"
+#include "rdfITripleVisitor.h"
+
+using mozilla::LogLevel;
+
+// This struct is used as the slot value in the forward and reverse
+// arcs hash tables.
+//
+// Assertion objects are reference counted, because each Assertion's
+// ownership is shared between the datasource and any enumerators that
+// are currently iterating over the datasource.
+//
+class Assertion
+{
+public:
+    Assertion(nsIRDFResource* aSource,      // normal assertion
+              nsIRDFResource* aProperty,
+              nsIRDFNode* aTarget,
+              bool aTruthValue);
+    explicit Assertion(nsIRDFResource* aSource);     // PLDHashTable assertion variant
+
+private:
+    ~Assertion();
+
+public:
+    void AddRef() {
+        if (mRefCnt == UINT16_MAX) {
+            NS_WARNING("refcount overflow, leaking Assertion");
+            return;
+        }
+        ++mRefCnt;
+    }
+
+    void Release() {
+        if (mRefCnt == UINT16_MAX) {
+            NS_WARNING("refcount overflow, leaking Assertion");
+            return;
+        }
+        if (--mRefCnt == 0)
+            delete this;
+    }
+
+    // For nsIRDFPurgeableDataSource
+    inline  void    Mark()      { u.as.mMarked = true; }
+    inline  bool    IsMarked()  { return u.as.mMarked; }
+    inline  void    Unmark()    { u.as.mMarked = false; }
+
+    // public for now, because I'm too lazy to go thru and clean this up.
+
+    // These are shared between hash/as (see the union below)
+    nsIRDFResource*         mSource;
+    Assertion*              mNext;
+
+    union
+    {
+        struct hash
+        {
+            PLDHashTable*  mPropertyHash;
+        } hash;
+        struct as
+        {
+            nsIRDFResource* mProperty;
+            nsIRDFNode*     mTarget;
+            Assertion*      mInvNext;
+            // make sure bool are final elements
+            bool            mTruthValue;
+            bool            mMarked;
+        } as;
+    } u;
+
+    // also shared between hash/as (see the union above)
+    // but placed after union definition to ensure that
+    // all 32-bit entries are long aligned
+    uint16_t                    mRefCnt;
+    bool                        mHashEntry;
+};
+
+
+struct Entry : PLDHashEntryHdr {
+    nsIRDFNode*     mNode;
+    Assertion*      mAssertions;
+};
+
+
+Assertion::Assertion(nsIRDFResource* aSource)
+    : mSource(aSource),
+      mNext(nullptr),
+      mRefCnt(0),
+      mHashEntry(true)
+{
+    MOZ_COUNT_CTOR(Assertion);
+
+    NS_ADDREF(mSource);
+
+    u.hash.mPropertyHash =
+        new PLDHashTable(PLDHashTable::StubOps(), sizeof(Entry));
+}
+
+Assertion::Assertion(nsIRDFResource* aSource,
+                     nsIRDFResource* aProperty,
+                     nsIRDFNode* aTarget,
+                     bool aTruthValue)
+    : mSource(aSource),
+      mNext(nullptr),
+      mRefCnt(0),
+      mHashEntry(false)
+{
+    MOZ_COUNT_CTOR(Assertion);
+
+    u.as.mProperty = aProperty;
+    u.as.mTarget = aTarget;
+
+    NS_ADDREF(mSource);
+    NS_ADDREF(u.as.mProperty);
+    NS_ADDREF(u.as.mTarget);
+
+    u.as.mInvNext = nullptr;
+    u.as.mTruthValue = aTruthValue;
+    u.as.mMarked = false;
+}
+
+Assertion::~Assertion()
+{
+    if (mHashEntry && u.hash.mPropertyHash) {
+        for (auto i = u.hash.mPropertyHash->Iter(); !i.Done(); i.Next()) {
+            auto entry = static_cast<Entry*>(i.Get());
+            Assertion* as = entry->mAssertions;
+            while (as) {
+                Assertion* doomed = as;
+                as = as->mNext;
+
+                // Unlink, and release the datasource's reference.
+                doomed->mNext = doomed->u.as.mInvNext = nullptr;
+                doomed->Release();
+            }
+        }
+        delete u.hash.mPropertyHash;
+        u.hash.mPropertyHash = nullptr;
+    }
+
+    MOZ_COUNT_DTOR(Assertion);
+#ifdef DEBUG_REFS
+    --gInstanceCount;
+    fprintf(stdout, "%d - RDF: Assertion\n", gInstanceCount);
+#endif
+
+    NS_RELEASE(mSource);
+    if (!mHashEntry)
+    {
+        NS_RELEASE(u.as.mProperty);
+        NS_RELEASE(u.as.mTarget);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////
+// InMemoryDataSource
+class InMemoryArcsEnumeratorImpl;
+class InMemoryAssertionEnumeratorImpl;
+class InMemoryResourceEnumeratorImpl;
+
+class InMemoryDataSource : public nsIRDFDataSource,
+                           public nsIRDFInMemoryDataSource,
+                           public nsIRDFPropagatableDataSource,
+                           public nsIRDFPurgeableDataSource,
+                           public rdfIDataSource
+{
+protected:
+    // These hash tables are keyed on pointers to nsIRDFResource
+    // objects (the nsIRDFService ensures that there is only ever one
+    // nsIRDFResource object per unique URI). The value of an entry is
+    // an Assertion struct, which is a linked list of (subject
+    // predicate object) triples.
+    PLDHashTable mForwardArcs;
+    PLDHashTable mReverseArcs;
+
+    nsCOMArray<nsIRDFObserver> mObservers;
+    uint32_t                   mNumObservers;
+
+    // VisitFoo needs to block writes, [Un]Assert only allowed
+    // during mReadCount == 0
+    uint32_t mReadCount;
+
+    friend class InMemoryArcsEnumeratorImpl;
+    friend class InMemoryAssertionEnumeratorImpl;
+    friend class InMemoryResourceEnumeratorImpl; // b/c it needs to enumerate mForwardArcs
+
+    // Thread-safe writer implementation methods.
+    nsresult
+    LockedAssert(nsIRDFResource* source,
+                 nsIRDFResource* property,
+                 nsIRDFNode* target,
+                 bool tv);
+
+    nsresult
+    LockedUnassert(nsIRDFResource* source,
+                   nsIRDFResource* property,
+                   nsIRDFNode* target);
+
+    explicit InMemoryDataSource(nsISupports* aOuter);
+    virtual ~InMemoryDataSource();
+
+    friend nsresult
+    NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult);
+
+public:
+    NS_DECL_CYCLE_COLLECTING_AGGREGATED
+    NS_DECL_AGGREGATED_CYCLE_COLLECTION_CLASS(InMemoryDataSource)
+
+    // nsIRDFDataSource methods
+    NS_DECL_NSIRDFDATASOURCE
+
+    // nsIRDFInMemoryDataSource methods
+    NS_DECL_NSIRDFINMEMORYDATASOURCE
+
+    // nsIRDFPropagatableDataSource methods
+    NS_DECL_NSIRDFPROPAGATABLEDATASOURCE
+
+    // nsIRDFPurgeableDataSource methods
+    NS_DECL_NSIRDFPURGEABLEDATASOURCE
+
+    // rdfIDataSource methods
+    NS_DECL_RDFIDATASOURCE
+
+protected:
+    struct SweepInfo {
+        Assertion* mUnassertList;
+        PLDHashTable* mReverseArcs;
+    };
+
+    static void
+    SweepForwardArcsEntries(PLDHashTable* aTable, SweepInfo* aArg);
+
+public:
+    // Implementation methods
+    Assertion*
+    GetForwardArcs(nsIRDFResource* u) {
+        PLDHashEntryHdr* hdr = mForwardArcs.Search(u);
+        return hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+    }
+
+    Assertion*
+    GetReverseArcs(nsIRDFNode* v) {
+        PLDHashEntryHdr* hdr = mReverseArcs.Search(v);
+        return hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+    }
+
+    void
+    SetForwardArcs(nsIRDFResource* u, Assertion* as) {
+        if (as) {
+            auto entry =
+                static_cast<Entry*>(mForwardArcs.Add(u, mozilla::fallible));
+            if (entry) {
+                entry->mNode = u;
+                entry->mAssertions = as;
+            }
+        }
+        else {
+            mForwardArcs.Remove(u);
+        }
+    }
+
+    void
+    SetReverseArcs(nsIRDFNode* v, Assertion* as) {
+        if (as) {
+            auto entry =
+                static_cast<Entry*>(mReverseArcs.Add(v, mozilla::fallible));
+            if (entry) {
+                entry->mNode = v;
+                entry->mAssertions = as;
+            }
+        }
+        else {
+            mReverseArcs.Remove(v);
+        }
+    }
+
+    void
+    LogOperation(const char* aOperation,
+                 nsIRDFResource* asource,
+                 nsIRDFResource* aProperty,
+                 nsIRDFNode* aTarget,
+                 bool aTruthValue = true);
+
+    bool    mPropagateChanges;
+
+private:
+    static mozilla::LazyLogModule gLog;
+};
+
+mozilla::LazyLogModule InMemoryDataSource::gLog("InMemoryDataSource");
+
+//----------------------------------------------------------------------
+//
+// InMemoryAssertionEnumeratorImpl
+//
+
+/**
+ * InMemoryAssertionEnumeratorImpl
+ */
+class InMemoryAssertionEnumeratorImpl : public nsISimpleEnumerator
+{
+private:
+    InMemoryDataSource* mDataSource;
+    nsIRDFResource* mSource;
+    nsIRDFResource* mProperty;
+    nsIRDFNode*     mTarget;
+    nsIRDFNode*     mValue;
+    bool            mTruthValue;
+    Assertion*      mNextAssertion;
+
+    virtual ~InMemoryAssertionEnumeratorImpl();
+
+public:
+    InMemoryAssertionEnumeratorImpl(InMemoryDataSource* aDataSource,
+                                    nsIRDFResource* aSource,
+                                    nsIRDFResource* aProperty,
+                                    nsIRDFNode* aTarget,
+                                    bool aTruthValue);
+
+    // nsISupports interface
+    NS_DECL_ISUPPORTS
+
+    // nsISimpleEnumerator interface
+    NS_DECL_NSISIMPLEENUMERATOR
+};
+
+////////////////////////////////////////////////////////////////////////
+
+
+InMemoryAssertionEnumeratorImpl::InMemoryAssertionEnumeratorImpl(
+                 InMemoryDataSource* aDataSource,
+                 nsIRDFResource* aSource,
+                 nsIRDFResource* aProperty,
+                 nsIRDFNode* aTarget,
+                 bool aTruthValue)
+    : mDataSource(aDataSource),
+      mSource(aSource),
+      mProperty(aProperty),
+      mTarget(aTarget),
+      mValue(nullptr),
+      mTruthValue(aTruthValue),
+      mNextAssertion(nullptr)
+{
+    NS_ADDREF(mDataSource);
+    NS_IF_ADDREF(mSource);
+    NS_ADDREF(mProperty);
+    NS_IF_ADDREF(mTarget);
+
+    if (mSource) {
+        mNextAssertion = mDataSource->GetForwardArcs(mSource);
+
+        if (mNextAssertion && mNextAssertion->mHashEntry) {
+            // its our magical HASH_ENTRY forward hash for assertions
+            PLDHashEntryHdr* hdr =
+                mNextAssertion->u.hash.mPropertyHash->Search(aProperty);
+            mNextAssertion =
+                hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+        }
+    }
+    else {
+        mNextAssertion = mDataSource->GetReverseArcs(mTarget);
+    }
+
+    // Add an owning reference from the enumerator
+    if (mNextAssertion)
+        mNextAssertion->AddRef();
+}
+
+InMemoryAssertionEnumeratorImpl::~InMemoryAssertionEnumeratorImpl()
+{
+#ifdef DEBUG_REFS
+    --gInstanceCount;
+    fprintf(stdout, "%d - RDF: InMemoryAssertionEnumeratorImpl\n", gInstanceCount);
+#endif
+
+    if (mNextAssertion)
+        mNextAssertion->Release();
+
+    NS_IF_RELEASE(mDataSource);
+    NS_IF_RELEASE(mSource);
+    NS_IF_RELEASE(mProperty);
+    NS_IF_RELEASE(mTarget);
+    NS_IF_RELEASE(mValue);
+}
+
+NS_IMPL_ADDREF(InMemoryAssertionEnumeratorImpl)
+NS_IMPL_RELEASE(InMemoryAssertionEnumeratorImpl)
+NS_IMPL_QUERY_INTERFACE(InMemoryAssertionEnumeratorImpl, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+InMemoryAssertionEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+    if (mValue) {
+        *aResult = true;
+        return NS_OK;
+    }
+
+    while (mNextAssertion) {
+        bool foundIt = false;
+        if ((mProperty == mNextAssertion->u.as.mProperty) &&
+            (mTruthValue == mNextAssertion->u.as.mTruthValue)) {
+            if (mSource) {
+                mValue = mNextAssertion->u.as.mTarget;
+                NS_ADDREF(mValue);
+            }
+            else {
+                mValue = mNextAssertion->mSource;
+                NS_ADDREF(mValue);
+            }
+            foundIt = true;
+        }
+
+        // Remember the last assertion we were holding on to
+        Assertion* as = mNextAssertion;
+
+        // iterate
+        mNextAssertion = (mSource) ? mNextAssertion->mNext : mNextAssertion->u.as.mInvNext;
+
+        // grab an owning reference from the enumerator to the next assertion
+        if (mNextAssertion)
+            mNextAssertion->AddRef();
+
+        // ...and release the reference from the enumerator to the old one.
+        as->Release();
+
+        if (foundIt) {
+            *aResult = true;
+            return NS_OK;
+        }
+    }
+
+    *aResult = false;
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+InMemoryAssertionEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+    nsresult rv;
+
+    bool hasMore;
+    rv = HasMoreElements(&hasMore);
+    if (NS_FAILED(rv)) return rv;
+
+    if (! hasMore)
+        return NS_ERROR_UNEXPECTED;
+
+    // Don't AddRef: we "transfer" ownership to the caller
+    *aResult = mValue;
+    mValue = nullptr;
+
+    return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+
+/**
+ * This class is a little bit bizarre in that it implements both the
+ * <tt>nsIRDFArcsOutCursor</tt> and <tt>nsIRDFArcsInCursor</tt> interfaces.
+ * Because the structure of the in-memory graph is pretty flexible, it's
+ * fairly easy to parameterize this class. The only funky thing to watch
+ * out for is the multiple inheritance clashes.
+ */
+
+class InMemoryArcsEnumeratorImpl : public nsISimpleEnumerator
+{
+private:
+    InMemoryDataSource* mDataSource;
+    nsIRDFResource*     mSource;
+    nsIRDFNode*         mTarget;
+    AutoTArray<nsCOMPtr<nsIRDFResource>, 8> mAlreadyReturned;
+    nsIRDFResource*     mCurrent;
+    Assertion*          mAssertion;
+    nsCOMArray<nsIRDFNode>* mHashArcs;
+
+    virtual ~InMemoryArcsEnumeratorImpl();
+
+public:
+    InMemoryArcsEnumeratorImpl(InMemoryDataSource* aDataSource,
+                               nsIRDFResource* aSource,
+                               nsIRDFNode* aTarget);
+
+    // nsISupports interface
+    NS_DECL_ISUPPORTS
+
+    // nsISimpleEnumerator interface
+    NS_DECL_NSISIMPLEENUMERATOR
+};
+
+
+InMemoryArcsEnumeratorImpl::InMemoryArcsEnumeratorImpl(InMemoryDataSource* aDataSource,
+                                                       nsIRDFResource* aSource,
+                                                       nsIRDFNode* aTarget)
+    : mDataSource(aDataSource),
+      mSource(aSource),
+      mTarget(aTarget),
+      mCurrent(nullptr),
+      mHashArcs(nullptr)
+{
+    NS_ADDREF(mDataSource);
+    NS_IF_ADDREF(mSource);
+    NS_IF_ADDREF(mTarget);
+
+    if (mSource) {
+        // cast okay because it's a closed system
+        mAssertion = mDataSource->GetForwardArcs(mSource);
+
+        if (mAssertion && mAssertion->mHashEntry) {
+            // its our magical HASH_ENTRY forward hash for assertions
+            mHashArcs = new nsCOMArray<nsIRDFNode>();
+            for (auto i = mAssertion->u.hash.mPropertyHash->Iter();
+                 !i.Done();
+                 i.Next()) {
+                auto entry = static_cast<Entry*>(i.Get());
+                mHashArcs->AppendElement(entry->mNode);
+            }
+            mAssertion = nullptr;
+        }
+    }
+    else {
+        mAssertion = mDataSource->GetReverseArcs(mTarget);
+    }
+}
+
+InMemoryArcsEnumeratorImpl::~InMemoryArcsEnumeratorImpl()
+{
+#ifdef DEBUG_REFS
+    --gInstanceCount;
+    fprintf(stdout, "%d - RDF: InMemoryArcsEnumeratorImpl\n", gInstanceCount);
+#endif
+
+    NS_RELEASE(mDataSource);
+    NS_IF_RELEASE(mSource);
+    NS_IF_RELEASE(mTarget);
+    NS_IF_RELEASE(mCurrent);
+    delete mHashArcs;
+}
+
+NS_IMPL_ADDREF(InMemoryArcsEnumeratorImpl)
+NS_IMPL_RELEASE(InMemoryArcsEnumeratorImpl)
+NS_IMPL_QUERY_INTERFACE(InMemoryArcsEnumeratorImpl, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+InMemoryArcsEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    if (mCurrent) {
+        *aResult = true;
+        return NS_OK;
+    }
+
+    if (mHashArcs) {
+        if (!mHashArcs->IsEmpty()) {
+            const uint32_t last = mHashArcs->Length() - 1;
+            nsCOMPtr<nsIRDFResource> tmp(do_QueryInterface(mHashArcs->ObjectAt(last)));
+            tmp.forget(&mCurrent);
+            mHashArcs->RemoveElementAt(last);
+            *aResult = true;
+            return NS_OK;
+        }
+    }
+    else
+        while (mAssertion) {
+            nsIRDFResource* next = mAssertion->u.as.mProperty;
+
+            // "next" is the property arc we are tentatively going to return
+            // in a subsequent GetNext() call.  It is important to do two
+            // things, however, before that can happen:
+            //   1) Make sure it's not an arc we've already returned.
+            //   2) Make sure that |mAssertion| is not left pointing to
+            //      another assertion that has the same property as this one.
+            // The first is a practical concern; the second a defense against
+            // an obscure crash and other erratic behavior.  To ensure the
+            // second condition, skip down the chain until we find the next
+            // assertion with a property that doesn't match the current one.
+            // (All these assertions would be skipped via mAlreadyReturned
+            // checks anyways; this is even a bit faster.)
+
+            do {
+                mAssertion = (mSource ? mAssertion->mNext :
+                        mAssertion->u.as.mInvNext);
+            }
+            while (mAssertion && (next == mAssertion->u.as.mProperty));
+
+            bool alreadyReturned = false;
+            for (int32_t i = mAlreadyReturned.Length() - 1; i >= 0; --i) {
+                if (mAlreadyReturned[i] == next) {
+                    alreadyReturned = true;
+                    break;
+                }
+            }
+
+            if (! alreadyReturned) {
+                mCurrent = next;
+                NS_ADDREF(mCurrent);
+                *aResult = true;
+                return NS_OK;
+            }
+        }
+
+    *aResult = false;
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+InMemoryArcsEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+    nsresult rv;
+
+    bool hasMore;
+    rv = HasMoreElements(&hasMore);
+    if (NS_FAILED(rv)) return rv;
+
+    if (! hasMore)
+        return NS_ERROR_UNEXPECTED;
+
+    // Add this to the set of things we've already returned so that we
+    // can ensure uniqueness
+    mAlreadyReturned.AppendElement(mCurrent);
+
+    // Don't AddRef: we "transfer" ownership to the caller
+    *aResult = mCurrent;
+    mCurrent = nullptr;
+
+    return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// InMemoryDataSource
+
+nsresult
+NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult)
+{
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+    *aResult = nullptr;
+
+    if (aOuter && !aIID.Equals(NS_GET_IID(nsISupports))) {
+        NS_ERROR("aggregation requires nsISupports");
+        return NS_ERROR_ILLEGAL_VALUE;
+    }
+
+    InMemoryDataSource* datasource = new InMemoryDataSource(aOuter);
+    NS_ADDREF(datasource);
+
+    datasource->fAggregated.AddRef();
+    nsresult rv = datasource->AggregatedQueryInterface(aIID, aResult); // This'll AddRef()
+    datasource->fAggregated.Release();
+
+    NS_RELEASE(datasource);
+    return rv;
+}
+
+
+InMemoryDataSource::InMemoryDataSource(nsISupports* aOuter)
+    : mForwardArcs(PLDHashTable::StubOps(), sizeof(Entry))
+    , mReverseArcs(PLDHashTable::StubOps(), sizeof(Entry))
+    , mNumObservers(0)
+    , mReadCount(0)
+{
+    NS_INIT_AGGREGATED(aOuter);
+
+    mPropagateChanges = true;
+}
+
+
+InMemoryDataSource::~InMemoryDataSource()
+{
+#ifdef DEBUG_REFS
+    --gInstanceCount;
+    fprintf(stdout, "%d - RDF: InMemoryDataSource\n", gInstanceCount);
+#endif
+
+    if (mForwardArcs.EntryCount() > 0) {
+        // This'll release all of the Assertion objects that are
+        // associated with this data source. We only need to do this
+        // for the forward arcs, because the reverse arcs table
+        // indexes the exact same set of resources.
+        for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+            auto entry = static_cast<Entry*>(iter.Get());
+            Assertion* as = entry->mAssertions;
+            while (as) {
+                Assertion* doomed = as;
+                as = as->mNext;
+
+                // Unlink, and release the datasource's reference.
+                doomed->mNext = doomed->u.as.mInvNext = nullptr;
+                doomed->Release();
+            }
+        }
+    }
+
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("InMemoryDataSource(%p): destroyed.", this));
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(InMemoryDataSource)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(InMemoryDataSource)
+    NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_AGGREGATED(InMemoryDataSource)
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_AGGREGATED(InMemoryDataSource)
+NS_INTERFACE_MAP_BEGIN_AGGREGATED(InMemoryDataSource)
+    NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION_AGGREGATED(InMemoryDataSource)
+    NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
+    NS_INTERFACE_MAP_ENTRY(nsIRDFInMemoryDataSource)
+    NS_INTERFACE_MAP_ENTRY(nsIRDFPropagatableDataSource)
+    NS_INTERFACE_MAP_ENTRY(nsIRDFPurgeableDataSource)
+    NS_INTERFACE_MAP_ENTRY(rdfIDataSource)
+NS_INTERFACE_MAP_END
+
+////////////////////////////////////////////////////////////////////////
+
+
+void
+InMemoryDataSource::LogOperation(const char* aOperation,
+                                 nsIRDFResource* aSource,
+                                 nsIRDFResource* aProperty,
+                                 nsIRDFNode* aTarget,
+                                 bool aTruthValue)
+{
+    if (! MOZ_LOG_TEST(gLog, LogLevel::Debug))
+        return;
+
+    nsCString uri;
+    aSource->GetValue(getter_Copies(uri));
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("InMemoryDataSource(%p): %s", this, aOperation));
+
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("  [(%p)%s]--", aSource, uri.get()));
+
+    aProperty->GetValue(getter_Copies(uri));
+
+    char tv = (aTruthValue ? '-' : '!');
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("  --%c[(%p)%s]--", tv, aProperty, uri.get()));
+
+    nsCOMPtr<nsIRDFResource> resource;
+    nsCOMPtr<nsIRDFLiteral> literal;
+
+    if ((resource = do_QueryInterface(aTarget)) != nullptr) {
+        resource->GetValue(getter_Copies(uri));
+        MOZ_LOG(gLog, LogLevel::Debug,
+           ("  -->[(%p)%s]", aTarget, uri.get()));
+    }
+    else if ((literal = do_QueryInterface(aTarget)) != nullptr) {
+        nsString value;
+        literal->GetValue(getter_Copies(value));
+        MOZ_LOG(gLog, LogLevel::Debug,
+           ("  -->(\"%s\")\n", NS_ConvertUTF16toUTF8(value).get()));
+    }
+    else {
+        MOZ_LOG(gLog, LogLevel::Debug,
+           ("  -->(unknown-type)\n"));
+    }
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::GetURI(nsACString& aURI)
+{
+    aURI.SetIsVoid(true);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetSource(nsIRDFResource* property,
+                              nsIRDFNode* target,
+                              bool tv,
+                              nsIRDFResource** source)
+{
+    NS_PRECONDITION(source != nullptr, "null ptr");
+    if (! source)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(property != nullptr, "null ptr");
+    if (! property)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(target != nullptr, "null ptr");
+    if (! target)
+        return NS_ERROR_NULL_POINTER;
+
+    for (Assertion* as = GetReverseArcs(target); as; as = as->u.as.mInvNext) {
+        if ((property == as->u.as.mProperty) && (tv == as->u.as.mTruthValue)) {
+            *source = as->mSource;
+            NS_ADDREF(*source);
+            return NS_OK;
+        }
+    }
+    *source = nullptr;
+    return NS_RDF_NO_VALUE;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetTarget(nsIRDFResource* source,
+                              nsIRDFResource* property,
+                              bool tv,
+                              nsIRDFNode** target)
+{
+    NS_PRECONDITION(source != nullptr, "null ptr");
+    if (! source)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(property != nullptr, "null ptr");
+    if (! property)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(target != nullptr, "null ptr");
+    if (! target)
+        return NS_ERROR_NULL_POINTER;
+
+    Assertion *as = GetForwardArcs(source);
+    if (as && as->mHashEntry) {
+        PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(property);
+        Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+        while (val) {
+            if (tv == val->u.as.mTruthValue) {
+                *target = val->u.as.mTarget;
+                NS_IF_ADDREF(*target);
+                return NS_OK;
+            }
+            val = val->mNext;
+        }
+    }
+    else
+    for (; as != nullptr; as = as->mNext) {
+        if ((property == as->u.as.mProperty) && (tv == (as->u.as.mTruthValue))) {
+            *target = as->u.as.mTarget;
+            NS_ADDREF(*target);
+            return NS_OK;
+        }
+    }
+
+    // If we get here, then there was no target with for the specified
+    // property & truth value.
+    *target = nullptr;
+    return NS_RDF_NO_VALUE;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::HasAssertion(nsIRDFResource* source,
+                                 nsIRDFResource* property,
+                                 nsIRDFNode* target,
+                                 bool tv,
+                                 bool* hasAssertion)
+{
+    if (! source)
+        return NS_ERROR_NULL_POINTER;
+
+    if (! property)
+        return NS_ERROR_NULL_POINTER;
+
+    if (! target)
+        return NS_ERROR_NULL_POINTER;
+
+    Assertion *as = GetForwardArcs(source);
+    if (as && as->mHashEntry) {
+        PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(property);
+        Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+        while (val) {
+            if ((val->u.as.mTarget == target) && (tv == (val->u.as.mTruthValue))) {
+                *hasAssertion = true;
+                return NS_OK;
+            }
+            val = val->mNext;
+        }
+    }
+    else
+    for (; as != nullptr; as = as->mNext) {
+        // check target first as its most unique
+        if (target != as->u.as.mTarget)
+            continue;
+
+        if (property != as->u.as.mProperty)
+            continue;
+
+        if (tv != (as->u.as.mTruthValue))
+            continue;
+
+        // found it!
+        *hasAssertion = true;
+        return NS_OK;
+    }
+
+    // If we get here, we couldn't find the assertion
+    *hasAssertion = false;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetSources(nsIRDFResource* aProperty,
+                               nsIRDFNode* aTarget,
+                               bool aTruthValue,
+                               nsISimpleEnumerator** aResult)
+{
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aTarget != nullptr, "null ptr");
+    if (! aTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    InMemoryAssertionEnumeratorImpl* result =
+        new InMemoryAssertionEnumeratorImpl(this, nullptr, aProperty,
+                                            aTarget, aTruthValue);
+
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(result);
+    *aResult = result;
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetTargets(nsIRDFResource* aSource,
+                               nsIRDFResource* aProperty,
+                               bool aTruthValue,
+                               nsISimpleEnumerator** aResult)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    InMemoryAssertionEnumeratorImpl* result =
+        new InMemoryAssertionEnumeratorImpl(this, aSource, aProperty,
+                                            nullptr, aTruthValue);
+
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(result);
+    *aResult = result;
+
+    return NS_OK;
+}
+
+
+nsresult
+InMemoryDataSource::LockedAssert(nsIRDFResource* aSource,
+                                 nsIRDFResource* aProperty,
+                                 nsIRDFNode* aTarget,
+                                 bool aTruthValue)
+{
+    LogOperation("ASSERT", aSource, aProperty, aTarget, aTruthValue);
+
+    Assertion* next = GetForwardArcs(aSource);
+    Assertion* prev = next;
+    Assertion* as = nullptr;
+
+    bool    haveHash = (next) ? next->mHashEntry : false;
+    if (haveHash) {
+        PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
+        Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+        while (val) {
+            if (val->u.as.mTarget == aTarget) {
+                // Wow, we already had the assertion. Make sure that the
+                // truth values are correct and bail.
+                val->u.as.mTruthValue = aTruthValue;
+                return NS_OK;
+            }
+            val = val->mNext;
+        }
+    }
+    else
+    {
+        while (next) {
+            // check target first as its most unique
+            if (aTarget == next->u.as.mTarget) {
+                if (aProperty == next->u.as.mProperty) {
+                    // Wow, we already had the assertion. Make sure that the
+                    // truth values are correct and bail.
+                    next->u.as.mTruthValue = aTruthValue;
+                    return NS_OK;
+                }
+            }
+
+            prev = next;
+            next = next->mNext;
+        }
+    }
+
+    as = new Assertion(aSource, aProperty, aTarget, aTruthValue);
+    if (! as)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    // Add the datasource's owning reference.
+    as->AddRef();
+
+    if (haveHash)
+    {
+        PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
+        Assertion *asRef =
+            hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+        if (asRef)
+        {
+            as->mNext = asRef->mNext;
+            asRef->mNext = as;
+        }
+        else
+        {
+            hdr = next->u.hash.mPropertyHash->Add(aProperty, mozilla::fallible);
+            if (hdr)
+            {
+                Entry* entry = static_cast<Entry*>(hdr);
+                entry->mNode = aProperty;
+                entry->mAssertions = as;
+            }
+        }
+    }
+    else
+    {
+        // Link it in to the "forward arcs" table
+        if (!prev) {
+            SetForwardArcs(aSource, as);
+        } else {
+            prev->mNext = as;
+        }
+    }
+
+    // Link it in to the "reverse arcs" table
+
+    next = GetReverseArcs(aTarget);
+    as->u.as.mInvNext = next;
+    next = as;
+    SetReverseArcs(aTarget, next);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::Assert(nsIRDFResource* aSource,
+                           nsIRDFResource* aProperty,
+                           nsIRDFNode* aTarget,
+                           bool aTruthValue)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aTarget != nullptr, "null ptr");
+    if (! aTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    if (mReadCount) {
+        NS_WARNING("Writing to InMemoryDataSource during read\n");
+        return NS_RDF_ASSERTION_REJECTED;
+    }
+
+    nsresult rv;
+    rv = LockedAssert(aSource, aProperty, aTarget, aTruthValue);
+    if (NS_FAILED(rv)) return rv;
+
+    // notify observers
+    for (int32_t i = (int32_t)mNumObservers - 1; mPropagateChanges && i >= 0; --i) {
+        nsIRDFObserver* obs = mObservers[i];
+
+        // XXX this should never happen, but it does, and we can't figure out why.
+        NS_ASSERTION(obs, "observer array corrupted!");
+        if (! obs)
+          continue;
+
+        obs->OnAssert(this, aSource, aProperty, aTarget);
+        // XXX ignore return value?
+    }
+
+    return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+nsresult
+InMemoryDataSource::LockedUnassert(nsIRDFResource* aSource,
+                                   nsIRDFResource* aProperty,
+                                   nsIRDFNode* aTarget)
+{
+    LogOperation("UNASSERT", aSource, aProperty, aTarget);
+
+    Assertion* next = GetForwardArcs(aSource);
+    Assertion* prev = next;
+    Assertion* root = next;
+    Assertion* as = nullptr;
+
+    bool    haveHash = (next) ? next->mHashEntry : false;
+    if (haveHash) {
+        PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
+        prev = next = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+        bool first = true;
+        while (next) {
+            if (aTarget == next->u.as.mTarget) {
+                break;
+            }
+            first = false;
+            prev = next;
+            next = next->mNext;
+        }
+        // We don't even have the assertion, so just bail.
+        if (!next)
+            return NS_OK;
+
+        as = next;
+
+        if (first) {
+            root->u.hash.mPropertyHash->RawRemove(hdr);
+
+            if (next && next->mNext) {
+                PLDHashEntryHdr* hdr =
+                    root->u.hash.mPropertyHash->Add(aProperty,
+                                                    mozilla::fallible);
+                if (hdr) {
+                    Entry* entry = static_cast<Entry*>(hdr);
+                    entry->mNode = aProperty;
+                    entry->mAssertions = next->mNext;
+                }
+            }
+            else {
+                // If this second-level hash empties out, clean it up.
+                if (!root->u.hash.mPropertyHash->EntryCount()) {
+                    root->Release();
+                    SetForwardArcs(aSource, nullptr);
+                }
+            }
+        }
+        else {
+            prev->mNext = next->mNext;
+        }
+    }
+    else
+    {
+        while (next) {
+            // check target first as its most unique
+            if (aTarget == next->u.as.mTarget) {
+                if (aProperty == next->u.as.mProperty) {
+                    if (prev == next) {
+                        SetForwardArcs(aSource, next->mNext);
+                    } else {
+                        prev->mNext = next->mNext;
+                    }
+                    as = next;
+                    break;
+                }
+            }
+
+            prev = next;
+            next = next->mNext;
+        }
+    }
+    // We don't even have the assertion, so just bail.
+    if (!as)
+        return NS_OK;
+
+#ifdef DEBUG
+    bool foundReverseArc = false;
+#endif
+
+    next = prev = GetReverseArcs(aTarget);
+    while (next) {
+        if (next == as) {
+            if (prev == next) {
+                SetReverseArcs(aTarget, next->u.as.mInvNext);
+            } else {
+                prev->u.as.mInvNext = next->u.as.mInvNext;
+            }
+#ifdef DEBUG
+            foundReverseArc = true;
+#endif
+            break;
+        }
+        prev = next;
+        next = next->u.as.mInvNext;
+    }
+
+#ifdef DEBUG
+    NS_ASSERTION(foundReverseArc, "in-memory db corrupted: unable to find reverse arc");
+#endif
+
+    // Unlink, and release the datasource's reference
+    as->mNext = as->u.as.mInvNext = nullptr;
+    as->Release();
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::Unassert(nsIRDFResource* aSource,
+                             nsIRDFResource* aProperty,
+                             nsIRDFNode* aTarget)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aTarget != nullptr, "null ptr");
+    if (! aTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    if (mReadCount) {
+        NS_WARNING("Writing to InMemoryDataSource during read\n");
+        return NS_RDF_ASSERTION_REJECTED;
+    }
+
+    nsresult rv;
+
+    rv = LockedUnassert(aSource, aProperty, aTarget);
+    if (NS_FAILED(rv)) return rv;
+
+    // Notify the world
+    for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+        nsIRDFObserver* obs = mObservers[i];
+
+        // XXX this should never happen, but it does, and we can't figure out why.
+        NS_ASSERTION(obs, "observer array corrupted!");
+        if (! obs)
+          continue;
+
+        obs->OnUnassert(this, aSource, aProperty, aTarget);
+        // XXX ignore return value?
+    }
+
+    return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::Change(nsIRDFResource* aSource,
+                           nsIRDFResource* aProperty,
+                           nsIRDFNode* aOldTarget,
+                           nsIRDFNode* aNewTarget)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aOldTarget != nullptr, "null ptr");
+    if (! aOldTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aNewTarget != nullptr, "null ptr");
+    if (! aNewTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    if (mReadCount) {
+        NS_WARNING("Writing to InMemoryDataSource during read\n");
+        return NS_RDF_ASSERTION_REJECTED;
+    }
+
+    nsresult rv;
+
+    // XXX We can implement LockedChange() if we decide that this
+    // is a performance bottleneck.
+
+    rv = LockedUnassert(aSource, aProperty, aOldTarget);
+    if (NS_FAILED(rv)) return rv;
+
+    rv = LockedAssert(aSource, aProperty, aNewTarget, true);
+    if (NS_FAILED(rv)) return rv;
+
+    // Notify the world
+    for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+        nsIRDFObserver* obs = mObservers[i];
+
+        // XXX this should never happen, but it does, and we can't figure out why.
+        NS_ASSERTION(obs, "observer array corrupted!");
+        if (! obs)
+          continue;
+
+        obs->OnChange(this, aSource, aProperty, aOldTarget, aNewTarget);
+        // XXX ignore return value?
+    }
+
+    return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::Move(nsIRDFResource* aOldSource,
+                         nsIRDFResource* aNewSource,
+                         nsIRDFResource* aProperty,
+                         nsIRDFNode* aTarget)
+{
+    NS_PRECONDITION(aOldSource != nullptr, "null ptr");
+    if (! aOldSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aNewSource != nullptr, "null ptr");
+    if (! aNewSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aTarget != nullptr, "null ptr");
+    if (! aTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    if (mReadCount) {
+        NS_WARNING("Writing to InMemoryDataSource during read\n");
+        return NS_RDF_ASSERTION_REJECTED;
+    }
+
+    nsresult rv;
+
+    // XXX We can implement LockedMove() if we decide that this
+    // is a performance bottleneck.
+
+    rv = LockedUnassert(aOldSource, aProperty, aTarget);
+    if (NS_FAILED(rv)) return rv;
+
+    rv = LockedAssert(aNewSource, aProperty, aTarget, true);
+    if (NS_FAILED(rv)) return rv;
+
+    // Notify the world
+    for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+        nsIRDFObserver* obs = mObservers[i];
+
+        // XXX this should never happen, but it does, and we can't figure out why.
+        NS_ASSERTION(obs, "observer array corrupted!");
+        if (! obs)
+          continue;
+
+        obs->OnMove(this, aOldSource, aNewSource, aProperty, aTarget);
+        // XXX ignore return value?
+    }
+
+    return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::AddObserver(nsIRDFObserver* aObserver)
+{
+    NS_PRECONDITION(aObserver != nullptr, "null ptr");
+    if (! aObserver)
+        return NS_ERROR_NULL_POINTER;
+
+    mObservers.AppendObject(aObserver);
+    mNumObservers = mObservers.Count();
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::RemoveObserver(nsIRDFObserver* aObserver)
+{
+    NS_PRECONDITION(aObserver != nullptr, "null ptr");
+    if (! aObserver)
+        return NS_ERROR_NULL_POINTER;
+
+    mObservers.RemoveObject(aObserver);
+    // note: use Count() instead of just decrementing
+    // in case aObserver wasn't in list, for example
+    mNumObservers = mObservers.Count();
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
+{
+    Assertion* ass = GetReverseArcs(aNode);
+    while (ass) {
+        nsIRDFResource* elbow = ass->u.as.mProperty;
+        if (elbow == aArc) {
+            *result = true;
+            return NS_OK;
+        }
+        ass = ass->u.as.mInvNext;
+    }
+    *result = false;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *result)
+{
+    Assertion* ass = GetForwardArcs(aSource);
+    if (ass && ass->mHashEntry) {
+        PLDHashEntryHdr* hdr = ass->u.hash.mPropertyHash->Search(aArc);
+        Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+        if (val) {
+            *result = true;
+            return NS_OK;
+        }
+        ass = ass->mNext;
+    }
+    while (ass) {
+        nsIRDFResource* elbow = ass->u.as.mProperty;
+        if (elbow == aArc) {
+            *result = true;
+            return NS_OK;
+        }
+        ass = ass->mNext;
+    }
+    *result = false;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::ArcLabelsIn(nsIRDFNode* aTarget, nsISimpleEnumerator** aResult)
+{
+    NS_PRECONDITION(aTarget != nullptr, "null ptr");
+    if (! aTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    InMemoryArcsEnumeratorImpl* result =
+        new InMemoryArcsEnumeratorImpl(this, nullptr, aTarget);
+
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(result);
+    *aResult = result;
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::ArcLabelsOut(nsIRDFResource* aSource, nsISimpleEnumerator** aResult)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    InMemoryArcsEnumeratorImpl* result =
+        new InMemoryArcsEnumeratorImpl(this, aSource, nullptr);
+
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(result);
+    *aResult = result;
+
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::GetAllResources(nsISimpleEnumerator** aResult)
+{
+    nsCOMArray<nsIRDFNode> nodes;
+    nodes.SetCapacity(mForwardArcs.EntryCount());
+
+    // Get all of our entries into an nsCOMArray
+    for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+        auto entry = static_cast<Entry*>(iter.Get());
+        nodes.AppendObject(entry->mNode);
+    }
+    return NS_NewArrayEnumerator(aResult, nodes);
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetAllCmds(nsIRDFResource* source,
+                               nsISimpleEnumerator/*<nsIRDFResource>*/** commands)
+{
+    return(NS_NewEmptyEnumerator(commands));
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::IsCommandEnabled(nsISupports* aSources,
+                                     nsIRDFResource*   aCommand,
+                                     nsISupports* aArguments,
+                                     bool* aResult)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::DoCommand(nsISupports* aSources,
+                              nsIRDFResource*   aCommand,
+                              nsISupports* aArguments)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::BeginUpdateBatch()
+{
+    for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+        nsIRDFObserver* obs = mObservers[i];
+        obs->OnBeginUpdateBatch(this);
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::EndUpdateBatch()
+{
+    for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+        nsIRDFObserver* obs = mObservers[i];
+        obs->OnEndUpdateBatch(this);
+    }
+    return NS_OK;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFInMemoryDataSource methods
+
+NS_IMETHODIMP
+InMemoryDataSource::EnsureFastContainment(nsIRDFResource* aSource)
+{
+    Assertion *as = GetForwardArcs(aSource);
+    bool    haveHash = (as) ? as->mHashEntry : false;
+
+    // if its already a hash, then nothing to do
+    if (haveHash)   return(NS_OK);
+
+    // convert aSource in forward hash into a hash
+    Assertion *hashAssertion = new Assertion(aSource);
+    NS_ASSERTION(hashAssertion, "unable to create Assertion");
+    if (!hashAssertion) return(NS_ERROR_OUT_OF_MEMORY);
+
+    // Add the datasource's owning reference.
+    hashAssertion->AddRef();
+
+    Assertion *first = GetForwardArcs(aSource);
+    SetForwardArcs(aSource, hashAssertion);
+
+    // mutate references of existing forward assertions into this hash
+    PLDHashTable *table = hashAssertion->u.hash.mPropertyHash;
+    Assertion *nextRef;
+    while(first) {
+        nextRef = first->mNext;
+        nsIRDFResource *prop = first->u.as.mProperty;
+
+        PLDHashEntryHdr* hdr = table->Search(prop);
+        Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+        if (val) {
+            first->mNext = val->mNext;
+            val->mNext = first;
+        }
+        else {
+            PLDHashEntryHdr* hdr = table->Add(prop, mozilla::fallible);
+            if (hdr) {
+                Entry* entry = static_cast<Entry*>(hdr);
+                entry->mNode = prop;
+                entry->mAssertions = first;
+                first->mNext = nullptr;
+            }
+        }
+        first = nextRef;
+    }
+    return(NS_OK);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFPropagatableDataSource methods
+NS_IMETHODIMP
+InMemoryDataSource::GetPropagateChanges(bool* aPropagateChanges)
+{
+    *aPropagateChanges = mPropagateChanges;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::SetPropagateChanges(bool aPropagateChanges)
+{
+    mPropagateChanges = aPropagateChanges;
+    return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFPurgeableDataSource methods
+
+NS_IMETHODIMP
+InMemoryDataSource::Mark(nsIRDFResource* aSource,
+                         nsIRDFResource* aProperty,
+                         nsIRDFNode* aTarget,
+                         bool aTruthValue,
+                         bool* aDidMark)
+{
+    NS_PRECONDITION(aSource != nullptr, "null ptr");
+    if (! aSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aTarget != nullptr, "null ptr");
+    if (! aTarget)
+        return NS_ERROR_NULL_POINTER;
+
+    Assertion *as = GetForwardArcs(aSource);
+    if (as && as->mHashEntry) {
+        PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(aProperty);
+        Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+        while (val) {
+            if ((val->u.as.mTarget == aTarget) &&
+                (aTruthValue == (val->u.as.mTruthValue))) {
+
+                // found it! so mark it.
+                as->Mark();
+                *aDidMark = true;
+
+                LogOperation("MARK", aSource, aProperty, aTarget, aTruthValue);
+
+                return NS_OK;
+            }
+            val = val->mNext;
+        }
+    }
+    else for (; as != nullptr; as = as->mNext) {
+        // check target first as its most unique
+        if (aTarget != as->u.as.mTarget)
+            continue;
+
+        if (aProperty != as->u.as.mProperty)
+            continue;
+
+        if (aTruthValue != (as->u.as.mTruthValue))
+            continue;
+
+        // found it! so mark it.
+        as->Mark();
+        *aDidMark = true;
+
+        LogOperation("MARK", aSource, aProperty, aTarget, aTruthValue);
+
+        return NS_OK;
+    }
+
+    // If we get here, we couldn't find the assertion
+    *aDidMark = false;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::Sweep()
+{
+    SweepInfo info = { nullptr, &mReverseArcs };
+
+    // Remove all the assertions, but don't notify anyone.
+    SweepForwardArcsEntries(&mForwardArcs, &info);
+
+    // Now do the notification.
+    Assertion* as = info.mUnassertList;
+    while (as) {
+        LogOperation("SWEEP", as->mSource, as->u.as.mProperty, as->u.as.mTarget, as->u.as.mTruthValue);
+        if (!(as->mHashEntry))
+        {
+            for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+                nsIRDFObserver* obs = mObservers[i];
+                // XXXbz other loops over mObservers null-check |obs| here!
+                obs->OnUnassert(this, as->mSource, as->u.as.mProperty, as->u.as.mTarget);
+                // XXX ignore return value?
+            }
+        }
+
+        Assertion* doomed = as;
+        as = as->mNext;
+
+        // Unlink, and release the datasource's reference
+        doomed->mNext = doomed->u.as.mInvNext = nullptr;
+        doomed->Release();
+    }
+
+    return NS_OK;
+}
+
+
+void
+InMemoryDataSource::SweepForwardArcsEntries(PLDHashTable* aTable,
+                                            SweepInfo* aInfo)
+{
+    for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
+        auto entry = static_cast<Entry*>(iter.Get());
+
+        Assertion* as = entry->mAssertions;
+        if (as && (as->mHashEntry)) {
+            // Stuff in sub-hashes must be swept recursively (max depth: 1)
+            SweepForwardArcsEntries(as->u.hash.mPropertyHash, aInfo);
+
+            // If the sub-hash is now empty, clean it up.
+            if (!as->u.hash.mPropertyHash->EntryCount()) {
+                as->Release();
+                iter.Remove();
+            }
+            continue;
+        }
+
+        Assertion* prev = nullptr;
+        while (as) {
+            if (as->IsMarked()) {
+                prev = as;
+                as->Unmark();
+                as = as->mNext;
+            }
+            else {
+                // remove from the list of assertions in the datasource
+                Assertion* next = as->mNext;
+                if (prev) {
+                    prev->mNext = next;
+                }
+                else {
+                    // it's the first one. update the hashtable entry.
+                    entry->mAssertions = next;
+                }
+
+                // remove from the reverse arcs
+                PLDHashEntryHdr* hdr =
+                    aInfo->mReverseArcs->Search(as->u.as.mTarget);
+                NS_ASSERTION(hdr, "no assertion in reverse arcs");
+
+                Entry* rentry = static_cast<Entry*>(hdr);
+                Assertion* ras = rentry->mAssertions;
+                Assertion* rprev = nullptr;
+                while (ras) {
+                    if (ras == as) {
+                        if (rprev) {
+                            rprev->u.as.mInvNext = ras->u.as.mInvNext;
+                        }
+                        else {
+                            // it's the first one. update the hashtable entry.
+                            rentry->mAssertions = ras->u.as.mInvNext;
+                        }
+                        as->u.as.mInvNext = nullptr; // for my sanity.
+                        break;
+                    }
+                    rprev = ras;
+                    ras = ras->u.as.mInvNext;
+                }
+
+                // Wow, it was the _only_ one. Unhash it.
+                if (! rentry->mAssertions) {
+                    aInfo->mReverseArcs->RawRemove(hdr);
+                }
+
+                // add to the list of assertions to unassert
+                as->mNext = aInfo->mUnassertList;
+                aInfo->mUnassertList = as;
+
+                // Advance to the next assertion
+                as = next;
+            }
+        }
+
+        // if no more assertions exist for this resource, then unhash it.
+        if (! entry->mAssertions) {
+            iter.Remove();
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////
+// rdfIDataSource methods
+
+NS_IMETHODIMP
+InMemoryDataSource::VisitAllSubjects(rdfITripleVisitor *aVisitor)
+{
+    // Lock datasource against writes
+    ++mReadCount;
+
+    // Enumerate all of our entries.
+    nsresult rv = NS_OK;
+    for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+        auto entry = static_cast<Entry*>(iter.Get());
+        nsresult rv2;
+        nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv2);
+        if (NS_FAILED(rv2)) {
+            NS_WARNING("QI to nsIRDFNode failed");
+            continue;
+        }
+        rv = aVisitor->Visit(subject, nullptr, nullptr, true);
+        if (NS_FAILED(rv) || rv == NS_RDF_STOP_VISIT) {
+            break;
+        }
+    }
+
+    // Unlock datasource
+    --mReadCount;
+
+    return rv;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::VisitAllTriples(rdfITripleVisitor *aVisitor)
+{
+    // Lock datasource against writes
+    ++mReadCount;
+
+    // Enumerate all of our entries.
+    nsresult rv = NS_OK;
+    for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+        auto entry = static_cast<Entry*>(iter.Get());
+
+        nsresult rv2;
+        nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv2);
+        if (NS_FAILED(rv2)) {
+            NS_WARNING("QI to nsIRDFNode failed");
+
+        } else if (entry->mAssertions->mHashEntry) {
+            for (auto iter = entry->mAssertions->u.hash.mPropertyHash->Iter();
+                 !iter.Done();
+                 iter.Next()) {
+                auto entry = static_cast<Entry*>(iter.Get());
+                Assertion* assertion = entry->mAssertions;
+                while (assertion) {
+                    NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
+                    rv = aVisitor->Visit(subject, assertion->u.as.mProperty,
+                                                  assertion->u.as.mTarget,
+                                                  assertion->u.as.mTruthValue);
+                    if (NS_FAILED(rv)) {
+                        goto end;
+                    }
+                    if (rv == NS_RDF_STOP_VISIT) {
+                        goto inner_end;
+                    }
+                    assertion = assertion->mNext;
+                }
+            }
+
+        } else {
+            Assertion* assertion = entry->mAssertions;
+            while (assertion) {
+                NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
+                rv = aVisitor->Visit(subject, assertion->u.as.mProperty,
+                                              assertion->u.as.mTarget,
+                                              assertion->u.as.mTruthValue);
+                if (NS_FAILED(rv) || rv == NS_RDF_STOP_VISIT) {
+                    goto end;
+                }
+                assertion = assertion->mNext;
+            }
+        }
+
+      inner_end:
+        (void) 0;
+    }
+
+  end:
+    // Unlock datasource
+    --mReadCount;
+
+    return rv;
+}
+
+////////////////////////////////////////////////////////////////////////
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsNameSpaceMap.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsNameSpaceMap.h"
+#include "nsReadableUtils.h"
+
+nsNameSpaceMap::nsNameSpaceMap()
+    : mEntries(nullptr)
+{
+    MOZ_COUNT_CTOR(nsNameSpaceMap);
+}
+
+nsNameSpaceMap::~nsNameSpaceMap()
+{
+    MOZ_COUNT_DTOR(nsNameSpaceMap);
+
+    while (mEntries) {
+        Entry* doomed = mEntries;
+        mEntries = mEntries->mNext;
+        delete doomed;
+    }
+}
+
+nsresult
+nsNameSpaceMap::Put(const nsAString& aURI, nsAtom* aPrefix)
+{
+    nsCString uriUTF8;
+    AppendUTF16toUTF8(aURI, uriUTF8);
+    return Put(uriUTF8, aPrefix);
+}
+
+nsresult
+nsNameSpaceMap::Put(const nsACString& aURI, nsAtom* aPrefix)
+{
+    Entry* entry;
+
+    // Make sure we're not adding a duplicate
+    for (entry = mEntries; entry != nullptr; entry = entry->mNext) {
+        if (entry->mURI == aURI || entry->mPrefix == aPrefix)
+            return NS_ERROR_FAILURE;
+    }
+
+    entry = new Entry(aURI, aPrefix);
+    if (! entry)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    entry->mNext = mEntries;
+    mEntries = entry;
+    return NS_OK;
+}
+
+nsNameSpaceMap::const_iterator
+nsNameSpaceMap::GetNameSpaceOf(const nsACString& aURI) const
+{
+    for (Entry* entry = mEntries; entry != nullptr; entry = entry->mNext) {
+        if (StringBeginsWith(aURI, entry->mURI))
+            return const_iterator(entry);
+    }
+
+    return last();
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsNameSpaceMap.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsNameSpaceMap_h__
+#define nsNameSpaceMap_h__
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsAtom.h"
+
+class nsNameSpaceMap
+{
+public:
+    class Entry {
+    public:
+        Entry(const nsACString& aURI, nsAtom* aPrefix)
+            : mURI(aURI), mPrefix(aPrefix), mNext(nullptr) {
+            MOZ_COUNT_CTOR(nsNameSpaceMap::Entry); }
+
+        ~Entry() { MOZ_COUNT_DTOR(nsNameSpaceMap::Entry); }
+
+        nsCString mURI;
+        RefPtr<nsAtom> mPrefix;
+
+        Entry* mNext;
+    };
+
+    nsNameSpaceMap();
+    ~nsNameSpaceMap();
+
+    nsresult
+    Put(const nsAString& aURI, nsAtom* aPrefix);
+
+    nsresult
+    Put(const nsACString& aURI, nsAtom* aPrefix);
+
+    class const_iterator {
+    protected:
+        friend class nsNameSpaceMap;
+
+        explicit const_iterator(const Entry* aCurrent)
+            : mCurrent(aCurrent) {}
+
+        const Entry* mCurrent;
+
+    public:
+        const_iterator()
+            : mCurrent(nullptr) {}
+
+        const_iterator(const const_iterator& iter)
+            : mCurrent(iter.mCurrent) {}
+
+        const_iterator&
+        operator=(const const_iterator& iter) {
+            mCurrent = iter.mCurrent;
+            return *this; }
+
+        const_iterator&
+        operator++() {
+            mCurrent = mCurrent->mNext;
+            return *this; }
+
+        const_iterator
+        operator++(int) {
+            const_iterator tmp(*this);
+            mCurrent = mCurrent->mNext;
+            return tmp; }
+
+        const Entry* operator->() const { return mCurrent; }
+
+        const Entry& operator*() const { return *mCurrent; }
+
+        bool
+        operator==(const const_iterator& iter) const {
+            return mCurrent == iter.mCurrent; }
+
+        bool
+        operator!=(const const_iterator& iter) const {
+            return ! iter.operator==(*this); }
+    };
+
+    const_iterator first() const {
+        return const_iterator(mEntries); }
+
+    const_iterator last() const {
+        return const_iterator(nullptr); }
+
+    const_iterator GetNameSpaceOf(const nsACString& aURI) const;
+
+protected:
+    Entry* mEntries;
+};
+
+
+#endif // nsNameSpaceMap_h__
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFBaseDataSources.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  This header file just contains prototypes for the factory methods
+  for "builtin" data sources that are included in rdf.dll.
+
+  Each of these data sources is exposed to the external world via its
+  CID in ../include/nsRDFCID.h.
+
+*/
+
+#ifndef nsBaseDataSources_h__
+#define nsBaseDataSources_h__
+
+#include "nsError.h"
+class nsIRDFDataSource;
+
+// in nsInMemoryDataSource.cpp
+nsresult
+NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult);
+
+// in nsRDFXMLDataSource.cpp
+extern nsresult
+NS_NewRDFXMLDataSource(nsIRDFDataSource** aResult);
+
+#endif // nsBaseDataSources_h__
+
+
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFContainer.cpp
@@ -0,0 +1,724 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  Implementation for the RDF container.
+
+  Notes
+  -----
+
+  1. RDF containers are one-indexed. This means that a lot of the loops
+     that you'd normally think you'd write like this:
+
+       for (i = 0; i < count; ++i) {}
+
+     You've gotta write like this:
+
+       for (i = 1; i <= count; ++i) {}
+
+     "Sure, right, yeah, of course.", you say. Well maybe I'm just
+     thick, but it's easy to slip up.
+
+  2. The RDF:nextVal property on the container is an
+     implementation-level hack that is used to quickly compute the
+     next value for appending to the container. It will no doubt
+     become royally screwed up in the case of aggregation.
+
+  3. The RDF:nextVal property is also used to retrieve the count of
+     elements in the container.
+
+ */
+
+
+#include "nsCOMPtr.h"
+#include "nsIRDFContainer.h"
+#include "nsIRDFContainerUtils.h"
+#include "nsIRDFInMemoryDataSource.h"
+#include "nsIRDFPropagatableDataSource.h"
+#include "nsIRDFService.h"
+#include "nsIServiceManager.h"
+#include "nsRDFCID.h"
+#include "nsString.h"
+#include "rdf.h"
+
+#define RDF_SEQ_LIST_LIMIT   8
+
+class RDFContainerImpl : public nsIRDFContainer
+{
+public:
+
+    // nsISupports interface
+    NS_DECL_ISUPPORTS
+
+    // nsIRDFContainer interface
+    NS_DECL_NSIRDFCONTAINER
+
+private:
+    friend nsresult NS_NewRDFContainer(nsIRDFContainer** aResult);
+
+    RDFContainerImpl();
+    virtual ~RDFContainerImpl();
+
+    nsresult Init();
+
+    nsresult Renumber(int32_t aStartIndex, int32_t aIncrement);
+    nsresult SetNextValue(int32_t aIndex);
+    nsresult GetNextValue(nsIRDFResource** aResult);
+
+    nsIRDFDataSource* mDataSource;
+    nsIRDFResource*   mContainer;
+
+    // pseudo constants
+    static int32_t gRefCnt;
+    static nsIRDFService*        gRDFService;
+    static nsIRDFContainerUtils* gRDFContainerUtils;
+    static nsIRDFResource*       kRDF_nextVal;
+};
+
+
+int32_t               RDFContainerImpl::gRefCnt = 0;
+nsIRDFService*        RDFContainerImpl::gRDFService;
+nsIRDFContainerUtils* RDFContainerImpl::gRDFContainerUtils;
+nsIRDFResource*       RDFContainerImpl::kRDF_nextVal;
+
+////////////////////////////////////////////////////////////////////////
+// nsISupports interface
+
+NS_IMPL_ISUPPORTS(RDFContainerImpl, nsIRDFContainer)
+
+
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFContainer interface
+
+NS_IMETHODIMP
+RDFContainerImpl::GetDataSource(nsIRDFDataSource** _retval)
+{
+    *_retval = mDataSource;
+    NS_IF_ADDREF(*_retval);
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::GetResource(nsIRDFResource** _retval)
+{
+    *_retval = mContainer;
+    NS_IF_ADDREF(*_retval);
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::Init(nsIRDFDataSource *aDataSource, nsIRDFResource *aContainer)
+{
+    NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+    if (! aDataSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aContainer != nullptr, "null ptr");
+    if (! aContainer)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+    bool isContainer;
+    rv = gRDFContainerUtils->IsContainer(aDataSource, aContainer, &isContainer);
+    if (NS_FAILED(rv)) return rv;
+
+    // ``throw'' if we can't create a container on the specified
+    // datasource/resource combination.
+    if (! isContainer)
+        return NS_ERROR_FAILURE;
+
+    NS_IF_RELEASE(mDataSource);
+    mDataSource = aDataSource;
+    NS_ADDREF(mDataSource);
+
+    NS_IF_RELEASE(mContainer);
+    mContainer = aContainer;
+    NS_ADDREF(mContainer);
+
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::GetCount(int32_t *aCount)
+{
+    if (!mDataSource || !mContainer)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    nsresult rv;
+
+    // Get the next value, which hangs off of the bag via the
+    // RDF:nextVal property. This is the _next value_ that will get
+    // assigned in a one-indexed array. So, it's actually _one more_
+    // than the actual count of elements in the container.
+    //
+    // XXX To handle aggregation, this should probably be a
+    // GetTargets() that enumerates all of the values and picks the
+    // largest one.
+    nsCOMPtr<nsIRDFNode> nextValNode;
+    rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode));
+    if (NS_FAILED(rv)) return rv;
+
+    if (rv == NS_RDF_NO_VALUE)
+        return NS_ERROR_UNEXPECTED;
+
+    nsCOMPtr<nsIRDFLiteral> nextValLiteral;
+    rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
+    if (NS_FAILED(rv)) return rv;
+
+    const char16_t *s;
+    rv = nextValLiteral->GetValueConst( &s );
+    if (NS_FAILED(rv)) return rv;
+
+    nsAutoString nextValStr(s);
+
+    int32_t nextVal;
+    nsresult err;
+    nextVal = nextValStr.ToInteger(&err);
+    if (NS_FAILED(err))
+        return NS_ERROR_UNEXPECTED;
+
+    *aCount = nextVal - 1;
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::GetElements(nsISimpleEnumerator **_retval)
+{
+    if (!mDataSource || !mContainer)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    return NS_NewContainerEnumerator(mDataSource, mContainer, _retval);
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::AppendElement(nsIRDFNode *aElement)
+{
+    if (!mDataSource || !mContainer)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    NS_PRECONDITION(aElement != nullptr, "null ptr");
+    if (! aElement)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    nsCOMPtr<nsIRDFResource> nextVal;
+    rv = GetNextValue(getter_AddRefs(nextVal));
+    if (NS_FAILED(rv)) return rv;
+
+    rv = mDataSource->Assert(mContainer, nextVal, aElement, true);
+    if (NS_FAILED(rv)) return rv;
+
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::RemoveElement(nsIRDFNode *aElement, bool aRenumber)
+{
+    if (!mDataSource || !mContainer)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    NS_PRECONDITION(aElement != nullptr, "null ptr");
+    if (! aElement)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    int32_t idx;
+    rv = IndexOf(aElement, &idx);
+    if (NS_FAILED(rv)) return rv;
+
+    if (idx < 0)
+        return NS_OK;
+
+    // Remove the element.
+    nsCOMPtr<nsIRDFResource> ordinal;
+    rv = gRDFContainerUtils->IndexToOrdinalResource(idx,
+                                                    getter_AddRefs(ordinal));
+    if (NS_FAILED(rv)) return rv;
+
+    rv = mDataSource->Unassert(mContainer, ordinal, aElement);
+    if (NS_FAILED(rv)) return rv;
+
+    if (aRenumber) {
+        // Now slide the rest of the collection backwards to fill in
+        // the gap. This will have the side effect of completely
+        // renumber the container from index to the end.
+        rv = Renumber(idx + 1, -1);
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerImpl::InsertElementAt(nsIRDFNode *aElement, int32_t aIndex, bool aRenumber)
+{
+    if (!mDataSource || !mContainer)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    NS_PRECONDITION(aElement != nullptr, "null ptr");
+    if (! aElement)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aIndex >= 1, "illegal value");
+    if (aIndex < 1)
+        return NS_ERROR_ILLEGAL_VALUE;
+
+    nsresult rv;
+
+    int32_t count;
+    rv = GetCount(&count);
+    if (NS_FAILED(rv)) return rv;
+
+    NS_ASSERTION(aIndex <= count + 1, "illegal value");
+    if (aIndex > count + 1)
+        return NS_ERROR_ILLEGAL_VALUE;
+
+    if (aRenumber) {
+        // Make a hole for the element. This will have the side effect of
+        // completely renumbering the container from 'aIndex' to 'count',
+        // and will spew assertions.
+        rv = Renumber(aIndex, +1);
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    nsCOMPtr<nsIRDFResource> ordinal;
+    rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
+    if (NS_FAILED(rv)) return rv;
+
+    rv = mDataSource->Assert(mContainer, ordinal, aElement, true);
+    if (NS_FAILED(rv)) return rv;
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContainerImpl::RemoveElementAt(int32_t aIndex, bool aRenumber, nsIRDFNode** _retval)
+{
+    if (!mDataSource || !mContainer)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    *_retval = nullptr;
+
+    if (aIndex< 1)
+        return NS_ERROR_ILLEGAL_VALUE;
+
+    nsresult rv;
+
+    int32_t count;
+    rv = GetCount(&count);
+    if (NS_FAILED(rv)) return rv;
+
+    if (aIndex > count)
+        return NS_ERROR_ILLEGAL_VALUE;
+
+    nsCOMPtr<nsIRDFResource> ordinal;
+    rv = gRDFContainerUtils->IndexToOrdinalResource(aIndex, getter_AddRefs(ordinal));
+    if (NS_FAILED(rv)) return rv;
+
+    nsCOMPtr<nsIRDFNode> old;
+    rv = mDataSource->GetTarget(mContainer, ordinal, true, getter_AddRefs(old));
+    if (NS_FAILED(rv)) return rv;
+
+    if (rv == NS_OK) {
+        rv = mDataSource->Unassert(mContainer, ordinal, old);
+        if (NS_FAILED(rv)) return rv;
+
+        if (aRenumber) {
+            // Now slide the rest of the collection backwards to fill in
+            // the gap. This will have the side effect of completely
+            // renumber the container from index to the end.
+            rv = Renumber(aIndex + 1, -1);
+            if (NS_FAILED(rv)) return rv;
+        }
+    }
+
+    old.swap(*_retval);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContainerImpl::IndexOf(nsIRDFNode *aElement, int32_t *aIndex)
+{
+    if (!mDataSource || !mContainer)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    return gRDFContainerUtils->IndexOf(mDataSource, mContainer,
+                                       aElement, aIndex);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+RDFContainerImpl::RDFContainerImpl()
+    : mDataSource(nullptr), mContainer(nullptr)
+{
+}
+
+
+nsresult
+RDFContainerImpl::Init()
+{
+    if (gRefCnt++ == 0) {
+        nsresult rv;
+
+        NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+        rv = CallGetService(kRDFServiceCID, &gRDFService);
+        if (NS_FAILED(rv)) {
+            NS_ERROR("unable to get RDF service");
+            return rv;
+        }
+
+        rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
+                                      &kRDF_nextVal);
+        if (NS_FAILED(rv)) return rv;
+
+        NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
+        rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
+        if (NS_FAILED(rv)) {
+            NS_ERROR("unable to get RDF container utils service");
+            return rv;
+        }
+    }
+
+    return NS_OK;
+}
+
+
+RDFContainerImpl::~RDFContainerImpl()
+{
+#ifdef DEBUG_REFS
+    --gInstanceCount;
+    fprintf(stdout, "%d - RDF: RDFContainerImpl\n", gInstanceCount);
+#endif
+
+    NS_IF_RELEASE(mContainer);
+    NS_IF_RELEASE(mDataSource);
+
+    if (--gRefCnt == 0) {
+        NS_IF_RELEASE(gRDFContainerUtils);
+        NS_IF_RELEASE(gRDFService);
+        NS_IF_RELEASE(kRDF_nextVal);
+    }
+}
+
+
+nsresult
+NS_NewRDFContainer(nsIRDFContainer** aResult)
+{
+    RDFContainerImpl* result = new RDFContainerImpl();
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    nsresult rv;
+    rv = result->Init();
+    if (NS_FAILED(rv)) {
+        delete result;
+        return rv;
+    }
+
+    NS_ADDREF(result);
+    *aResult = result;
+    return NS_OK;
+}
+
+
+nsresult
+NS_NewRDFContainer(nsIRDFDataSource* aDataSource,
+                   nsIRDFResource* aResource,
+                   nsIRDFContainer** aResult)
+{
+    nsresult rv;
+    rv = NS_NewRDFContainer(aResult);
+    if (NS_FAILED(rv)) return rv;
+
+    rv = (*aResult)->Init(aDataSource, aResource);
+    if (NS_FAILED(rv)) {
+        NS_RELEASE(*aResult);
+    }
+    return rv;
+}
+
+
+nsresult
+RDFContainerImpl::Renumber(int32_t aStartIndex, int32_t aIncrement)
+{
+    if (!mDataSource || !mContainer)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    // Renumber the elements in the container starting with
+    // aStartIndex, updating each element's index by aIncrement. For
+    // example,
+    //
+    //   (1:a 2:b 3:c)
+    //   Renumber(2, +1);
+    //   (1:a 3:b 4:c)
+    //   Renumber(3, -1);
+    //   (1:a 2:b 3:c)
+    //
+    nsresult rv;
+
+    if (! aIncrement)
+        return NS_OK;
+
+    int32_t count;
+    rv = GetCount(&count);
+    if (NS_FAILED(rv)) return rv;
+
+    if (aIncrement > 0) {
+        // Update the container's nextVal to reflect the
+        // renumbering. We do this now if aIncrement > 0 because we'll
+        // want to be able to acknowledge that new elements are in the
+        // container.
+        rv = SetNextValue(count + aIncrement + 1);
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    int32_t i;
+    if (aIncrement < 0) {
+        i = aStartIndex;
+    }
+    else {
+        i = count; // we're one-indexed.
+    }
+
+    // Note: once we disable notifications, don't exit this method until
+    // enabling notifications
+    nsCOMPtr<nsIRDFPropagatableDataSource> propagatable =
+        do_QueryInterface(mDataSource);
+    if (propagatable) {
+        propagatable->SetPropagateChanges(false);
+    }
+
+    bool    err = false;
+    while (!err && ((aIncrement < 0) ? (i <= count) : (i >= aStartIndex)))
+    {
+        nsCOMPtr<nsIRDFResource> oldOrdinal;
+        rv = gRDFContainerUtils->IndexToOrdinalResource(i, getter_AddRefs(oldOrdinal));
+        if (NS_FAILED(rv))
+        {
+            err = true;
+            continue;
+        }
+
+        nsCOMPtr<nsIRDFResource> newOrdinal;
+        rv = gRDFContainerUtils->IndexToOrdinalResource(i + aIncrement, getter_AddRefs(newOrdinal));
+        if (NS_FAILED(rv))
+        {
+            err = true;
+            continue;
+        }
+
+        // Because of aggregation, we need to be paranoid about the
+        // possibility that >1 element may be present per ordinal. If
+        // there _is_ in fact more than one element, they'll all get
+        // assigned to the same new ordinal; i.e., we don't make any
+        // attempt to "clean up" the duplicate numbering. (Doing so
+        // would require two passes.)
+        nsCOMPtr<nsISimpleEnumerator> targets;
+        rv = mDataSource->GetTargets(mContainer, oldOrdinal, true, getter_AddRefs(targets));
+        if (NS_FAILED(rv))
+        {
+            err = true;
+            continue;
+        }
+
+        while (1) {
+            bool hasMore;
+            rv = targets->HasMoreElements(&hasMore);
+            if (NS_FAILED(rv))
+            {
+                err = true;
+                break;
+            }
+
+            if (! hasMore)
+                break;
+
+            nsCOMPtr<nsISupports> isupports;
+            rv = targets->GetNext(getter_AddRefs(isupports));
+            if (NS_FAILED(rv))
+            {
+                err = true;
+                break;
+            }
+
+            nsCOMPtr<nsIRDFNode> element( do_QueryInterface(isupports) );
+            NS_ASSERTION(element != nullptr, "something funky in the enumerator");
+            if (! element)
+            {
+                err = true;
+                rv = NS_ERROR_UNEXPECTED;
+                break;
+            }
+
+            rv = mDataSource->Unassert(mContainer, oldOrdinal, element);
+            if (NS_FAILED(rv))
+            {
+                err = true;
+                break;
+            }
+
+            rv = mDataSource->Assert(mContainer, newOrdinal, element, true);
+            if (NS_FAILED(rv))
+            {
+                err = true;
+                break;
+            }
+        }
+
+        i -= aIncrement;
+    }
+
+    if (!err && (aIncrement < 0))
+    {
+        // Update the container's nextVal to reflect the
+        // renumbering. We do this now if aIncrement < 0 because, up
+        // until this point, we'll want people to be able to find
+        // things that are still "at the end".
+        rv = SetNextValue(count + aIncrement + 1);
+        if (NS_FAILED(rv))
+        {
+            err = true;
+        }
+    }
+
+    // Note: MUST enable notifications before exiting this method
+    if (propagatable) {
+        propagatable->SetPropagateChanges(true);
+    }
+
+    if (err) return(rv);
+
+    return NS_OK;
+}
+
+
+
+nsresult
+RDFContainerImpl::SetNextValue(int32_t aIndex)
+{
+    if (!mDataSource || !mContainer)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    nsresult rv;
+
+    // Remove the current value of nextVal, if there is one.
+    nsCOMPtr<nsIRDFNode> nextValNode;
+    if (NS_SUCCEEDED(rv = mDataSource->GetTarget(mContainer,
+                                                 kRDF_nextVal,
+                                                 true,
+                                                 getter_AddRefs(nextValNode)))) {
+        if (NS_FAILED(rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValNode))) {
+            NS_ERROR("unable to update nextVal");
+            return rv;
+        }
+    }
+
+    nsAutoString s;
+    s.AppendInt(aIndex, 10);
+
+    nsCOMPtr<nsIRDFLiteral> nextVal;
+    if (NS_FAILED(rv = gRDFService->GetLiteral(s.get(), getter_AddRefs(nextVal)))) {
+        NS_ERROR("unable to get nextVal literal");
+        return rv;
+    }
+
+    rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextVal, true);
+    if (rv != NS_RDF_ASSERTION_ACCEPTED) {
+        NS_ERROR("unable to update nextVal");
+        return NS_ERROR_FAILURE;
+    }
+
+    return NS_OK;
+}
+
+
+nsresult
+RDFContainerImpl::GetNextValue(nsIRDFResource** aResult)
+{
+    if (!mDataSource || !mContainer)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    nsresult rv;
+
+    // Get the next value, which hangs off of the bag via the
+    // RDF:nextVal property.
+    nsCOMPtr<nsIRDFNode> nextValNode;
+    rv = mDataSource->GetTarget(mContainer, kRDF_nextVal, true, getter_AddRefs(nextValNode));
+    if (NS_FAILED(rv)) return rv;
+
+    if (rv == NS_RDF_NO_VALUE)
+        return NS_ERROR_UNEXPECTED;
+
+    nsCOMPtr<nsIRDFLiteral> nextValLiteral;
+    rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
+    if (NS_FAILED(rv)) return rv;
+
+    const char16_t* s;
+    rv = nextValLiteral->GetValueConst(&s);
+    if (NS_FAILED(rv)) return rv;
+
+    int32_t nextVal = 0;
+    {
+        for (const char16_t* p = s; *p != 0; ++p) {
+            NS_ASSERTION(*p >= '0' && *p <= '9', "not a digit");
+            if (*p < '0' || *p > '9')
+                break;
+
+            nextVal *= 10;
+            nextVal += *p - '0';
+        }
+    }
+
+    static const char kRDFNameSpaceURI[] = RDF_NAMESPACE_URI;
+    nsAutoCStringN<sizeof(kRDFNameSpaceURI) + 16> nextValStr;
+    nextValStr = kRDFNameSpaceURI;
+    nextValStr.Append('_');
+    nextValStr.AppendInt(nextVal, 10);
+
+    rv = gRDFService->GetResource(nextValStr, aResult);
+    if (NS_FAILED(rv)) return rv;
+
+    // Now increment the RDF:nextVal property.
+    rv = mDataSource->Unassert(mContainer, kRDF_nextVal, nextValLiteral);
+    if (NS_FAILED(rv)) return rv;
+
+    ++nextVal;
+    nextValStr.Truncate();
+    nextValStr.AppendInt(nextVal, 10);
+
+    rv = gRDFService->GetLiteral(NS_ConvertASCIItoUTF16(nextValStr).get(), getter_AddRefs(nextValLiteral));
+    if (NS_FAILED(rv)) return rv;
+
+    rv = mDataSource->Assert(mContainer, kRDF_nextVal, nextValLiteral, true);
+    if (NS_FAILED(rv)) return rv;
+
+    if (RDF_SEQ_LIST_LIMIT == nextVal)
+    {
+        // focal point for RDF container mutation;
+        // basically, provide a hint to allow for fast access
+        nsCOMPtr<nsIRDFInMemoryDataSource> inMem = do_QueryInterface(mDataSource);
+        if (inMem)
+        {
+            // ignore error; failure just means slower access
+            (void)inMem->EnsureFastContainment(mContainer);
+        }
+    }
+
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFContainerUtils.cpp
@@ -0,0 +1,513 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  Implementation for the RDF container utils.
+
+ */
+
+
+#include "nsCOMPtr.h"
+#include "nsIServiceManager.h"
+#include "nsIRDFContainer.h"
+#include "nsIRDFContainerUtils.h"
+#include "nsIRDFService.h"
+#include "nsRDFCID.h"
+#include "nsString.h"
+#include "plstr.h"
+#include "rdf.h"
+#include "rdfutil.h"
+
+class RDFContainerUtilsImpl : public nsIRDFContainerUtils
+{
+public:
+    // nsISupports interface
+    NS_DECL_ISUPPORTS
+
+    // nsIRDFContainerUtils interface
+    NS_DECL_NSIRDFCONTAINERUTILS
+
+private:
+    friend nsresult NS_NewRDFContainerUtils(nsIRDFContainerUtils** aResult);
+
+    RDFContainerUtilsImpl();
+    virtual ~RDFContainerUtilsImpl();
+
+    nsresult MakeContainer(nsIRDFDataSource* aDataSource,
+                           nsIRDFResource* aResource,
+                           nsIRDFResource* aType,
+                           nsIRDFContainer** aResult);
+
+    bool IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType);
+
+    // pseudo constants
+    static int32_t gRefCnt;
+    static nsIRDFService* gRDFService;
+    static nsIRDFResource* kRDF_instanceOf;
+    static nsIRDFResource* kRDF_nextVal;
+    static nsIRDFResource* kRDF_Bag;
+    static nsIRDFResource* kRDF_Seq;
+    static nsIRDFResource* kRDF_Alt;
+    static nsIRDFLiteral* kOne;
+    static const char kRDFNameSpaceURI[];
+};
+
+int32_t         RDFContainerUtilsImpl::gRefCnt = 0;
+nsIRDFService*  RDFContainerUtilsImpl::gRDFService;
+nsIRDFResource* RDFContainerUtilsImpl::kRDF_instanceOf;
+nsIRDFResource* RDFContainerUtilsImpl::kRDF_nextVal;
+nsIRDFResource* RDFContainerUtilsImpl::kRDF_Bag;
+nsIRDFResource* RDFContainerUtilsImpl::kRDF_Seq;
+nsIRDFResource* RDFContainerUtilsImpl::kRDF_Alt;
+nsIRDFLiteral*  RDFContainerUtilsImpl::kOne;
+const char      RDFContainerUtilsImpl::kRDFNameSpaceURI[] = RDF_NAMESPACE_URI;
+
+////////////////////////////////////////////////////////////////////////
+// nsISupports interface
+
+NS_IMPL_ISUPPORTS(RDFContainerUtilsImpl, nsIRDFContainerUtils)
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFContainerUtils interface
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsOrdinalProperty(nsIRDFResource *aProperty, bool *_retval)
+{
+    NS_PRECONDITION(aProperty != nullptr, "null ptr");
+    if (! aProperty)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    const char	*propertyStr;
+    rv = aProperty->GetValueConst( &propertyStr );
+    if (NS_FAILED(rv)) return rv;
+
+    if (PL_strncmp(propertyStr, kRDFNameSpaceURI, sizeof(kRDFNameSpaceURI) - 1) != 0) {
+        *_retval = false;
+        return NS_OK;
+    }
+
+    const char* s = propertyStr;
+    s += sizeof(kRDFNameSpaceURI) - 1;
+    if (*s != '_') {
+        *_retval = false;
+        return NS_OK;
+    }
+
+    ++s;
+    while (*s) {
+        if (*s < '0' || *s > '9') {
+            *_retval = false;
+            return NS_OK;
+        }
+
+        ++s;
+    }
+
+    *_retval = true;
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IndexToOrdinalResource(int32_t aIndex, nsIRDFResource **aOrdinal)
+{
+    NS_PRECONDITION(aIndex > 0, "illegal value");
+    if (aIndex <= 0)
+        return NS_ERROR_ILLEGAL_VALUE;
+
+    nsAutoCString uri(kRDFNameSpaceURI);
+    uri.Append('_');
+    uri.AppendInt(aIndex);
+
+    nsresult rv = gRDFService->GetResource(uri, aOrdinal);
+    NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get ordinal resource");
+    if (NS_FAILED(rv)) return rv;
+
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::OrdinalResourceToIndex(nsIRDFResource *aOrdinal, int32_t *aIndex)
+{
+    NS_PRECONDITION(aOrdinal != nullptr, "null ptr");
+    if (! aOrdinal)
+        return NS_ERROR_NULL_POINTER;
+
+    const char	*ordinalStr;
+    if (NS_FAILED(aOrdinal->GetValueConst( &ordinalStr )))
+        return NS_ERROR_FAILURE;
+
+    const char* s = ordinalStr;
+    if (PL_strncmp(s, kRDFNameSpaceURI, sizeof(kRDFNameSpaceURI) - 1) != 0) {
+        NS_ERROR("not an ordinal");
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    s += sizeof(kRDFNameSpaceURI) - 1;
+    if (*s != '_') {
+        NS_ERROR("not an ordinal");
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    int32_t idx = 0;
+
+    ++s;
+    while (*s) {
+        if (*s < '0' || *s > '9') {
+            NS_ERROR("not an ordinal");
+            return NS_ERROR_UNEXPECTED;
+        }
+
+        idx *= 10;
+        idx += (*s - '0');
+
+        ++s;
+    }
+
+    *aIndex = idx;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsContainer(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval)
+{
+    NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+    if (! aDataSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResource != nullptr, "null ptr");
+    if (! aResource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(_retval != nullptr, "null ptr");
+    if (! _retval)
+        return NS_ERROR_NULL_POINTER;
+
+    if (IsA(aDataSource, aResource, kRDF_Seq) ||
+        IsA(aDataSource, aResource, kRDF_Bag) ||
+        IsA(aDataSource, aResource, kRDF_Alt)) {
+        *_retval = true;
+    }
+    else {
+        *_retval = false;
+    }
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsEmpty(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, bool* _retval)
+{
+    if (! aDataSource)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    // By default, say that we're an empty container. Even if we're not
+    // really even a container.
+    *_retval = true;
+
+    nsCOMPtr<nsIRDFNode> nextValNode;
+    rv = aDataSource->GetTarget(aResource, kRDF_nextVal, true, getter_AddRefs(nextValNode));
+    if (NS_FAILED(rv)) return rv;
+
+    if (rv == NS_RDF_NO_VALUE)
+        return NS_OK;
+
+    nsCOMPtr<nsIRDFLiteral> nextValLiteral;
+    rv = nextValNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), getter_AddRefs(nextValLiteral));
+    if (NS_FAILED(rv)) return rv;
+
+    if (nextValLiteral.get() != kOne)
+        *_retval = false;
+
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsBag(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval)
+{
+    NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+    if (! aDataSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResource != nullptr, "null ptr");
+    if (! aResource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(_retval != nullptr, "null ptr");
+    if (! _retval)
+        return NS_ERROR_NULL_POINTER;
+
+    *_retval = IsA(aDataSource, aResource, kRDF_Bag);
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsSeq(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval)
+{
+    NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+    if (! aDataSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResource != nullptr, "null ptr");
+    if (! aResource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(_retval != nullptr, "null ptr");
+    if (! _retval)
+        return NS_ERROR_NULL_POINTER;
+
+    *_retval = IsA(aDataSource, aResource, kRDF_Seq);
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IsAlt(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, bool *_retval)
+{
+    NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+    if (! aDataSource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResource != nullptr, "null ptr");
+    if (! aResource)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(_retval != nullptr, "null ptr");
+    if (! _retval)
+        return NS_ERROR_NULL_POINTER;
+
+    *_retval = IsA(aDataSource, aResource, kRDF_Alt);
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::MakeBag(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, nsIRDFContainer **_retval)
+{
+    return MakeContainer(aDataSource, aResource, kRDF_Bag, _retval);
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::MakeSeq(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, nsIRDFContainer **_retval)
+{
+    return MakeContainer(aDataSource, aResource, kRDF_Seq, _retval);
+}
+
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::MakeAlt(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource, nsIRDFContainer **_retval)
+{
+    return MakeContainer(aDataSource, aResource, kRDF_Alt, _retval);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+RDFContainerUtilsImpl::RDFContainerUtilsImpl()
+{
+    if (gRefCnt++ == 0) {
+        nsresult rv;
+
+        NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+        rv = CallGetService(kRDFServiceCID, &gRDFService);
+
+        NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
+        if (NS_SUCCEEDED(rv)) {
+            gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"),
+                                     &kRDF_instanceOf);
+            gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
+                                     &kRDF_nextVal);
+            gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"),
+                                     &kRDF_Bag);
+            gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"),
+                                     &kRDF_Seq);
+            gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"),
+                                     &kRDF_Alt);
+            gRDFService->GetLiteral(u"1", &kOne);
+        }
+    }
+}
+
+
+RDFContainerUtilsImpl::~RDFContainerUtilsImpl()
+{
+#ifdef DEBUG_REFS
+    --gInstanceCount;
+    fprintf(stdout, "%d - RDF: RDFContainerUtilsImpl\n", gInstanceCount);
+#endif
+
+    if (--gRefCnt == 0) {
+        NS_IF_RELEASE(gRDFService);
+        NS_IF_RELEASE(kRDF_instanceOf);
+        NS_IF_RELEASE(kRDF_nextVal);
+        NS_IF_RELEASE(kRDF_Bag);
+        NS_IF_RELEASE(kRDF_Seq);
+        NS_IF_RELEASE(kRDF_Alt);
+        NS_IF_RELEASE(kOne);
+    }
+}
+
+
+
+nsresult
+NS_NewRDFContainerUtils(nsIRDFContainerUtils** aResult)
+{
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    RDFContainerUtilsImpl* result =
+        new RDFContainerUtilsImpl();
+
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(result);
+    *aResult = result;
+    return NS_OK;
+}
+
+
+nsresult
+RDFContainerUtilsImpl::MakeContainer(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType, nsIRDFContainer** aResult)
+{
+    NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+    if (! aDataSource)	return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResource != nullptr, "null ptr");
+    if (! aResource)	return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aType != nullptr, "null ptr");
+    if (! aType)	return NS_ERROR_NULL_POINTER;
+
+    if (aResult)	*aResult = nullptr;
+
+    nsresult rv;
+
+    // Check to see if somebody has already turned it into a container; if so
+    // don't try to do it again.
+    bool isContainer;
+    rv = IsContainer(aDataSource, aResource, &isContainer);
+    if (NS_FAILED(rv)) return rv;
+
+    if (!isContainer)
+    {
+	rv = aDataSource->Assert(aResource, kRDF_instanceOf, aType, true);
+	if (NS_FAILED(rv)) return rv;
+
+	rv = aDataSource->Assert(aResource, kRDF_nextVal, kOne, true);
+	if (NS_FAILED(rv)) return rv;
+    }
+
+    if (aResult) {
+        rv = NS_NewRDFContainer(aResult);
+        if (NS_FAILED(rv)) return rv;
+
+        rv = (*aResult)->Init(aDataSource, aResource);
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    return NS_OK;
+}
+
+bool
+RDFContainerUtilsImpl::IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType)
+{
+    if (!aDataSource || !aResource || !aType) {
+        NS_WARNING("Unexpected null argument");
+        return false;
+    }
+
+    nsresult rv;
+
+    bool result;
+    rv = aDataSource->HasAssertion(aResource, kRDF_instanceOf, aType, true, &result);
+    if (NS_FAILED(rv))
+      return false;
+
+    return result;
+}
+
+NS_IMETHODIMP
+RDFContainerUtilsImpl::IndexOf(nsIRDFDataSource* aDataSource, nsIRDFResource* aContainer, nsIRDFNode* aElement, int32_t* aIndex)
+{
+    if (!aDataSource || !aContainer)
+        return NS_ERROR_NULL_POINTER;
+
+    // Assume we can't find it.
+    *aIndex = -1;
+
+    // If the resource is null, bail quietly
+    if (! aElement)
+      return NS_OK;
+
+    // We'll assume that fan-out is much higher than fan-in, so grovel
+    // through the inbound arcs, look for an ordinal resource, and
+    // decode it.
+    nsCOMPtr<nsISimpleEnumerator> arcsIn;
+    aDataSource->ArcLabelsIn(aElement, getter_AddRefs(arcsIn));
+    if (! arcsIn)
+        return NS_OK;
+
+    while (1) {
+        bool hasMoreArcs = false;
+        arcsIn->HasMoreElements(&hasMoreArcs);
+        if (! hasMoreArcs)
+            break;
+
+        nsCOMPtr<nsISupports> isupports;
+        arcsIn->GetNext(getter_AddRefs(isupports));
+        if (! isupports)
+            break;
+
+        nsCOMPtr<nsIRDFResource> property =
+            do_QueryInterface(isupports);
+
+        if (! property)
+            continue;
+
+        bool isOrdinal;
+        IsOrdinalProperty(property, &isOrdinal);
+        if (! isOrdinal)
+            continue;
+
+        nsCOMPtr<nsISimpleEnumerator> sources;
+        aDataSource->GetSources(property, aElement, true, getter_AddRefs(sources));
+        if (! sources)
+            continue;
+
+        while (1) {
+            bool hasMoreSources = false;
+            sources->HasMoreElements(&hasMoreSources);
+            if (! hasMoreSources)
+                break;
+
+            nsCOMPtr<nsISupports> isupports2;
+            sources->GetNext(getter_AddRefs(isupports2));
+            if (! isupports2)
+                break;
+
+            nsCOMPtr<nsIRDFResource> source =
+                do_QueryInterface(isupports2);
+
+            if (source == aContainer)
+                // Found it.
+                return OrdinalResourceToIndex(property, aIndex);
+        }
+    }
+
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFContentSink.cpp
@@ -0,0 +1,1444 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  An implementation for an NGLayout-style content sink that knows how
+  to build an RDF content model from XML-serialized RDF.
+
+  For more information on the RDF/XML syntax,
+  see http://www.w3.org/TR/REC-rdf-syntax/
+
+  This code is based on the final W3C Recommendation,
+  http://www.w3.org/TR/1999/REC-rdf-syntax-19990222.
+
+  Open Issues ------------------
+
+  1) factoring code with nsXMLContentSink - There's some amount of
+     common code between this and the HTML content sink. This will
+     increase as we support more and more HTML elements. How can code
+     from XML/HTML be factored?
+
+  2) We don't support the `parseType' attribute on the Description
+     tag; therefore, it is impossible to "inline" raw XML in this
+     implemenation.
+
+  3) We don't build the reifications at parse time due to the
+     footprint overhead it would incur for large RDF documents. (It
+     may be possible to attach a "reification" wrapper datasource that
+     would present this information at query-time.) Because of this,
+     the `bagID' attribute is not processed correctly.
+
+  4) No attempt is made to `resolve URIs' to a canonical form (the
+     specification hints that an implementation should do this). This
+     is omitted for the obvious reason that we can ill afford to
+     resolve each URI reference.
+
+*/
+
+#include "nsCOMPtr.h"
+#include "nsInterfaceHashtable.h"
+#include "nsIContentSink.h"
+#include "nsIRDFContainer.h"
+#include "nsIRDFContainerUtils.h"
+#include "nsIRDFContentSink.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFService.h"
+#include "nsIRDFXMLSink.h"
+#include "nsIServiceManager.h"
+#include "nsIURL.h"
+#include "nsIXMLContentSink.h"
+#include "nsRDFCID.h"
+#include "nsTArray.h"
+#include "nsString.h"
+#include "mozilla/Logging.h"
+#include "rdf.h"
+#include "rdfutil.h"
+#include "nsReadableUtils.h"
+#include "nsIExpatSink.h"
+#include "nsCRT.h"
+#include "nsAtom.h"
+#include "nsGkAtoms.h"
+#include "nsIScriptError.h"
+#include "nsIDTD.h"
+
+using namespace mozilla;
+
+///////////////////////////////////////////////////////////////////////
+
+enum RDFContentSinkState {
+    eRDFContentSinkState_InProlog,
+    eRDFContentSinkState_InDocumentElement,
+    eRDFContentSinkState_InDescriptionElement,
+    eRDFContentSinkState_InContainerElement,
+    eRDFContentSinkState_InPropertyElement,
+    eRDFContentSinkState_InMemberElement,
+    eRDFContentSinkState_InEpilog
+};
+
+enum RDFContentSinkParseMode {
+    eRDFContentSinkParseMode_Resource,
+    eRDFContentSinkParseMode_Literal,
+    eRDFContentSinkParseMode_Int,
+    eRDFContentSinkParseMode_Date
+};
+
+typedef decltype(&nsIRDFContainerUtils::IsAlt) nsContainerTestFn;
+typedef decltype(&nsIRDFContainerUtils::MakeAlt) nsMakeContainerFn;
+
+class RDFContentSinkImpl : public nsIRDFContentSink,
+                           public nsIExpatSink
+{
+public:
+    RDFContentSinkImpl();
+
+    // nsISupports
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIEXPATSINK
+
+    // nsIContentSink
+    NS_IMETHOD WillParse(void) override;
+    NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) override;
+    NS_IMETHOD DidBuildModel(bool aTerminated) override;
+    NS_IMETHOD WillInterrupt(void) override;
+    NS_IMETHOD WillResume(void) override;
+    NS_IMETHOD SetParser(nsParserBase* aParser) override;
+    virtual void FlushPendingNotifications(mozilla::FlushType aType) override { }
+    virtual void SetDocumentCharset(NotNull<const Encoding*> aEncoding)
+      override { }
+    virtual nsISupports *GetTarget() override { return nullptr; }
+
+    // nsIRDFContentSink
+    NS_IMETHOD Init(nsIURI* aURL) override;
+    NS_IMETHOD SetDataSource(nsIRDFDataSource* aDataSource) override;
+    NS_IMETHOD GetDataSource(nsIRDFDataSource*& aDataSource) override;
+
+    // pseudo constants
+    static int32_t gRefCnt;
+    static nsIRDFService* gRDFService;
+    static nsIRDFContainerUtils* gRDFContainerUtils;
+    static nsIRDFResource* kRDF_type;
+    static nsIRDFResource* kRDF_instanceOf; // XXX should be RDF:type
+    static nsIRDFResource* kRDF_Alt;
+    static nsIRDFResource* kRDF_Bag;
+    static nsIRDFResource* kRDF_Seq;
+    static nsIRDFResource* kRDF_nextVal;
+
+    typedef struct ContainerInfo {
+        nsIRDFResource**  mType;
+        nsContainerTestFn mTestFn;
+        nsMakeContainerFn mMakeFn;
+    } ContainerInfo;
+
+protected:
+    virtual ~RDFContentSinkImpl();
+
+    // Text management
+    void ParseText(nsIRDFNode **aResult);
+
+    nsresult FlushText();
+    nsresult AddText(const char16_t* aText, int32_t aLength);
+
+    // RDF-specific parsing
+    nsresult OpenRDF(const char16_t* aName);
+    nsresult OpenObject(const char16_t* aName ,const char16_t** aAttributes);
+    nsresult OpenProperty(const char16_t* aName, const char16_t** aAttributes);
+    nsresult OpenMember(const char16_t* aName, const char16_t** aAttributes);
+    nsresult OpenValue(const char16_t* aName, const char16_t** aAttributes);
+
+    nsresult GetIdAboutAttribute(const char16_t** aAttributes, nsIRDFResource** aResource, bool* aIsAnonymous = nullptr);
+    nsresult GetResourceAttribute(const char16_t** aAttributes, nsIRDFResource** aResource);
+    nsresult AddProperties(const char16_t** aAttributes, nsIRDFResource* aSubject, int32_t* aCount = nullptr);
+    void SetParseMode(const char16_t **aAttributes);
+
+    char16_t* mText;
+    int32_t mTextLength;
+    int32_t mTextSize;
+
+    /**
+     * From the set of given attributes, this method extracts the
+     * namespace definitions and feeds them to the datasource.
+     * These can then be suggested to the serializer to be used again.
+     * Hopefully, this will keep namespace definitions intact in a
+     * parse - serialize cycle.
+     */
+    void RegisterNamespaces(const char16_t **aAttributes);
+
+    /**
+     * Extracts the localname from aExpatName, the name that the Expat parser
+     * passes us.
+     * aLocalName will contain the localname in aExpatName.
+     * The return value is a dependent string containing just the namespace.
+     */
+    const nsDependentSubstring SplitExpatName(const char16_t *aExpatName,
+                                              nsAtom **aLocalName);
+
+    enum eContainerType { eBag, eSeq, eAlt };
+    nsresult InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer);
+    nsresult ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer);
+
+    // The datasource in which we're assigning assertions
+    nsCOMPtr<nsIRDFDataSource> mDataSource;
+
+    // A hash of all the node IDs referred to
+    nsInterfaceHashtable<nsStringHashKey, nsIRDFResource> mNodeIDMap;
+
+    // The current state of the content sink
+    RDFContentSinkState mState;
+    RDFContentSinkParseMode mParseMode;
+
+    // content stack management
+    int32_t
+    PushContext(nsIRDFResource *aContext,
+                RDFContentSinkState aState,
+                RDFContentSinkParseMode aParseMode);
+
+    nsresult
+    PopContext(nsIRDFResource         *&aContext,
+               RDFContentSinkState     &aState,
+               RDFContentSinkParseMode &aParseMode);
+
+    nsIRDFResource* GetContextElement(int32_t ancestor = 0);
+
+
+    struct RDFContextStackElement {
+        nsCOMPtr<nsIRDFResource> mResource;
+        RDFContentSinkState      mState;
+        RDFContentSinkParseMode  mParseMode;
+    };
+
+    AutoTArray<RDFContextStackElement, 8>* mContextStack;
+
+    nsCOMPtr<nsIURI> mDocumentURL;
+
+private:
+    static mozilla::LazyLogModule gLog;
+};
+
+int32_t         RDFContentSinkImpl::gRefCnt = 0;
+nsIRDFService*  RDFContentSinkImpl::gRDFService;
+nsIRDFContainerUtils* RDFContentSinkImpl::gRDFContainerUtils;
+nsIRDFResource* RDFContentSinkImpl::kRDF_type;
+nsIRDFResource* RDFContentSinkImpl::kRDF_instanceOf;
+nsIRDFResource* RDFContentSinkImpl::kRDF_Alt;
+nsIRDFResource* RDFContentSinkImpl::kRDF_Bag;
+nsIRDFResource* RDFContentSinkImpl::kRDF_Seq;
+nsIRDFResource* RDFContentSinkImpl::kRDF_nextVal;
+
+mozilla::LazyLogModule RDFContentSinkImpl::gLog("nsRDFContentSink");
+
+////////////////////////////////////////////////////////////////////////
+
+RDFContentSinkImpl::RDFContentSinkImpl()
+    : mText(nullptr),
+      mTextLength(0),
+      mTextSize(0),
+      mState(eRDFContentSinkState_InProlog),
+      mParseMode(eRDFContentSinkParseMode_Literal),
+      mContextStack(nullptr)
+{
+    if (gRefCnt++ == 0) {
+        NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+        nsresult rv = CallGetService(kRDFServiceCID, &gRDFService);
+
+        NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
+        if (NS_SUCCEEDED(rv)) {
+            rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
+                                          &kRDF_type);
+            rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"),
+                                          &kRDF_instanceOf);
+            rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"),
+                                          &kRDF_Alt);
+            rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"),
+                                          &kRDF_Bag);
+            rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"),
+                                          &kRDF_Seq);
+            rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
+                                          &kRDF_nextVal);
+        }
+
+        NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
+        rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
+    }
+}
+
+
+RDFContentSinkImpl::~RDFContentSinkImpl()
+{
+#ifdef DEBUG_REFS
+    --gInstanceCount;
+    fprintf(stdout, "%d - RDF: RDFContentSinkImpl\n", gInstanceCount);
+#endif
+
+    if (mContextStack) {
+        MOZ_LOG(gLog, LogLevel::Warning,
+               ("rdfxml: warning! unclosed tag"));
+
+        // XXX we should never need to do this, but, we'll write the
+        // code all the same. If someone left the content stack dirty,
+        // pop all the elements off the stack and release them.
+        int32_t i = mContextStack->Length();
+        while (0 < i--) {
+            nsIRDFResource* resource = nullptr;
+            RDFContentSinkState state;
+            RDFContentSinkParseMode parseMode;
+            PopContext(resource, state, parseMode);
+
+            // print some fairly useless debugging info
+            // XXX we should save line numbers on the context stack: this'd
+            // be about 1000x more helpful.
+            if (resource && MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
+                nsCString uri;
+                resource->GetValue(getter_Copies(uri));
+                MOZ_LOG(gLog, LogLevel::Debug,
+                       ("rdfxml:   uri=%s", uri.get()));
+            }
+
+            NS_IF_RELEASE(resource);
+        }
+
+        delete mContextStack;
+    }
+    free(mText);
+
+
+    if (--gRefCnt == 0) {
+        NS_IF_RELEASE(gRDFService);
+        NS_IF_RELEASE(gRDFContainerUtils);
+        NS_IF_RELEASE(kRDF_type);
+        NS_IF_RELEASE(kRDF_instanceOf);
+        NS_IF_RELEASE(kRDF_Alt);
+        NS_IF_RELEASE(kRDF_Bag);
+        NS_IF_RELEASE(kRDF_Seq);
+        NS_IF_RELEASE(kRDF_nextVal);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsISupports interface
+
+NS_IMPL_ADDREF(RDFContentSinkImpl)
+NS_IMPL_RELEASE(RDFContentSinkImpl)
+
+NS_IMETHODIMP
+RDFContentSinkImpl::QueryInterface(REFNSIID iid, void** result)
+{
+    NS_PRECONDITION(result, "null ptr");
+    if (! result)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_DEFINE_IID(kIContentSinkIID,    NS_ICONTENT_SINK_IID);
+    NS_DEFINE_IID(kIExpatSinkIID,      NS_IEXPATSINK_IID);
+    NS_DEFINE_IID(kISupportsIID,       NS_ISUPPORTS_IID);
+    NS_DEFINE_IID(kIXMLContentSinkIID, NS_IXMLCONTENT_SINK_IID);
+    NS_DEFINE_IID(kIRDFContentSinkIID, NS_IRDFCONTENTSINK_IID);
+
+    *result = nullptr;
+    if (iid.Equals(kIRDFContentSinkIID) ||
+        iid.Equals(kIXMLContentSinkIID) ||
+        iid.Equals(kIContentSinkIID) ||
+        iid.Equals(kISupportsIID)) {
+        *result = static_cast<nsIXMLContentSink*>(this);
+        AddRef();
+        return NS_OK;
+    }
+    else if (iid.Equals(kIExpatSinkIID)) {
+      *result = static_cast<nsIExpatSink*>(this);
+       AddRef();
+       return NS_OK;
+    }
+    return NS_NOINTERFACE;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleStartElement(const char16_t *aName,
+                                       const char16_t **aAtts,
+                                       uint32_t aAttsCount,
+                                       uint32_t aLineNumber)
+{
+  FlushText();
+
+  nsresult rv = NS_ERROR_UNEXPECTED; // XXX
+
+  RegisterNamespaces(aAtts);
+
+  switch (mState) {
+  case eRDFContentSinkState_InProlog:
+      rv = OpenRDF(aName);
+      break;
+
+  case eRDFContentSinkState_InDocumentElement:
+      rv = OpenObject(aName,aAtts);
+      break;
+
+  case eRDFContentSinkState_InDescriptionElement:
+      rv = OpenProperty(aName,aAtts);
+      break;
+
+  case eRDFContentSinkState_InContainerElement:
+      rv = OpenMember(aName,aAtts);
+      break;
+
+  case eRDFContentSinkState_InPropertyElement:
+  case eRDFContentSinkState_InMemberElement:
+      rv = OpenValue(aName,aAtts);
+      break;
+
+  case eRDFContentSinkState_InEpilog:
+      MOZ_LOG(gLog, LogLevel::Warning,
+             ("rdfxml: unexpected content in epilog at line %d",
+              aLineNumber));
+      break;
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleEndElement(const char16_t *aName)
+{
+  FlushText();
+
+  nsIRDFResource* resource;
+  if (NS_FAILED(PopContext(resource, mState, mParseMode))) {
+      // XXX parser didn't catch unmatched tags?
+      if (MOZ_LOG_TEST(gLog, LogLevel::Warning)) {
+          nsAutoString tagStr(aName);
+          char* tagCStr = ToNewCString(tagStr);
+
+          MOZ_LOG(gLog, LogLevel::Warning,
+                 ("rdfxml: extra close tag '%s' at line %d",
+                  tagCStr, 0/*XXX fix me */));
+
+          free(tagCStr);
+      }
+
+      return NS_ERROR_UNEXPECTED; // XXX
+  }
+
+  // If we've just popped a member or property element, _now_ is the
+  // time to add that element to the graph.
+  switch (mState) {
+    case eRDFContentSinkState_InMemberElement:
+      {
+        nsCOMPtr<nsIRDFContainer> container;
+        NS_NewRDFContainer(getter_AddRefs(container));
+        container->Init(mDataSource, GetContextElement(1));
+        container->AppendElement(resource);
+      }
+      break;
+
+    case eRDFContentSinkState_InPropertyElement:
+      {
+        mDataSource->Assert(GetContextElement(1), GetContextElement(0), resource, true);
+      } break;
+    default:
+      break;
+  }
+
+  if (mContextStack->IsEmpty())
+      mState = eRDFContentSinkState_InEpilog;
+
+  NS_IF_RELEASE(resource);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleComment(const char16_t *aName)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleCDataSection(const char16_t *aData,
+                                       uint32_t aLength)
+{
+  return aData ?  AddText(aData, aLength) : NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset,
+                                      const nsAString & aName,
+                                      const nsAString & aSystemId,
+                                      const nsAString & aPublicId,
+                                      nsISupports* aCatalogData)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleCharacterData(const char16_t *aData,
+                                        uint32_t aLength)
+{
+  return aData ?  AddText(aData, aLength) : NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleProcessingInstruction(const char16_t *aTarget,
+                                                const char16_t *aData)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::HandleXMLDeclaration(const char16_t *aVersion,
+                                         const char16_t *aEncoding,
+                                         int32_t aStandalone)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::ReportError(const char16_t* aErrorText,
+                                const char16_t* aSourceText,
+                                nsIScriptError *aError,
+                                bool *_retval)
+{
+  NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!");
+
+  // The expat driver should report the error.
+  *_retval = true;
+  return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsIContentSink interface
+
+NS_IMETHODIMP
+RDFContentSinkImpl::WillParse(void)
+{
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContentSinkImpl::WillBuildModel(nsDTDMode)
+{
+    if (mDataSource) {
+        nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
+        if (sink)
+            return sink->BeginLoad();
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::DidBuildModel(bool aTerminated)
+{
+    if (mDataSource) {
+        nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
+        if (sink)
+            return sink->EndLoad();
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::WillInterrupt(void)
+{
+    if (mDataSource) {
+        nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
+        if (sink)
+            return sink->Interrupt();
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::WillResume(void)
+{
+    if (mDataSource) {
+        nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
+        if (sink)
+            return sink->Resume();
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::SetParser(nsParserBase* aParser)
+{
+    return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFContentSink interface
+
+NS_IMETHODIMP
+RDFContentSinkImpl::Init(nsIURI* aURL)
+{
+    NS_PRECONDITION(aURL != nullptr, "null ptr");
+    if (! aURL)
+        return NS_ERROR_NULL_POINTER;
+
+    mDocumentURL = aURL;
+    mState = eRDFContentSinkState_InProlog;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFContentSinkImpl::SetDataSource(nsIRDFDataSource* aDataSource)
+{
+    NS_PRECONDITION(aDataSource != nullptr, "SetDataSource null ptr");
+    mDataSource = aDataSource;
+    NS_ASSERTION(mDataSource != nullptr,"Couldn't QI RDF DataSource");
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFContentSinkImpl::GetDataSource(nsIRDFDataSource*& aDataSource)
+{
+    aDataSource = mDataSource;
+    NS_IF_ADDREF(aDataSource);
+    return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// Text buffering
+
+static bool
+rdf_IsDataInBuffer(char16_t* buffer, int32_t length)
+{
+    for (int32_t i = 0; i < length; ++i) {
+        if (buffer[i] == ' ' ||
+            buffer[i] == '\t' ||
+            buffer[i] == '\n' ||
+            buffer[i] == '\r')
+            continue;
+
+        return true;
+    }
+    return false;
+}
+
+void
+RDFContentSinkImpl::ParseText(nsIRDFNode **aResult)
+{
+    // XXXwaterson wasteful, but we'd need to make a copy anyway to be
+    // able to call nsIRDFService::Get[Resource|Literal|...]().
+    nsAutoString value;
+    value.Append(mText, mTextLength);
+    value.Trim(" \t\n\r");
+
+    switch (mParseMode) {
+    case eRDFContentSinkParseMode_Literal:
+        {
+            nsIRDFLiteral *result;
+            gRDFService->GetLiteral(value.get(), &result);
+            *aResult = result;
+        }
+        break;
+
+    case eRDFContentSinkParseMode_Resource:
+        {
+            nsIRDFResource *result;
+            gRDFService->GetUnicodeResource(value, &result);
+            *aResult = result;
+        }
+        break;
+
+    case eRDFContentSinkParseMode_Int:
+        {
+            nsresult err;
+            int32_t i = value.ToInteger(&err);
+            nsIRDFInt *result;
+            gRDFService->GetIntLiteral(i, &result);
+            *aResult = result;
+        }
+        break;
+
+    case eRDFContentSinkParseMode_Date:
+        {
+            PRTime t = rdf_ParseDate(nsDependentCString(NS_LossyConvertUTF16toASCII(value).get(), value.Length()));
+            nsIRDFDate *result;
+            gRDFService->GetDateLiteral(t, &result);
+            *aResult = result;
+        }
+        break;
+
+    default:
+        NS_NOTREACHED("unknown parse type");
+        break;
+    }
+}
+
+nsresult
+RDFContentSinkImpl::FlushText()
+{
+    nsresult rv = NS_OK;
+    if (0 != mTextLength) {
+        if (rdf_IsDataInBuffer(mText, mTextLength)) {
+            // XXX if there's anything but whitespace, then we'll
+            // create a text node.
+
+            switch (mState) {
+            case eRDFContentSinkState_InMemberElement: {
+                nsCOMPtr<nsIRDFNode> node;
+                ParseText(getter_AddRefs(node));
+
+                nsCOMPtr<nsIRDFContainer> container;
+                NS_NewRDFContainer(getter_AddRefs(container));
+                container->Init(mDataSource, GetContextElement(1));
+
+                container->AppendElement(node);
+            } break;
+
+            case eRDFContentSinkState_InPropertyElement: {
+                nsCOMPtr<nsIRDFNode> node;
+                ParseText(getter_AddRefs(node));
+
+                mDataSource->Assert(GetContextElement(1), GetContextElement(0), node, true);
+            } break;
+
+            default:
+                // just ignore it
+                break;
+            }
+        }
+        mTextLength = 0;
+    }
+    return rv;
+}
+
+
+nsresult
+RDFContentSinkImpl::AddText(const char16_t* aText, int32_t aLength)
+{
+    // Create buffer when we first need it
+    if (0 == mTextSize) {
+        mText = (char16_t *) malloc(sizeof(char16_t) * 4096);
+        if (!mText) {
+            return NS_ERROR_OUT_OF_MEMORY;
+        }
+        mTextSize = 4096;
+    }
+
+    // Copy data from string into our buffer; grow the buffer as needed.
+    // It never shrinks, but since the content sink doesn't stick around,
+    // this shouldn't be a bloat issue.
+    int32_t amount = mTextSize - mTextLength;
+    if (amount < aLength) {
+        // Grow the buffer by at least a factor of two to prevent thrashing.
+        // Since realloc() will leave mText intact if the call fails,
+        // don't clobber mText or mTextSize until the new mem is allocated.
+        int32_t newSize = (2 * mTextSize > (mTextSize + aLength)) ?
+                          (2 * mTextSize) : (mTextSize + aLength);
+        char16_t* newText =
+            (char16_t *) realloc(mText, sizeof(char16_t) * newSize);
+        if (!newText)
+            return NS_ERROR_OUT_OF_MEMORY;
+        mTextSize = newSize;
+        mText = newText;
+    }
+    memcpy(&mText[mTextLength], aText, sizeof(char16_t) * aLength);
+    mTextLength += aLength;
+
+    return NS_OK;
+}
+
+bool
+rdf_RequiresAbsoluteURI(const nsString& uri)
+{
+    // cheap shot at figuring out if this requires an absolute url translation
+    return !(StringBeginsWith(uri, NS_LITERAL_STRING("urn:")) ||
+             StringBeginsWith(uri, NS_LITERAL_STRING("chrome:")));
+}
+
+nsresult
+RDFContentSinkImpl::GetIdAboutAttribute(const char16_t** aAttributes,
+                                        nsIRDFResource** aResource,
+                                        bool* aIsAnonymous)
+{
+    // This corresponds to the dirty work of production [6.5]
+    nsresult rv = NS_OK;
+
+    nsAutoString nodeID;
+
+    RefPtr<nsAtom> localName;
+    for (; *aAttributes; aAttributes += 2) {
+        const nsDependentSubstring& nameSpaceURI =
+            SplitExpatName(aAttributes[0], getter_AddRefs(localName));
+
+        // We'll accept either `ID' or `rdf:ID' (ibid with `about' or
+        // `rdf:about') in the spirit of being liberal towards the
+        // input that we receive.
+        if (!nameSpaceURI.IsEmpty() &&
+            !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
+          continue;
+        }
+
+        // XXX you can't specify both, but we'll just pick up the
+        // first thing that was specified and ignore the other.
+
+        if (localName == nsGkAtoms::about) {
+            if (aIsAnonymous)
+                *aIsAnonymous = false;
+
+            nsAutoString relURI(aAttributes[1]);
+            if (rdf_RequiresAbsoluteURI(relURI)) {
+                nsAutoCString uri;
+                rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri);
+                if (NS_FAILED(rv)) return rv;
+
+                return gRDFService->GetResource(uri,
+                                                aResource);
+            }
+            return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]),
+                                            aResource);
+        }
+        else if (localName == nsGkAtoms::ID) {
+            if (aIsAnonymous)
+                *aIsAnonymous = false;
+            // In the spirit of leniency, we do not bother trying to
+            // enforce that this be a valid "XML Name" (see
+            // http://www.w3.org/TR/REC-xml#NT-Nmtoken), as per
+            // 6.21. If we wanted to, this would be where to do it.
+
+            // Construct an in-line resource whose URI is the
+            // document's URI plus the XML name specified in the ID
+            // attribute.
+            nsAutoCString name;
+            nsAutoCString ref('#');
+            AppendUTF16toUTF8(aAttributes[1], ref);
+
+            rv = mDocumentURL->Resolve(ref, name);
+            if (NS_FAILED(rv)) return rv;
+
+            return gRDFService->GetResource(name, aResource);
+        }
+        else if (localName == nsGkAtoms::nodeID) {
+            nodeID.Assign(aAttributes[1]);
+        }
+        else if (localName == nsGkAtoms::about) {
+            // XXX we don't deal with aboutEach...
+            //MOZ_LOG(gLog, LogLevel::Warning,
+            //       ("rdfxml: ignoring aboutEach at line %d",
+            //        aNode.GetSourceLineNumber()));
+        }
+    }
+
+    // Otherwise, we couldn't find anything, so just gensym one...
+    if (aIsAnonymous)
+        *aIsAnonymous = true;
+
+    // If nodeID is present, check if we already know about it. If we've seen
+    // the nodeID before, use the same resource, otherwise generate a new one.
+    if (!nodeID.IsEmpty()) {
+        mNodeIDMap.Get(nodeID,aResource);
+
+        if (!*aResource) {
+            rv = gRDFService->GetAnonymousResource(aResource);
+            mNodeIDMap.Put(nodeID,*aResource);
+        }
+    }
+    else {
+        rv = gRDFService->GetAnonymousResource(aResource);
+    }
+
+    return rv;
+}
+
+nsresult
+RDFContentSinkImpl::GetResourceAttribute(const char16_t** aAttributes,
+                                         nsIRDFResource** aResource)
+{
+  RefPtr<nsAtom> localName;
+
+  nsAutoString nodeID;
+
+  for (; *aAttributes; aAttributes += 2) {
+      const nsDependentSubstring& nameSpaceURI =
+          SplitExpatName(aAttributes[0], getter_AddRefs(localName));
+
+      // We'll accept `resource' or `rdf:resource', under the spirit
+      // that we should be liberal towards the input that we
+      // receive.
+      if (!nameSpaceURI.IsEmpty() &&
+          !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
+          continue;
+      }
+
+      // XXX you can't specify both, but we'll just pick up the
+      // first thing that was specified and ignore the other.
+
+      if (localName == nsGkAtoms::resource) {
+          // XXX Take the URI and make it fully qualified by
+          // sticking it into the document's URL. This may not be
+          // appropriate...
+          nsAutoString relURI(aAttributes[1]);
+          if (rdf_RequiresAbsoluteURI(relURI)) {
+              nsresult rv;
+              nsAutoCString uri;
+
+              rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri);
+              if (NS_FAILED(rv)) return rv;
+
+              return gRDFService->GetResource(uri, aResource);
+          }
+          return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]),
+                                          aResource);
+      }
+      else if (localName == nsGkAtoms::nodeID) {
+          nodeID.Assign(aAttributes[1]);
+      }
+  }
+
+  // If nodeID is present, check if we already know about it. If we've seen
+  // the nodeID before, use the same resource, otherwise generate a new one.
+  if (!nodeID.IsEmpty()) {
+      mNodeIDMap.Get(nodeID,aResource);
+
+      if (!*aResource) {
+          nsresult rv;
+          rv = gRDFService->GetAnonymousResource(aResource);
+          if (NS_FAILED(rv)) {
+              return rv;
+          }
+          mNodeIDMap.Put(nodeID,*aResource);
+      }
+      return NS_OK;
+  }
+
+  return NS_ERROR_FAILURE;
+}
+
+nsresult
+RDFContentSinkImpl::AddProperties(const char16_t** aAttributes,
+                                  nsIRDFResource* aSubject,
+                                  int32_t* aCount)
+{
+  if (aCount)
+      *aCount = 0;
+
+  RefPtr<nsAtom> localName;
+  for (; *aAttributes; aAttributes += 2) {
+      const nsDependentSubstring& nameSpaceURI =
+          SplitExpatName(aAttributes[0], getter_AddRefs(localName));
+
+      // skip 'xmlns' directives, these are "meta" information
+      if (nameSpaceURI.EqualsLiteral("http://www.w3.org/2000/xmlns/")) {
+        continue;
+      }
+
+      // skip `about', `ID', `resource', and 'nodeID' attributes (either with or
+      // without the `rdf:' prefix); these are all "special" and
+      // should've been dealt with by the caller.
+      if (localName == nsGkAtoms::about || localName == nsGkAtoms::ID ||
+          localName == nsGkAtoms::resource || localName == nsGkAtoms::nodeID) {
+          if (nameSpaceURI.IsEmpty() ||
+              nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI))
+              continue;
+      }
+
+      // Skip `parseType', `RDF:parseType', and `NC:parseType'. This
+      // is meta-information that will be handled in SetParseMode.
+      if (localName == nsGkAtoms::parseType) {
+          if (nameSpaceURI.IsEmpty() ||
+              nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
+              nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) {
+              continue;
+          }
+      }
+
+      NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI);
+      propertyStr.Append(nsAtomCString(localName));
+
+      // Add the assertion to RDF
+      nsCOMPtr<nsIRDFResource> property;
+      gRDFService->GetResource(propertyStr, getter_AddRefs(property));
+
+      nsCOMPtr<nsIRDFLiteral> target;
+      gRDFService->GetLiteral(aAttributes[1],
+                              getter_AddRefs(target));
+
+      mDataSource->Assert(aSubject, property, target, true);
+  }
+  return NS_OK;
+}
+
+void
+RDFContentSinkImpl::SetParseMode(const char16_t **aAttributes)
+{
+    RefPtr<nsAtom> localName;
+    for (; *aAttributes; aAttributes += 2) {
+        const nsDependentSubstring& nameSpaceURI =
+            SplitExpatName(aAttributes[0], getter_AddRefs(localName));
+
+        if (localName == nsGkAtoms::parseType) {
+            nsDependentString v(aAttributes[1]);
+
+            if (nameSpaceURI.IsEmpty() ||
+                nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
+                if (v.EqualsLiteral("Resource"))
+                    mParseMode = eRDFContentSinkParseMode_Resource;
+
+                break;
+            }
+            else if (nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) {
+                if (v.EqualsLiteral("Date"))
+                    mParseMode = eRDFContentSinkParseMode_Date;
+                else if (v.EqualsLiteral("Integer"))
+                    mParseMode = eRDFContentSinkParseMode_Int;
+
+                break;
+            }
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////
+// RDF-specific routines used to build the model
+
+nsresult
+RDFContentSinkImpl::OpenRDF(const char16_t* aName)
+{
+    // ensure that we're actually reading RDF by making sure that the
+    // opening tag is <rdf:RDF>, where "rdf:" corresponds to whatever
+    // they've declared the standard RDF namespace to be.
+    RefPtr<nsAtom> localName;
+    const nsDependentSubstring& nameSpaceURI =
+        SplitExpatName(aName, getter_AddRefs(localName));
+
+    if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
+        localName != nsGkAtoms::RDF) {
+       // MOZ_LOG(gLog, LogLevel::Info,
+       //        ("rdfxml: expected RDF:RDF at line %d",
+       //         aNode.GetSourceLineNumber()));
+
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    PushContext(nullptr, mState, mParseMode);
+    mState = eRDFContentSinkState_InDocumentElement;
+    return NS_OK;
+}
+
+nsresult
+RDFContentSinkImpl::OpenObject(const char16_t* aName,
+                               const char16_t** aAttributes)
+{
+    // an "object" non-terminal is either a "description", a "typed
+    // node", or a "container", so this change the content sink's
+    // state appropriately.
+    RefPtr<nsAtom> localName;
+    const nsDependentSubstring& nameSpaceURI =
+        SplitExpatName(aName, getter_AddRefs(localName));
+
+    // Figure out the URI of this object, and create an RDF node for it.
+    nsCOMPtr<nsIRDFResource> source;
+    GetIdAboutAttribute(aAttributes, getter_AddRefs(source));
+
+    // If there is no `ID' or `about', then there's not much we can do.
+    if (! source)
+        return NS_ERROR_FAILURE;
+
+    // Push the element onto the context stack
+    PushContext(source, mState, mParseMode);
+
+    // Now figure out what kind of state transition we need to
+    // make. We'll either be going into a mode where we parse a
+    // description or a container.
+    bool isaTypedNode = true;
+
+    if (nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
+        isaTypedNode = false;
+
+        if (localName == nsGkAtoms::Description) {
+            // it's a description
+            mState = eRDFContentSinkState_InDescriptionElement;
+        }
+        else if (localName == nsGkAtoms::Bag) {
+            // it's a bag container
+            InitContainer(kRDF_Bag, source);
+            mState = eRDFContentSinkState_InContainerElement;
+        }
+        else if (localName == nsGkAtoms::Seq) {
+            // it's a seq container
+            InitContainer(kRDF_Seq, source);
+            mState = eRDFContentSinkState_InContainerElement;
+        }
+        else if (localName == nsGkAtoms::Alt) {
+            // it's an alt container
+            InitContainer(kRDF_Alt, source);
+            mState = eRDFContentSinkState_InContainerElement;
+        }
+        else {
+            // heh, that's not *in* the RDF namespace: just treat it
+            // like a typed node
+            isaTypedNode = true;
+        }
+    }
+
+    if (isaTypedNode) {
+        NS_ConvertUTF16toUTF8 typeStr(nameSpaceURI);
+        typeStr.Append(nsAtomCString(localName));
+
+        nsCOMPtr<nsIRDFResource> type;
+        nsresult rv = gRDFService->GetResource(typeStr, getter_AddRefs(type));
+        if (NS_FAILED(rv)) return rv;
+
+        rv = mDataSource->Assert(source, kRDF_type, type, true);
+        if (NS_FAILED(rv)) return rv;
+
+        mState = eRDFContentSinkState_InDescriptionElement;
+    }
+
+    AddProperties(aAttributes, source);
+    return NS_OK;
+}
+
+nsresult
+RDFContentSinkImpl::OpenProperty(const char16_t* aName, const char16_t** aAttributes)
+{
+    nsresult rv;
+
+    // an "object" non-terminal is either a "description", a "typed
+    // node", or a "container", so this change the content sink's
+    // state appropriately.
+    RefPtr<nsAtom> localName;
+    const nsDependentSubstring& nameSpaceURI =
+        SplitExpatName(aName, getter_AddRefs(localName));
+
+    NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI);
+    propertyStr.Append(nsAtomCString(localName));
+
+    nsCOMPtr<nsIRDFResource> property;
+    rv = gRDFService->GetResource(propertyStr, getter_AddRefs(property));
+    if (NS_FAILED(rv)) return rv;
+
+    // See if they've specified a 'resource' attribute, in which case
+    // they mean *that* to be the object of this property.
+    nsCOMPtr<nsIRDFResource> target;
+    GetResourceAttribute(aAttributes, getter_AddRefs(target));
+
+    bool isAnonymous = false;
+
+    if (! target) {
+        // See if an 'ID' attribute has been specified, in which case
+        // this corresponds to the fourth form of [6.12].
+
+        // XXX strictly speaking, we should reject the RDF/XML as
+        // invalid if they've specified both an 'ID' and a 'resource'
+        // attribute. Bah.
+
+        // XXX strictly speaking, 'about=' isn't allowed here, but
+        // what the hell.
+        GetIdAboutAttribute(aAttributes, getter_AddRefs(target), &isAnonymous);
+    }
+
+    if (target) {
+        // They specified an inline resource for the value of this
+        // property. Create an RDF resource for the inline resource
+        // URI, add the properties to it, and attach the inline
+        // resource to its parent.
+        int32_t count;
+        rv = AddProperties(aAttributes, target, &count);
+        NS_ASSERTION(NS_SUCCEEDED(rv), "problem adding properties");
+        if (NS_FAILED(rv)) return rv;
+
+        if (count || !isAnonymous) {
+            // If the resource was "anonymous" (i.e., they hadn't
+            // explicitly set an ID or resource attribute), then we'll
+            // only assert this property from the context element *if*
+            // there were properties specified on the anonymous
+            // resource.
+            rv = mDataSource->Assert(GetContextElement(0), property, target, true);
+            if (NS_FAILED(rv)) return rv;
+        }
+
+        // XXX Technically, we should _not_ fall through here and push
+        // the element onto the stack: this is supposed to be a closed
+        // node. But right now I'm lazy and the code will just Do The
+        // Right Thing so long as the RDF is well-formed.
+    }
+
+    // Push the element onto the context stack and change state.
+    PushContext(property, mState, mParseMode);
+    mState = eRDFContentSinkState_InPropertyElement;
+    SetParseMode(aAttributes);
+
+    return NS_OK;
+}
+
+nsresult
+RDFContentSinkImpl::OpenMember(const char16_t* aName,
+                               const char16_t** aAttributes)
+{
+    // ensure that we're actually reading a member element by making
+    // sure that the opening tag is <rdf:li>, where "rdf:" corresponds
+    // to whatever they've declared the standard RDF namespace to be.
+    nsresult rv;
+
+    RefPtr<nsAtom> localName;
+    const nsDependentSubstring& nameSpaceURI =
+        SplitExpatName(aName, getter_AddRefs(localName));
+
+    if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
+        localName != nsGkAtoms::li) {
+        MOZ_LOG(gLog, LogLevel::Error,
+               ("rdfxml: expected RDF:li at line %d",
+                -1)); // XXX pass in line number
+
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    // The parent element is the container.
+    nsIRDFResource* container = GetContextElement(0);
+    if (! container)
+        return NS_ERROR_NULL_POINTER;
+
+    nsIRDFResource* resource;
+    if (NS_SUCCEEDED(rv = GetResourceAttribute(aAttributes, &resource))) {
+        // Okay, this node has an RDF:resource="..." attribute. That
+        // means that it's a "referenced item," as covered in [6.29].
+        nsCOMPtr<nsIRDFContainer> c;
+        NS_NewRDFContainer(getter_AddRefs(c));
+        c->Init(mDataSource, container);
+        c->AppendElement(resource);
+
+        // XXX Technically, we should _not_ fall through here and push
+        // the element onto the stack: this is supposed to be a closed
+        // node. But right now I'm lazy and the code will just Do The
+        // Right Thing so long as the RDF is well-formed.
+        NS_RELEASE(resource);
+    }
+
+    // Change state. Pushing a null context element is a bit weird,
+    // but the idea is that there really is _no_ context "property".
+    // The contained element will use nsIRDFContainer::AppendElement() to add
+    // the element to the container, which requires only the container
+    // and the element to be added.
+    PushContext(nullptr, mState, mParseMode);
+    mState = eRDFContentSinkState_InMemberElement;
+    SetParseMode(aAttributes);
+
+    return NS_OK;
+}
+
+
+nsresult
+RDFContentSinkImpl::OpenValue(const char16_t* aName, const char16_t** aAttributes)
+{
+    // a "value" can either be an object or a string: we'll only get
+    // *here* if it's an object, as raw text is added as a leaf.
+    return OpenObject(aName,aAttributes);
+}
+
+////////////////////////////////////////////////////////////////////////
+// namespace resolution
+void
+RDFContentSinkImpl::RegisterNamespaces(const char16_t **aAttributes)
+{
+    nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
+    if (!sink) {
+        return;
+    }
+    NS_NAMED_LITERAL_STRING(xmlns, "http://www.w3.org/2000/xmlns/");
+    for (; *aAttributes; aAttributes += 2) {
+        // check the namespace
+        const char16_t* attr = aAttributes[0];
+        const char16_t* xmlnsP = xmlns.BeginReading();
+        while (*attr ==  *xmlnsP) {
+            ++attr;
+            ++xmlnsP;
+        }
+        if (*attr != 0xFFFF ||
+            xmlnsP != xmlns.EndReading()) {
+            continue;
+        }
+        // get the localname (or "xmlns" for the default namespace)
+        const char16_t* endLocal = ++attr;
+        while (*endLocal && *endLocal != 0xFFFF) {
+            ++endLocal;
+        }
+        nsDependentSubstring lname(attr, endLocal);
+        RefPtr<nsAtom> preferred = NS_Atomize(lname);
+        if (preferred == nsGkAtoms::xmlns) {
+            preferred = nullptr;
+        }
+        sink->AddNameSpace(preferred, nsDependentString(aAttributes[1]));
+    }
+}
+
+////////////////////////////////////////////////////////////////////////
+// Qualified name resolution
+
+const nsDependentSubstring
+RDFContentSinkImpl::SplitExpatName(const char16_t *aExpatName,
+                                   nsAtom **aLocalName)
+{
+    /**
+     *  Expat can send the following:
+     *    localName
+     *    namespaceURI<separator>localName
+     *    namespaceURI<separator>localName<separator>prefix
+     *
+     *  and we use 0xFFFF for the <separator>.
+     *
+     */
+
+    const char16_t *uriEnd = aExpatName;
+    const char16_t *nameStart = aExpatName;
+    const char16_t *pos;
+    for (pos = aExpatName; *pos; ++pos) {
+        if (*pos == 0xFFFF) {
+            if (uriEnd != aExpatName) {
+                break;
+            }
+
+            uriEnd = pos;
+            nameStart = pos + 1;
+        }
+    }
+
+    const nsDependentSubstring& nameSpaceURI = Substring(aExpatName, uriEnd);
+    *aLocalName = NS_Atomize(Substring(nameStart, pos)).take();
+    return nameSpaceURI;
+}
+
+nsresult
+RDFContentSinkImpl::InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer)
+{
+    // Do the right kind of initialization based on the container
+    // 'type' resource, and the state of the container (i.e., 'make' a
+    // new container vs. 'reinitialize' the container).
+    nsresult rv;
+
+    static const ContainerInfo gContainerInfo[] = {
+        { &RDFContentSinkImpl::kRDF_Alt, &nsIRDFContainerUtils::IsAlt, &nsIRDFContainerUtils::MakeAlt },
+        { &RDFContentSinkImpl::kRDF_Bag, &nsIRDFContainerUtils::IsBag, &nsIRDFContainerUtils::MakeBag },
+        { &RDFContentSinkImpl::kRDF_Seq, &nsIRDFContainerUtils::IsSeq, &nsIRDFContainerUtils::MakeSeq },
+        { 0, 0, 0 },
+    };
+
+    for (const ContainerInfo* info = gContainerInfo; info->mType != 0; ++info) {
+        if (*info->mType != aContainerType)
+            continue;
+
+        bool isContainer;
+        rv = (gRDFContainerUtils->*(info->mTestFn))(mDataSource, aContainer, &isContainer);
+        if (isContainer) {
+            rv = ReinitContainer(aContainerType, aContainer);
+        }
+        else {
+            rv = (gRDFContainerUtils->*(info->mMakeFn))(mDataSource, aContainer, nullptr);
+        }
+        return rv;
+    }
+
+    NS_NOTREACHED("not an RDF container type");
+    return NS_ERROR_FAILURE;
+}
+
+
+
+nsresult
+RDFContentSinkImpl::ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer)
+{
+    // Mega-kludge to deal with the fact that Make[Seq|Alt|Bag] is
+    // idempotent, and as such, containers will have state (e.g.,
+    // RDF:nextVal) maintained in the graph across loads. This
+    // re-initializes each container's RDF:nextVal to '1', and 'marks'
+    // the container as such.
+    nsresult rv;
+
+    nsCOMPtr<nsIRDFLiteral> one;
+    rv = gRDFService->GetLiteral(u"1", getter_AddRefs(one));
+    if (NS_FAILED(rv)) return rv;
+
+    // Re-initialize the 'nextval' property
+    nsCOMPtr<nsIRDFNode> nextval;
+    rv = mDataSource->GetTarget(aContainer, kRDF_nextVal, true, getter_AddRefs(nextval));
+    if (NS_FAILED(rv)) return rv;
+
+    rv = mDataSource->Change(aContainer, kRDF_nextVal, nextval, one);
+    if (NS_FAILED(rv)) return rv;
+
+    // Re-mark as a container. XXX should be kRDF_type
+    rv = mDataSource->Assert(aContainer, kRDF_instanceOf, aContainerType, true);
+    NS_ASSERTION(NS_SUCCEEDED(rv), "unable to mark container as such");
+    if (NS_FAILED(rv)) return rv;
+
+    return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// Content stack management
+
+nsIRDFResource*
+RDFContentSinkImpl::GetContextElement(int32_t ancestor /* = 0 */)
+{
+    if ((nullptr == mContextStack) ||
+        (uint32_t(ancestor) >= mContextStack->Length())) {
+        return nullptr;
+    }
+
+    return mContextStack->ElementAt(
+           mContextStack->Length()-ancestor-1).mResource;
+}
+
+int32_t
+RDFContentSinkImpl::PushContext(nsIRDFResource         *aResource,
+                                RDFContentSinkState     aState,
+                                RDFContentSinkParseMode aParseMode)
+{
+    if (! mContextStack) {
+        mContextStack = new AutoTArray<RDFContextStackElement, 8>();
+        if (! mContextStack)
+            return 0;
+    }
+
+    RDFContextStackElement* e = mContextStack->AppendElement();
+    if (! e)
+        return mContextStack->Length();
+
+    e->mResource  = aResource;
+    e->mState     = aState;
+    e->mParseMode = aParseMode;
+
+    return mContextStack->Length();
+}
+
+nsresult
+RDFContentSinkImpl::PopContext(nsIRDFResource         *&aResource,
+                               RDFContentSinkState     &aState,
+                               RDFContentSinkParseMode &aParseMode)
+{
+    if ((nullptr == mContextStack) ||
+        (mContextStack->IsEmpty())) {
+        return NS_ERROR_NULL_POINTER;
+    }
+
+    uint32_t i = mContextStack->Length() - 1;
+    RDFContextStackElement &e = mContextStack->ElementAt(i);
+
+    aResource  = e.mResource;
+    NS_IF_ADDREF(aResource);
+    aState     = e.mState;
+    aParseMode = e.mParseMode;
+
+    mContextStack->RemoveElementAt(i);
+    return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_NewRDFContentSink(nsIRDFContentSink** aResult)
+{
+    NS_PRECONDITION(aResult != nullptr, "null ptr");
+    if (! aResult)
+        return NS_ERROR_NULL_POINTER;
+
+    RDFContentSinkImpl* sink = new RDFContentSinkImpl();
+    if (! sink)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(sink);
+    *aResult = sink;
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFResource.cpp
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsRDFResource.h"
+#include "nsIServiceManager.h"
+#include "nsIRDFDelegateFactory.h"
+#include "nsIRDFService.h"
+#include "nsRDFCID.h"
+#include "mozilla/Logging.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+
+nsIRDFService* nsRDFResource::gRDFService = nullptr;
+nsrefcnt nsRDFResource::gRDFServiceRefCnt = 0;
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsRDFResource::nsRDFResource(void)
+    : mDelegates(nullptr)
+{
+}
+
+nsRDFResource::~nsRDFResource(void)
+{
+    // Release all of the delegate objects
+    while (mDelegates) {
+        DelegateEntry* doomed = mDelegates;
+        mDelegates = mDelegates->mNext;
+        delete doomed;
+    }
+
+    if (!gRDFService)
+        return;
+
+    gRDFService->UnregisterResource(this);
+
+    if (--gRDFServiceRefCnt == 0)
+        NS_RELEASE(gRDFService);
+}
+
+NS_IMPL_ISUPPORTS(nsRDFResource, nsIRDFResource, nsIRDFNode)
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIRDFNode methods:
+
+NS_IMETHODIMP
+nsRDFResource::EqualsNode(nsIRDFNode* aNode, bool* aResult)
+{
+    NS_PRECONDITION(aNode != nullptr, "null ptr");
+    if (! aNode)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+    nsIRDFResource* resource;
+    rv = aNode->QueryInterface(NS_GET_IID(nsIRDFResource), (void**)&resource);
+    if (NS_SUCCEEDED(rv)) {
+        *aResult = (static_cast<nsIRDFResource*>(this) == resource);
+        NS_RELEASE(resource);
+        return NS_OK;
+    }
+    if (rv == NS_NOINTERFACE) {
+        *aResult = false;
+        return NS_OK;
+    }
+
+    return rv;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIRDFResource methods:
+
+NS_IMETHODIMP
+nsRDFResource::Init(const char* aURI)
+{
+    NS_PRECONDITION(aURI != nullptr, "null ptr");
+    if (! aURI)
+        return NS_ERROR_NULL_POINTER;
+
+    mURI = aURI;
+
+    if (gRDFServiceRefCnt++ == 0) {
+        nsresult rv = CallGetService(kRDFServiceCID, &gRDFService);
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    // don't replace an existing resource with the same URI automatically
+    return gRDFService->RegisterResource(this, true);
+}
+
+NS_IMETHODIMP
+nsRDFResource::GetValue(char* *aURI)
+{
+    NS_ASSERTION(aURI, "Null out param.");
+
+    *aURI = ToNewCString(mURI);
+
+    if (!*aURI)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRDFResource::GetValueUTF8(nsACString& aResult)
+{
+    aResult = mURI;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRDFResource::GetValueConst(const char** aURI)
+{
+    *aURI = mURI.get();
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRDFResource::EqualsString(const char* aURI, bool* aResult)
+{
+    NS_PRECONDITION(aURI != nullptr, "null ptr");
+    if (! aURI)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aResult, "null ptr");
+
+    *aResult = mURI.Equals(aURI);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRDFResource::GetDelegate(const char* aKey, REFNSIID aIID, void** aResult)
+{
+    NS_PRECONDITION(aKey != nullptr, "null ptr");
+    if (! aKey)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+    *aResult = nullptr;
+
+    DelegateEntry* entry = mDelegates;
+    while (entry) {
+        if (entry->mKey.Equals(aKey)) {
+            rv = entry->mDelegate->QueryInterface(aIID, aResult);
+            return rv;
+        }
+
+        entry = entry->mNext;
+    }
+
+    // Construct a ContractID of the form "@mozilla.org/rdf/delegate/[key]/[scheme];1
+    nsAutoCString contractID(NS_RDF_DELEGATEFACTORY_CONTRACTID_PREFIX);
+    contractID.Append(aKey);
+    contractID.AppendLiteral("&scheme=");
+
+    int32_t i = mURI.FindChar(':');
+    contractID += StringHead(mURI, i);
+
+    nsCOMPtr<nsIRDFDelegateFactory> delegateFactory =
+             do_CreateInstance(contractID.get(), &rv);
+    if (NS_FAILED(rv)) return rv;
+
+    rv = delegateFactory->CreateDelegate(this, aKey, aIID, aResult);
+    if (NS_FAILED(rv)) return rv;
+
+    // Okay, we've successfully created a delegate. Let's remember it.
+    entry = new DelegateEntry;
+    if (! entry) {
+        NS_RELEASE(*reinterpret_cast<nsISupports**>(aResult));
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    entry->mKey      = aKey;
+    entry->mDelegate = do_QueryInterface(*reinterpret_cast<nsISupports**>(aResult), &rv);
+    if (NS_FAILED(rv)) {
+        NS_ERROR("nsRDFResource::GetDelegate(): can't QI to nsISupports!");
+
+        delete entry;
+        NS_RELEASE(*reinterpret_cast<nsISupports**>(aResult));
+        return NS_ERROR_FAILURE;
+    }
+
+    entry->mNext     = mDelegates;
+
+    mDelegates = entry;
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsRDFResource::ReleaseDelegate(const char* aKey)
+{
+    NS_PRECONDITION(aKey != nullptr, "null ptr");
+    if (! aKey)
+        return NS_ERROR_NULL_POINTER;
+
+    DelegateEntry* entry = mDelegates;
+    DelegateEntry** link = &mDelegates;
+
+    while (entry) {
+        if (entry->mKey.Equals(aKey)) {
+            *link = entry->mNext;
+            delete entry;
+            return NS_OK;
+        }
+
+        link = &(entry->mNext);
+        entry = entry->mNext;
+    }
+
+    NS_WARNING("nsRDFResource::ReleaseDelegate() no delegate found");
+    return NS_OK;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFResource.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsRDFResource_h__
+#define nsRDFResource_h__
+
+#include "nsCOMPtr.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFResource.h"
+#include "nscore.h"
+#include "nsString.h"
+#include "rdf.h"
+
+class nsIRDFService;
+
+/**
+ * This simple base class implements nsIRDFResource, and can be used as a
+ * superclass for more sophisticated resource implementations.
+ */
+class nsRDFResource : public nsIRDFResource {
+public:
+
+    NS_DECL_THREADSAFE_ISUPPORTS
+
+    // nsIRDFNode methods:
+    NS_IMETHOD EqualsNode(nsIRDFNode* aNode, bool* aResult) override;
+
+    // nsIRDFResource methods:
+    NS_IMETHOD Init(const char* aURI) override;
+    NS_IMETHOD GetValue(char* *aURI) override;
+    NS_IMETHOD GetValueUTF8(nsACString& aResult) override;
+    NS_IMETHOD GetValueConst(const char** aURI) override;
+    NS_IMETHOD EqualsString(const char* aURI, bool* aResult) override;
+    NS_IMETHOD GetDelegate(const char* aKey, REFNSIID aIID, void** aResult) override;
+    NS_IMETHOD ReleaseDelegate(const char* aKey) override;
+
+    // nsRDFResource methods:
+    nsRDFResource(void);
+
+protected:
+    virtual ~nsRDFResource(void);
+    static nsIRDFService* gRDFService;
+    static nsrefcnt gRDFServiceRefCnt;
+
+protected:
+    nsCString mURI;
+
+    struct DelegateEntry {
+        nsCString             mKey;
+        nsCOMPtr<nsISupports> mDelegate;
+        DelegateEntry*        mNext;
+    };
+
+    DelegateEntry* mDelegates;
+};
+
+#endif // nsRDFResource_h__
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFService.cpp
@@ -0,0 +1,1540 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.  */
+
+/*
+
+  This file provides the implementation for the RDF service manager.
+
+  TO DO
+  -----
+
+  1) Implement the CreateDataBase() methods.
+
+  2) Cache date and int literals.
+
+ */
+
+#include "nsRDFService.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsMemory.h"
+#include "nsAtom.h"
+#include "nsIComponentManager.h"
+#include "nsIRDFDataSource.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFRemoteDataSource.h"
+#include "nsIServiceManager.h"
+#include "nsIFactory.h"
+#include "nsRDFCID.h"
+#include "nsString.h"
+#include "nsNetUtil.h"
+#include "nsIURI.h"
+#include "PLDHashTable.h"
+#include "plhash.h"
+#include "plstr.h"
+#include "mozilla/Logging.h"
+#include "prprf.h"
+#include "rdf.h"
+#include "nsCRT.h"
+#include "nsCRTGlue.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/IntegerPrintfMacros.h"
+
+using namespace mozilla;
+
+////////////////////////////////////////////////////////////////////////
+
+static NS_DEFINE_CID(kRDFXMLDataSourceCID,    NS_RDFXMLDATASOURCE_CID);
+static NS_DEFINE_CID(kRDFDefaultResourceCID,  NS_RDFDEFAULTRESOURCE_CID);
+
+static NS_DEFINE_IID(kIRDFLiteralIID,         NS_IRDFLITERAL_IID);
+static NS_DEFINE_IID(kIRDFDateIID,         NS_IRDFDATE_IID);
+static NS_DEFINE_IID(kIRDFIntIID,         NS_IRDFINT_IID);
+static NS_DEFINE_IID(kIRDFNodeIID,            NS_IRDFNODE_IID);
+static NS_DEFINE_IID(kISupportsIID,           NS_ISUPPORTS_IID);
+
+static LazyLogModule gLog("nsRDFService");
+
+class BlobImpl;
+
+// These functions are copied from nsprpub/lib/ds/plhash.c, with one
+// change to free the key in DataSourceFreeEntry.
+// XXX sigh, why were DefaultAllocTable et. al. declared static, anyway?
+
+static void *
+DataSourceAllocTable(void *pool, size_t size)
+{
+    return malloc(size);
+}
+
+static void
+DataSourceFreeTable(void *pool, void *item)
+{
+    free(item);
+}
+
+static PLHashEntry *
+DataSourceAllocEntry(void *pool, const void *key)
+{
+    return (PLHashEntry*) malloc(sizeof(PLHashEntry));
+}
+
+static void
+DataSourceFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
+{
+    if (flag == HT_FREE_ENTRY) {
+        PL_strfree((char*) he->key);
+        free(he);
+    }
+}
+
+static PLHashAllocOps dataSourceHashAllocOps = {
+    DataSourceAllocTable, DataSourceFreeTable,
+    DataSourceAllocEntry, DataSourceFreeEntry
+};
+
+//----------------------------------------------------------------------
+//
+// For the mResources hashtable.
+//
+
+struct ResourceHashEntry : public PLDHashEntryHdr {
+    const char *mKey;
+    nsIRDFResource *mResource;
+
+    static PLDHashNumber
+    HashKey(const void *key)
+    {
+        return HashString(static_cast<const char *>(key));
+    }
+
+    static bool
+    MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+    {
+        const ResourceHashEntry *entry =
+            static_cast<const ResourceHashEntry *>(hdr);
+
+        return 0 == nsCRT::strcmp(static_cast<const char *>(key),
+                                  entry->mKey);
+    }
+};
+
+static const PLDHashTableOps gResourceTableOps = {
+    ResourceHashEntry::HashKey,
+    ResourceHashEntry::MatchEntry,
+    PLDHashTable::MoveEntryStub,
+    PLDHashTable::ClearEntryStub,
+    nullptr
+};
+
+// ----------------------------------------------------------------------
+//
+// For the mLiterals hashtable.
+//
+
+struct LiteralHashEntry : public PLDHashEntryHdr {
+    nsIRDFLiteral *mLiteral;
+    const char16_t *mKey;
+
+    static PLDHashNumber
+    HashKey(const void *key)
+    {
+        return HashString(static_cast<const char16_t *>(key));
+    }
+
+    static bool
+    MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+    {
+        const LiteralHashEntry *entry =
+            static_cast<const LiteralHashEntry *>(hdr);
+
+        return 0 == nsCRT::strcmp(static_cast<const char16_t *>(key),
+                                  entry->mKey);
+    }
+};
+
+static const PLDHashTableOps gLiteralTableOps = {
+    LiteralHashEntry::HashKey,
+    LiteralHashEntry::MatchEntry,
+    PLDHashTable::MoveEntryStub,
+    PLDHashTable::ClearEntryStub,
+    nullptr
+};
+
+// ----------------------------------------------------------------------
+//
+// For the mInts hashtable.
+//
+
+struct IntHashEntry : public PLDHashEntryHdr {
+    nsIRDFInt *mInt;
+    int32_t    mKey;
+
+    static PLDHashNumber
+    HashKey(const void *key)
+    {
+        return PLDHashNumber(*static_cast<const int32_t *>(key));
+    }
+
+    static bool
+    MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+    {
+        const IntHashEntry *entry =
+            static_cast<const IntHashEntry *>(hdr);
+
+        return *static_cast<const int32_t *>(key) == entry->mKey;
+    }
+};
+
+static const PLDHashTableOps gIntTableOps = {
+    IntHashEntry::HashKey,
+    IntHashEntry::MatchEntry,
+    PLDHashTable::MoveEntryStub,
+    PLDHashTable::ClearEntryStub,
+    nullptr
+};
+
+// ----------------------------------------------------------------------
+//
+// For the mDates hashtable.
+//
+
+struct DateHashEntry : public PLDHashEntryHdr {
+    nsIRDFDate *mDate;
+    PRTime      mKey;
+
+    static PLDHashNumber
+    HashKey(const void *key)
+    {
+        // xor the low 32 bits with the high 32 bits.
+        PRTime t = *static_cast<const PRTime *>(key);
+        int32_t h32 = int32_t(t >> 32);
+        int32_t l32 = int32_t(0xffffffff & t);
+        return PLDHashNumber(l32 ^ h32);
+    }
+
+    static bool
+    MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+    {
+        const DateHashEntry *entry =
+            static_cast<const DateHashEntry *>(hdr);
+
+        return *static_cast<const PRTime *>(key) == entry->mKey;
+    }
+};
+
+static const PLDHashTableOps gDateTableOps = {
+    DateHashEntry::HashKey,
+    DateHashEntry::MatchEntry,
+    PLDHashTable::MoveEntryStub,
+    PLDHashTable::ClearEntryStub,
+    nullptr
+};
+
+class BlobImpl : public nsIRDFBlob
+{
+public:
+    struct Data {
+        int32_t  mLength;
+        uint8_t *mBytes;
+    };
+
+    BlobImpl(const uint8_t *aBytes, int32_t aLength)
+    {
+        mData.mLength = aLength;
+        mData.mBytes = new uint8_t[aLength];
+        memcpy(mData.mBytes, aBytes, aLength);
+        NS_ADDREF(RDFServiceImpl::gRDFService);
+        RDFServiceImpl::gRDFService->RegisterBlob(this);
+    }
+
+protected:
+    virtual ~BlobImpl()
+    {
+        RDFServiceImpl::gRDFService->UnregisterBlob(this);
+        // Use NS_RELEASE2() here, because we want to decrease the
+        // refcount, but not null out the gRDFService pointer (which is
+        // what a vanilla NS_RELEASE() would do).
+        nsrefcnt refcnt;
+        NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
+        delete[] mData.mBytes;
+    }
+
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIRDFNODE
+    NS_DECL_NSIRDFBLOB
+
+    Data mData;
+};
+
+NS_IMPL_ISUPPORTS(BlobImpl, nsIRDFNode, nsIRDFBlob)
+
+NS_IMETHODIMP
+BlobImpl::EqualsNode(nsIRDFNode *aNode, bool *aEquals)
+{
+    nsCOMPtr<nsIRDFBlob> blob = do_QueryInterface(aNode);
+    if (blob) {
+        int32_t length;
+        blob->GetLength(&length);
+
+        if (length == mData.mLength) {
+            const uint8_t *bytes;
+            blob->GetValue(&bytes);
+
+            if (0 == memcmp(bytes, mData.mBytes, length)) {
+                *aEquals = true;
+                return NS_OK;
+            }
+        }
+    }
+
+    *aEquals = false;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+BlobImpl::GetValue(const uint8_t **aResult)
+{
+    *aResult = mData.mBytes;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+BlobImpl::GetLength(int32_t *aResult)
+{
+    *aResult = mData.mLength;
+    return NS_OK;
+}
+
+// ----------------------------------------------------------------------
+//
+// For the mBlobs hashtable.
+//
+
+struct BlobHashEntry : public PLDHashEntryHdr {
+    BlobImpl *mBlob;
+
+    static PLDHashNumber
+    HashKey(const void *key)
+    {
+        const BlobImpl::Data *data =
+            static_cast<const BlobImpl::Data *>(key);
+        return HashBytes(data->mBytes, data->mLength);
+    }
+
+    static bool
+    MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+    {
+        const BlobHashEntry *entry =
+            static_cast<const BlobHashEntry *>(hdr);
+
+        const BlobImpl::Data *left = &entry->mBlob->mData;
+
+        const BlobImpl::Data *right =
+            static_cast<const BlobImpl::Data *>(key);
+
+        return (left->mLength == right->mLength)
+            && 0 == memcmp(left->mBytes, right->mBytes, right->mLength);
+    }
+};
+
+static const PLDHashTableOps gBlobTableOps = {
+    BlobHashEntry::HashKey,
+    BlobHashEntry::MatchEntry,
+    PLDHashTable::MoveEntryStub,
+    PLDHashTable::ClearEntryStub,
+    nullptr
+};
+
+////////////////////////////////////////////////////////////////////////
+// LiteralImpl
+//
+//   Currently, all literals are implemented exactly the same way;
+//   i.e., there is are no resource factories to allow you to generate
+//   customer resources. I doubt that makes sense, anyway.
+//
+class LiteralImpl : public nsIRDFLiteral {
+public:
+    static nsresult
+    Create(const char16_t* aValue, nsIRDFLiteral** aResult);
+
+    // nsISupports
+    NS_DECL_THREADSAFE_ISUPPORTS
+
+    // nsIRDFNode
+    NS_DECL_NSIRDFNODE
+
+    // nsIRDFLiteral
+    NS_DECL_NSIRDFLITERAL
+
+protected:
+    explicit LiteralImpl(const char16_t* s);
+    virtual ~LiteralImpl();
+
+    const char16_t* GetValue() const {
+        size_t objectSize = ((sizeof(LiteralImpl) + sizeof(char16_t) - 1) / sizeof(char16_t)) * sizeof(char16_t);
+        return reinterpret_cast<const char16_t*>(reinterpret_cast<const unsigned char*>(this) + objectSize);
+    }
+};
+
+
+nsresult
+LiteralImpl::Create(const char16_t* aValue, nsIRDFLiteral** aResult)
+{
+    // Goofy math to get alignment right. Copied from nsSharedString.h.
+    size_t objectSize = ((sizeof(LiteralImpl) + sizeof(char16_t) - 1) / sizeof(char16_t)) * sizeof(char16_t);
+    size_t stringLen = nsCharTraits<char16_t>::length(aValue);
+    size_t stringSize = (stringLen + 1) * sizeof(char16_t);
+
+    void* objectPtr = operator new(objectSize + stringSize);
+    if (! objectPtr)
+        return NS_ERROR_NULL_POINTER;
+
+    char16_t* buf = reinterpret_cast<char16_t*>(static_cast<unsigned char*>(objectPtr) + objectSize);
+    nsCharTraits<char16_t>::copy(buf, aValue, stringLen + 1);
+
+    NS_ADDREF(*aResult = new (objectPtr) LiteralImpl(buf));
+    return NS_OK;
+}
+
+
+LiteralImpl::LiteralImpl(const char16_t* s)
+{
+    RDFServiceImpl::gRDFService->RegisterLiteral(this);
+    NS_ADDREF(RDFServiceImpl::gRDFService);
+}
+
+LiteralImpl::~LiteralImpl()
+{
+    RDFServiceImpl::gRDFService->UnregisterLiteral(this);
+
+    // Use NS_RELEASE2() here, because we want to decrease the
+    // refcount, but not null out the gRDFService pointer (which is
+    // what a vanilla NS_RELEASE() would do).
+    nsrefcnt refcnt;
+    NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
+}
+
+NS_IMPL_ADDREF(LiteralImpl)
+NS_IMPL_RELEASE(LiteralImpl)
+
+nsresult
+LiteralImpl::QueryInterface(REFNSIID iid, void** result)
+{
+    if (! result)
+        return NS_ERROR_NULL_POINTER;
+
+    *result = nullptr;
+    if (iid.Equals(kIRDFLiteralIID) ||
+        iid.Equals(kIRDFNodeIID) ||
+        iid.Equals(kISupportsIID)) {
+        *result = static_cast<nsIRDFLiteral*>(this);
+        AddRef();
+        return NS_OK;
+    }
+    return NS_NOINTERFACE;
+}
+
+NS_IMETHODIMP
+LiteralImpl::EqualsNode(nsIRDFNode* aNode, bool* aResult)
+{
+    nsresult rv;
+    nsIRDFLiteral* literal;
+    rv = aNode->QueryInterface(kIRDFLiteralIID, (void**) &literal);
+    if (NS_SUCCEEDED(rv)) {
+        *aResult = (static_cast<nsIRDFLiteral*>(this) == literal);
+        NS_RELEASE(literal);
+        return NS_OK;
+    }
+    else if (rv == NS_NOINTERFACE) {
+        *aResult = false;
+        return NS_OK;
+    }
+    else {
+        return rv;
+    }
+}
+
+NS_IMETHODIMP
+LiteralImpl::GetValue(char16_t* *value)
+{
+    NS_ASSERTION(value, "null ptr");
+    if (! value)
+        return NS_ERROR_NULL_POINTER;
+
+    const char16_t *temp = GetValue();
+    *value = temp? NS_strdup(temp) : 0;
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+LiteralImpl::GetValueConst(const char16_t** aValue)
+{
+    *aValue = GetValue();
+    return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// DateImpl
+//
+
+class DateImpl : public nsIRDFDate {
+public:
+    explicit DateImpl(const PRTime s);
+
+    // nsISupports
+    NS_DECL_ISUPPORTS
+
+    // nsIRDFNode
+    NS_DECL_NSIRDFNODE
+
+    // nsIRDFDate
+    NS_IMETHOD GetValue(PRTime *value) override;
+
+private:
+    virtual ~DateImpl();
+
+    nsresult EqualsDate(nsIRDFDate* date, bool* result);
+    PRTime mValue;
+};
+
+
+DateImpl::DateImpl(const PRTime s)
+    : mValue(s)
+{
+    RDFServiceImpl::gRDFService->RegisterDate(this);
+    NS_ADDREF(RDFServiceImpl::gRDFService);
+}
+
+DateImpl::~DateImpl()
+{
+    RDFServiceImpl::gRDFService->UnregisterDate(this);
+
+    // Use NS_RELEASE2() here, because we want to decrease the
+    // refcount, but not null out the gRDFService pointer (which is
+    // what a vanilla NS_RELEASE() would do).
+    nsrefcnt refcnt;
+    NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
+}
+
+NS_IMPL_ADDREF(DateImpl)
+NS_IMPL_RELEASE(DateImpl)
+
+nsresult
+DateImpl::QueryInterface(REFNSIID iid, void** result)
+{
+    if (! result)
+        return NS_ERROR_NULL_POINTER;
+
+    *result = nullptr;
+    if (iid.Equals(kIRDFDateIID) ||
+        iid.Equals(kIRDFNodeIID) ||
+        iid.Equals(kISupportsIID)) {
+        *result = static_cast<nsIRDFDate*>(this);
+        AddRef();
+        return NS_OK;
+    }
+    return NS_NOINTERFACE;
+}
+
+NS_IMETHODIMP
+DateImpl::EqualsNode(nsIRDFNode* node, bool* result)
+{
+    nsresult rv;
+    nsIRDFDate* date;
+    if (NS_SUCCEEDED(node->QueryInterface(kIRDFDateIID, (void**) &date))) {
+        rv = EqualsDate(date, result);
+        NS_RELEASE(date);
+    }
+    else {
+        *result = false;
+        rv = NS_OK;
+    }
+    return rv;
+}
+
+NS_IMETHODIMP
+DateImpl::GetValue(PRTime *value)
+{
+    NS_ASSERTION(value, "null ptr");
+    if (! value)
+        return NS_ERROR_NULL_POINTER;
+
+    *value = mValue;
+    return NS_OK;
+}
+
+
+nsresult
+DateImpl::EqualsDate(nsIRDFDate* date, bool* result)
+{
+    NS_ASSERTION(date && result, "null ptr");
+    if (!date || !result)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+    PRTime p;
+    if (NS_FAILED(rv = date->GetValue(&p)))
+        return rv;
+
+    *result = p == mValue;
+    return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// IntImpl
+//
+
+class IntImpl : public nsIRDFInt {
+public:
+    explicit IntImpl(int32_t s);
+
+    // nsISupports
+    NS_DECL_ISUPPORTS
+
+    // nsIRDFNode
+    NS_DECL_NSIRDFNODE
+
+    // nsIRDFInt
+    NS_IMETHOD GetValue(int32_t *value) override;
+
+private:
+    virtual ~IntImpl();
+
+    nsresult EqualsInt(nsIRDFInt* value, bool* result);
+    int32_t mValue;
+};
+
+
+IntImpl::IntImpl(int32_t s)
+    : mValue(s)
+{
+    RDFServiceImpl::gRDFService->RegisterInt(this);
+    NS_ADDREF(RDFServiceImpl::gRDFService);
+}
+
+IntImpl::~IntImpl()
+{
+    RDFServiceImpl::gRDFService->UnregisterInt(this);
+
+    // Use NS_RELEASE2() here, because we want to decrease the
+    // refcount, but not null out the gRDFService pointer (which is
+    // what a vanilla NS_RELEASE() would do).
+    nsrefcnt refcnt;
+    NS_RELEASE2(RDFServiceImpl::gRDFService, refcnt);
+}
+
+NS_IMPL_ADDREF(IntImpl)
+NS_IMPL_RELEASE(IntImpl)
+
+nsresult
+IntImpl::QueryInterface(REFNSIID iid, void** result)
+{
+    if (! result)
+        return NS_ERROR_NULL_POINTER;
+
+    *result = nullptr;
+    if (iid.Equals(kIRDFIntIID) ||
+        iid.Equals(kIRDFNodeIID) ||
+        iid.Equals(kISupportsIID)) {
+        *result = static_cast<nsIRDFInt*>(this);
+        AddRef();
+        return NS_OK;
+    }
+    return NS_NOINTERFACE;
+}
+
+NS_IMETHODIMP
+IntImpl::EqualsNode(nsIRDFNode* node, bool* result)
+{
+    nsresult rv;
+    nsIRDFInt* intValue;
+    if (NS_SUCCEEDED(node->QueryInterface(kIRDFIntIID, (void**) &intValue))) {
+        rv = EqualsInt(intValue, result);
+        NS_RELEASE(intValue);
+    }
+    else {
+        *result = false;
+        rv = NS_OK;
+    }
+    return rv;
+}
+
+NS_IMETHODIMP
+IntImpl::GetValue(int32_t *value)
+{
+    NS_ASSERTION(value, "null ptr");
+    if (! value)
+        return NS_ERROR_NULL_POINTER;
+
+    *value = mValue;
+    return NS_OK;
+}
+
+
+nsresult
+IntImpl::EqualsInt(nsIRDFInt* intValue, bool* result)
+{
+    NS_ASSERTION(intValue && result, "null ptr");
+    if (!intValue || !result)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+    int32_t p;
+    if (NS_FAILED(rv = intValue->GetValue(&p)))
+        return rv;
+
+    *result = (p == mValue);
+    return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// RDFServiceImpl
+
+RDFServiceImpl*
+RDFServiceImpl::gRDFService;
+
+RDFServiceImpl::RDFServiceImpl()
+    : mNamedDataSources(nullptr)
+    , mResources(&gResourceTableOps, sizeof(ResourceHashEntry))
+    , mLiterals(&gLiteralTableOps, sizeof(LiteralHashEntry))
+    , mInts(&gIntTableOps, sizeof(IntHashEntry))
+    , mDates(&gDateTableOps, sizeof(DateHashEntry))
+    , mBlobs(&gBlobTableOps, sizeof(BlobHashEntry))
+{
+    gRDFService = this;
+}
+
+nsresult
+RDFServiceImpl::Init()
+{
+    nsresult rv;
+
+    mNamedDataSources = PL_NewHashTable(23,
+                                        PL_HashString,
+                                        PL_CompareStrings,
+                                        PL_CompareValues,
+                                        &dataSourceHashAllocOps, nullptr);
+
+    if (! mNamedDataSources)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    mDefaultResourceFactory = do_GetClassObject(kRDFDefaultResourceCID, &rv);
+    NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get default resource factory");
+    if (NS_FAILED(rv)) return rv;
+
+    return NS_OK;
+}
+
+
+RDFServiceImpl::~RDFServiceImpl()
+{
+    if (mNamedDataSources) {
+        PL_HashTableDestroy(mNamedDataSources);
+        mNamedDataSources = nullptr;
+    }
+    gRDFService = nullptr;
+}
+
+
+// static
+nsresult
+RDFServiceImpl::CreateSingleton(nsISupports* aOuter,
+                                const nsIID& aIID, void **aResult)
+{
+    NS_ENSURE_NO_AGGREGATION(aOuter);
+
+    if (gRDFService) {
+        NS_ERROR("Trying to create RDF serviec twice.");
+        return gRDFService->QueryInterface(aIID, aResult);
+    }
+
+    RefPtr<RDFServiceImpl> serv = new RDFServiceImpl();
+    nsresult rv = serv->Init();
+    if (NS_FAILED(rv))
+        return rv;
+
+    return serv->QueryInterface(aIID, aResult);
+}
+
+NS_IMPL_ISUPPORTS(RDFServiceImpl, nsIRDFService, nsISupportsWeakReference)
+
+// Per RFC2396.
+static const uint8_t
+kLegalSchemeChars[] = {
+          //        ASCII    Bits     Ordered  Hex
+          //                 01234567 76543210
+    0x00, // 00-07
+    0x00, // 08-0F
+    0x00, // 10-17
+    0x00, // 18-1F
+    0x00, // 20-27   !"#$%&' 00000000 00000000
+    0x28, // 28-2F  ()*+,-./ 00010100 00101000 0x28
+    0xff, // 30-37  01234567 11111111 11111111 0xFF
+    0x03, // 38-3F  89:;<=>? 11000000 00000011 0x03
+    0xfe, // 40-47  @ABCDEFG 01111111 11111110 0xFE
+    0xff, // 48-4F  HIJKLMNO 11111111 11111111 0xFF
+    0xff, // 50-57  PQRSTUVW 11111111 11111111 0xFF
+    0x87, // 58-5F  XYZ[\]^_ 11100001 10000111 0x87
+    0xfe, // 60-67  `abcdefg 01111111 11111110 0xFE
+    0xff, // 68-6F  hijklmno 11111111 11111111 0xFF
+    0xff, // 70-77  pqrstuvw 11111111 11111111 0xFF
+    0x07, // 78-7F  xyz{|}~  11100000 00000111 0x07
+    0x00, 0x00, 0x00, 0x00, // >= 80
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00
+};
+
+static inline bool
+IsLegalSchemeCharacter(const char aChar)
+{
+    uint8_t mask = kLegalSchemeChars[aChar >> 3];
+    uint8_t bit = 1u << (aChar & 0x7);
+    return bool((mask & bit) != 0);
+}
+
+
+NS_IMETHODIMP
+RDFServiceImpl::GetResource(const nsACString& aURI, nsIRDFResource** aResource)
+{
+    // Sanity checks
+    NS_PRECONDITION(aResource != nullptr, "null ptr");
+    NS_PRECONDITION(!aURI.IsEmpty(), "URI is empty");
+    if (! aResource)
+        return NS_ERROR_NULL_POINTER;
+    if (aURI.IsEmpty())
+        return NS_ERROR_INVALID_ARG;
+
+    const nsCString& flatURI = PromiseFlatCString(aURI);
+    MOZ_LOG(gLog, LogLevel::Debug, ("rdfserv get-resource %s", flatURI.get()));
+
+    // First, check the cache to see if we've already created and
+    // registered this thing.
+    PLDHashEntryHdr *hdr = mResources.Search(flatURI.get());
+    if (hdr) {
+        ResourceHashEntry *entry = static_cast<ResourceHashEntry *>(hdr);
+        NS_ADDREF(*aResource = entry->mResource);
+        return NS_OK;
+    }
+
+    // Nope. So go to the repository to create it.
+
+    // Compute the scheme of the URI. Scan forward until we either:
+    //
+    // 1. Reach the end of the string
+    // 2. Encounter a non-alpha character
+    // 3. Encouter a colon.
+    //
+    // If we encounter a colon _before_ encountering a non-alpha
+    // character, then assume it's the scheme.
+    //
+    // XXX Although it's really not correct, we'll allow underscore
+    // characters ('_'), too.
+    nsACString::const_iterator p, end;
+    aURI.BeginReading(p);
+    aURI.EndReading(end);
+    while (p != end && IsLegalSchemeCharacter(*p))
+        ++p;
+
+    nsresult rv;
+    nsCOMPtr<nsIFactory> factory;
+
+    nsACString::const_iterator begin;
+    aURI.BeginReading(begin);
+    if (*p == ':') {
+        // There _was_ a scheme. First see if it's the same scheme
+        // that we just tried to use...
+        if (mLastFactory && mLastURIPrefix.Equals(Substring(begin, p)))
+            factory = mLastFactory;
+        else {
+            // Try to find a factory using the component manager.
+            nsACString::const_iterator begin;
+            aURI.BeginReading(begin);
+            nsAutoCString contractID;
+            contractID = NS_LITERAL_CSTRING(NS_RDF_RESOURCE_FACTORY_CONTRACTID_PREFIX) +
+                         Substring(begin, p);
+
+            factory = do_GetClassObject(contractID.get());
+            if (factory) {
+                // Store the factory in our one-element cache.
+                if (p != begin) {
+                    mLastFactory = factory;
+                    mLastURIPrefix = Substring(begin, p);
+                }
+            }
+        }
+    }
+
+    if (! factory) {
+        // fall through to using the "default" resource factory if either:
+        //
+        // 1. The URI didn't have a scheme, or
+        // 2. There was no resource factory registered for the scheme.
+        factory = mDefaultResourceFactory;
+
+        // Store the factory in our one-element cache.
+        if (p != begin) {
+            mLastFactory = factory;
+            mLastURIPrefix = Substring(begin, p);
+        }
+    }
+
+    nsIRDFResource *result;
+    rv = factory->CreateInstance(nullptr, NS_GET_IID(nsIRDFResource), (void**) &result);
+    if (NS_FAILED(rv)) return rv;
+
+    // Now initialize it with its URI. At this point, the resource
+    // implementation should register itself with the RDF service.
+    rv = result->Init(flatURI.get());
+    if (NS_FAILED(rv)) {
+        NS_ERROR("unable to initialize resource");
+        NS_RELEASE(result);
+        return rv;
+    }
+
+    *aResource = result; // already refcounted from repository
+    return rv;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetUnicodeResource(const nsAString& aURI, nsIRDFResource** aResource)
+{
+    return GetResource(NS_ConvertUTF16toUTF8(aURI), aResource);
+}
+
+
+NS_IMETHODIMP
+RDFServiceImpl::GetAnonymousResource(nsIRDFResource** aResult)
+{
+static uint32_t gCounter = 0;
+static char gChars[] = "0123456789abcdef"
+                       "ghijklmnopqrstuv"
+                       "wxyzABCDEFGHIJKL"
+                       "MNOPQRSTUVWXYZ.+";
+
+static int32_t kMask  = 0x003f;
+static int32_t kShift = 6;
+
+    if (! gCounter) {
+        // Start it at a semi-unique value, just to minimize the
+        // chance that we get into a situation where
+        //
+        // 1. An anonymous resource gets serialized out in a graph
+        // 2. Reboot
+        // 3. The same anonymous resource gets requested, and refers
+        //    to something completely different.
+        // 4. The serialization is read back in.
+        gCounter = uint32_t(PR_Now());
+    }
+
+    nsresult rv;
+    nsAutoCString s;
+
+    do {
+        // Ugh, this is a really sloppy way to do this; I copied the
+        // implementation from the days when it lived outside the RDF
+        // service. Now that it's a member we can be more cleverer.
+
+        s.Truncate();
+        s.AppendLiteral("rdf:#$");
+
+        uint32_t id = ++gCounter;
+        while (id) {
+            char ch = gChars[(id & kMask)];
+            s.Append(ch);
+            id >>= kShift;
+        }
+
+        nsIRDFResource* resource;
+        rv = GetResource(s, &resource);
+        if (NS_FAILED(rv)) return rv;
+
+        // XXX an ugly but effective way to make sure that this
+        // resource is really unique in the world.
+        resource->AddRef();
+        nsrefcnt refcnt = resource->Release();
+
+        if (refcnt == 1) {
+            *aResult = resource;
+            break;
+        }
+
+        NS_RELEASE(resource);
+    } while (1);
+
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+RDFServiceImpl::GetLiteral(const char16_t* aValue, nsIRDFLiteral** aLiteral)
+{
+    NS_PRECONDITION(aValue != nullptr, "null ptr");
+    if (! aValue)
+        return NS_ERROR_NULL_POINTER;
+
+    NS_PRECONDITION(aLiteral != nullptr, "null ptr");
+    if (! aLiteral)
+        return NS_ERROR_NULL_POINTER;
+
+    // See if we have one already cached
+    PLDHashEntryHdr *hdr = mLiterals.Search(aValue);
+    if (hdr) {
+        LiteralHashEntry *entry = static_cast<LiteralHashEntry *>(hdr);
+        NS_ADDREF(*aLiteral = entry->mLiteral);
+        return NS_OK;
+    }
+
+    // Nope. Create a new one
+    return LiteralImpl::Create(aValue, aLiteral);
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetDateLiteral(PRTime aTime, nsIRDFDate** aResult)
+{
+    // See if we have one already cached
+    PLDHashEntryHdr *hdr = mDates.Search(&aTime);
+    if (hdr) {
+        DateHashEntry *entry = static_cast<DateHashEntry *>(hdr);
+        NS_ADDREF(*aResult = entry->mDate);
+        return NS_OK;
+    }
+
+    DateImpl* result = new DateImpl(aTime);
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(*aResult = result);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetIntLiteral(int32_t aInt, nsIRDFInt** aResult)
+{
+    // See if we have one already cached
+    PLDHashEntryHdr *hdr = mInts.Search(&aInt);
+    if (hdr) {
+        IntHashEntry *entry = static_cast<IntHashEntry *>(hdr);
+        NS_ADDREF(*aResult = entry->mInt);
+        return NS_OK;
+    }
+
+    IntImpl* result = new IntImpl(aInt);
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(*aResult = result);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetBlobLiteral(const uint8_t *aBytes, int32_t aLength,
+                               nsIRDFBlob **aResult)
+{
+    BlobImpl::Data key = { aLength, const_cast<uint8_t *>(aBytes) };
+
+    PLDHashEntryHdr *hdr = mBlobs.Search(&key);
+    if (hdr) {
+        BlobHashEntry *entry = static_cast<BlobHashEntry *>(hdr);
+        NS_ADDREF(*aResult = entry->mBlob);
+        return NS_OK;
+    }
+
+    BlobImpl *result = new BlobImpl(aBytes, aLength);
+    if (! result)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(*aResult = result);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::IsAnonymousResource(nsIRDFResource* aResource, bool* _result)
+{
+    NS_PRECONDITION(aResource != nullptr, "null ptr");
+    if (! aResource)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    const char* uri;
+    rv = aResource->GetValueConst(&uri);
+    if (NS_FAILED(rv)) return rv;
+
+    if ((uri[0] == 'r') &&
+        (uri[1] == 'd') &&
+        (uri[2] == 'f') &&
+        (uri[3] == ':') &&
+        (uri[4] == '#') &&
+        (uri[5] == '$')) {
+        *_result = true;
+    }
+    else {
+        *_result = false;
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::RegisterResource(nsIRDFResource* aResource, bool aReplace)
+{
+    NS_PRECONDITION(aResource != nullptr, "null ptr");
+    if (! aResource)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    const char* uri;
+    rv = aResource->GetValueConst(&uri);
+    NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get URI from resource");
+    if (NS_FAILED(rv)) return rv;
+
+    NS_ASSERTION(uri != nullptr, "resource has no URI");
+    if (! uri)
+        return NS_ERROR_NULL_POINTER;
+
+    PLDHashEntryHdr *hdr = mResources.Search(uri);
+    if (hdr) {
+        if (!aReplace) {
+            NS_WARNING("resource already registered, and replace not specified");
+            return NS_ERROR_FAILURE;    // already registered
+        }
+
+        // N.B., we do _not_ release the original resource because we
+        // only ever held a weak reference to it. We simply replace
+        // it.
+
+        MOZ_LOG(gLog, LogLevel::Debug,
+               ("rdfserv   replace-resource [%p] <-- [%p] %s",
+                static_cast<ResourceHashEntry *>(hdr)->mResource,
+                aResource, (const char*) uri));
+    }
+    else {
+        hdr = mResources.Add(uri, fallible);
+        if (! hdr)
+            return NS_ERROR_OUT_OF_MEMORY;
+
+        MOZ_LOG(gLog, LogLevel::Debug,
+               ("rdfserv   register-resource [%p] %s",
+                aResource, (const char*) uri));
+    }
+
+    // N.B., we only hold a weak reference to the resource: that way,
+    // the resource can be destroyed when the last refcount goes
+    // away. The single addref that the CreateResource() call made
+    // will be owned by the callee.
+    ResourceHashEntry *entry = static_cast<ResourceHashEntry *>(hdr);
+    entry->mResource = aResource;
+    entry->mKey = uri;
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::UnregisterResource(nsIRDFResource* aResource)
+{
+    NS_PRECONDITION(aResource != nullptr, "null ptr");
+    if (! aResource)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    const char* uri;
+    rv = aResource->GetValueConst(&uri);
+    if (NS_FAILED(rv)) return rv;
+
+    NS_ASSERTION(uri != nullptr, "resource has no URI");
+    if (! uri)
+        return NS_ERROR_UNEXPECTED;
+
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("rdfserv unregister-resource [%p] %s",
+            aResource, (const char*) uri));
+
+#ifdef DEBUG
+    if (!mResources.Search(uri))
+        NS_WARNING("resource was never registered");
+#endif
+
+    mResources.Remove(uri);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::RegisterDataSource(nsIRDFDataSource* aDataSource, bool aReplace)
+{
+    NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+    if (! aDataSource)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    nsAutoCString uri;
+    rv = aDataSource->GetURI(uri);
+    if (NS_FAILED(rv)) return rv;
+
+    PLHashEntry** hep =
+      PL_HashTableRawLookup(mNamedDataSources,
+                            (*mNamedDataSources->keyHash)(uri.get()),
+                            uri.get());
+
+    if (*hep) {
+        if (! aReplace)
+            return NS_ERROR_FAILURE; // already registered
+
+        // N.B., we only hold a weak reference to the datasource, so
+        // just replace the old with the new and don't touch any
+        // refcounts.
+        MOZ_LOG(gLog, LogLevel::Debug,
+               ("rdfserv    replace-datasource [%p] <-- [%p] %s",
+                (*hep)->value, aDataSource, uri.get()));
+
+        (*hep)->value = aDataSource;
+    }
+    else {
+        const char* key = PL_strdup(uri.get());
+        if (! key)
+            return NS_ERROR_OUT_OF_MEMORY;
+
+        PL_HashTableAdd(mNamedDataSources, key, aDataSource);
+
+        MOZ_LOG(gLog, LogLevel::Debug,
+               ("rdfserv   register-datasource [%p] %s",
+                aDataSource, uri.get()));
+
+        // N.B., we only hold a weak reference to the datasource, so don't
+        // addref.
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::UnregisterDataSource(nsIRDFDataSource* aDataSource)
+{
+    NS_PRECONDITION(aDataSource != nullptr, "null ptr");
+    if (! aDataSource)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    nsAutoCString uri;
+    rv = aDataSource->GetURI(uri);
+    if (NS_FAILED(rv)) return rv;
+
+    //NS_ASSERTION(uri != nullptr, "datasource has no URI");
+    if (uri.IsVoid())
+        return NS_ERROR_UNEXPECTED;
+
+    PLHashEntry** hep =
+        PL_HashTableRawLookup(mNamedDataSources,
+                              (*mNamedDataSources->keyHash)(uri.get()),
+                              uri.get());
+
+    // It may well be that this datasource was never registered. If
+    // so, don't unregister it.
+    if (! *hep || ((*hep)->value != aDataSource))
+        return NS_OK;
+
+    // N.B., we only held a weak reference to the datasource, so we
+    // don't release here.
+    PL_HashTableRawRemove(mNamedDataSources, hep, *hep);
+
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("rdfserv unregister-datasource [%p] %s",
+            aDataSource, uri.get()));
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetDataSource(const char* aURI, nsIRDFDataSource** aDataSource)
+{
+    // Use the other GetDataSource and ask for a non-blocking Refresh.
+    // If you wanted it loaded synchronously, then you should've tried to do it
+    // yourself, or used GetDataSourceBlocking.
+    return GetDataSource( aURI, false, aDataSource );
+}
+
+NS_IMETHODIMP
+RDFServiceImpl::GetDataSourceBlocking(const char* aURI, nsIRDFDataSource** aDataSource)
+{
+    // Use GetDataSource and ask for a blocking Refresh.
+    return GetDataSource( aURI, true, aDataSource );
+}
+
+nsresult
+RDFServiceImpl::GetDataSource(const char* aURI, bool aBlock, nsIRDFDataSource** aDataSource)
+{
+    NS_PRECONDITION(aURI != nullptr, "null ptr");
+    if (! aURI)
+        return NS_ERROR_NULL_POINTER;
+
+    nsresult rv;
+
+    // Attempt to canonify the URI before we look for it in the
+    // cache. We won't bother doing this on `rdf:' URIs to avoid
+    // useless (and expensive) protocol handler lookups.
+    nsAutoCString spec(aURI);
+
+    if (!StringBeginsWith(spec, NS_LITERAL_CSTRING("rdf:"))) {
+        nsCOMPtr<nsIURI> uri;
+        NS_NewURI(getter_AddRefs(uri), spec);
+        if (uri) {
+            rv = uri->GetSpec(spec);
+            if (NS_FAILED(rv)) return rv;
+        }
+    }
+
+    // First, check the cache to see if we already have this
+    // datasource loaded and initialized.
+    {
+        nsIRDFDataSource* cached =
+            static_cast<nsIRDFDataSource*>(PL_HashTableLookup(mNamedDataSources, spec.get()));
+
+        if (cached) {
+            NS_ADDREF(cached);
+            *aDataSource = cached;
+            return NS_OK;
+        }
+    }
+
+    // Nope. So go to the repository to try to create it.
+    nsCOMPtr<nsIRDFDataSource> ds;
+    if (StringBeginsWith(spec, NS_LITERAL_CSTRING("rdf:"))) {
+        // It's a built-in data source. Convert it to a contract ID.
+        nsAutoCString contractID(
+                NS_LITERAL_CSTRING(NS_RDF_DATASOURCE_CONTRACTID_PREFIX) +
+                Substring(spec, 4, spec.Length() - 4));
+
+        // Strip params to get ``base'' contractID for data source.
+        int32_t p = contractID.FindChar(char16_t('&'));
+        if (p >= 0)
+            contractID.Truncate(p);
+
+        ds = do_GetService(contractID.get(), &rv);
+        if (NS_FAILED(rv)) return rv;
+
+        nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(ds);
+        if (remote) {
+            rv = remote->Init(spec.get());
+            if (NS_FAILED(rv)) return rv;
+        }
+    }
+    else {
+        // Try to load this as an RDF/XML data source
+        ds = do_CreateInstance(kRDFXMLDataSourceCID, &rv);
+        if (NS_FAILED(rv)) return rv;
+
+        nsCOMPtr<nsIRDFRemoteDataSource> remote(do_QueryInterface(ds));
+        NS_ASSERTION(remote, "not a remote RDF/XML data source!");
+        if (! remote) return NS_ERROR_UNEXPECTED;
+
+        rv = remote->Init(spec.get());
+        if (NS_FAILED(rv)) return rv;
+
+        rv = remote->Refresh(aBlock);
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    *aDataSource = ds;
+    NS_ADDREF(*aDataSource);
+    return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult
+RDFServiceImpl::RegisterLiteral(nsIRDFLiteral* aLiteral)
+{
+    const char16_t* value;
+    aLiteral->GetValueConst(&value);
+
+    NS_ASSERTION(!mLiterals.Search(value), "literal already registered");
+
+    PLDHashEntryHdr *hdr = mLiterals.Add(value, fallible);
+    if (! hdr)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    LiteralHashEntry *entry = static_cast<LiteralHashEntry *>(hdr);
+
+    // N.B., we only hold a weak reference to the literal: that
+    // way, the literal can be destroyed when the last refcount
+    // goes away. The single addref that the CreateLiteral() call
+    // made will be owned by the callee.
+    entry->mLiteral = aLiteral;
+    entry->mKey = value;
+
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("rdfserv   register-literal [%p] %s",
+            aLiteral, NS_ConvertUTF16toUTF8(value).get()));
+
+    return NS_OK;
+}
+
+
+nsresult
+RDFServiceImpl::UnregisterLiteral(nsIRDFLiteral* aLiteral)
+{
+    const char16_t* value;
+    aLiteral->GetValueConst(&value);
+
+    NS_ASSERTION(mLiterals.Search(value), "literal was never registered");
+
+    mLiterals.Remove(value);
+
+    // N.B. that we _don't_ release the literal: we only held a weak
+    // reference to it in the hashtable.
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("rdfserv unregister-literal [%p] %s",
+            aLiteral, NS_ConvertUTF16toUTF8(value).get()));
+
+    return NS_OK;
+}
+
+//----------------------------------------------------------------------
+
+nsresult
+RDFServiceImpl::RegisterInt(nsIRDFInt* aInt)
+{
+    int32_t value;
+    aInt->GetValue(&value);
+
+    NS_ASSERTION(!mInts.Search(&value), "int already registered");
+
+    PLDHashEntryHdr *hdr = mInts.Add(&value, fallible);
+    if (! hdr)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    IntHashEntry *entry = static_cast<IntHashEntry *>(hdr);
+
+    // N.B., we only hold a weak reference to the literal: that
+    // way, the literal can be destroyed when the last refcount
+    // goes away. The single addref that the CreateInt() call
+    // made will be owned by the callee.
+    entry->mInt = aInt;
+    entry->mKey = value;
+
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("rdfserv   register-int [%p] %d",
+            aInt, value));
+
+    return NS_OK;
+}
+
+
+nsresult
+RDFServiceImpl::UnregisterInt(nsIRDFInt* aInt)
+{
+    int32_t value;
+    aInt->GetValue(&value);
+
+    NS_ASSERTION(mInts.Search(&value), "int was never registered");
+
+    mInts.Remove(&value);
+
+    // N.B. that we _don't_ release the literal: we only held a weak
+    // reference to it in the hashtable.
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("rdfserv unregister-int [%p] %d",
+            aInt, value));
+
+    return NS_OK;
+}
+
+//----------------------------------------------------------------------
+
+nsresult
+RDFServiceImpl::RegisterDate(nsIRDFDate* aDate)
+{
+    PRTime value;
+    aDate->GetValue(&value);
+
+    NS_ASSERTION(!mDates.Search(&value), "date already registered");
+
+    PLDHashEntryHdr *hdr = mDates.Add(&value, fallible);
+    if (! hdr)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    DateHashEntry *entry = static_cast<DateHashEntry *>(hdr);
+
+    // N.B., we only hold a weak reference to the literal: that
+    // way, the literal can be destroyed when the last refcount
+    // goes away. The single addref that the CreateDate() call
+    // made will be owned by the callee.
+    entry->mDate = aDate;
+    entry->mKey = value;
+
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("rdfserv   register-date [%p] %" PRId64,
+            aDate, value));
+
+    return NS_OK;
+}
+
+
+nsresult
+RDFServiceImpl::UnregisterDate(nsIRDFDate* aDate)
+{
+    PRTime value;
+    aDate->GetValue(&value);
+
+    NS_ASSERTION(mDates.Search(&value), "date was never registered");
+
+    mDates.Remove(&value);
+
+    // N.B. that we _don't_ release the literal: we only held a weak
+    // reference to it in the hashtable.
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("rdfserv unregister-date [%p] %" PRId64,
+            aDate, value));
+
+    return NS_OK;
+}
+
+nsresult
+RDFServiceImpl::RegisterBlob(BlobImpl *aBlob)
+{
+    NS_ASSERTION(!mBlobs.Search(&aBlob->mData), "blob already registered");
+
+    PLDHashEntryHdr *hdr = mBlobs.Add(&aBlob->mData, fallible);
+    if (! hdr)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    BlobHashEntry *entry = static_cast<BlobHashEntry *>(hdr);
+
+    // N.B., we only hold a weak reference to the literal: that
+    // way, the literal can be destroyed when the last refcount
+    // goes away. The single addref that the CreateInt() call
+    // made will be owned by the callee.
+    entry->mBlob = aBlob;
+
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("rdfserv   register-blob [%p] %s",
+            aBlob, aBlob->mData.mBytes));
+
+    return NS_OK;
+}
+
+nsresult
+RDFServiceImpl::UnregisterBlob(BlobImpl *aBlob)
+{
+    NS_ASSERTION(mBlobs.Search(&aBlob->mData), "blob was never registered");
+
+    mBlobs.Remove(&aBlob->mData);
+
+     // N.B. that we _don't_ release the literal: we only held a weak
+     // reference to it in the hashtable.
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("rdfserv unregister-blob [%p] %s",
+            aBlob, aBlob->mData.mBytes));
+
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFService.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsRDFService_h__
+#define nsRDFService_h__
+
+#include "nsIRDFService.h"
+#include "nsWeakReference.h"
+#include "nsIFactory.h"
+#include "nsCOMPtr.h"
+#include "PLDHashTable.h"
+#include "nsString.h"
+
+struct PLHashTable;
+class nsIRDFLiteral;
+class nsIRDFInt;
+class nsIRDFDate;
+class BlobImpl;
+
+class RDFServiceImpl final : public nsIRDFService,
+                             public nsSupportsWeakReference
+{
+protected:
+    PLHashTable* mNamedDataSources;
+    PLDHashTable mResources;
+    PLDHashTable mLiterals;
+    PLDHashTable mInts;
+    PLDHashTable mDates;
+    PLDHashTable mBlobs;
+
+    nsCString mLastURIPrefix;
+    nsCOMPtr<nsIFactory> mLastFactory;
+    nsCOMPtr<nsIFactory> mDefaultResourceFactory;
+
+    RDFServiceImpl();
+    nsresult Init();
+    virtual ~RDFServiceImpl();
+
+public:
+    static RDFServiceImpl *gRDFService NS_VISIBILITY_HIDDEN;
+    static nsresult CreateSingleton(nsISupports* aOuter,
+                                    const nsIID& aIID, void **aResult);
+
+    // nsISupports
+    NS_DECL_ISUPPORTS
+
+    // nsIRDFService
+    NS_DECL_NSIRDFSERVICE
+
+    // Implementation methods
+    nsresult RegisterLiteral(nsIRDFLiteral* aLiteral);
+    nsresult UnregisterLiteral(nsIRDFLiteral* aLiteral);
+    nsresult RegisterInt(nsIRDFInt* aInt);
+    nsresult UnregisterInt(nsIRDFInt* aInt);
+    nsresult RegisterDate(nsIRDFDate* aDate);
+    nsresult UnregisterDate(nsIRDFDate* aDate);
+    nsresult RegisterBlob(BlobImpl* aBlob);
+    nsresult UnregisterBlob(BlobImpl* aBlob);
+
+    nsresult GetDataSource(const char *aURI, bool aBlock, nsIRDFDataSource **aDataSource );
+};
+
+#endif // nsRDFService_h__
new file mode 100644
--- /dev/null
+++ b/rdf/base/nsRDFXMLDataSource.cpp
@@ -0,0 +1,1173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+  A data source that can read itself from and write itself to an
+  RDF/XML stream.
+
+  For more information on the RDF/XML syntax,
+  see http://www.w3.org/TR/REC-rdf-syntax/.
+
+  This code is based on the final W3C Recommendation,
+  http://www.w3.org/TR/1999/REC-rdf-syntax-19990222.
+
+
+  TO DO
+  -----
+
+  1) Right now, the only kind of stream data sources that are _really_
+     writable are "file:" URIs. (In fact, _all_ "file:" URIs are
+     writable, modulo file system permissions; this may lead to some
+     surprising behavior.) Eventually, it'd be great if we could open
+     an arbitrary nsIOutputStream on *any* URL, and Netlib could just
+     do the magic.
+
+  2) Implement a more terse output for "typed" nodes; that is, instead
+     of "RDF:Description type='ns:foo'", just output "ns:foo".
+
+  3) When re-serializing, we "cheat" for Descriptions that talk about
+     inline resources (i.e.., using the `ID' attribute specified in