1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/rdf/base/src/nsRDFContentSink.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1489 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + 1.11 + An implementation for an NGLayout-style content sink that knows how 1.12 + to build an RDF content model from XML-serialized RDF. 1.13 + 1.14 + For more information on the RDF/XML syntax, 1.15 + see http://www.w3.org/TR/REC-rdf-syntax/ 1.16 + 1.17 + This code is based on the final W3C Recommendation, 1.18 + http://www.w3.org/TR/1999/REC-rdf-syntax-19990222. 1.19 + 1.20 + Open Issues ------------------ 1.21 + 1.22 + 1) factoring code with nsXMLContentSink - There's some amount of 1.23 + common code between this and the HTML content sink. This will 1.24 + increase as we support more and more HTML elements. How can code 1.25 + from XML/HTML be factored? 1.26 + 1.27 + 2) We don't support the `parseType' attribute on the Description 1.28 + tag; therefore, it is impossible to "inline" raw XML in this 1.29 + implemenation. 1.30 + 1.31 + 3) We don't build the reifications at parse time due to the 1.32 + footprint overhead it would incur for large RDF documents. (It 1.33 + may be possible to attach a "reification" wrapper datasource that 1.34 + would present this information at query-time.) Because of this, 1.35 + the `bagID' attribute is not processed correctly. 1.36 + 1.37 + 4) No attempt is made to `resolve URIs' to a canonical form (the 1.38 + specification hints that an implementation should do this). This 1.39 + is omitted for the obvious reason that we can ill afford to 1.40 + resolve each URI reference. 1.41 + 1.42 +*/ 1.43 + 1.44 +#include "nsCOMPtr.h" 1.45 +#include "nsInterfaceHashtable.h" 1.46 +#include "nsIContentSink.h" 1.47 +#include "nsIRDFContainer.h" 1.48 +#include "nsIRDFContainerUtils.h" 1.49 +#include "nsIRDFContentSink.h" 1.50 +#include "nsIRDFNode.h" 1.51 +#include "nsIRDFService.h" 1.52 +#include "nsIRDFXMLSink.h" 1.53 +#include "nsIServiceManager.h" 1.54 +#include "nsIURL.h" 1.55 +#include "nsIXMLContentSink.h" 1.56 +#include "nsRDFCID.h" 1.57 +#include "nsTArray.h" 1.58 +#include "nsXPIDLString.h" 1.59 +#include "prlog.h" 1.60 +#include "rdf.h" 1.61 +#include "rdfutil.h" 1.62 +#include "nsReadableUtils.h" 1.63 +#include "nsIExpatSink.h" 1.64 +#include "nsCRT.h" 1.65 +#include "nsIAtom.h" 1.66 +#include "nsStaticAtom.h" 1.67 +#include "nsIScriptError.h" 1.68 +#include "nsIDTD.h" 1.69 + 1.70 +using namespace mozilla; 1.71 + 1.72 +/////////////////////////////////////////////////////////////////////// 1.73 + 1.74 +enum RDFContentSinkState { 1.75 + eRDFContentSinkState_InProlog, 1.76 + eRDFContentSinkState_InDocumentElement, 1.77 + eRDFContentSinkState_InDescriptionElement, 1.78 + eRDFContentSinkState_InContainerElement, 1.79 + eRDFContentSinkState_InPropertyElement, 1.80 + eRDFContentSinkState_InMemberElement, 1.81 + eRDFContentSinkState_InEpilog 1.82 +}; 1.83 + 1.84 +enum RDFContentSinkParseMode { 1.85 + eRDFContentSinkParseMode_Resource, 1.86 + eRDFContentSinkParseMode_Literal, 1.87 + eRDFContentSinkParseMode_Int, 1.88 + eRDFContentSinkParseMode_Date 1.89 +}; 1.90 + 1.91 +typedef 1.92 +NS_STDCALL_FUNCPROTO(nsresult, 1.93 + nsContainerTestFn, 1.94 + nsIRDFContainerUtils, IsAlt, 1.95 + (nsIRDFDataSource*, nsIRDFResource*, bool*)); 1.96 + 1.97 +typedef 1.98 +NS_STDCALL_FUNCPROTO(nsresult, 1.99 + nsMakeContainerFn, 1.100 + nsIRDFContainerUtils, MakeAlt, 1.101 + (nsIRDFDataSource*, nsIRDFResource*, nsIRDFContainer**)); 1.102 + 1.103 +class RDFContentSinkImpl : public nsIRDFContentSink, 1.104 + public nsIExpatSink 1.105 +{ 1.106 +public: 1.107 + RDFContentSinkImpl(); 1.108 + virtual ~RDFContentSinkImpl(); 1.109 + 1.110 + // nsISupports 1.111 + NS_DECL_ISUPPORTS 1.112 + NS_DECL_NSIEXPATSINK 1.113 + 1.114 + // nsIContentSink 1.115 + NS_IMETHOD WillParse(void); 1.116 + NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode); 1.117 + NS_IMETHOD DidBuildModel(bool aTerminated); 1.118 + NS_IMETHOD WillInterrupt(void); 1.119 + NS_IMETHOD WillResume(void); 1.120 + NS_IMETHOD SetParser(nsParserBase* aParser); 1.121 + virtual void FlushPendingNotifications(mozFlushType aType) { } 1.122 + NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; } 1.123 + virtual nsISupports *GetTarget() { return nullptr; } 1.124 + 1.125 + // nsIRDFContentSink 1.126 + NS_IMETHOD Init(nsIURI* aURL); 1.127 + NS_IMETHOD SetDataSource(nsIRDFDataSource* aDataSource); 1.128 + NS_IMETHOD GetDataSource(nsIRDFDataSource*& aDataSource); 1.129 + 1.130 + // pseudo constants 1.131 + static int32_t gRefCnt; 1.132 + static nsIRDFService* gRDFService; 1.133 + static nsIRDFContainerUtils* gRDFContainerUtils; 1.134 + static nsIRDFResource* kRDF_type; 1.135 + static nsIRDFResource* kRDF_instanceOf; // XXX should be RDF:type 1.136 + static nsIRDFResource* kRDF_Alt; 1.137 + static nsIRDFResource* kRDF_Bag; 1.138 + static nsIRDFResource* kRDF_Seq; 1.139 + static nsIRDFResource* kRDF_nextVal; 1.140 + 1.141 +#define RDF_ATOM(name_, value_) static nsIAtom* name_; 1.142 +#include "nsRDFContentSinkAtomList.h" 1.143 +#undef RDF_ATOM 1.144 + 1.145 + typedef struct ContainerInfo { 1.146 + nsIRDFResource** mType; 1.147 + nsContainerTestFn mTestFn; 1.148 + nsMakeContainerFn mMakeFn; 1.149 + } ContainerInfo; 1.150 + 1.151 +protected: 1.152 + // Text management 1.153 + void ParseText(nsIRDFNode **aResult); 1.154 + 1.155 + nsresult FlushText(); 1.156 + nsresult AddText(const char16_t* aText, int32_t aLength); 1.157 + 1.158 + // RDF-specific parsing 1.159 + nsresult OpenRDF(const char16_t* aName); 1.160 + nsresult OpenObject(const char16_t* aName ,const char16_t** aAttributes); 1.161 + nsresult OpenProperty(const char16_t* aName, const char16_t** aAttributes); 1.162 + nsresult OpenMember(const char16_t* aName, const char16_t** aAttributes); 1.163 + nsresult OpenValue(const char16_t* aName, const char16_t** aAttributes); 1.164 + 1.165 + nsresult GetIdAboutAttribute(const char16_t** aAttributes, nsIRDFResource** aResource, bool* aIsAnonymous = nullptr); 1.166 + nsresult GetResourceAttribute(const char16_t** aAttributes, nsIRDFResource** aResource); 1.167 + nsresult AddProperties(const char16_t** aAttributes, nsIRDFResource* aSubject, int32_t* aCount = nullptr); 1.168 + void SetParseMode(const char16_t **aAttributes); 1.169 + 1.170 + char16_t* mText; 1.171 + int32_t mTextLength; 1.172 + int32_t mTextSize; 1.173 + 1.174 + /** 1.175 + * From the set of given attributes, this method extracts the 1.176 + * namespace definitions and feeds them to the datasource. 1.177 + * These can then be suggested to the serializer to be used again. 1.178 + * Hopefully, this will keep namespace definitions intact in a 1.179 + * parse - serialize cycle. 1.180 + */ 1.181 + void RegisterNamespaces(const char16_t **aAttributes); 1.182 + 1.183 + /** 1.184 + * Extracts the localname from aExpatName, the name that the Expat parser 1.185 + * passes us. 1.186 + * aLocalName will contain the localname in aExpatName. 1.187 + * The return value is a dependent string containing just the namespace. 1.188 + */ 1.189 + const nsDependentSubstring SplitExpatName(const char16_t *aExpatName, 1.190 + nsIAtom **aLocalName); 1.191 + 1.192 + enum eContainerType { eBag, eSeq, eAlt }; 1.193 + nsresult InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer); 1.194 + nsresult ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer); 1.195 + 1.196 + // The datasource in which we're assigning assertions 1.197 + nsCOMPtr<nsIRDFDataSource> mDataSource; 1.198 + 1.199 + // A hash of all the node IDs referred to 1.200 + nsInterfaceHashtable<nsStringHashKey, nsIRDFResource> mNodeIDMap; 1.201 + 1.202 + // The current state of the content sink 1.203 + RDFContentSinkState mState; 1.204 + RDFContentSinkParseMode mParseMode; 1.205 + 1.206 + // content stack management 1.207 + int32_t 1.208 + PushContext(nsIRDFResource *aContext, 1.209 + RDFContentSinkState aState, 1.210 + RDFContentSinkParseMode aParseMode); 1.211 + 1.212 + nsresult 1.213 + PopContext(nsIRDFResource *&aContext, 1.214 + RDFContentSinkState &aState, 1.215 + RDFContentSinkParseMode &aParseMode); 1.216 + 1.217 + nsIRDFResource* GetContextElement(int32_t ancestor = 0); 1.218 + 1.219 + 1.220 + struct RDFContextStackElement { 1.221 + nsCOMPtr<nsIRDFResource> mResource; 1.222 + RDFContentSinkState mState; 1.223 + RDFContentSinkParseMode mParseMode; 1.224 + }; 1.225 + 1.226 + nsAutoTArray<RDFContextStackElement, 8>* mContextStack; 1.227 + 1.228 + nsIURI* mDocumentURL; 1.229 + 1.230 +private: 1.231 +#ifdef PR_LOGGING 1.232 + static PRLogModuleInfo* gLog; 1.233 +#endif 1.234 +}; 1.235 + 1.236 +int32_t RDFContentSinkImpl::gRefCnt = 0; 1.237 +nsIRDFService* RDFContentSinkImpl::gRDFService; 1.238 +nsIRDFContainerUtils* RDFContentSinkImpl::gRDFContainerUtils; 1.239 +nsIRDFResource* RDFContentSinkImpl::kRDF_type; 1.240 +nsIRDFResource* RDFContentSinkImpl::kRDF_instanceOf; 1.241 +nsIRDFResource* RDFContentSinkImpl::kRDF_Alt; 1.242 +nsIRDFResource* RDFContentSinkImpl::kRDF_Bag; 1.243 +nsIRDFResource* RDFContentSinkImpl::kRDF_Seq; 1.244 +nsIRDFResource* RDFContentSinkImpl::kRDF_nextVal; 1.245 + 1.246 +#ifdef PR_LOGGING 1.247 +PRLogModuleInfo* RDFContentSinkImpl::gLog; 1.248 +#endif 1.249 + 1.250 +//////////////////////////////////////////////////////////////////////// 1.251 + 1.252 +#define RDF_ATOM(name_, value_) nsIAtom* RDFContentSinkImpl::name_; 1.253 +#include "nsRDFContentSinkAtomList.h" 1.254 +#undef RDF_ATOM 1.255 + 1.256 +#define RDF_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_) 1.257 +#include "nsRDFContentSinkAtomList.h" 1.258 +#undef RDF_ATOM 1.259 + 1.260 +static const nsStaticAtom rdf_atoms[] = { 1.261 +#define RDF_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &RDFContentSinkImpl::name_), 1.262 +#include "nsRDFContentSinkAtomList.h" 1.263 +#undef RDF_ATOM 1.264 +}; 1.265 + 1.266 +RDFContentSinkImpl::RDFContentSinkImpl() 1.267 + : mText(nullptr), 1.268 + mTextLength(0), 1.269 + mTextSize(0), 1.270 + mState(eRDFContentSinkState_InProlog), 1.271 + mParseMode(eRDFContentSinkParseMode_Literal), 1.272 + mContextStack(nullptr), 1.273 + mDocumentURL(nullptr) 1.274 +{ 1.275 + if (gRefCnt++ == 0) { 1.276 + NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); 1.277 + nsresult rv = CallGetService(kRDFServiceCID, &gRDFService); 1.278 + 1.279 + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service"); 1.280 + if (NS_SUCCEEDED(rv)) { 1.281 + rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"), 1.282 + &kRDF_type); 1.283 + rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"), 1.284 + &kRDF_instanceOf); 1.285 + rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"), 1.286 + &kRDF_Alt); 1.287 + rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"), 1.288 + &kRDF_Bag); 1.289 + rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"), 1.290 + &kRDF_Seq); 1.291 + rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), 1.292 + &kRDF_nextVal); 1.293 + } 1.294 + 1.295 + NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID); 1.296 + rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils); 1.297 + 1.298 + NS_RegisterStaticAtoms(rdf_atoms); 1.299 + } 1.300 + 1.301 +#ifdef PR_LOGGING 1.302 + if (! gLog) 1.303 + gLog = PR_NewLogModule("nsRDFContentSink"); 1.304 +#endif 1.305 +} 1.306 + 1.307 + 1.308 +RDFContentSinkImpl::~RDFContentSinkImpl() 1.309 +{ 1.310 +#ifdef DEBUG_REFS 1.311 + --gInstanceCount; 1.312 + fprintf(stdout, "%d - RDF: RDFContentSinkImpl\n", gInstanceCount); 1.313 +#endif 1.314 + 1.315 + NS_IF_RELEASE(mDocumentURL); 1.316 + 1.317 + if (mContextStack) { 1.318 + PR_LOG(gLog, PR_LOG_WARNING, 1.319 + ("rdfxml: warning! unclosed tag")); 1.320 + 1.321 + // XXX we should never need to do this, but, we'll write the 1.322 + // code all the same. If someone left the content stack dirty, 1.323 + // pop all the elements off the stack and release them. 1.324 + int32_t i = mContextStack->Length(); 1.325 + while (0 < i--) { 1.326 + nsIRDFResource* resource = nullptr; 1.327 + RDFContentSinkState state; 1.328 + RDFContentSinkParseMode parseMode; 1.329 + PopContext(resource, state, parseMode); 1.330 + 1.331 +#ifdef PR_LOGGING 1.332 + // print some fairly useless debugging info 1.333 + // XXX we should save line numbers on the context stack: this'd 1.334 + // be about 1000x more helpful. 1.335 + if (resource) { 1.336 + nsXPIDLCString uri; 1.337 + resource->GetValue(getter_Copies(uri)); 1.338 + PR_LOG(gLog, PR_LOG_NOTICE, 1.339 + ("rdfxml: uri=%s", (const char*) uri)); 1.340 + } 1.341 +#endif 1.342 + 1.343 + NS_IF_RELEASE(resource); 1.344 + } 1.345 + 1.346 + delete mContextStack; 1.347 + } 1.348 + moz_free(mText); 1.349 + 1.350 + 1.351 + if (--gRefCnt == 0) { 1.352 + NS_IF_RELEASE(gRDFService); 1.353 + NS_IF_RELEASE(gRDFContainerUtils); 1.354 + NS_IF_RELEASE(kRDF_type); 1.355 + NS_IF_RELEASE(kRDF_instanceOf); 1.356 + NS_IF_RELEASE(kRDF_Alt); 1.357 + NS_IF_RELEASE(kRDF_Bag); 1.358 + NS_IF_RELEASE(kRDF_Seq); 1.359 + NS_IF_RELEASE(kRDF_nextVal); 1.360 + } 1.361 +} 1.362 + 1.363 +//////////////////////////////////////////////////////////////////////// 1.364 +// nsISupports interface 1.365 + 1.366 +NS_IMPL_ADDREF(RDFContentSinkImpl) 1.367 +NS_IMPL_RELEASE(RDFContentSinkImpl) 1.368 + 1.369 +NS_IMETHODIMP 1.370 +RDFContentSinkImpl::QueryInterface(REFNSIID iid, void** result) 1.371 +{ 1.372 + NS_PRECONDITION(result, "null ptr"); 1.373 + if (! result) 1.374 + return NS_ERROR_NULL_POINTER; 1.375 + 1.376 + NS_DEFINE_IID(kIContentSinkIID, NS_ICONTENT_SINK_IID); 1.377 + NS_DEFINE_IID(kIExpatSinkIID, NS_IEXPATSINK_IID); 1.378 + NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); 1.379 + NS_DEFINE_IID(kIXMLContentSinkIID, NS_IXMLCONTENT_SINK_IID); 1.380 + NS_DEFINE_IID(kIRDFContentSinkIID, NS_IRDFCONTENTSINK_IID); 1.381 + 1.382 + *result = nullptr; 1.383 + if (iid.Equals(kIRDFContentSinkIID) || 1.384 + iid.Equals(kIXMLContentSinkIID) || 1.385 + iid.Equals(kIContentSinkIID) || 1.386 + iid.Equals(kISupportsIID)) { 1.387 + *result = static_cast<nsIXMLContentSink*>(this); 1.388 + AddRef(); 1.389 + return NS_OK; 1.390 + } 1.391 + else if (iid.Equals(kIExpatSinkIID)) { 1.392 + *result = static_cast<nsIExpatSink*>(this); 1.393 + AddRef(); 1.394 + return NS_OK; 1.395 + } 1.396 + return NS_NOINTERFACE; 1.397 +} 1.398 + 1.399 +NS_IMETHODIMP 1.400 +RDFContentSinkImpl::HandleStartElement(const char16_t *aName, 1.401 + const char16_t **aAtts, 1.402 + uint32_t aAttsCount, 1.403 + int32_t aIndex, 1.404 + uint32_t aLineNumber) 1.405 +{ 1.406 + FlushText(); 1.407 + 1.408 + nsresult rv = NS_ERROR_UNEXPECTED; // XXX 1.409 + 1.410 + RegisterNamespaces(aAtts); 1.411 + 1.412 + switch (mState) { 1.413 + case eRDFContentSinkState_InProlog: 1.414 + rv = OpenRDF(aName); 1.415 + break; 1.416 + 1.417 + case eRDFContentSinkState_InDocumentElement: 1.418 + rv = OpenObject(aName,aAtts); 1.419 + break; 1.420 + 1.421 + case eRDFContentSinkState_InDescriptionElement: 1.422 + rv = OpenProperty(aName,aAtts); 1.423 + break; 1.424 + 1.425 + case eRDFContentSinkState_InContainerElement: 1.426 + rv = OpenMember(aName,aAtts); 1.427 + break; 1.428 + 1.429 + case eRDFContentSinkState_InPropertyElement: 1.430 + case eRDFContentSinkState_InMemberElement: 1.431 + rv = OpenValue(aName,aAtts); 1.432 + break; 1.433 + 1.434 + case eRDFContentSinkState_InEpilog: 1.435 + PR_LOG(gLog, PR_LOG_WARNING, 1.436 + ("rdfxml: unexpected content in epilog at line %d", 1.437 + aLineNumber)); 1.438 + break; 1.439 + } 1.440 + 1.441 + return rv; 1.442 +} 1.443 + 1.444 +NS_IMETHODIMP 1.445 +RDFContentSinkImpl::HandleEndElement(const char16_t *aName) 1.446 +{ 1.447 + FlushText(); 1.448 + 1.449 + nsIRDFResource* resource; 1.450 + if (NS_FAILED(PopContext(resource, mState, mParseMode))) { 1.451 + // XXX parser didn't catch unmatched tags? 1.452 +#ifdef PR_LOGGING 1.453 + if (PR_LOG_TEST(gLog, PR_LOG_WARNING)) { 1.454 + nsAutoString tagStr(aName); 1.455 + char* tagCStr = ToNewCString(tagStr); 1.456 + 1.457 + PR_LogPrint 1.458 + ("rdfxml: extra close tag '%s' at line %d", 1.459 + tagCStr, 0/*XXX fix me */); 1.460 + 1.461 + NS_Free(tagCStr); 1.462 + } 1.463 +#endif 1.464 + 1.465 + return NS_ERROR_UNEXPECTED; // XXX 1.466 + } 1.467 + 1.468 + // If we've just popped a member or property element, _now_ is the 1.469 + // time to add that element to the graph. 1.470 + switch (mState) { 1.471 + case eRDFContentSinkState_InMemberElement: 1.472 + { 1.473 + nsCOMPtr<nsIRDFContainer> container; 1.474 + NS_NewRDFContainer(getter_AddRefs(container)); 1.475 + container->Init(mDataSource, GetContextElement(1)); 1.476 + container->AppendElement(resource); 1.477 + } 1.478 + break; 1.479 + 1.480 + case eRDFContentSinkState_InPropertyElement: 1.481 + { 1.482 + mDataSource->Assert(GetContextElement(1), GetContextElement(0), resource, true); 1.483 + } break; 1.484 + default: 1.485 + break; 1.486 + } 1.487 + 1.488 + if (mContextStack->IsEmpty()) 1.489 + mState = eRDFContentSinkState_InEpilog; 1.490 + 1.491 + NS_IF_RELEASE(resource); 1.492 + return NS_OK; 1.493 +} 1.494 + 1.495 +NS_IMETHODIMP 1.496 +RDFContentSinkImpl::HandleComment(const char16_t *aName) 1.497 +{ 1.498 + return NS_OK; 1.499 +} 1.500 + 1.501 +NS_IMETHODIMP 1.502 +RDFContentSinkImpl::HandleCDataSection(const char16_t *aData, 1.503 + uint32_t aLength) 1.504 +{ 1.505 + return aData ? AddText(aData, aLength) : NS_OK; 1.506 +} 1.507 + 1.508 +NS_IMETHODIMP 1.509 +RDFContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset, 1.510 + const nsAString & aName, 1.511 + const nsAString & aSystemId, 1.512 + const nsAString & aPublicId, 1.513 + nsISupports* aCatalogData) 1.514 +{ 1.515 + return NS_OK; 1.516 +} 1.517 + 1.518 +NS_IMETHODIMP 1.519 +RDFContentSinkImpl::HandleCharacterData(const char16_t *aData, 1.520 + uint32_t aLength) 1.521 +{ 1.522 + return aData ? AddText(aData, aLength) : NS_OK; 1.523 +} 1.524 + 1.525 +NS_IMETHODIMP 1.526 +RDFContentSinkImpl::HandleProcessingInstruction(const char16_t *aTarget, 1.527 + const char16_t *aData) 1.528 +{ 1.529 + return NS_OK; 1.530 +} 1.531 + 1.532 +NS_IMETHODIMP 1.533 +RDFContentSinkImpl::HandleXMLDeclaration(const char16_t *aVersion, 1.534 + const char16_t *aEncoding, 1.535 + int32_t aStandalone) 1.536 +{ 1.537 + return NS_OK; 1.538 +} 1.539 + 1.540 +NS_IMETHODIMP 1.541 +RDFContentSinkImpl::ReportError(const char16_t* aErrorText, 1.542 + const char16_t* aSourceText, 1.543 + nsIScriptError *aError, 1.544 + bool *_retval) 1.545 +{ 1.546 + NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!"); 1.547 + 1.548 + // The expat driver should report the error. 1.549 + *_retval = true; 1.550 + return NS_OK; 1.551 +} 1.552 + 1.553 +//////////////////////////////////////////////////////////////////////// 1.554 +// nsIContentSink interface 1.555 + 1.556 +NS_IMETHODIMP 1.557 +RDFContentSinkImpl::WillParse(void) 1.558 +{ 1.559 + return NS_OK; 1.560 +} 1.561 + 1.562 + 1.563 +NS_IMETHODIMP 1.564 +RDFContentSinkImpl::WillBuildModel(nsDTDMode) 1.565 +{ 1.566 + if (mDataSource) { 1.567 + nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource); 1.568 + if (sink) 1.569 + return sink->BeginLoad(); 1.570 + } 1.571 + return NS_OK; 1.572 +} 1.573 + 1.574 +NS_IMETHODIMP 1.575 +RDFContentSinkImpl::DidBuildModel(bool aTerminated) 1.576 +{ 1.577 + if (mDataSource) { 1.578 + nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource); 1.579 + if (sink) 1.580 + return sink->EndLoad(); 1.581 + } 1.582 + return NS_OK; 1.583 +} 1.584 + 1.585 +NS_IMETHODIMP 1.586 +RDFContentSinkImpl::WillInterrupt(void) 1.587 +{ 1.588 + if (mDataSource) { 1.589 + nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource); 1.590 + if (sink) 1.591 + return sink->Interrupt(); 1.592 + } 1.593 + return NS_OK; 1.594 +} 1.595 + 1.596 +NS_IMETHODIMP 1.597 +RDFContentSinkImpl::WillResume(void) 1.598 +{ 1.599 + if (mDataSource) { 1.600 + nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource); 1.601 + if (sink) 1.602 + return sink->Resume(); 1.603 + } 1.604 + return NS_OK; 1.605 +} 1.606 + 1.607 +NS_IMETHODIMP 1.608 +RDFContentSinkImpl::SetParser(nsParserBase* aParser) 1.609 +{ 1.610 + return NS_OK; 1.611 +} 1.612 + 1.613 +//////////////////////////////////////////////////////////////////////// 1.614 +// nsIRDFContentSink interface 1.615 + 1.616 +NS_IMETHODIMP 1.617 +RDFContentSinkImpl::Init(nsIURI* aURL) 1.618 +{ 1.619 + NS_PRECONDITION(aURL != nullptr, "null ptr"); 1.620 + if (! aURL) 1.621 + return NS_ERROR_NULL_POINTER; 1.622 + 1.623 + mDocumentURL = aURL; 1.624 + NS_ADDREF(aURL); 1.625 + 1.626 + mState = eRDFContentSinkState_InProlog; 1.627 + return NS_OK; 1.628 +} 1.629 + 1.630 +NS_IMETHODIMP 1.631 +RDFContentSinkImpl::SetDataSource(nsIRDFDataSource* aDataSource) 1.632 +{ 1.633 + NS_PRECONDITION(aDataSource != nullptr, "SetDataSource null ptr"); 1.634 + mDataSource = aDataSource; 1.635 + NS_ASSERTION(mDataSource != nullptr,"Couldn't QI RDF DataSource"); 1.636 + return NS_OK; 1.637 +} 1.638 + 1.639 + 1.640 +NS_IMETHODIMP 1.641 +RDFContentSinkImpl::GetDataSource(nsIRDFDataSource*& aDataSource) 1.642 +{ 1.643 + aDataSource = mDataSource; 1.644 + NS_IF_ADDREF(aDataSource); 1.645 + return NS_OK; 1.646 +} 1.647 + 1.648 +//////////////////////////////////////////////////////////////////////// 1.649 +// Text buffering 1.650 + 1.651 +static bool 1.652 +rdf_IsDataInBuffer(char16_t* buffer, int32_t length) 1.653 +{ 1.654 + for (int32_t i = 0; i < length; ++i) { 1.655 + if (buffer[i] == ' ' || 1.656 + buffer[i] == '\t' || 1.657 + buffer[i] == '\n' || 1.658 + buffer[i] == '\r') 1.659 + continue; 1.660 + 1.661 + return true; 1.662 + } 1.663 + return false; 1.664 +} 1.665 + 1.666 +void 1.667 +RDFContentSinkImpl::ParseText(nsIRDFNode **aResult) 1.668 +{ 1.669 + // XXXwaterson wasteful, but we'd need to make a copy anyway to be 1.670 + // able to call nsIRDFService::Get[Resource|Literal|...](). 1.671 + nsAutoString value; 1.672 + value.Append(mText, mTextLength); 1.673 + value.Trim(" \t\n\r"); 1.674 + 1.675 + switch (mParseMode) { 1.676 + case eRDFContentSinkParseMode_Literal: 1.677 + { 1.678 + nsIRDFLiteral *result; 1.679 + gRDFService->GetLiteral(value.get(), &result); 1.680 + *aResult = result; 1.681 + } 1.682 + break; 1.683 + 1.684 + case eRDFContentSinkParseMode_Resource: 1.685 + { 1.686 + nsIRDFResource *result; 1.687 + gRDFService->GetUnicodeResource(value, &result); 1.688 + *aResult = result; 1.689 + } 1.690 + break; 1.691 + 1.692 + case eRDFContentSinkParseMode_Int: 1.693 + { 1.694 + nsresult err; 1.695 + int32_t i = value.ToInteger(&err); 1.696 + nsIRDFInt *result; 1.697 + gRDFService->GetIntLiteral(i, &result); 1.698 + *aResult = result; 1.699 + } 1.700 + break; 1.701 + 1.702 + case eRDFContentSinkParseMode_Date: 1.703 + { 1.704 + PRTime t = rdf_ParseDate(nsDependentCString(NS_LossyConvertUTF16toASCII(value).get(), value.Length())); 1.705 + nsIRDFDate *result; 1.706 + gRDFService->GetDateLiteral(t, &result); 1.707 + *aResult = result; 1.708 + } 1.709 + break; 1.710 + 1.711 + default: 1.712 + NS_NOTREACHED("unknown parse type"); 1.713 + break; 1.714 + } 1.715 +} 1.716 + 1.717 +nsresult 1.718 +RDFContentSinkImpl::FlushText() 1.719 +{ 1.720 + nsresult rv = NS_OK; 1.721 + if (0 != mTextLength) { 1.722 + if (rdf_IsDataInBuffer(mText, mTextLength)) { 1.723 + // XXX if there's anything but whitespace, then we'll 1.724 + // create a text node. 1.725 + 1.726 + switch (mState) { 1.727 + case eRDFContentSinkState_InMemberElement: { 1.728 + nsCOMPtr<nsIRDFNode> node; 1.729 + ParseText(getter_AddRefs(node)); 1.730 + 1.731 + nsCOMPtr<nsIRDFContainer> container; 1.732 + NS_NewRDFContainer(getter_AddRefs(container)); 1.733 + container->Init(mDataSource, GetContextElement(1)); 1.734 + 1.735 + container->AppendElement(node); 1.736 + } break; 1.737 + 1.738 + case eRDFContentSinkState_InPropertyElement: { 1.739 + nsCOMPtr<nsIRDFNode> node; 1.740 + ParseText(getter_AddRefs(node)); 1.741 + 1.742 + mDataSource->Assert(GetContextElement(1), GetContextElement(0), node, true); 1.743 + } break; 1.744 + 1.745 + default: 1.746 + // just ignore it 1.747 + break; 1.748 + } 1.749 + } 1.750 + mTextLength = 0; 1.751 + } 1.752 + return rv; 1.753 +} 1.754 + 1.755 + 1.756 +nsresult 1.757 +RDFContentSinkImpl::AddText(const char16_t* aText, int32_t aLength) 1.758 +{ 1.759 + // Create buffer when we first need it 1.760 + if (0 == mTextSize) { 1.761 + mText = (char16_t *) moz_malloc(sizeof(char16_t) * 4096); 1.762 + if (!mText) { 1.763 + return NS_ERROR_OUT_OF_MEMORY; 1.764 + } 1.765 + mTextSize = 4096; 1.766 + } 1.767 + 1.768 + // Copy data from string into our buffer; grow the buffer as needed. 1.769 + // It never shrinks, but since the content sink doesn't stick around, 1.770 + // this shouldn't be a bloat issue. 1.771 + int32_t amount = mTextSize - mTextLength; 1.772 + if (amount < aLength) { 1.773 + // Grow the buffer by at least a factor of two to prevent thrashing. 1.774 + // Since PR_REALLOC will leave mText intact if the call fails, 1.775 + // don't clobber mText or mTextSize until the new mem is allocated. 1.776 + int32_t newSize = (2 * mTextSize > (mTextSize + aLength)) ? 1.777 + (2 * mTextSize) : (mTextSize + aLength); 1.778 + char16_t* newText = 1.779 + (char16_t *) moz_realloc(mText, sizeof(char16_t) * newSize); 1.780 + if (!newText) 1.781 + return NS_ERROR_OUT_OF_MEMORY; 1.782 + mTextSize = newSize; 1.783 + mText = newText; 1.784 + } 1.785 + memcpy(&mText[mTextLength], aText, sizeof(char16_t) * aLength); 1.786 + mTextLength += aLength; 1.787 + 1.788 + return NS_OK; 1.789 +} 1.790 + 1.791 +bool 1.792 +rdf_RequiresAbsoluteURI(const nsString& uri) 1.793 +{ 1.794 + // cheap shot at figuring out if this requires an absolute url translation 1.795 + return !(StringBeginsWith(uri, NS_LITERAL_STRING("urn:")) || 1.796 + StringBeginsWith(uri, NS_LITERAL_STRING("chrome:"))); 1.797 +} 1.798 + 1.799 +nsresult 1.800 +RDFContentSinkImpl::GetIdAboutAttribute(const char16_t** aAttributes, 1.801 + nsIRDFResource** aResource, 1.802 + bool* aIsAnonymous) 1.803 +{ 1.804 + // This corresponds to the dirty work of production [6.5] 1.805 + nsresult rv = NS_OK; 1.806 + 1.807 + nsAutoString nodeID; 1.808 + 1.809 + nsCOMPtr<nsIAtom> localName; 1.810 + for (; *aAttributes; aAttributes += 2) { 1.811 + const nsDependentSubstring& nameSpaceURI = 1.812 + SplitExpatName(aAttributes[0], getter_AddRefs(localName)); 1.813 + 1.814 + // We'll accept either `ID' or `rdf:ID' (ibid with `about' or 1.815 + // `rdf:about') in the spirit of being liberal towards the 1.816 + // input that we receive. 1.817 + if (!nameSpaceURI.IsEmpty() && 1.818 + !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) { 1.819 + continue; 1.820 + } 1.821 + 1.822 + // XXX you can't specify both, but we'll just pick up the 1.823 + // first thing that was specified and ignore the other. 1.824 + 1.825 + if (localName == kAboutAtom) { 1.826 + if (aIsAnonymous) 1.827 + *aIsAnonymous = false; 1.828 + 1.829 + nsAutoString relURI(aAttributes[1]); 1.830 + if (rdf_RequiresAbsoluteURI(relURI)) { 1.831 + nsAutoCString uri; 1.832 + rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri); 1.833 + if (NS_FAILED(rv)) return rv; 1.834 + 1.835 + return gRDFService->GetResource(uri, 1.836 + aResource); 1.837 + } 1.838 + return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]), 1.839 + aResource); 1.840 + } 1.841 + else if (localName == kIdAtom) { 1.842 + if (aIsAnonymous) 1.843 + *aIsAnonymous = false; 1.844 + // In the spirit of leniency, we do not bother trying to 1.845 + // enforce that this be a valid "XML Name" (see 1.846 + // http://www.w3.org/TR/REC-xml#NT-Nmtoken), as per 1.847 + // 6.21. If we wanted to, this would be where to do it. 1.848 + 1.849 + // Construct an in-line resource whose URI is the 1.850 + // document's URI plus the XML name specified in the ID 1.851 + // attribute. 1.852 + nsAutoCString name; 1.853 + nsAutoCString ref('#'); 1.854 + AppendUTF16toUTF8(aAttributes[1], ref); 1.855 + 1.856 + rv = mDocumentURL->Resolve(ref, name); 1.857 + if (NS_FAILED(rv)) return rv; 1.858 + 1.859 + return gRDFService->GetResource(name, aResource); 1.860 + } 1.861 + else if (localName == kNodeIdAtom) { 1.862 + nodeID.Assign(aAttributes[1]); 1.863 + } 1.864 + else if (localName == kAboutEachAtom) { 1.865 + // XXX we don't deal with aboutEach... 1.866 + //PR_LOG(gLog, PR_LOG_WARNING, 1.867 + // ("rdfxml: ignoring aboutEach at line %d", 1.868 + // aNode.GetSourceLineNumber())); 1.869 + } 1.870 + } 1.871 + 1.872 + // Otherwise, we couldn't find anything, so just gensym one... 1.873 + if (aIsAnonymous) 1.874 + *aIsAnonymous = true; 1.875 + 1.876 + // If nodeID is present, check if we already know about it. If we've seen 1.877 + // the nodeID before, use the same resource, otherwise generate a new one. 1.878 + if (!nodeID.IsEmpty()) { 1.879 + mNodeIDMap.Get(nodeID,aResource); 1.880 + 1.881 + if (!*aResource) { 1.882 + rv = gRDFService->GetAnonymousResource(aResource); 1.883 + mNodeIDMap.Put(nodeID,*aResource); 1.884 + } 1.885 + } 1.886 + else { 1.887 + rv = gRDFService->GetAnonymousResource(aResource); 1.888 + } 1.889 + 1.890 + return rv; 1.891 +} 1.892 + 1.893 +nsresult 1.894 +RDFContentSinkImpl::GetResourceAttribute(const char16_t** aAttributes, 1.895 + nsIRDFResource** aResource) 1.896 +{ 1.897 + nsCOMPtr<nsIAtom> localName; 1.898 + 1.899 + nsAutoString nodeID; 1.900 + 1.901 + for (; *aAttributes; aAttributes += 2) { 1.902 + const nsDependentSubstring& nameSpaceURI = 1.903 + SplitExpatName(aAttributes[0], getter_AddRefs(localName)); 1.904 + 1.905 + // We'll accept `resource' or `rdf:resource', under the spirit 1.906 + // that we should be liberal towards the input that we 1.907 + // receive. 1.908 + if (!nameSpaceURI.IsEmpty() && 1.909 + !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) { 1.910 + continue; 1.911 + } 1.912 + 1.913 + // XXX you can't specify both, but we'll just pick up the 1.914 + // first thing that was specified and ignore the other. 1.915 + 1.916 + if (localName == kResourceAtom) { 1.917 + // XXX Take the URI and make it fully qualified by 1.918 + // sticking it into the document's URL. This may not be 1.919 + // appropriate... 1.920 + nsAutoString relURI(aAttributes[1]); 1.921 + if (rdf_RequiresAbsoluteURI(relURI)) { 1.922 + nsresult rv; 1.923 + nsAutoCString uri; 1.924 + 1.925 + rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri); 1.926 + if (NS_FAILED(rv)) return rv; 1.927 + 1.928 + return gRDFService->GetResource(uri, aResource); 1.929 + } 1.930 + return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]), 1.931 + aResource); 1.932 + } 1.933 + else if (localName == kNodeIdAtom) { 1.934 + nodeID.Assign(aAttributes[1]); 1.935 + } 1.936 + } 1.937 + 1.938 + // If nodeID is present, check if we already know about it. If we've seen 1.939 + // the nodeID before, use the same resource, otherwise generate a new one. 1.940 + if (!nodeID.IsEmpty()) { 1.941 + mNodeIDMap.Get(nodeID,aResource); 1.942 + 1.943 + if (!*aResource) { 1.944 + nsresult rv; 1.945 + rv = gRDFService->GetAnonymousResource(aResource); 1.946 + if (NS_FAILED(rv)) { 1.947 + return rv; 1.948 + } 1.949 + mNodeIDMap.Put(nodeID,*aResource); 1.950 + } 1.951 + return NS_OK; 1.952 + } 1.953 + 1.954 + return NS_ERROR_FAILURE; 1.955 +} 1.956 + 1.957 +nsresult 1.958 +RDFContentSinkImpl::AddProperties(const char16_t** aAttributes, 1.959 + nsIRDFResource* aSubject, 1.960 + int32_t* aCount) 1.961 +{ 1.962 + if (aCount) 1.963 + *aCount = 0; 1.964 + 1.965 + nsCOMPtr<nsIAtom> localName; 1.966 + for (; *aAttributes; aAttributes += 2) { 1.967 + const nsDependentSubstring& nameSpaceURI = 1.968 + SplitExpatName(aAttributes[0], getter_AddRefs(localName)); 1.969 + 1.970 + // skip 'xmlns' directives, these are "meta" information 1.971 + if (nameSpaceURI.EqualsLiteral("http://www.w3.org/2000/xmlns/")) { 1.972 + continue; 1.973 + } 1.974 + 1.975 + // skip `about', `ID', `resource', and 'nodeID' attributes (either with or 1.976 + // without the `rdf:' prefix); these are all "special" and 1.977 + // should've been dealt with by the caller. 1.978 + if (localName == kAboutAtom || localName == kIdAtom || 1.979 + localName == kResourceAtom || localName == kNodeIdAtom) { 1.980 + if (nameSpaceURI.IsEmpty() || 1.981 + nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) 1.982 + continue; 1.983 + } 1.984 + 1.985 + // Skip `parseType', `RDF:parseType', and `NC:parseType'. This 1.986 + // is meta-information that will be handled in SetParseMode. 1.987 + if (localName == kParseTypeAtom) { 1.988 + if (nameSpaceURI.IsEmpty() || 1.989 + nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) || 1.990 + nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) { 1.991 + continue; 1.992 + } 1.993 + } 1.994 + 1.995 + NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI); 1.996 + propertyStr.Append(nsAtomCString(localName)); 1.997 + 1.998 + // Add the assertion to RDF 1.999 + nsCOMPtr<nsIRDFResource> property; 1.1000 + gRDFService->GetResource(propertyStr, getter_AddRefs(property)); 1.1001 + 1.1002 + nsCOMPtr<nsIRDFLiteral> target; 1.1003 + gRDFService->GetLiteral(aAttributes[1], 1.1004 + getter_AddRefs(target)); 1.1005 + 1.1006 + mDataSource->Assert(aSubject, property, target, true); 1.1007 + } 1.1008 + return NS_OK; 1.1009 +} 1.1010 + 1.1011 +void 1.1012 +RDFContentSinkImpl::SetParseMode(const char16_t **aAttributes) 1.1013 +{ 1.1014 + nsCOMPtr<nsIAtom> localName; 1.1015 + for (; *aAttributes; aAttributes += 2) { 1.1016 + const nsDependentSubstring& nameSpaceURI = 1.1017 + SplitExpatName(aAttributes[0], getter_AddRefs(localName)); 1.1018 + 1.1019 + if (localName == kParseTypeAtom) { 1.1020 + nsDependentString v(aAttributes[1]); 1.1021 + 1.1022 + if (nameSpaceURI.IsEmpty() || 1.1023 + nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) { 1.1024 + if (v.EqualsLiteral("Resource")) 1.1025 + mParseMode = eRDFContentSinkParseMode_Resource; 1.1026 + 1.1027 + break; 1.1028 + } 1.1029 + else if (nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) { 1.1030 + if (v.EqualsLiteral("Date")) 1.1031 + mParseMode = eRDFContentSinkParseMode_Date; 1.1032 + else if (v.EqualsLiteral("Integer")) 1.1033 + mParseMode = eRDFContentSinkParseMode_Int; 1.1034 + 1.1035 + break; 1.1036 + } 1.1037 + } 1.1038 + } 1.1039 +} 1.1040 + 1.1041 +//////////////////////////////////////////////////////////////////////// 1.1042 +// RDF-specific routines used to build the model 1.1043 + 1.1044 +nsresult 1.1045 +RDFContentSinkImpl::OpenRDF(const char16_t* aName) 1.1046 +{ 1.1047 + // ensure that we're actually reading RDF by making sure that the 1.1048 + // opening tag is <rdf:RDF>, where "rdf:" corresponds to whatever 1.1049 + // they've declared the standard RDF namespace to be. 1.1050 + nsCOMPtr<nsIAtom> localName; 1.1051 + const nsDependentSubstring& nameSpaceURI = 1.1052 + SplitExpatName(aName, getter_AddRefs(localName)); 1.1053 + 1.1054 + if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) || localName != kRDFAtom) { 1.1055 + // PR_LOG(gLog, PR_LOG_ALWAYS, 1.1056 + // ("rdfxml: expected RDF:RDF at line %d", 1.1057 + // aNode.GetSourceLineNumber())); 1.1058 + 1.1059 + return NS_ERROR_UNEXPECTED; 1.1060 + } 1.1061 + 1.1062 + PushContext(nullptr, mState, mParseMode); 1.1063 + mState = eRDFContentSinkState_InDocumentElement; 1.1064 + return NS_OK; 1.1065 +} 1.1066 + 1.1067 +nsresult 1.1068 +RDFContentSinkImpl::OpenObject(const char16_t* aName, 1.1069 + const char16_t** aAttributes) 1.1070 +{ 1.1071 + // an "object" non-terminal is either a "description", a "typed 1.1072 + // node", or a "container", so this change the content sink's 1.1073 + // state appropriately. 1.1074 + nsCOMPtr<nsIAtom> localName; 1.1075 + const nsDependentSubstring& nameSpaceURI = 1.1076 + SplitExpatName(aName, getter_AddRefs(localName)); 1.1077 + 1.1078 + // Figure out the URI of this object, and create an RDF node for it. 1.1079 + nsCOMPtr<nsIRDFResource> source; 1.1080 + GetIdAboutAttribute(aAttributes, getter_AddRefs(source)); 1.1081 + 1.1082 + // If there is no `ID' or `about', then there's not much we can do. 1.1083 + if (! source) 1.1084 + return NS_ERROR_FAILURE; 1.1085 + 1.1086 + // Push the element onto the context stack 1.1087 + PushContext(source, mState, mParseMode); 1.1088 + 1.1089 + // Now figure out what kind of state transition we need to 1.1090 + // make. We'll either be going into a mode where we parse a 1.1091 + // description or a container. 1.1092 + bool isaTypedNode = true; 1.1093 + 1.1094 + if (nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) { 1.1095 + isaTypedNode = false; 1.1096 + 1.1097 + if (localName == kDescriptionAtom) { 1.1098 + // it's a description 1.1099 + mState = eRDFContentSinkState_InDescriptionElement; 1.1100 + } 1.1101 + else if (localName == kBagAtom) { 1.1102 + // it's a bag container 1.1103 + InitContainer(kRDF_Bag, source); 1.1104 + mState = eRDFContentSinkState_InContainerElement; 1.1105 + } 1.1106 + else if (localName == kSeqAtom) { 1.1107 + // it's a seq container 1.1108 + InitContainer(kRDF_Seq, source); 1.1109 + mState = eRDFContentSinkState_InContainerElement; 1.1110 + } 1.1111 + else if (localName == kAltAtom) { 1.1112 + // it's an alt container 1.1113 + InitContainer(kRDF_Alt, source); 1.1114 + mState = eRDFContentSinkState_InContainerElement; 1.1115 + } 1.1116 + else { 1.1117 + // heh, that's not *in* the RDF namespace: just treat it 1.1118 + // like a typed node 1.1119 + isaTypedNode = true; 1.1120 + } 1.1121 + } 1.1122 + 1.1123 + if (isaTypedNode) { 1.1124 + NS_ConvertUTF16toUTF8 typeStr(nameSpaceURI); 1.1125 + typeStr.Append(nsAtomCString(localName)); 1.1126 + 1.1127 + nsCOMPtr<nsIRDFResource> type; 1.1128 + nsresult rv = gRDFService->GetResource(typeStr, getter_AddRefs(type)); 1.1129 + if (NS_FAILED(rv)) return rv; 1.1130 + 1.1131 + rv = mDataSource->Assert(source, kRDF_type, type, true); 1.1132 + if (NS_FAILED(rv)) return rv; 1.1133 + 1.1134 + mState = eRDFContentSinkState_InDescriptionElement; 1.1135 + } 1.1136 + 1.1137 + AddProperties(aAttributes, source); 1.1138 + return NS_OK; 1.1139 +} 1.1140 + 1.1141 +nsresult 1.1142 +RDFContentSinkImpl::OpenProperty(const char16_t* aName, const char16_t** aAttributes) 1.1143 +{ 1.1144 + nsresult rv; 1.1145 + 1.1146 + // an "object" non-terminal is either a "description", a "typed 1.1147 + // node", or a "container", so this change the content sink's 1.1148 + // state appropriately. 1.1149 + nsCOMPtr<nsIAtom> localName; 1.1150 + const nsDependentSubstring& nameSpaceURI = 1.1151 + SplitExpatName(aName, getter_AddRefs(localName)); 1.1152 + 1.1153 + NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI); 1.1154 + propertyStr.Append(nsAtomCString(localName)); 1.1155 + 1.1156 + nsCOMPtr<nsIRDFResource> property; 1.1157 + rv = gRDFService->GetResource(propertyStr, getter_AddRefs(property)); 1.1158 + if (NS_FAILED(rv)) return rv; 1.1159 + 1.1160 + // See if they've specified a 'resource' attribute, in which case 1.1161 + // they mean *that* to be the object of this property. 1.1162 + nsCOMPtr<nsIRDFResource> target; 1.1163 + GetResourceAttribute(aAttributes, getter_AddRefs(target)); 1.1164 + 1.1165 + bool isAnonymous = false; 1.1166 + 1.1167 + if (! target) { 1.1168 + // See if an 'ID' attribute has been specified, in which case 1.1169 + // this corresponds to the fourth form of [6.12]. 1.1170 + 1.1171 + // XXX strictly speaking, we should reject the RDF/XML as 1.1172 + // invalid if they've specified both an 'ID' and a 'resource' 1.1173 + // attribute. Bah. 1.1174 + 1.1175 + // XXX strictly speaking, 'about=' isn't allowed here, but 1.1176 + // what the hell. 1.1177 + GetIdAboutAttribute(aAttributes, getter_AddRefs(target), &isAnonymous); 1.1178 + } 1.1179 + 1.1180 + if (target) { 1.1181 + // They specified an inline resource for the value of this 1.1182 + // property. Create an RDF resource for the inline resource 1.1183 + // URI, add the properties to it, and attach the inline 1.1184 + // resource to its parent. 1.1185 + int32_t count; 1.1186 + rv = AddProperties(aAttributes, target, &count); 1.1187 + NS_ASSERTION(NS_SUCCEEDED(rv), "problem adding properties"); 1.1188 + if (NS_FAILED(rv)) return rv; 1.1189 + 1.1190 + if (count || !isAnonymous) { 1.1191 + // If the resource was "anonymous" (i.e., they hadn't 1.1192 + // explicitly set an ID or resource attribute), then we'll 1.1193 + // only assert this property from the context element *if* 1.1194 + // there were properties specified on the anonymous 1.1195 + // resource. 1.1196 + rv = mDataSource->Assert(GetContextElement(0), property, target, true); 1.1197 + if (NS_FAILED(rv)) return rv; 1.1198 + } 1.1199 + 1.1200 + // XXX Technically, we should _not_ fall through here and push 1.1201 + // the element onto the stack: this is supposed to be a closed 1.1202 + // node. But right now I'm lazy and the code will just Do The 1.1203 + // Right Thing so long as the RDF is well-formed. 1.1204 + } 1.1205 + 1.1206 + // Push the element onto the context stack and change state. 1.1207 + PushContext(property, mState, mParseMode); 1.1208 + mState = eRDFContentSinkState_InPropertyElement; 1.1209 + SetParseMode(aAttributes); 1.1210 + 1.1211 + return NS_OK; 1.1212 +} 1.1213 + 1.1214 +nsresult 1.1215 +RDFContentSinkImpl::OpenMember(const char16_t* aName, 1.1216 + const char16_t** aAttributes) 1.1217 +{ 1.1218 + // ensure that we're actually reading a member element by making 1.1219 + // sure that the opening tag is <rdf:li>, where "rdf:" corresponds 1.1220 + // to whatever they've declared the standard RDF namespace to be. 1.1221 + nsresult rv; 1.1222 + 1.1223 + nsCOMPtr<nsIAtom> localName; 1.1224 + const nsDependentSubstring& nameSpaceURI = 1.1225 + SplitExpatName(aName, getter_AddRefs(localName)); 1.1226 + 1.1227 + if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) || 1.1228 + localName != kLiAtom) { 1.1229 + PR_LOG(gLog, PR_LOG_ALWAYS, 1.1230 + ("rdfxml: expected RDF:li at line %d", 1.1231 + -1)); // XXX pass in line number 1.1232 + 1.1233 + return NS_ERROR_UNEXPECTED; 1.1234 + } 1.1235 + 1.1236 + // The parent element is the container. 1.1237 + nsIRDFResource* container = GetContextElement(0); 1.1238 + if (! container) 1.1239 + return NS_ERROR_NULL_POINTER; 1.1240 + 1.1241 + nsIRDFResource* resource; 1.1242 + if (NS_SUCCEEDED(rv = GetResourceAttribute(aAttributes, &resource))) { 1.1243 + // Okay, this node has an RDF:resource="..." attribute. That 1.1244 + // means that it's a "referenced item," as covered in [6.29]. 1.1245 + nsCOMPtr<nsIRDFContainer> c; 1.1246 + NS_NewRDFContainer(getter_AddRefs(c)); 1.1247 + c->Init(mDataSource, container); 1.1248 + c->AppendElement(resource); 1.1249 + 1.1250 + // XXX Technically, we should _not_ fall through here and push 1.1251 + // the element onto the stack: this is supposed to be a closed 1.1252 + // node. But right now I'm lazy and the code will just Do The 1.1253 + // Right Thing so long as the RDF is well-formed. 1.1254 + NS_RELEASE(resource); 1.1255 + } 1.1256 + 1.1257 + // Change state. Pushing a null context element is a bit weird, 1.1258 + // but the idea is that there really is _no_ context "property". 1.1259 + // The contained element will use nsIRDFContainer::AppendElement() to add 1.1260 + // the element to the container, which requires only the container 1.1261 + // and the element to be added. 1.1262 + PushContext(nullptr, mState, mParseMode); 1.1263 + mState = eRDFContentSinkState_InMemberElement; 1.1264 + SetParseMode(aAttributes); 1.1265 + 1.1266 + return NS_OK; 1.1267 +} 1.1268 + 1.1269 + 1.1270 +nsresult 1.1271 +RDFContentSinkImpl::OpenValue(const char16_t* aName, const char16_t** aAttributes) 1.1272 +{ 1.1273 + // a "value" can either be an object or a string: we'll only get 1.1274 + // *here* if it's an object, as raw text is added as a leaf. 1.1275 + return OpenObject(aName,aAttributes); 1.1276 +} 1.1277 + 1.1278 +//////////////////////////////////////////////////////////////////////// 1.1279 +// namespace resolution 1.1280 +void 1.1281 +RDFContentSinkImpl::RegisterNamespaces(const char16_t **aAttributes) 1.1282 +{ 1.1283 + nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource); 1.1284 + if (!sink) { 1.1285 + return; 1.1286 + } 1.1287 + NS_NAMED_LITERAL_STRING(xmlns, "http://www.w3.org/2000/xmlns/"); 1.1288 + for (; *aAttributes; aAttributes += 2) { 1.1289 + // check the namespace 1.1290 + const char16_t* attr = aAttributes[0]; 1.1291 + const char16_t* xmlnsP = xmlns.BeginReading(); 1.1292 + while (*attr == *xmlnsP) { 1.1293 + ++attr; 1.1294 + ++xmlnsP; 1.1295 + } 1.1296 + if (*attr != 0xFFFF || 1.1297 + xmlnsP != xmlns.EndReading()) { 1.1298 + continue; 1.1299 + } 1.1300 + // get the localname (or "xmlns" for the default namespace) 1.1301 + const char16_t* endLocal = ++attr; 1.1302 + while (*endLocal && *endLocal != 0xFFFF) { 1.1303 + ++endLocal; 1.1304 + } 1.1305 + nsDependentSubstring lname(attr, endLocal); 1.1306 + nsCOMPtr<nsIAtom> preferred = do_GetAtom(lname); 1.1307 + if (preferred == kXMLNSAtom) { 1.1308 + preferred = nullptr; 1.1309 + } 1.1310 + sink->AddNameSpace(preferred, nsDependentString(aAttributes[1])); 1.1311 + } 1.1312 +} 1.1313 + 1.1314 +//////////////////////////////////////////////////////////////////////// 1.1315 +// Qualified name resolution 1.1316 + 1.1317 +const nsDependentSubstring 1.1318 +RDFContentSinkImpl::SplitExpatName(const char16_t *aExpatName, 1.1319 + nsIAtom **aLocalName) 1.1320 +{ 1.1321 + /** 1.1322 + * Expat can send the following: 1.1323 + * localName 1.1324 + * namespaceURI<separator>localName 1.1325 + * namespaceURI<separator>localName<separator>prefix 1.1326 + * 1.1327 + * and we use 0xFFFF for the <separator>. 1.1328 + * 1.1329 + */ 1.1330 + 1.1331 + const char16_t *uriEnd = aExpatName; 1.1332 + const char16_t *nameStart = aExpatName; 1.1333 + const char16_t *pos; 1.1334 + for (pos = aExpatName; *pos; ++pos) { 1.1335 + if (*pos == 0xFFFF) { 1.1336 + if (uriEnd != aExpatName) { 1.1337 + break; 1.1338 + } 1.1339 + 1.1340 + uriEnd = pos; 1.1341 + nameStart = pos + 1; 1.1342 + } 1.1343 + } 1.1344 + 1.1345 + const nsDependentSubstring& nameSpaceURI = Substring(aExpatName, uriEnd); 1.1346 + *aLocalName = NS_NewAtom(Substring(nameStart, pos)).take(); 1.1347 + return nameSpaceURI; 1.1348 +} 1.1349 + 1.1350 +nsresult 1.1351 +RDFContentSinkImpl::InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer) 1.1352 +{ 1.1353 + // Do the right kind of initialization based on the container 1.1354 + // 'type' resource, and the state of the container (i.e., 'make' a 1.1355 + // new container vs. 'reinitialize' the container). 1.1356 + nsresult rv; 1.1357 + 1.1358 + static const ContainerInfo gContainerInfo[] = { 1.1359 + { &RDFContentSinkImpl::kRDF_Alt, &nsIRDFContainerUtils::IsAlt, &nsIRDFContainerUtils::MakeAlt }, 1.1360 + { &RDFContentSinkImpl::kRDF_Bag, &nsIRDFContainerUtils::IsBag, &nsIRDFContainerUtils::MakeBag }, 1.1361 + { &RDFContentSinkImpl::kRDF_Seq, &nsIRDFContainerUtils::IsSeq, &nsIRDFContainerUtils::MakeSeq }, 1.1362 + { 0, 0, 0 }, 1.1363 + }; 1.1364 + 1.1365 + for (const ContainerInfo* info = gContainerInfo; info->mType != 0; ++info) { 1.1366 + if (*info->mType != aContainerType) 1.1367 + continue; 1.1368 + 1.1369 + bool isContainer; 1.1370 + rv = (gRDFContainerUtils->*(info->mTestFn))(mDataSource, aContainer, &isContainer); 1.1371 + if (isContainer) { 1.1372 + rv = ReinitContainer(aContainerType, aContainer); 1.1373 + } 1.1374 + else { 1.1375 + rv = (gRDFContainerUtils->*(info->mMakeFn))(mDataSource, aContainer, nullptr); 1.1376 + } 1.1377 + return rv; 1.1378 + } 1.1379 + 1.1380 + NS_NOTREACHED("not an RDF container type"); 1.1381 + return NS_ERROR_FAILURE; 1.1382 +} 1.1383 + 1.1384 + 1.1385 + 1.1386 +nsresult 1.1387 +RDFContentSinkImpl::ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer) 1.1388 +{ 1.1389 + // Mega-kludge to deal with the fact that Make[Seq|Alt|Bag] is 1.1390 + // idempotent, and as such, containers will have state (e.g., 1.1391 + // RDF:nextVal) maintained in the graph across loads. This 1.1392 + // re-initializes each container's RDF:nextVal to '1', and 'marks' 1.1393 + // the container as such. 1.1394 + nsresult rv; 1.1395 + 1.1396 + nsCOMPtr<nsIRDFLiteral> one; 1.1397 + rv = gRDFService->GetLiteral(MOZ_UTF16("1"), getter_AddRefs(one)); 1.1398 + if (NS_FAILED(rv)) return rv; 1.1399 + 1.1400 + // Re-initialize the 'nextval' property 1.1401 + nsCOMPtr<nsIRDFNode> nextval; 1.1402 + rv = mDataSource->GetTarget(aContainer, kRDF_nextVal, true, getter_AddRefs(nextval)); 1.1403 + if (NS_FAILED(rv)) return rv; 1.1404 + 1.1405 + rv = mDataSource->Change(aContainer, kRDF_nextVal, nextval, one); 1.1406 + if (NS_FAILED(rv)) return rv; 1.1407 + 1.1408 + // Re-mark as a container. XXX should be kRDF_type 1.1409 + rv = mDataSource->Assert(aContainer, kRDF_instanceOf, aContainerType, true); 1.1410 + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to mark container as such"); 1.1411 + if (NS_FAILED(rv)) return rv; 1.1412 + 1.1413 + return NS_OK; 1.1414 +} 1.1415 + 1.1416 +//////////////////////////////////////////////////////////////////////// 1.1417 +// Content stack management 1.1418 + 1.1419 +nsIRDFResource* 1.1420 +RDFContentSinkImpl::GetContextElement(int32_t ancestor /* = 0 */) 1.1421 +{ 1.1422 + if ((nullptr == mContextStack) || 1.1423 + (uint32_t(ancestor) >= mContextStack->Length())) { 1.1424 + return nullptr; 1.1425 + } 1.1426 + 1.1427 + return mContextStack->ElementAt( 1.1428 + mContextStack->Length()-ancestor-1).mResource; 1.1429 +} 1.1430 + 1.1431 +int32_t 1.1432 +RDFContentSinkImpl::PushContext(nsIRDFResource *aResource, 1.1433 + RDFContentSinkState aState, 1.1434 + RDFContentSinkParseMode aParseMode) 1.1435 +{ 1.1436 + if (! mContextStack) { 1.1437 + mContextStack = new nsAutoTArray<RDFContextStackElement, 8>(); 1.1438 + if (! mContextStack) 1.1439 + return 0; 1.1440 + } 1.1441 + 1.1442 + RDFContextStackElement* e = mContextStack->AppendElement(); 1.1443 + if (! e) 1.1444 + return mContextStack->Length(); 1.1445 + 1.1446 + e->mResource = aResource; 1.1447 + e->mState = aState; 1.1448 + e->mParseMode = aParseMode; 1.1449 + 1.1450 + return mContextStack->Length(); 1.1451 +} 1.1452 + 1.1453 +nsresult 1.1454 +RDFContentSinkImpl::PopContext(nsIRDFResource *&aResource, 1.1455 + RDFContentSinkState &aState, 1.1456 + RDFContentSinkParseMode &aParseMode) 1.1457 +{ 1.1458 + if ((nullptr == mContextStack) || 1.1459 + (mContextStack->IsEmpty())) { 1.1460 + return NS_ERROR_NULL_POINTER; 1.1461 + } 1.1462 + 1.1463 + uint32_t i = mContextStack->Length() - 1; 1.1464 + RDFContextStackElement &e = mContextStack->ElementAt(i); 1.1465 + 1.1466 + aResource = e.mResource; 1.1467 + NS_IF_ADDREF(aResource); 1.1468 + aState = e.mState; 1.1469 + aParseMode = e.mParseMode; 1.1470 + 1.1471 + mContextStack->RemoveElementAt(i); 1.1472 + return NS_OK; 1.1473 +} 1.1474 + 1.1475 + 1.1476 +//////////////////////////////////////////////////////////////////////// 1.1477 + 1.1478 +nsresult 1.1479 +NS_NewRDFContentSink(nsIRDFContentSink** aResult) 1.1480 +{ 1.1481 + NS_PRECONDITION(aResult != nullptr, "null ptr"); 1.1482 + if (! aResult) 1.1483 + return NS_ERROR_NULL_POINTER; 1.1484 + 1.1485 + RDFContentSinkImpl* sink = new RDFContentSinkImpl(); 1.1486 + if (! sink) 1.1487 + return NS_ERROR_OUT_OF_MEMORY; 1.1488 + 1.1489 + NS_ADDREF(sink); 1.1490 + *aResult = sink; 1.1491 + return NS_OK; 1.1492 +}