michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: * michael@0: * michael@0: * This Original Code has been modified by IBM Corporation. michael@0: * Modifications made by IBM described herein are michael@0: * Copyright (c) International Business Machines michael@0: * Corporation, 2000 michael@0: * michael@0: * Modifications to Mozilla code or documentation michael@0: * identified per MPL Section 3.3 michael@0: * michael@0: * Date Modified by Description of modification michael@0: * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink michael@0: * use in OS2 michael@0: */ michael@0: michael@0: michael@0: /* michael@0: michael@0: A package of routines shared by the XUL content code. michael@0: michael@0: */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMXULCommandDispatcher.h" michael@0: #include "nsIDOMXULDocument.h" michael@0: #include "nsIRDFNode.h" michael@0: #include "nsIRDFService.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIURL.h" michael@0: #include "nsXULContentUtils.h" michael@0: #include "nsLayoutCID.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsRDFCID.h" michael@0: #include "nsString.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "prlog.h" michael@0: #include "prtime.h" michael@0: #include "rdf.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIDateTimeFormat.h" michael@0: #include "nsDateTimeFormatCID.h" michael@0: #include "nsIScriptableDateFormat.h" michael@0: #include "nsICollation.h" michael@0: #include "nsCollationCID.h" michael@0: #include "nsILocale.h" michael@0: #include "nsILocaleService.h" michael@0: #include "nsIConsoleService.h" michael@0: #include "nsEscape.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: //------------------------------------------------------------------------ michael@0: michael@0: nsIRDFService* nsXULContentUtils::gRDF; michael@0: nsIDateTimeFormat* nsXULContentUtils::gFormat; michael@0: nsICollation *nsXULContentUtils::gCollation; michael@0: michael@0: #ifdef PR_LOGGING michael@0: extern PRLogModuleInfo* gXULTemplateLog; michael@0: #endif michael@0: michael@0: #define XUL_RESOURCE(ident, uri) nsIRDFResource* nsXULContentUtils::ident michael@0: #define XUL_LITERAL(ident, val) nsIRDFLiteral* nsXULContentUtils::ident michael@0: #include "nsXULResourceList.h" michael@0: #undef XUL_RESOURCE michael@0: #undef XUL_LITERAL michael@0: michael@0: //------------------------------------------------------------------------ michael@0: // Constructors n' stuff michael@0: // michael@0: michael@0: nsresult michael@0: nsXULContentUtils::Init() michael@0: { michael@0: static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); michael@0: nsresult rv = CallGetService(kRDFServiceCID, &gRDF); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: #define XUL_RESOURCE(ident, uri) \ michael@0: PR_BEGIN_MACRO \ michael@0: rv = gRDF->GetResource(NS_LITERAL_CSTRING(uri), &(ident)); \ michael@0: if (NS_FAILED(rv)) return rv; \ michael@0: PR_END_MACRO michael@0: michael@0: #define XUL_LITERAL(ident, val) \ michael@0: PR_BEGIN_MACRO \ michael@0: rv = gRDF->GetLiteral(NS_LITERAL_STRING(val).get(), &(ident)); \ michael@0: if (NS_FAILED(rv)) return rv; \ michael@0: PR_END_MACRO michael@0: michael@0: #include "nsXULResourceList.h" michael@0: #undef XUL_RESOURCE michael@0: #undef XUL_LITERAL michael@0: michael@0: rv = CallCreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &gFormat); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsXULContentUtils::Finish() michael@0: { michael@0: NS_IF_RELEASE(gRDF); michael@0: michael@0: #define XUL_RESOURCE(ident, uri) NS_IF_RELEASE(ident) michael@0: #define XUL_LITERAL(ident, val) NS_IF_RELEASE(ident) michael@0: #include "nsXULResourceList.h" michael@0: #undef XUL_RESOURCE michael@0: #undef XUL_LITERAL michael@0: michael@0: NS_IF_RELEASE(gFormat); michael@0: NS_IF_RELEASE(gCollation); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsICollation* michael@0: nsXULContentUtils::GetCollation() michael@0: { michael@0: if (!gCollation) { michael@0: nsresult rv; michael@0: michael@0: // get a locale service michael@0: nsCOMPtr localeService = michael@0: do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr locale; michael@0: rv = localeService->GetApplicationLocale(getter_AddRefs(locale)); michael@0: if (NS_SUCCEEDED(rv) && locale) { michael@0: nsCOMPtr colFactory = michael@0: do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID); michael@0: if (colFactory) { michael@0: rv = colFactory->CreateCollation(locale, &gCollation); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), michael@0: "couldn't create collation instance"); michael@0: } else michael@0: NS_ERROR("couldn't create instance of collation factory"); michael@0: } else michael@0: NS_ERROR("unable to get application locale"); michael@0: } else michael@0: NS_ERROR("couldn't get locale factory"); michael@0: } michael@0: michael@0: return gCollation; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------ michael@0: michael@0: nsresult michael@0: nsXULContentUtils::FindChildByTag(nsIContent* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aTag, michael@0: nsIContent** aResult) michael@0: { michael@0: for (nsIContent* child = aElement->GetFirstChild(); michael@0: child; michael@0: child = child->GetNextSibling()) { michael@0: michael@0: if (child->NodeInfo()->Equals(aTag, aNameSpaceID)) { michael@0: NS_ADDREF(*aResult = child); michael@0: michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: *aResult = nullptr; michael@0: return NS_RDF_NO_VALUE; // not found michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsXULContentUtils::GetElementResource(nsIContent* aElement, nsIRDFResource** aResult) michael@0: { michael@0: // Perform a reverse mapping from an element in the content model michael@0: // to an RDF resource. michael@0: nsresult rv; michael@0: michael@0: char16_t buf[128]; michael@0: nsFixedString id(buf, ArrayLength(buf), 0); michael@0: michael@0: // Whoa. Why the "id" attribute? What if it's not even a XUL michael@0: // element? This is totally bogus! michael@0: aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); michael@0: if (id.IsEmpty()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Since the element will store its ID attribute as a document-relative value, michael@0: // we may need to qualify it first... michael@0: nsCOMPtr doc = aElement->GetDocument(); michael@0: NS_ASSERTION(doc, "element is not in any document"); michael@0: if (! doc) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: rv = nsXULContentUtils::MakeElementResource(doc, id, aResult); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /* michael@0: Note: this routine is similar, yet distinctly different from, nsBookmarksService::GetTextForNode michael@0: */ michael@0: michael@0: nsresult michael@0: nsXULContentUtils::GetTextForNode(nsIRDFNode* aNode, nsAString& aResult) michael@0: { michael@0: if (! aNode) { michael@0: aResult.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: // Literals are the most common, so try these first. michael@0: nsCOMPtr literal = do_QueryInterface(aNode); michael@0: if (literal) { michael@0: const char16_t* p; michael@0: rv = literal->GetValueConst(&p); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: aResult = p; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr dateLiteral = do_QueryInterface(aNode); michael@0: if (dateLiteral) { michael@0: PRTime value; michael@0: rv = dateLiteral->GetValue(&value); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsAutoString str; michael@0: rv = gFormat->FormatPRTime(nullptr /* nsILocale* locale */, michael@0: kDateFormatShort, michael@0: kTimeFormatSeconds, michael@0: value, michael@0: str); michael@0: aResult.Assign(str); michael@0: michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr intLiteral = do_QueryInterface(aNode); michael@0: if (intLiteral) { michael@0: int32_t value; michael@0: rv = intLiteral->GetValue(&value); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: aResult.Truncate(); michael@0: nsAutoString intStr; michael@0: intStr.AppendInt(value, 10); michael@0: aResult.Append(intStr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsCOMPtr resource = do_QueryInterface(aNode); michael@0: if (resource) { michael@0: const char* p; michael@0: rv = resource->GetValueConst(&p); michael@0: if (NS_FAILED(rv)) return rv; michael@0: CopyUTF8toUTF16(p, aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ERROR("not a resource or a literal"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULContentUtils::MakeElementURI(nsIDocument* aDocument, michael@0: const nsAString& aElementID, michael@0: nsCString& aURI) michael@0: { michael@0: // Convert an element's ID to a URI that can be used to refer to michael@0: // the element in the XUL graph. michael@0: michael@0: nsIURI *docURI = aDocument->GetDocumentURI(); michael@0: NS_ENSURE_TRUE(docURI, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsRefPtr docURIClone; michael@0: nsresult rv = docURI->Clone(getter_AddRefs(docURIClone)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = docURIClone->SetRef(NS_ConvertUTF16toUTF8(aElementID)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: return docURIClone->GetSpec(aURI); michael@0: } michael@0: michael@0: // docURIClone is apparently immutable. Fine - we can append ref manually. michael@0: rv = docURI->GetSpec(aURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString ref; michael@0: NS_EscapeURL(NS_ConvertUTF16toUTF8(aElementID), esc_FilePath | esc_AlwaysCopy, ref); michael@0: michael@0: aURI.Append('#'); michael@0: aURI.Append(ref); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsXULContentUtils::MakeElementResource(nsIDocument* aDocument, const nsAString& aID, nsIRDFResource** aResult) michael@0: { michael@0: nsresult rv; michael@0: michael@0: char buf[256]; michael@0: nsFixedCString uri(buf, sizeof(buf), 0); michael@0: rv = MakeElementURI(aDocument, aID, uri); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = gRDF->GetResource(uri, aResult); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create resource"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: michael@0: nsresult michael@0: nsXULContentUtils::MakeElementID(nsIDocument* aDocument, michael@0: const nsACString& aURI, michael@0: nsAString& aElementID) michael@0: { michael@0: // Convert a URI into an element ID that can be accessed from the michael@0: // DOM APIs. michael@0: nsCOMPtr uri; michael@0: nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI, michael@0: aDocument->GetDocumentCharacterSet().get()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString ref; michael@0: uri->GetRef(ref); michael@0: CopyUTF8toUTF16(ref, aElementID); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULContentUtils::GetResource(int32_t aNameSpaceID, nsIAtom* aAttribute, nsIRDFResource** aResult) michael@0: { michael@0: // construct a fully-qualified URI from the namespace/tag pair. michael@0: NS_PRECONDITION(aAttribute != nullptr, "null ptr"); michael@0: if (! aAttribute) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: return GetResource(aNameSpaceID, nsDependentAtomString(aAttribute), michael@0: aResult); michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsXULContentUtils::GetResource(int32_t aNameSpaceID, const nsAString& aAttribute, nsIRDFResource** aResult) michael@0: { michael@0: // construct a fully-qualified URI from the namespace/tag pair. michael@0: michael@0: // XXX should we allow nodes with no namespace??? michael@0: //NS_PRECONDITION(aNameSpaceID != kNameSpaceID_Unknown, "no namespace"); michael@0: //if (aNameSpaceID == kNameSpaceID_Unknown) michael@0: // return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsresult rv; michael@0: michael@0: char16_t buf[256]; michael@0: nsFixedString uri(buf, ArrayLength(buf), 0); michael@0: if (aNameSpaceID != kNameSpaceID_Unknown && aNameSpaceID != kNameSpaceID_None) { michael@0: rv = nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, uri); michael@0: // XXX ignore failure; treat as "no namespace" michael@0: } michael@0: michael@0: // XXX check to see if we need to insert a '/' or a '#'. Oy. michael@0: if (!uri.IsEmpty() && uri.Last() != '#' && uri.Last() != '/' && aAttribute.First() != '#') michael@0: uri.Append(char16_t('#')); michael@0: michael@0: uri.Append(aAttribute); michael@0: michael@0: rv = gRDF->GetUnicodeResource(uri, aResult); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsXULContentUtils::SetCommandUpdater(nsIDocument* aDocument, nsIContent* aElement) michael@0: { michael@0: // Deal with setting up a 'commandupdater'. Pulls the 'events' and michael@0: // 'targets' attributes off of aElement, and adds it to the michael@0: // document's command dispatcher. michael@0: NS_PRECONDITION(aDocument != nullptr, "null ptr"); michael@0: if (! aDocument) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_PRECONDITION(aElement != nullptr, "null ptr"); michael@0: if (! aElement) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr xuldoc = do_QueryInterface(aDocument); michael@0: NS_ASSERTION(xuldoc != nullptr, "not a xul document"); michael@0: if (! xuldoc) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsCOMPtr dispatcher; michael@0: rv = xuldoc->GetCommandDispatcher(getter_AddRefs(dispatcher)); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get dispatcher"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: NS_ASSERTION(dispatcher != nullptr, "no dispatcher"); michael@0: if (! dispatcher) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsAutoString events; michael@0: aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::events, events); michael@0: if (events.IsEmpty()) michael@0: events.AssignLiteral("*"); michael@0: michael@0: nsAutoString targets; michael@0: aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::targets, targets); michael@0: michael@0: if (targets.IsEmpty()) michael@0: targets.AssignLiteral("*"); michael@0: michael@0: nsCOMPtr domelement = do_QueryInterface(aElement); michael@0: NS_ASSERTION(domelement != nullptr, "not a DOM element"); michael@0: if (! domelement) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: rv = dispatcher->AddCommandUpdater(domelement, events, targets); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsXULContentUtils::LogTemplateError(const char* aStr) michael@0: { michael@0: nsAutoString message; michael@0: message.AssignLiteral("Error parsing template: "); michael@0: message.Append(NS_ConvertUTF8toUTF16(aStr).get()); michael@0: michael@0: nsCOMPtr cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); michael@0: if (cs) { michael@0: cs->LogStringMessage(message.get()); michael@0: PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS, ("Error parsing template: %s", aStr)); michael@0: } michael@0: }