/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the web scripts access security code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): Harish Dhurvasula * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsWebScriptsAccess.h" #include "nsString.h" #include "nsAutoPtr.h" #include "nsIDOMDocument.h" #include "nsIDOMElement.h" #include "nsIDOMNodeList.h" #include "nsIDOMAttr.h" #include "nsIDOMNamedNodeMap.h" #include "nsIPrincipal.h" #include "nsIURL.h" #include "nsReadableUtils.h" #include "nsIHttpChannel.h" #include "nsNetUtil.h" #include "nsIXPConnect.h" #include "jsapi.h" #include "nsISOAPCall.h" #include "nsISOAPEncoding.h" #include "nsISOAPResponse.h" #include "nsISOAPFault.h" #include "nsISOAPParameter.h" #include "nsISOAPBlock.h" #include "nsIVariant.h" #include "nsIPrefService.h" #include "nsIPrefBranch2.h" #include "nsIJSContextStack.h" #define WSA_GRANT_ACCESS_TO_ALL (1 << 0) #define WSA_FILE_NOT_FOUND (1 << 1) #define WSA_FILE_DELEGATED (1 << 2) #define SERVICE_LISTED_PUBLIC (1 << 3) #define HAS_MASTER_SERVICE_DECISION (1 << 4) static PRBool PR_CALLBACK FreeEntries(nsHashKey *aKey, void *aData, void* aClosure) { AccessInfoEntry* entry = reinterpret_cast(aData); delete entry; return PR_TRUE; } NS_IMPL_ISUPPORTS1(nsWebScriptsAccess, nsIWebScriptsAccessService) nsWebScriptsAccess::nsWebScriptsAccess() : NS_LITERAL_STRING_INIT(kNamespace2002, "http://www.mozilla.org/2002/soap/security") , NS_LITERAL_STRING_INIT(kWebScriptAccessTag, "webScriptAccess") , NS_LITERAL_STRING_INIT(kDelegateTag, "delegate") , NS_LITERAL_STRING_INIT(kAllowTag, "allow") , NS_LITERAL_STRING_INIT(kTypeAttr, "type") , NS_LITERAL_STRING_INIT(kFromAttr, "from") , NS_LITERAL_STRING_INIT(kAny, "any") , NS_LITERAL_STRING_INIT(kIsServicePublic, "isServicePublic") { } nsWebScriptsAccess::~nsWebScriptsAccess() { mAccessInfoTable.Enumerate(FreeEntries, this); } NS_IMETHODIMP nsWebScriptsAccess::CanAccess(nsIURI* aTransportURI, const nsAString& aRequestType, PRBool* aAccessGranted) { *aAccessGranted = PR_FALSE; NS_ENSURE_ARG_POINTER(aTransportURI); nsresult rv; if (!mSecurityManager) { mSecurityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } rv = mSecurityManager->IsCapabilityEnabled("UniversalBrowserRead", aAccessGranted); if (NS_FAILED(rv) || *aAccessGranted) return rv; mServiceURI = aTransportURI; nsXPIDLCString path; aTransportURI->GetPrePath(path); path += '/'; AccessInfoEntry* entry = 0; rv = GetAccessInfoEntry(path, &entry); if (!entry) { rv = mSecurityManager->CheckSameOrigin(0, aTransportURI); if (NS_SUCCEEDED(rv)) { // script security manager has granted access *aAccessGranted = PR_TRUE; return rv; } else { // Script security manager has denied access and has set an // exception. Clear the exception and fall back on the new // security model's decision. nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID())); if (xpc) { nsAXPCNativeCallContext *cc = nsnull; xpc->GetCurrentNativeCallContext(&cc); if (cc) { JSContext* cx; rv = cc->GetJSContext(&cx); NS_ENSURE_SUCCESS(rv, rv); JS_ClearPendingException(cx); cc->SetExceptionWasThrown(PR_FALSE); } } } rv = CreateEntry(path, PR_FALSE, &entry); NS_ENSURE_SUCCESS(rv, rv); } return CheckAccess(entry, aRequestType, aAccessGranted); } NS_IMETHODIMP nsWebScriptsAccess::InvalidateCache(const char* aTransportURI) { if (aTransportURI) { nsCStringKey key(aTransportURI); if (mAccessInfoTable.Exists(&key)) { AccessInfoEntry* entry = reinterpret_cast(mAccessInfoTable.Remove(&key)); delete entry; } } else { // If a URI is not specified then we clear the entire cache. mAccessInfoTable.Enumerate(FreeEntries, this); } return NS_OK; } nsresult nsWebScriptsAccess::GetAccessInfoEntry(const char* aKey, AccessInfoEntry** aEntry) { nsCStringKey key(aKey); *aEntry = reinterpret_cast(mAccessInfoTable.Get(&key)); if (*aEntry && ((*aEntry)->mFlags & WSA_FILE_DELEGATED)) { nsresult rv; nsCOMPtr url(do_QueryInterface(mServiceURI, &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCAutoString path; url->GetPrePath(path); nsCAutoString directory; url->GetDirectory(directory); path += directory; return GetAccessInfoEntry(path.get(), aEntry); } return NS_OK; } nsresult nsWebScriptsAccess::GetDocument(const nsACString& aDeclFilePath, nsIDOMDocument** aDocument) { nsresult rv = NS_OK; if (!mRequest) { mRequest = do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } const nsAString& empty = EmptyString(); rv = mRequest->OpenRequest(NS_LITERAL_CSTRING("GET"), aDeclFilePath, PR_FALSE, empty, empty); NS_ENSURE_SUCCESS(rv, rv); rv = mRequest->OverrideMimeType(NS_LITERAL_CSTRING("application/xml")); NS_ENSURE_SUCCESS(rv, rv); rv = mRequest->Send(0); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr channel; mRequest->GetChannel(getter_AddRefs(channel)); nsCOMPtr httpChannel(do_QueryInterface(channel, &rv)); NS_ENSURE_TRUE(httpChannel, rv); PRBool succeeded; httpChannel->GetRequestSucceeded(&succeeded); if (succeeded) { rv = mRequest->GetResponseXML(aDocument); NS_ENSURE_SUCCESS(rv, rv); } return rv; } nsresult nsWebScriptsAccess::GetCodebaseURI(nsIURI** aCodebase) { nsresult rv = NS_OK; if (!mSecurityManager) { mSecurityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr principal; rv = mSecurityManager->GetSubjectPrincipal(getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); return principal->GetURI(aCodebase); } nsresult nsWebScriptsAccess::CreateEntry(const char* aKey, const PRBool aIsDelegated, AccessInfoEntry** aEntry) { NS_ENSURE_ARG_POINTER(aEntry); *aEntry = nsnull; // create an entry by loading the declaration file ( // web-scripts-access.xml ) and extracting access information from // it. Record the extracted info. for this session nsCOMPtr document; nsresult rv = GetDocument(nsDependentCString(aKey) + NS_LITERAL_CSTRING("web-scripts-access.xml"), getter_AddRefs(document)); NS_ENSURE_SUCCESS(rv, rv); if (document) { // Create an entry by extracting access information from the document. rv = CreateEntry(document, aIsDelegated, aEntry); NS_ENSURE_SUCCESS(rv, rv); // If the document is invalid then an entry will not be created. if (!*aEntry) return NS_OK; } else { rv = CreateEntry(WSA_FILE_NOT_FOUND, aEntry); NS_ENSURE_SUCCESS(rv, rv); } nsCStringKey key(aKey); mAccessInfoTable.Put(&key, *aEntry); NS_ASSERTION(*aEntry, "unexpected: access info entry is null!"); if (*aEntry && ((*aEntry)->mFlags & WSA_FILE_DELEGATED)) rv = CreateDelegatedEntry(aEntry); return rv; } nsresult nsWebScriptsAccess::CreateEntry(nsIDOMDocument* aDocument, const PRBool aIsDelegated, AccessInfoEntry** aEntry) { NS_ENSURE_ARG_POINTER(aDocument); NS_ENSURE_ARG_POINTER(aEntry); *aEntry = nsnull; PRBool valid; nsresult rv = ValidateDocument(aDocument, &valid); NS_ENSURE_SUCCESS(rv, rv); if (!valid) { return NS_OK; // XXX should I return an error instead ? } if (!aIsDelegated) { nsCOMPtr delegateList; rv = aDocument->GetElementsByTagNameNS(kNamespace2002, kDelegateTag, getter_AddRefs(delegateList)); NS_ENSURE_TRUE(delegateList, rv); nsCOMPtr node; delegateList->Item(0, getter_AddRefs(node)); if (node) return CreateEntry(WSA_FILE_DELEGATED, aEntry); } nsCOMPtr allowList; rv = aDocument->GetElementsByTagNameNS(kNamespace2002, kAllowTag, getter_AddRefs(allowList)); NS_ENSURE_TRUE(allowList, rv); PRUint32 count; allowList->GetLength(&count); if (count) { rv = CreateEntry(allowList, aEntry); } else { // Since there are no ALLOW elements present grant access to all. rv = CreateEntry(WSA_GRANT_ACCESS_TO_ALL, aEntry); } return rv; } nsresult nsWebScriptsAccess::CreateEntry(const PRInt32 aFlags, AccessInfoEntry** aEntry) { *aEntry = new AccessInfoEntry(aFlags); NS_ENSURE_TRUE(*aEntry, NS_ERROR_OUT_OF_MEMORY); return NS_OK; } nsresult nsWebScriptsAccess::CreateEntry(nsIDOMNodeList* aAllowList, AccessInfoEntry** aEntry) { NS_ENSURE_ARG_POINTER(aAllowList); NS_ENSURE_ARG_POINTER(aEntry); *aEntry = nsnull; nsAutoPtr entry(new AccessInfoEntry()); NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); PRUint32 count; aAllowList->GetLength(&count); PRUint32 index; nsCOMPtr node; nsAutoString type, from; for (index = 0; index < count; index++) { aAllowList->Item(index, getter_AddRefs(node)); NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED); nsCOMPtr element(do_QueryInterface(node)); element->GetAttribute(kTypeAttr, type); element->GetAttribute(kFromAttr, from); PRBool found_type = !type.IsEmpty(); PRBool found_from = !from.IsEmpty(); if (!found_type && !found_from) { // Minor optimization - If the "type" and "from" // attributes aren't present then no need to check // for attributes in other "allow" elements because // access will be granted to all regardless. entry->mFlags |= WSA_GRANT_ACCESS_TO_ALL; break; } nsAutoPtr access_info(new AccessInfo()); NS_ENSURE_TRUE(access_info, NS_ERROR_OUT_OF_MEMORY); if (found_type) { access_info->mType = ToNewUnicode(type); NS_ENSURE_TRUE(access_info->mType, NS_ERROR_OUT_OF_MEMORY); } if (found_from) { access_info->mFrom = ToNewUnicode(from); NS_ENSURE_TRUE(access_info->mFrom, NS_ERROR_OUT_OF_MEMORY); } entry->mInfoArray.AppendElement(access_info.forget()); type.Truncate(); from.Truncate(); } *aEntry = entry.forget(); return NS_OK; } nsresult nsWebScriptsAccess::CreateDelegatedEntry(AccessInfoEntry** aEntry) { NS_ENSURE_ARG_POINTER(aEntry); *aEntry = nsnull; nsresult rv; nsCOMPtr url(do_QueryInterface(mServiceURI, &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCAutoString path; url->GetPrePath(path); nsCAutoString directory; url->GetDirectory(directory); path += directory; return CreateEntry(path.get(), PR_TRUE, aEntry); } nsresult nsWebScriptsAccess::CheckAccess(AccessInfoEntry* aEntry, const nsAString& aRequestType, PRBool* aAccessGranted) { #ifdef DEBUG static PRBool verified = PR_FALSE; if (!verified) { verified = PR_TRUE; nsWSAUtils::VerifyIsEqual(); } #endif *aAccessGranted = PR_FALSE; NS_ENSURE_ARG_POINTER(aEntry); nsresult rv = NS_OK; if (aEntry->mFlags & WSA_FILE_NOT_FOUND) { if (aEntry->mFlags & HAS_MASTER_SERVICE_DECISION) { if (aEntry->mFlags & SERVICE_LISTED_PUBLIC) *aAccessGranted = PR_TRUE; return rv; } nsCAutoString fqdn; rv = nsWSAUtils::GetOfficialHostName(mServiceURI, fqdn); if (NS_FAILED(rv) || fqdn.IsEmpty()) return rv; PRBool isPublic = PR_FALSE; rv = IsPublicService(fqdn.get(), &isPublic); if (NS_SUCCEEDED(rv)) { if (isPublic) { aEntry->mFlags |= SERVICE_LISTED_PUBLIC; *aAccessGranted = PR_TRUE; } aEntry->mFlags |= HAS_MASTER_SERVICE_DECISION; } return rv; } if (aEntry->mFlags & WSA_GRANT_ACCESS_TO_ALL) { *aAccessGranted = PR_TRUE; return NS_OK; } nsCOMPtr codebase_uri; rv = GetCodebaseURI(getter_AddRefs(codebase_uri)); NS_ENSURE_SUCCESS(rv, rv); nsXPIDLCString tmp; codebase_uri->GetSpec(tmp); const nsAString& codebase = NS_ConvertUTF8toUTF16(tmp); PRUint32 count = aEntry->mInfoArray.Count(); PRUint32 index; for (index = 0; index < count; index++) { AccessInfo* access_info = reinterpret_cast(aEntry->mInfoArray.ElementAt(index)); NS_ASSERTION(access_info, "Entry is missing attribute information"); if (!access_info->mType || kAny.Equals(access_info->mType) || aRequestType.Equals(access_info->mType)) { if (!access_info->mFrom) { // If "from" is not specified, then all scripts will be allowed *aAccessGranted = PR_TRUE; break; } else { if (nsWSAUtils::IsEqual(nsDependentString(access_info->mFrom), codebase)) { *aAccessGranted = PR_TRUE; break; } } } } return NS_OK; } /** * Validation is based on the following syntax: * * * * * . * */ nsresult nsWebScriptsAccess::ValidateDocument(nsIDOMDocument* aDocument, PRBool* aIsValid) { NS_ENSURE_ARG_POINTER(aDocument); *aIsValid = PR_FALSE; nsCOMPtr rootElement; aDocument->GetDocumentElement(getter_AddRefs(rootElement)); nsAutoString ns; nsAutoString name; nsresult rv = rootElement->GetNamespaceURI(ns); if (NS_FAILED(rv)) return rv; rootElement->GetLocalName(name); if (NS_FAILED(rv)) return rv; if (!ns.Equals(kNamespace2002)) { const PRUnichar *inputs[1] = { ns.get() }; return nsWSAUtils::ReportError( NS_LITERAL_STRING("UnsupportedNamespace").get(), inputs, 1); } if (!name.Equals(kWebScriptAccessTag)) { const PRUnichar *inputs[1] = { name.get() }; return nsWSAUtils::ReportError( NS_LITERAL_STRING("UnknownRootElement").get(), inputs, 1); } nsCOMPtr rootNode(do_QueryInterface(rootElement)); NS_ENSURE_TRUE(rootNode, NS_ERROR_UNEXPECTED); nsCOMPtr children; rootNode->GetChildNodes(getter_AddRefs(children)); NS_ENSURE_TRUE(children, NS_ERROR_UNEXPECTED); PRUint32 length; children->GetLength(&length); PRBool hadDelegate = PR_FALSE; nsCOMPtr child, attr; nsCOMPtr attrs; PRUint32 i; for (i = 0; i < length; i++) { children->Item(i, getter_AddRefs(child)); NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED); PRUint16 type; child->GetNodeType(&type); if (nsIDOMNode::ELEMENT_NODE == type) { rv = child->GetNamespaceURI(ns); if (NS_FAILED(rv)) return rv; rv = child->GetLocalName(name); if (NS_FAILED(rv)) return rv; if (!ns.Equals(kNamespace2002)) continue; // ignore elements with different ns. PRBool hasChildNodes = PR_FALSE; if (name.Equals(kDelegateTag)) { // There can me no more than one delegate element. if (hadDelegate) { const PRUnichar *inputs[1] = { name.get() }; return nsWSAUtils::ReportError( NS_LITERAL_STRING("TooManyElements").get(), inputs, 1); } // Make sure that the delegate element is EMPTY. child->HasChildNodes(&hasChildNodes); if (hasChildNodes) { const PRUnichar *inputs[1] = { name.get() }; return nsWSAUtils::ReportError( NS_LITERAL_STRING("ElementNotEmpty").get(), inputs, 1); } hadDelegate = PR_TRUE; } else if (name.Equals(kAllowTag)) { // Make sure that the allow element is EMPTY. child->HasChildNodes(&hasChildNodes); if (hasChildNodes) { const PRUnichar *inputs[1] = { name.get() }; return nsWSAUtils::ReportError( NS_LITERAL_STRING("ElementNotEmpty").get(), inputs, 1); } rv = child->GetAttributes(getter_AddRefs(attrs)); if (NS_FAILED(rv)) return rv; PRUint32 count, i; attrs->GetLength(&count); for (i = 0; i < count; i++) { attrs->Item(i, getter_AddRefs(attr)); if (attr) { rv = attr->GetLocalName(name); if (NS_FAILED(rv)) return rv; if (!name.Equals(kTypeAttr) && !name.Equals(kFromAttr)) { const PRUnichar *inputs[1] = { name.get() }; return nsWSAUtils::ReportError( NS_LITERAL_STRING("UnknownAttribute").get(), inputs, 1); } } } } else { const PRUnichar *inputs[1] = { name.get() }; return nsWSAUtils::ReportError( NS_LITERAL_STRING("UnknownElement").get(), inputs, 1); } } } *aIsValid = PR_TRUE; return NS_OK; } static PRBool IsCharInSet(const char* aSet, const PRUnichar aChar) { PRUnichar ch; while ((ch = *aSet)) { if (aChar == PRUnichar(ch)) { return PR_TRUE; } ++aSet; } return PR_FALSE; } nsresult nsWebScriptsAccess::IsPublicService(const char* aHost, PRBool* aReturn) { *aReturn = PR_FALSE; nsresult rv = NS_OK; // Cache the master services included in the prefs. if (mMasterServices.Count() == 0) { nsCOMPtr prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID); if (!prefBranch) return rv; nsXPIDLCString value; rv = prefBranch->GetCharPref("xml.webservice.security.masterservices", getter_Copies(value)); if (NS_FAILED(rv) || value.IsEmpty()) return NS_OK; nsACString::const_iterator begin, end, curr; nsACString::const_iterator uri_begin, uri_end; value.BeginReading(begin); value.EndReading(end); // Parse the comma separated pref. value static const char* kWhitespace = " \n\r\t\b"; while (begin != end) { curr = begin; // strip leading whitespaces while (IsCharInSet(kWhitespace, *curr) && ++curr != end); uri_begin = curr; // consume until the delimiter ( comma ). while (curr != end && *curr != ',') ++curr; uri_end = curr; // strip trailing whitespaces while (uri_end != uri_begin) { if (!IsCharInSet(kWhitespace, *(--uri_end))) { ++uri_end; // include the last non whitespace char. break; } } const nsAFlatString& transportURI = NS_ConvertUTF8toUTF16(Substring(uri_begin, uri_end)); if (!transportURI.IsEmpty()) mMasterServices.AppendString(transportURI); begin = (*curr == ',' && curr != end) ? ++curr : curr; } } // Do nothing if the pref value turns out to be // strings with nothing but whitespaces. if (mMasterServices.Count() == 0) return rv; // Allocate param block. nsISOAPParameter** bodyBlocks = static_cast (nsMemory::Alloc(1 * sizeof(nsISOAPParameter*))); if (!bodyBlocks) return NS_ERROR_OUT_OF_MEMORY; rv = CallCreateInstance(NS_SOAPPARAMETER_CONTRACTID, &bodyBlocks[0]); if (NS_FAILED(rv)) { NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(0, bodyBlocks); return rv; } nsCOMPtr block = do_QueryInterface(bodyBlocks[0], &rv); if (NS_FAILED(rv)) return rv; block->SetName(NS_LITERAL_STRING("fqdn")); nsCOMPtr variant = do_CreateInstance(NS_VARIANT_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; variant->SetAsString(aHost); block->SetValue(variant); // Create the call instance nsCOMPtr call = do_CreateInstance(NS_SOAPCALL_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr encoding = do_CreateInstance(NS_SOAPENCODING_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; call->SetEncoding(encoding); // Since the SOAP request to the central server will be made // from the native code we can safely override cross-domain // checks by pushing in a null jscontext on the context stack. nsCOMPtr stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1"); if (stack) stack->Push(nsnull); nsCOMPtr response; PRInt32 i, count = mMasterServices.Count(); for (i = 0; i < count && !response; i++) { rv = call->SetTransportURI(*mMasterServices.StringAt(i)); if (NS_FAILED(rv)) break; rv = call->Encode(nsISOAPMessage::VERSION_1_1, kIsServicePublic, kNamespace2002, // The target URI 0, 0, 1, bodyBlocks); if (NS_FAILED(rv)) break; call->Invoke(getter_AddRefs(response)); // XXX - How to handle time out and 404? } if (stack) { JSContext* cx; stack->Pop(&cx); NS_ASSERTION(!cx, "context should be null"); } if (!response) return rv; nsCOMPtr fault; response->GetFault(getter_AddRefs(fault)); if (fault) { nsAutoString faultNamespaceURI, faultCode, faultString; fault->GetFaultNamespaceURI(faultNamespaceURI); fault->GetFaultCode(faultCode); fault->GetFaultString(faultString); const PRUnichar *inputs[5] = { kNamespace2002.get(), kIsServicePublic.get(), faultNamespaceURI.get(), faultCode.get(), faultString.get() }; return nsWSAUtils::ReportError( NS_LITERAL_STRING("SOAPFault").get(), inputs, 5); } else { PRUint32 bodyCount; rv = response->GetParameters(PR_FALSE, &bodyCount, &bodyBlocks); NS_ASSERTION(bodyBlocks, "insufficient information"); if (!bodyBlocks || NS_FAILED(rv)) return rv; NS_ASSERTION(bodyCount == 1, "body seems to contain unnecessary information."); block = do_QueryInterface(bodyBlocks[0], &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr value; rv = block->GetValue(getter_AddRefs(value)); if (NS_FAILED(rv) || !value) return rv; rv = value->GetAsBool(aReturn); } return rv; }