From 15257af8d8df11b01c487f9063926aad916cc01d Mon Sep 17 00:00:00 2001 From: "aaronr%us.ibm.com" Date: Tue, 4 Mar 2008 22:47:47 +0000 Subject: [PATCH] [XForms] NPOTDB We need to be able to set context information for xforms events. Bug 280423, p=msterlin r=surkov+olli sr=jonas a=dveditz git-svn-id: svn://10.0.0.236/branches/MOZILLA_1_8_BRANCH@247050 18797224-902f-48f8-a5cc-f745e15eee43 --- .../source/xpath/XFormsFunctionCall.cpp | 45 ++ .../source/xpath/XFormsFunctions.h | 3 +- .../source/xpath/nsIXFormsUtilityService.h | 19 +- .../source/xpath/nsXFormsXPathEvaluator.cpp | 5 + .../source/xpath/txXPathAtomList.h | 1 + mozilla/extensions/xforms/Makefile.in | 4 + .../xforms/nsIXFormsActionModuleElement.idl | 8 +- .../xforms/nsXFormsActionElement.cpp | 7 + .../xforms/nsXFormsActionModuleBase.cpp | 8 + .../xforms/nsXFormsActionModuleBase.h | 6 + .../extensions/xforms/nsXFormsContextInfo.cpp | 6 +- .../extensions/xforms/nsXFormsContextInfo.h | 1 + .../xforms/nsXFormsInsertDeleteElement.cpp | 63 ++- .../xforms/nsXFormsLabelElement.cpp | 42 +- .../xforms/nsXFormsMessageElement.cpp | 48 ++- .../xforms/nsXFormsModelElement.cpp | 49 ++- .../extensions/xforms/nsXFormsModelElement.h | 10 + .../xforms/nsXFormsSubmissionElement.cpp | 383 ++++++++++++++++-- .../xforms/nsXFormsSubmissionElement.h | 35 +- .../xforms/nsXFormsUtilityService.cpp | 72 ++++ mozilla/extensions/xforms/nsXFormsUtils.cpp | 52 ++- mozilla/extensions/xforms/nsXFormsUtils.h | 9 +- 22 files changed, 806 insertions(+), 70 deletions(-) diff --git a/mozilla/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp b/mozilla/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp index fb5a13cce66..45079046d9f 100644 --- a/mozilla/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp +++ b/mozilla/extensions/transformiix/source/xpath/XFormsFunctionCall.cpp @@ -21,6 +21,7 @@ * * Contributor(s): * Aaron Reed + * Merle Sterling * * 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 @@ -530,6 +531,45 @@ XFormsFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult) *aResult = resultSet; NS_ADDREF(*aResult); + return NS_OK; + } + case EVENT: + { + // The Event function returns a nodeset of context info associated with + // an event. + nsresult rv; + if (!requireParams(1, 1, aContext)) + return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; + + nsRefPtr resultSet; + rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet)); + NS_ENSURE_SUCCESS(rv, rv); + + // Get the name of the context info property. + nsAutoString contextName; + evaluateToString((Expr*)iter.next(), aContext, contextName); + + nsCOMPtrxformsService = + do_GetService("@mozilla.org/xforms-utility-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // mNode is the node that contained the Event XPath expression. + nsCOMArray contextInfo; + rv = xformsService->GetEventContextInfo(contextName, mNode, &contextInfo); + NS_ENSURE_SUCCESS(rv, rv); + + // Add each of the context info nodes to the resultSet. + PRInt32 i; + for (i = 0; i < contextInfo.Count(); ++i) { + nsAutoPtr txNode(txXPathNativeNode::createXPathNode(contextInfo[i])); + if (txNode) { + resultSet->add(*txNode); + } + } + + *aResult = resultSet; + NS_ADDREF(*aResult); + return NS_OK; } } /* switch() */ @@ -619,6 +659,11 @@ XFormsFunctionCall::getNameAtom(nsIAtom** aAtom) *aAtom = txXPathAtoms::current; break; } + case EVENT: + { + *aAtom = txXPathAtoms::event; + break; + } default: { *aAtom = 0; diff --git a/mozilla/extensions/transformiix/source/xpath/XFormsFunctions.h b/mozilla/extensions/transformiix/source/xpath/XFormsFunctions.h index 02492fb732a..81559759e12 100644 --- a/mozilla/extensions/transformiix/source/xpath/XFormsFunctions.h +++ b/mozilla/extensions/transformiix/source/xpath/XFormsFunctions.h @@ -67,7 +67,8 @@ public: PROPERTY, // property() SECONDS, // seconds() SECONDSFROMDATETIME, // seconds-from-dateTime() - CURRENT // current() + CURRENT, // current() + EVENT // event() }; /* diff --git a/mozilla/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h b/mozilla/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h index 9303d53d48b..e16661cc90c 100644 --- a/mozilla/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h +++ b/mozilla/extensions/transformiix/source/xpath/nsIXFormsUtilityService.h @@ -21,6 +21,7 @@ * * Contributor(s): * Aaron Reed + * Merle Sterling * * 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 @@ -41,6 +42,7 @@ #include "nsISupports.h" +#include "nsCOMArray.h" /* For IDL files that don't want to include root IDL files. */ #ifndef NS_NO_VTABLE @@ -50,13 +52,12 @@ class nsIDOMNode; /* forward declaration */ class nsIXFormsModelElement; /* forward declaration */ - /* starting interface: nsIXFormsUtilityService */ -#define NS_IXFORMSUTILITYSERVICE_IID_STR "1e51f8fb-22e1-4d5d-a5f3-ab34e76efe8e" +#define NS_IXFORMSUTILITYSERVICE_IID_STR "2ad098f4-5ee7-4282-a9f7-584eb95e1d69" #define NS_IXFORMSUTILITYSERVICE_IID \ - {0x1e51f8fb, 0x22e1, 0x4d5d, \ - { 0xa5, 0xf3, 0xab, 0x34, 0xe7, 0x6e, 0xfe, 0x8e }} + {0x2ad098f4, 0x5ee7, 0x4282, \ + { 0xa9, 0xf7, 0x58, 0x4e, 0xb9, 0x5e, 0x1d, 0x69 }} #define NS_XFORMS_UTILITY_CONTRACTID "@mozilla.org/xforms-utility-service;1" @@ -70,7 +71,8 @@ class nsIXFormsModelElement; /* forward declaration */ NS_IMETHOD GetMonths(const nsAString & aValue, PRInt32 *aMonths); \ NS_IMETHOD GetSeconds(const nsAString & aValue, double *aSeconds); \ NS_IMETHOD GetSecondsFromDateTime(const nsAString & aValue, double *aSeconds); \ - NS_IMETHOD GetDaysFromDateTime(const nsAString & aValue, PRInt32 *aDays); + NS_IMETHOD GetDaysFromDateTime(const nsAString & aValue, PRInt32 *aDays); \ + NS_IMETHOD GetEventContextInfo(const nsAString & aContextName, nsIDOMNode *aNode, nsCOMArray *aResult); /** * Private interface implemented by the nsXFormsUtilityService in XForms extension. @@ -156,6 +158,13 @@ class NS_NO_VTABLE nsIXFormsUtilityService : public nsISupports { /* AString getDaysFromDateTime (in DOMString aValue); */ NS_IMETHOD GetDaysFromDateTime(const nsAString & aValue, PRInt32 * aDays) = 0; + + /** + * Function to retrieve the context info for the property 'aContextName' + */ + /* nsCOMArray getEventContextInfo (in DOMString aContextName, in nsIDOMNode aNode); */ + NS_IMETHOD GetEventContextInfo(const nsAString & aContextName, nsIDOMNode *aNode, + nsCOMArray *aResult) = 0; }; #define NS_ERROR_XFORMS_CALCUATION_EXCEPTION \ diff --git a/mozilla/extensions/transformiix/source/xpath/nsXFormsXPathEvaluator.cpp b/mozilla/extensions/transformiix/source/xpath/nsXFormsXPathEvaluator.cpp index 7cab473f44b..74fa310296b 100644 --- a/mozilla/extensions/transformiix/source/xpath/nsXFormsXPathEvaluator.cpp +++ b/mozilla/extensions/transformiix/source/xpath/nsXFormsXPathEvaluator.cpp @@ -21,6 +21,7 @@ * * Contributor(s): * Aaron Reed + * Merle Sterling * * 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 @@ -250,6 +251,10 @@ nsXFormsXPathEvaluator::XFormsParseContextImpl::resolveFunctionCall( aFnCall = new XFormsFunctionCall(XFormsFunctionCall::CURRENT, mOriginalContext); } + else if (aName == txXPathAtoms::event) { + aFnCall = new XFormsFunctionCall(XFormsFunctionCall::EVENT, + mResolverNode); + } else { // didn't find functioncall here, aFnCall should be null isOutOfMem = PR_FALSE; diff --git a/mozilla/extensions/transformiix/source/xpath/txXPathAtomList.h b/mozilla/extensions/transformiix/source/xpath/txXPathAtomList.h index 256b0b06fde..c08976ce8d7 100644 --- a/mozilla/extensions/transformiix/source/xpath/txXPathAtomList.h +++ b/mozilla/extensions/transformiix/source/xpath/txXPathAtomList.h @@ -108,4 +108,5 @@ TX_ATOM(property, "property") TX_ATOM(seconds, "seconds") TX_ATOM(secondsFromDateTime, "seconds-from-dateTime") TX_ATOM(current, "current") +TX_ATOM(event, "event") #endif diff --git a/mozilla/extensions/xforms/Makefile.in b/mozilla/extensions/xforms/Makefile.in index 6c9435ecbe9..a6974239a0b 100644 --- a/mozilla/extensions/xforms/Makefile.in +++ b/mozilla/extensions/xforms/Makefile.in @@ -150,6 +150,8 @@ XPIDLSRCS = \ nsIXFormsCopyElement.idl \ nsIXFormsEphemeralMessageUI.idl \ nsIXFormsRangeConditionAccessors.idl \ + nsIXFormsDOMEvent.idl \ + nsIXFormsContextInfo.idl \ $(NULL) # XForms source files @@ -211,6 +213,8 @@ CPPSRCS = \ nsXFormsRangeAccessors.cpp \ nsXFormsCopyElement.cpp \ nsXFormsRangeConditionAccessors.cpp \ + nsXFormsDOMEvent.cpp \ + nsXFormsContextInfo.cpp \ $(NULL) # Standard Mozilla make rules diff --git a/mozilla/extensions/xforms/nsIXFormsActionModuleElement.idl b/mozilla/extensions/xforms/nsIXFormsActionModuleElement.idl index 414cb8d7987..3a6ff7a5eab 100644 --- a/mozilla/extensions/xforms/nsIXFormsActionModuleElement.idl +++ b/mozilla/extensions/xforms/nsIXFormsActionModuleElement.idl @@ -41,9 +41,15 @@ interface nsIDOMEvent; interface nsIXFormsActionElement; -[uuid(b19c47e4-2478-4a73-91a3-d86f91d91ffd)] +[uuid(4fa5fd18-902b-4ea8-a197-e93d5ce1ce71)] interface nsIXFormsActionModuleElement : nsISupports { void handleAction(in nsIDOMEvent aEvent, in nsIXFormsActionElement aParentAction); + /** + * Get the event currently being processed in handleAction. The XPath + * event() method uses this method to get the context info for the + * current event. + */ + nsIDOMEvent getCurrentEvent(); }; diff --git a/mozilla/extensions/xforms/nsXFormsActionElement.cpp b/mozilla/extensions/xforms/nsXFormsActionElement.cpp index 8877fe3a14a..18f1d237f4a 100644 --- a/mozilla/extensions/xforms/nsXFormsActionElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsActionElement.cpp @@ -285,6 +285,13 @@ nsXFormsActionElement::SetRefresh(nsIModelElementPrivate* aModel, return NS_OK; } +NS_IMETHODIMP +nsXFormsActionElement::GetCurrentEvent(nsIDOMEvent** aEvent) +{ + NS_IF_ADDREF(*aEvent = mCurrentEvent); + return NS_OK; +} + NS_HIDDEN_(nsresult) NS_NewXFormsActionElement(nsIXTFElement **aResult) { diff --git a/mozilla/extensions/xforms/nsXFormsActionModuleBase.cpp b/mozilla/extensions/xforms/nsXFormsActionModuleBase.cpp index 5e114bc999e..0eec3de4104 100644 --- a/mozilla/extensions/xforms/nsXFormsActionModuleBase.cpp +++ b/mozilla/extensions/xforms/nsXFormsActionModuleBase.cpp @@ -156,6 +156,7 @@ nsXFormsActionModuleBase::DoHandleAction(nsXFormsActionModuleHelper *aXFormsActi if (!element) { return NS_OK; } + aXFormsAction->SetCurrentEvent(aEvent); // Set the maximum run time for the loop (in microseconds). PRTime microseconds = nsXFormsUtils::waitLimit * PR_USEC_PER_SEC; @@ -273,3 +274,10 @@ nsXFormsActionModuleBase::CanPerformAction(nsIDOMElement *aElement, return PR_TRUE; } + +NS_IMETHODIMP +nsXFormsActionModuleBase::GetCurrentEvent(nsIDOMEvent **aEvent) +{ + NS_IF_ADDREF(*aEvent = mCurrentEvent); + return NS_OK; +} diff --git a/mozilla/extensions/xforms/nsXFormsActionModuleBase.h b/mozilla/extensions/xforms/nsXFormsActionModuleBase.h index 13c6f97a723..6fb78fbf9c8 100644 --- a/mozilla/extensions/xforms/nsXFormsActionModuleBase.h +++ b/mozilla/extensions/xforms/nsXFormsActionModuleBase.h @@ -63,6 +63,7 @@ public: nsXFormsActionModuleHelper() : mCanIterate(PR_TRUE) {} virtual nsIDOMElement* GetElement() = 0; PRBool CanIterate() { return mCanIterate; } + void SetCurrentEvent(nsIDOMEvent* aEvent) { mCurrentEvent = aEvent; } /** * With the `while` attribute, actions can potentially be iterated. The * `HandleSingleAction` method processes one iteration of an action (that @@ -82,6 +83,11 @@ protected: * additional information in the future. */ PRBool mCanIterate; + + /** + * The event currently being processed. + */ + nsCOMPtr mCurrentEvent; }; class nsXFormsActionModuleBase : public nsIDOMEventListener, diff --git a/mozilla/extensions/xforms/nsXFormsContextInfo.cpp b/mozilla/extensions/xforms/nsXFormsContextInfo.cpp index 8b668a12e08..a6b397a51be 100644 --- a/mozilla/extensions/xforms/nsXFormsContextInfo.cpp +++ b/mozilla/extensions/xforms/nsXFormsContextInfo.cpp @@ -130,9 +130,9 @@ nsXFormsContextInfo::GetNumberValue(PRInt32 *aResult) nsAutoString numberStr; nsXFormsUtils::GetNodeValue(mNode, numberStr); - nsresult rv; - *aResult = numberStr.ToInteger(&rv); - NS_ENSURE_SUCCESS(rv, rv); + PRInt32 errCode; + *aResult = numberStr.ToInteger(&errCode); + NS_ENSURE_TRUE(errCode == 0, NS_ERROR_FAILURE); } else { NS_WARNING("GetNumberValue: context type is not a number!"); } diff --git a/mozilla/extensions/xforms/nsXFormsContextInfo.h b/mozilla/extensions/xforms/nsXFormsContextInfo.h index 4b315c40739..341f83cf030 100644 --- a/mozilla/extensions/xforms/nsXFormsContextInfo.h +++ b/mozilla/extensions/xforms/nsXFormsContextInfo.h @@ -41,6 +41,7 @@ #include "nsCOMArray.h" #include "nsCOMPtr.h" +#include "nsString.h" #include "nsIDOMElement.h" #include "nsIDOMNode.h" #include "nsIDOMXPathResult.h" diff --git a/mozilla/extensions/xforms/nsXFormsInsertDeleteElement.cpp b/mozilla/extensions/xforms/nsXFormsInsertDeleteElement.cpp index a63425b40a0..ed3d2cb1d2f 100644 --- a/mozilla/extensions/xforms/nsXFormsInsertDeleteElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsInsertDeleteElement.cpp @@ -46,6 +46,7 @@ #include "nsIDOMNamedNodeMap.h" #include "nsIXFormsRepeatElement.h" #include "nsIXFormsControl.h" +#include "nsIXFormsContextInfo.h" #include "nsString.h" @@ -63,14 +64,6 @@ /** * Implementation of the XForms \ and \ elements. - * - * @see http://www.w3.org/TR/xforms/slice9.html#action-insert - * - * @todo The spec. states that the events must set their Context Info to: - * "Path expression used for insert/delete (xsd:string)" (XXX) - * @see http://www.w3.org/TR/xforms/slice4.html#evt-insert - * @see https://bugzilla.mozilla.org/show_bug.cgi?id=280423 - * */ class nsXFormsInsertDeleteElement : public nsXFormsActionModuleBase { @@ -83,6 +76,9 @@ private: eLocation_FirstChild }; + // Context Info for events. + nsCOMArray mContextInfo; + /** Get the first node of a given type in aNodes. * * @param aNodes array of nodes @@ -108,13 +104,15 @@ private: nsresult RefreshRepeats(nsCOMArray *aNodes); public: - NS_DECL_NSIXFORMSACTIONMODULEELEMENT /** Constructor */ nsXFormsInsertDeleteElement(PRBool aIsInsert) : mIsInsert(aIsInsert) {} + NS_IMETHOD + HandleAction(nsIDOMEvent* aEvent, nsIXFormsActionElement *aParentAction); + /** * Normally, an action element implements the body of its action handler * using this method; however, since an `nsXFormsInsertDeleteElement` sets @@ -138,6 +136,8 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, if (!element) return NS_OK; + mCurrentEvent = aEvent; + nsresult rv; // Set the maximum run time for the loop (in microseconds). @@ -249,9 +249,9 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, // nsCOMPtr nodeset; PRUint32 nodesetSize = 0; + nsAutoString nodesetExpr; if (bindExpr.IsEmpty()) { - nsAutoString nodesetExpr; element->GetAttribute(NS_LITERAL_STRING("nodeset"), nodesetExpr); if (!nodesetExpr.IsEmpty()) { // Evaluate the nodeset attribute within the insert context. @@ -328,6 +328,13 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, // is the empty node-set. if (originNodesetSize < 1) return NS_OK; + + // Context Info: 'origin-nodes' + nsCOMPtr contextInfo = + new nsXFormsContextInfo(element); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetNodesetValue("origin-nodes", originNodeset); + mContextInfo.AppendObject(contextInfo); } } @@ -411,6 +418,20 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, } } + // Context Info: 'insert-location-node' or 'delete-location' + nsCOMPtr contextInfo; + if (mIsInsert) { + contextInfo = new nsXFormsContextInfo(element); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetNodeValue("insert-location-node", locationNode); + mContextInfo.AppendObject(contextInfo); + } else { + contextInfo = new nsXFormsContextInfo(element); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetNumberValue("delete-location", atInt); + mContextInfo.AppendObject(contextInfo); + } + // // Step 5 (Insert): Each node in the origin node-set is cloned in the // order it appears in the origin node-set. If the origin node-set is empty @@ -444,6 +465,12 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, cloneNodesetSize = originNodesetSize; cloneIndex = 0; } + // Context Info: 'inserted-nodes' + nsCOMPtr contextInfo = + new nsXFormsContextInfo(element); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetNodesetValue("inserted-nodes", cloneNodeset); + mContextInfo.AppendObject(contextInfo); cloneNodeset->SnapshotItem(cloneIndex, getter_AddRefs(prototypeNode)); NS_ENSURE_STATE(prototypeNode); @@ -563,6 +590,13 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, return NS_ERROR_FAILURE; } } + // Context Info: 'position'. + nsCOMPtr contextInfo = + new nsXFormsContextInfo(element); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetStringValue("position", position); + mContextInfo.AppendObject(contextInfo); + InsertNode(locationNode, newNode, insertAfter ? eLocation_After: eLocation_Before, getter_AddRefs(resNode)); @@ -598,6 +632,12 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, deleteIndex = atInt - 1; deleteCount = atInt; } + // Context Info: 'deleted-nodes' + nsCOMPtr contextInfo = + new nsXFormsContextInfo(element); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetNodesetValue("deleted-nodes", nodeset); + mContextInfo.AppendObject(contextInfo); nodeset->SnapshotItem(deleteIndex, getter_AddRefs(locationNode)); NS_ENSURE_STATE(locationNode); @@ -652,7 +692,8 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, // Dispatch xforms-insert/delete event to the instance node we have modified // data for rv = nsXFormsUtils::DispatchEvent(instNode, - mIsInsert ? eEvent_Insert : eEvent_Delete); + mIsInsert ? eEvent_Insert : eEvent_Delete, + nsnull, nsnull, &mContextInfo); NS_ENSURE_SUCCESS(rv, rv); // Dispatch refreshing events to the model diff --git a/mozilla/extensions/xforms/nsXFormsLabelElement.cpp b/mozilla/extensions/xforms/nsXFormsLabelElement.cpp index 7ef4c7006c4..123d0177256 100644 --- a/mozilla/extensions/xforms/nsXFormsLabelElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsLabelElement.cpp @@ -93,9 +93,18 @@ public: private: NS_HIDDEN_(void) LoadExternalLabel(const nsAString& aValue); + /** Set context info for events. + * + * @param aName Name of the context property. + * @param aValue Value of the context property. + */ + nsresult SetContextInfo(const char *aName, const nsAString &aValue); + nsCString mSrcAttrText; nsCOMPtr mChannel; PRBool mWidgetLoaded; + // Context Info for events. + nsCOMArray mContextInfo; }; NS_IMPL_ISUPPORTS_INHERITED3(nsXFormsLabelElement, @@ -263,8 +272,13 @@ nsXFormsLabelElement::LoadExternalLabel(const nsAString& aSrc) nsCOMPtr modelPriv = nsXFormsUtils::GetModel(mElement); nsCOMPtr model = do_QueryInterface(modelPriv); + + // Context Info: 'resource-uri' + // The URI associated with the failed link. + nsAutoString resourceURI(aSrc); + SetContextInfo("resource-uri", resourceURI); nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull, - mElement); + mElement, &mContextInfo); } } } else { @@ -274,7 +288,13 @@ nsXFormsLabelElement::LoadExternalLabel(const nsAString& aSrc) nsCOMPtr modelPriv = nsXFormsUtils::GetModel(mElement); nsCOMPtr model = do_QueryInterface(modelPriv); - nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull, mElement); + + // Context Info: 'resource-uri' + // The URI associated with the failed link. + nsAutoString resourceURI(aSrc); + SetContextInfo("resource-uri", resourceURI); + nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull, mElement, + &mContextInfo); } } } @@ -414,7 +434,12 @@ nsXFormsLabelElement::OnStopRequest(nsIRequest *aRequest, nsCOMPtr modelPriv = nsXFormsUtils::GetModel(mElement); nsCOMPtr model = do_QueryInterface(modelPriv); - nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull, mElement); + + // Context Info: 'resource-uri' + // The URI associated with the failed link. + SetContextInfo("resource-uri", src); + nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull, mElement, + &mContextInfo); mSrcAttrText.Truncate(); } @@ -425,6 +450,17 @@ nsXFormsLabelElement::OnStopRequest(nsIRequest *aRequest, return NS_OK; } +nsresult +nsXFormsLabelElement::SetContextInfo(const char *aName, const nsAString &aValue) +{ + nsCOMPtr contextInfo = new nsXFormsContextInfo(mElement); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetStringValue(aName, aValue); + mContextInfo.AppendObject(contextInfo); + + return NS_OK; +} + NS_HIDDEN_(nsresult) NS_NewXFormsLabelElement(nsIXTFElement **aResult) { diff --git a/mozilla/extensions/xforms/nsXFormsMessageElement.cpp b/mozilla/extensions/xforms/nsXFormsMessageElement.cpp index 8bbc2c5e893..ce8811e5d11 100644 --- a/mozilla/extensions/xforms/nsXFormsMessageElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsMessageElement.cpp @@ -198,6 +198,13 @@ private: */ PRBool IsEphemeral(); + /** Set context info for events. + * + * @param aName Name of the context property. + * @param aValue Value of the context property. + */ + nsresult SetContextInfo(const char *aName, const nsAString &aValue); + MessageType mType; // The position of the ephemeral message @@ -210,6 +217,9 @@ private: StopType mStopType; nsCString mSrcAttrText; PRBool mDoneAddingChildren; + // Context Info for events. + nsCOMArray mContextInfo; + nsString mSrc; }; NS_IMPL_ADDREF_INHERITED(nsXFormsMessageElement, nsXFormsDelegateStub) @@ -467,7 +477,13 @@ nsXFormsMessageElement::HandleSingleAction(nsIDOMEvent *aEvent, nsCOMPtr modelPriv = nsXFormsUtils::GetModel(mElement); nsCOMPtr model = do_QueryInterface(modelPriv); - nsXFormsUtils::DispatchEvent(model, eEvent_LinkError); + + // Context Info: 'resource-uri' + // The URI associated with the failed link. + SetContextInfo("resource-uri", mSrc); + + nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull, nsnull, + &mContextInfo); return NS_OK; } @@ -929,6 +945,9 @@ nsXFormsMessageElement::TestExternalFile() if (src.IsEmpty()) { return NS_OK; } + // Remember the src attribute so we can set it in the context info + // for the xforms-link-error event if the link fails. + mSrc = src; nsCOMPtr domDoc; mElement->GetOwnerDocument(getter_AddRefs(domDoc)); @@ -993,6 +1012,9 @@ nsXFormsMessageElement::TestExternalFile() nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLink1Error"), strings, 2, mElement, mElement); mStopType = eStopType_LinkError; + // Remember the src attribute so we can set it in the context info + // for the xforms-link-error event. + mSrc = src; return NS_ERROR_FAILURE; } @@ -1103,6 +1125,9 @@ nsXFormsMessageElement::OnStartRequest(nsIRequest *aRequest, nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLink2Error"), strings, 2, mElement, mElement); mStopType = eStopType_LinkError; + // Remember the src attribute so we can set it in the context info + // for the xforms-link-error event. + mSrc = src; } AddRemoveExternalResource(PR_FALSE); @@ -1129,6 +1154,9 @@ nsXFormsMessageElement::OnStartRequest(nsIRequest *aRequest, nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLink2Error"), strings, 2, mElement, mElement); mStopType = eStopType_LinkError; + // Remember the src attribute so we can set it in the context info + // for the xforms-link-error event. + mSrc = src; } } @@ -1256,6 +1284,24 @@ PRBool nsXFormsMessageElement::IsEphemeral() return level.Equals(NS_LITERAL_STRING("ephemeral")); } +nsresult +nsXFormsMessageElement::SetContextInfo(const char *aName, const nsAString &aValue) +{ + nsCOMPtr contextInfo = new nsXFormsContextInfo(mElement); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetStringValue(aName, aValue); + mContextInfo.AppendObject(contextInfo); + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsMessageElement::GetCurrentEvent(nsIDOMEvent **aEvent) +{ + NS_IF_ADDREF(*aEvent = mCurrentEvent); + return NS_OK; +} + NS_HIDDEN_(nsresult) NS_NewXFormsMessageElement(nsIXTFElement **aResult) { diff --git a/mozilla/extensions/xforms/nsXFormsModelElement.cpp b/mozilla/extensions/xforms/nsXFormsModelElement.cpp index 315186c0751..63b8c17e575 100644 --- a/mozilla/extensions/xforms/nsXFormsModelElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsModelElement.cpp @@ -809,6 +809,7 @@ nsXFormsModelElement::InitializeInstances() for (PRInt32 i=0; i newURI; NS_NewURI(getter_AddRefs(newURI), *schemas[i], nsnull, baseURI); nsCOMPtr newURL = do_QueryInterface(newURI); @@ -846,7 +847,6 @@ nsXFormsModelElement::InitializeInstances() i--; } } else { - nsCAutoString uriSpec; newURI->GetSpec(uriSpec); rv = mSchemas->LoadAsync(NS_ConvertUTF8toUTF16(uriSpec), this); } @@ -854,7 +854,11 @@ nsXFormsModelElement::InitializeInstances() if (NS_FAILED(rv)) { // this is a fatal error nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaLoadError"), mElement); - nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException); + // Context Info: 'resource-uri' + // The URI associated with the failed link. + SetContextInfo("resource-uri", NS_ConvertUTF8toUTF16(uriSpec)); + nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException, nsnull, + nsnull, &mContextInfo); return NS_OK; } } @@ -2330,7 +2334,15 @@ nsXFormsModelElement::MaybeNotifyCompletion() if (!extFunctionAtt.IsEmpty()) { nsXFormsUtils::ReportError(NS_LITERAL_STRING("invalidExtFunction"), tElement); - nsXFormsUtils::DispatchEvent(tElement, eEvent_ComputeException); + + // Context Info: 'error-message' + // Error message containing the expression being processed. + nsAutoString errorMsg; + errorMsg.AssignLiteral("Non-existent extension functions: "); + errorMsg.Append(extFunctionAtt); + SetContextInfo("error-message", errorMsg); + nsXFormsUtils::DispatchEvent(tElement, eEvent_ComputeException, nsnull, + nsnull, &mContextInfo); return; } } @@ -2416,7 +2428,15 @@ nsXFormsModelElement::ProcessBind(nsIXFormsXPathEvaluator *aEvaluator, const PRUnichar *strings[] = { flat.get() }; nsXFormsUtils::ReportError(NS_LITERAL_STRING("exprParseError"), strings, 1, aBindElement, nsnull); - nsXFormsUtils::DispatchEvent(mElement, eEvent_ComputeException); + + // Context Info: 'error-message' + // Error message containing the expression being processed. + nsAutoString errorMsg; + errorMsg.AssignLiteral("Error parsing XPath expression: "); + errorMsg.Append(expr); + SetContextInfo("error-message", errorMsg); + nsXFormsUtils::DispatchEvent(mElement, eEvent_ComputeException, nsnull, + nsnull, &mContextInfo); } else { #ifdef DEBUG printf("xforms-binding-exception: XPath Evaluation failed\n"); @@ -2504,7 +2524,15 @@ nsXFormsModelElement::ProcessBind(nsIXFormsXPathEvaluator *aEvaluator, const PRUnichar *strings[] = { propStrings[j].get() }; nsXFormsUtils::ReportError(NS_LITERAL_STRING("mipParseError"), strings, 1, aBindElement, aBindElement); - nsXFormsUtils::DispatchEvent(mElement, eEvent_ComputeException); + + // Context Info: 'error-message' + // Error message containing the expression being processed. + nsAutoString errorMsg; + errorMsg.AssignLiteral("Error while parsing model item property: "); + errorMsg.Append(propStrings[j]); + SetContextInfo("error-message", errorMsg); + nsXFormsUtils::DispatchEvent(mElement, eEvent_ComputeException, + nsnull, nsnull, &mContextInfo); return rv; } @@ -3431,6 +3459,17 @@ nsXFormsModelElement::IsDuplicateSchema(nsIDOMElement *aSchemaElement) return PR_TRUE; } +nsresult +nsXFormsModelElement::SetContextInfo(const char *aName, const nsAString &aValue) +{ + nsCOMPtr contextInfo = new nsXFormsContextInfo(mElement); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetStringValue(aName, aValue); + mContextInfo.AppendObject(contextInfo); + + return NS_OK; +} + nsresult NS_NewXFormsModelElement(nsIXTFElement **aResult) { diff --git a/mozilla/extensions/xforms/nsXFormsModelElement.h b/mozilla/extensions/xforms/nsXFormsModelElement.h index 1aa2de43f8a..a2e492fcc36 100644 --- a/mozilla/extensions/xforms/nsXFormsModelElement.h +++ b/mozilla/extensions/xforms/nsXFormsModelElement.h @@ -464,6 +464,13 @@ private: NS_HIDDEN_(nsresult) BuiltinTypeToPrimative(nsISchemaBuiltinType *aSchemaType, PRUint16 *aPrimType); + /** Set context info for events. + * + * @param aName Name of the context property. + * @param aValue Value of the context property. + */ + nsresult SetContextInfo(const char *aName, const nsAString &aValue); + nsIDOMElement *mElement; nsCOMPtr mSchemas; nsStringArray mPendingInlineSchemas; @@ -557,6 +564,9 @@ private: */ nsClassHashtable mNodeToP3PType; + // Context Info for events. + nsCOMArray mContextInfo; + friend class Updating; }; diff --git a/mozilla/extensions/xforms/nsXFormsSubmissionElement.cpp b/mozilla/extensions/xforms/nsXFormsSubmissionElement.cpp index 91356b9bb3e..f96458755ce 100644 --- a/mozilla/extensions/xforms/nsXFormsSubmissionElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsSubmissionElement.cpp @@ -22,6 +22,7 @@ * Contributor(s): * Darin Fisher * Doron Rosenberg + * Merle Sterling * * 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 @@ -117,6 +118,20 @@ #define ENCODING_MULTIPART_RELATED 0x40 // multipart/related #define ENCODING_MULTIPART_FORM_DATA 0x80 // multipart/form-data +// submission errors +#define kError_SubmissionInProgress \ + NS_LITERAL_STRING("submission-in-progress"); +#define kError_NoData \ + NS_LITERAL_STRING("no-data"); +#define kError_ValidationError \ + NS_LITERAL_STRING("validation-error"); +#define kError_ParseError \ + NS_LITERAL_STRING("parse-error"); +#define kError_ResourceError \ + NS_LITERAL_STRING("resource-error"); +#define kError_TargetError \ + NS_LITERAL_STRING("target-error"); + struct SubmissionFormat { const char *method; @@ -302,6 +317,23 @@ nsXFormsSubmissionElement::HandleDefault(nsIDOMEvent *aEvent, PRBool *aHandled) EndSubmit(PR_FALSE); } + *aHandled = PR_TRUE; + } else if (type.EqualsLiteral("xforms-submit-serialize")) { + nsCOMPtr xfEvent = do_QueryInterface(aEvent); + if (xfEvent) { + nsCOMPtr contextInfo; + nsAutoString contextName; + contextName.AssignLiteral("submission-body"); + xfEvent->GetContextInfo(contextName, getter_AddRefs(contextInfo)); + if (contextInfo) { + nsAutoString submissionBody; + contextInfo->GetStringValue(submissionBody); + if (!submissionBody.EqualsLiteral(" ")) { + // Save the new submission body. + contextInfo->GetNodeValue(getter_AddRefs(mSubmissionBody)); + } + } + } *aHandled = PR_TRUE; } else { *aHandled = PR_FALSE; @@ -415,37 +447,58 @@ nsXFormsSubmissionElement::OnStopRequest(nsIRequest *aRequest, PRBool succeeded = NS_SUCCEEDED(aStatus); if (succeeded) { + PRUint32 avail = 0; + mPipeIn->Available(&avail); + if (avail > 0) { + nsresult rv; - // If it is a HTTP request, then check for error responses, which should - // result in NOP and an xforms-submit-error. - nsCOMPtr httpChannel = do_QueryInterface(channel); - if (httpChannel) { - PRUint32 response; - nsresult rv = httpChannel->GetResponseStatus(&response); - succeeded = NS_SUCCEEDED(rv) && (response < 400); - } + // Regardless of whether the response status represents success + // or failure, we want to read the response. For an error response + // nothing in the document is replaced, and submission processing + // concludes after dispatching xforms-submit-error with appropriate + // context information, including an error-type of resource-error. + nsCOMPtr httpChannel = do_QueryInterface(channel); + if (httpChannel) { + PRUint32 response; + nsresult rv = httpChannel->GetResponseStatus(&response); + nsCAutoString statusText; + httpChannel->GetResponseStatusText(statusText); + httpChannel->VisitResponseHeaders(this); + SetHttpContextInfo(response, NS_ConvertUTF8toUTF16(statusText)); - if (succeeded) { - PRUint32 avail = 0; - mPipeIn->Available(&avail); - if (avail > 0) { + PRBool requestSucceeded; + httpChannel->GetRequestSucceeded(&requestSucceeded); + if (!requestSucceeded) { + // Server returned an error response code. Parse the error + // response body into an XML document for 'response-body' + // context info. + ParseErrorResponse(httpChannel); + mSubmitError = kError_ResourceError; + succeeded = PR_FALSE; + } else { + succeeded = PR_TRUE; + } + } - nsresult rv; + if (succeeded) { if (mIsReplaceInstance) { rv = LoadReplaceInstance(channel); } else { nsAutoString replace; mElement->GetAttribute(NS_LITERAL_STRING("replace"), replace); - if (replace.IsEmpty() || replace.EqualsLiteral("all")) + if (replace.IsEmpty() || replace.EqualsLiteral("all")) { rv = LoadReplaceAll(channel); - else + } else { + // replace="none" rv = NS_OK; + } } succeeded = NS_SUCCEEDED(rv); } + } else { + mSubmitError = kError_ResourceError; } } - mPipeIn = 0; EndSubmit(succeeded); @@ -463,8 +516,20 @@ nsXFormsSubmissionElement::EndSubmit(PRBool aSucceeded) mActivator->SetDisabled(PR_FALSE); mActivator = nsnull; } + + // If there were any errors, set 'error-type' context info. + if (!mSubmitError.IsEmpty()) { + nsCOMPtr contextInfo = + new nsXFormsContextInfo(mElement); + if (contextInfo) { + contextInfo->SetStringValue("error-type", mSubmitError); + mContextInfo.AppendObject(contextInfo); + } + } + nsXFormsUtils::DispatchEvent(mElement, aSucceeded ? - eEvent_SubmitDone : eEvent_SubmitError); + eEvent_SubmitDone : eEvent_SubmitError, + nsnull, nsnull, &mContextInfo); } already_AddRefed @@ -512,6 +577,7 @@ nsXFormsSubmissionElement::LoadReplaceInstance(nsIChannel *channel) if (!newDoc) { nsXFormsUtils::ReportError(NS_LITERAL_STRING("instanceParseError"), mElement); + mSubmitError = kError_ParseError; return NS_ERROR_UNEXPECTED; } @@ -529,6 +595,7 @@ nsXFormsSubmissionElement::LoadReplaceInstance(nsIChannel *channel) namespaceURI.EqualsLiteral("http://www.mozilla.org/newlayout/xml/parsererror.xml")) { nsXFormsUtils::ReportError(NS_LITERAL_STRING("instanceParseError"), mElement); + mSubmitError = kError_ParseError; return NS_ERROR_UNEXPECTED; } } @@ -566,9 +633,10 @@ nsXFormsSubmissionElement::LoadReplaceInstance(nsIChannel *channel) model->Recalculate(); model->Revalidate(); model->Refresh(); + } else { + mSubmitError = kError_NoData; } - return NS_OK; } @@ -633,6 +701,7 @@ nsXFormsSubmissionElement::Submit() if (mSubmissionActive) { nsXFormsUtils::ReportError(NS_LITERAL_STRING("warnSubmitAlreadyRunning"), mElement, nsIScriptError::warningFlag); + mSubmitError = kError_SubmissionInProgress; return NS_ERROR_FAILURE; } mSubmissionActive = PR_TRUE; @@ -646,14 +715,37 @@ nsXFormsSubmissionElement::Submit() mElement->GetAttribute(NS_LITERAL_STRING("replace"), replace); mIsReplaceInstance = replace.EqualsLiteral("instance"); + // 2. Dispatch xforms-submit-serialize. + // If the event context submission-body property string is empty, then no + // operation is performed so that the submission will use the normal + // serialization data. Otherwise, if the event context submission-body + // property string is non-empty, then the serialization data for the + // submission is set to be the content of the submission-body string. + nsCOMPtr contextInfo = new nsXFormsContextInfo(mElement); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + nsAutoString submissionBody; + submissionBody.AssignLiteral(" "); + contextInfo->SetStringValue("submission-body", submissionBody); + mContextInfo.AppendObject(contextInfo); + nsXFormsUtils::DispatchEvent(mElement, eEvent_SubmitSerialize, nsnull, + nsnull, &mContextInfo); + // // 2. get selected node from the instance data nsCOMPtr data; - rv = GetBoundInstanceData(getter_AddRefs(data)); - NS_ENSURE_SUCCESS(rv, rv); + if (mSubmissionBody) { + // submission-body property was modified during submit-serialize and its + // contents is the new serialization data. + data = mSubmissionBody; + } else { + // get selected node from the instance data. + rv = GetBoundInstanceData(getter_AddRefs(data)); + NS_ENSURE_SUCCESS(rv, rv); + } // No data to submit if (!data) { + mSubmitError = kError_NoData; EndSubmit(PR_FALSE); return NS_OK; } @@ -662,8 +754,10 @@ nsXFormsSubmissionElement::Submit() // 3. Create submission document (include namespaces, purge non-relevant // nodes, check simple type validity) nsCOMPtr submissionDoc; - rv = CreateSubmissionDoc(data, getter_AddRefs(submissionDoc)); - NS_ENSURE_SUCCESS(rv, rv); + if (!mSubmissionBody) { + rv = CreateSubmissionDoc(data, getter_AddRefs(submissionDoc)); + NS_ENSURE_SUCCESS(rv, rv); + } // // 4. Validate document @@ -682,7 +776,22 @@ nsXFormsSubmissionElement::Submit() nsCAutoString uri, contentType; GetSubmissionURI(uri); - rv = SerializeData(submissionDoc, uri, getter_AddRefs(stream), contentType); + if (mSubmissionBody) { + // submission-body property was modified during submit-serialize and we will + // serialize it as a simple string. + nsAutoString nodeValue; + nsXFormsUtils::GetNodeValue(mSubmissionBody, nodeValue); + + // make new stream + NS_NewCStringInputStream(getter_AddRefs(stream), + NS_ConvertUTF16toUTF8(nodeValue)); + NS_ENSURE_STATE(stream); + contentType.AssignLiteral("application/xml"); + rv = NS_OK; + } else { + // Serialize a document based on the submission format and content type. + rv = SerializeData(submissionDoc, uri, getter_AddRefs(stream), contentType); + } if (NS_FAILED(rv)) { nsXFormsUtils::ReportError(NS_LITERAL_STRING("warnSubmitSerializeFailed"), mElement, nsIScriptError::warningFlag); @@ -795,6 +904,13 @@ nsXFormsSubmissionElement::GetSubmissionURI(nsACString& aURI) nsXFormsUtils::ReportError(NS_LITERAL_STRING("warnSubmitURI"), mElement, nsIScriptError::warningFlag); + // Context Info: 'resource-uri' + nsCOMPtr contextInfo = + new nsXFormsContextInfo(mElement); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetStringValue("resource-uri", uri); + mContextInfo.AppendObject(contextInfo); + CopyUTF16toUTF8(uri, aURI); return rv; @@ -1560,6 +1676,7 @@ nsXFormsSubmissionElement::CopyChildren(nsIModelElementPrivate *aModel, // abort nsXFormsUtils::ReportError(NS_LITERAL_STRING("warnSubmitInvalidNode"), currentNode, nsIScriptError::warningFlag); + mSubmitError = kError_ValidationError; return NS_ERROR_ILLEGAL_VALUE; } @@ -1595,6 +1712,7 @@ nsXFormsSubmissionElement::CopyChildren(nsIModelElementPrivate *aModel, // abort nsXFormsUtils::ReportError(NS_LITERAL_STRING("warnSubmitInvalidNode"), currentNode, nsIScriptError::warningFlag); + mSubmitError = kError_ValidationError; return NS_ERROR_ILLEGAL_VALUE; } @@ -2435,6 +2553,225 @@ nsXFormsSubmissionElement::SendData(const nsCString &uriSpec, return rv; } +// nsIHttpHeaderVisitor +NS_IMETHODIMP +nsXFormsSubmissionElement::VisitHeader(const nsACString &aHeader, + const nsACString &aValue) +{ + nsresult rv; + nsCOMPtr rootElt; + nsCOMPtr newChild; + + // Every time this callback is called, we add another + //
aHeaderaValue
element + // to the http header document. The header document is used to create + // a nodeset of header elements in the context info. + if (!mHttpHeaderDoc) { + nsCOMPtr doc; + rv = mElement->GetOwnerDocument(getter_AddRefs(doc)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr domImpl; + rv = doc->GetImplementation(getter_AddRefs(domImpl)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = domImpl->CreateDocument(EmptyString(), EmptyString(), nsnull, + getter_AddRefs(mHttpHeaderDoc)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mHttpHeaderDoc->CreateElement(NS_LITERAL_STRING("headers"), + getter_AddRefs(rootElt)); + NS_ENSURE_SUCCESS(rv, rv); + + mHttpHeaderDoc->AppendChild(rootElt, getter_AddRefs(newChild)); + } + + nsCOMPtr headerElt, nameElt, valueElt; + nsCOMPtr rootNode; + + // Root element. + rv = mHttpHeaderDoc->GetFirstChild(getter_AddRefs(rootNode)); + NS_ENSURE_SUCCESS(rv, rv); + + //
+ rv = mHttpHeaderDoc->CreateElement(NS_LITERAL_STRING("header"), + getter_AddRefs(headerElt)); + + // + rv = mHttpHeaderDoc->CreateElement(NS_LITERAL_STRING("name"), + getter_AddRefs(nameElt)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr nameTextNode; + rv = mHttpHeaderDoc->CreateTextNode(NS_ConvertUTF8toUTF16(aHeader), + getter_AddRefs(nameTextNode)); + NS_ENSURE_SUCCESS(rv, rv); + + nameElt->AppendChild(nameTextNode, getter_AddRefs(newChild)); + headerElt->AppendChild(nameElt, getter_AddRefs(newChild)); + + // + rv = mHttpHeaderDoc->CreateElement(NS_LITERAL_STRING("value"), + getter_AddRefs(valueElt)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr valueTextNode; + rv = mHttpHeaderDoc->CreateTextNode(NS_ConvertUTF8toUTF16(aValue), + getter_AddRefs(valueTextNode)); + NS_ENSURE_SUCCESS(rv, rv); + + valueElt->AppendChild(valueTextNode, getter_AddRefs(newChild)); + headerElt->AppendChild(valueElt, getter_AddRefs(newChild)); + + // Append
element to root element. + rootElt = do_QueryInterface(rootNode); + rootElt->AppendChild(headerElt, getter_AddRefs(newChild)); + + return NS_OK; +} + +nsresult +nsXFormsSubmissionElement::SetContextInfo() +{ + + return NS_OK; +} + +nsresult +nsXFormsSubmissionElement::SetHttpContextInfo(PRUint32 aResponse, + const nsAString &aResponseText) +{ + nsresult rv; + + nsCOMPtr contextInfo = new nsXFormsContextInfo(mElement); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + // response-status-code + if (aResponse > 0) { + contextInfo->SetNumberValue("response-status-code", aResponse); + mContextInfo.AppendObject(contextInfo); + } + // response-reason-phrase + contextInfo = new nsXFormsContextInfo(mElement); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetStringValue("response-reason-phrase", aResponseText); + mContextInfo.AppendObject(contextInfo); + // response-headers + if (mHttpHeaderDoc) { + nsCOMPtr rootNode; + rv = mHttpHeaderDoc->GetFirstChild(getter_AddRefs(rootNode)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr headerNodeset; + nsAutoString expr; + expr.AssignLiteral("header"); + rv = nsXFormsUtils::EvaluateXPath(expr, rootNode, rootNode, + nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, + getter_AddRefs(headerNodeset)); + + NS_ENSURE_SUCCESS(rv, rv); + + if (headerNodeset) { + contextInfo = new nsXFormsContextInfo(mElement); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetNodesetValue("response-headers", headerNodeset); + mContextInfo.AppendObject(contextInfo); +#ifdef DEBUG + PRUint32 nodesetSize = 0; + headerNodeset->GetSnapshotLength(&nodesetSize); + for (PRUint32 i = 0; i < nodesetSize; i++) { + nsCOMPtr headerNode, nameNode, valueNode; + headerNodeset->SnapshotItem(i, getter_AddRefs(headerNode)); + headerNode->GetFirstChild(getter_AddRefs(nameNode)); + nsAutoString name, value; + nsXFormsUtils::GetNodeValue(nameNode, name); + nameNode->GetNextSibling(getter_AddRefs(valueNode)); + nsXFormsUtils::GetNodeValue(valueNode, value); + } +#endif // DEBUG + } + } + + return NS_OK; +} + +nsresult +nsXFormsSubmissionElement::ParseErrorResponse(nsIChannel *aChannel) +{ + // Context Info: response-body + // When the error response specifies an XML media type as defined by + // RFC 3023], the response body is parsed into an XML document and the + // root element of the document is returned. If the parse fails, or if + // the error response specifies a text media type (starting with text/), + // then the response body is returned as a string. + // Otherwise, an empty string is returned. + nsCString contentCharset, contentType; + aChannel->GetContentCharset(contentCharset); + aChannel->GetContentType(contentType); + + // use DOM parser to construct nsIDOMDocument + nsCOMPtr parser = + do_CreateInstance("@mozilla.org/xmlextras/domparser;1"); + NS_ENSURE_STATE(parser); + + PRUint32 contentLength; + mPipeIn->Available(&contentLength); + + // set the base uri so that the document can get the correct security + // principal. + nsCOMPtr uri; + nsresult rv = aChannel->GetURI(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, rv); + rv = parser->SetBaseURI(uri); + NS_ENSURE_SUCCESS(rv, rv); + + // Try to parse the content into an XML document. If the parse fails, the + // content type is not an XML type that ParseFromStream can handle. In that + // case, read the response as a simple string. + nsCOMPtr contextInfo; + nsCOMPtr newDoc; + rv = parser->ParseFromStream(mPipeIn, contentCharset.get(), contentLength, + contentType.get(), getter_AddRefs(newDoc)); + if (NS_SUCCEEDED(rv)) { + // Succeeded in parsing the error response as an XML document. + nsCOMPtr responseBody = do_QueryInterface(newDoc); + if (newDoc) { + contextInfo = new nsXFormsContextInfo(mElement); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + + contextInfo->SetNodeValue("response-body", responseBody); + mContextInfo.AppendObject(contextInfo); + } + } else { + // Read the content as a simple string and set a string into the + // context info. + PRUint32 len, read, numReadIn = 1; + nsCAutoString responseBody; + + rv = mPipeIn->Available(&len); + NS_ENSURE_SUCCESS(rv, rv); + + char *buf = new char[len+1]; + NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY); + memset(buf, 0, len+1); + + // Read returns 0 if eos + while (numReadIn != 0) { + numReadIn = mPipeIn->Read(buf, len, &read); + responseBody.Append(buf); + } + delete [] buf; + + // Set the string response body as context info. + contextInfo = new nsXFormsContextInfo(mElement); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetStringValue("response-body", + NS_ConvertUTF8toUTF16(responseBody)); + mContextInfo.AppendObject(contextInfo); + } + + return NS_OK; +} + // factory constructor nsresult diff --git a/mozilla/extensions/xforms/nsXFormsSubmissionElement.h b/mozilla/extensions/xforms/nsXFormsSubmissionElement.h index e8dff08a8aa..80eabf4a126 100644 --- a/mozilla/extensions/xforms/nsXFormsSubmissionElement.h +++ b/mozilla/extensions/xforms/nsXFormsSubmissionElement.h @@ -21,6 +21,7 @@ * * Contributor(s): * Darin Fisher + * Merle Sterling * * 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 @@ -50,6 +51,9 @@ #include "nsIInterfaceRequestor.h" #include "nsHashSets.h" #include "nsIDocument.h" +#include "nsIHttpHeaderVisitor.h" +#include "nsIXFormsContextInfo.h" +#include "nsDataHashtable.h" class nsIMultiplexInputStream; @@ -71,7 +75,8 @@ class nsXFormsSubmissionElement : public nsXFormsStubElement, public nsIRequestObserver, public nsIXFormsSubmissionElement, public nsIChannelEventSink, - public nsIInterfaceRequestor + public nsIInterfaceRequestor, + public nsIHttpHeaderVisitor { public: NS_DECL_ISUPPORTS_INHERITED @@ -79,13 +84,15 @@ public: NS_DECL_NSIXFORMSSUBMISSIONELEMENT NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIHTTPHEADERVISITOR nsXFormsSubmissionElement() : mElement(nsnull), mSubmissionActive(PR_FALSE), mIsReplaceInstance(PR_FALSE), mIsSOAPRequest(PR_FALSE), - mFormat(0) + mFormat(0), + mSubmissionBody(nsnull) {} // nsIXTFGenericElement overrides @@ -157,6 +164,15 @@ private: // input end of pipe, which contains response data. nsCOMPtr mPipeIn; + // Context Info for events. + nsCOMArray mContextInfo; + // Document for http header context info. + nsCOMPtr mHttpHeaderDoc; + // Submission body from xforms-submit-serialize. + nsCOMPtr mSubmissionBody; + // Type of submit error. + nsString mSubmitError; + /** * @return true if aTestURI has the same origin as aBaseURI or if * there is no need for a same origin check. @@ -197,6 +213,21 @@ private: */ nsresult CreateAttachments(nsIModelElementPrivate *aModel, nsIDOMNode *aDoc, SubmissionAttachmentArray *aAttachments); + + /** + * Set context info. + */ + nsresult SetContextInfo(); + + /** + * Set Http context info. + * + * @param aResponse Protocol response code + * @param aResponseText Protocol response reason phrase + */ + nsresult SetHttpContextInfo(PRUint32 aResponse, const nsAString &aResponseText); + + nsresult ParseErrorResponse(nsIChannel *aChannel); }; NS_HIDDEN_(nsresult) diff --git a/mozilla/extensions/xforms/nsXFormsUtilityService.cpp b/mozilla/extensions/xforms/nsXFormsUtilityService.cpp index d423a221a15..3b90bab84ae 100644 --- a/mozilla/extensions/xforms/nsXFormsUtilityService.cpp +++ b/mozilla/extensions/xforms/nsXFormsUtilityService.cpp @@ -21,6 +21,7 @@ * * Contributor(s): * Aaron Reed + * Merle Sterling * * 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 @@ -54,6 +55,8 @@ #include "prdtoa.h" #include "nsIXFormsControl.h" #include "nsIModelElementPrivate.h" +#include "nsIXFormsActionModuleElement.h" +#include "nsIXFormsContextInfo.h" NS_IMPL_ISUPPORTS1(nsXFormsUtilityService, nsIXFormsUtilityService) @@ -429,3 +432,72 @@ nsXFormsUtilityService::GetDaysFromDateTime(const nsAString & aValue, return NS_OK; } +NS_IMETHODIMP +nsXFormsUtilityService::GetEventContextInfo(const nsAString & aContextName, + nsIDOMNode * aNode, + nsCOMArray *aResult) +{ + nsresult rv; + + nsCOMPtr contextInfo; + nsCOMPtr actionElt(do_QueryInterface(aNode)); + if (!actionElt) + return NS_OK; + + nsCOMPtr domEvent; + actionElt->GetCurrentEvent(getter_AddRefs(domEvent)); + nsCOMPtr xfEvent(do_QueryInterface(domEvent)); + if (!xfEvent) { + // Event being called for an nsIDOMEvent that is not an + // nsIXFormsDOMEvent. + return NS_OK; + } + xfEvent->GetContextInfo(aContextName, getter_AddRefs(contextInfo)); + if (!contextInfo) { + // The requested context info property does not exist. + return NS_OK; + } + + // Determine the type of context info property. + PRInt32 resultType; + contextInfo->GetType(&resultType); + + if (resultType == nsIXFormsContextInfo::NODESET_TYPE) { + // The context property is a nodeset. Snapshot each individual node + // in the nodeset and add them one at a time to the context info array. + nsCOMPtr nodeset; + contextInfo->GetNodesetValue(getter_AddRefs(nodeset)); + if (nodeset) { + PRUint32 nodesetSize; + rv = nodeset->GetSnapshotLength(&nodesetSize); + NS_ENSURE_SUCCESS(rv, rv); + for (PRUint32 i=0; i < nodesetSize; ++i) { + nsCOMPtr node; + nodeset->SnapshotItem(i, getter_AddRefs(node)); + aResult->AppendObject(node); + } + } + } else { + // The type is a dom node, string, or number. Strings and numbers + // are encapsulated in a text node. + nsCOMPtr node; + contextInfo->GetNodeValue(getter_AddRefs(node)); + if (node) { + aResult->AppendObject(node); + } +#ifdef DEBUG + PRInt32 type; + contextInfo->GetType(&type); + if (type == nsXFormsContextInfo::STRING_TYPE) { + nsAutoString str; + contextInfo->GetStringValue(str); + } else if (type == nsXFormsContextInfo::NUMBER_TYPE) { + PRInt32 number; + contextInfo->GetNumberValue(&number); + } +#endif + } + + return NS_OK; +} + diff --git a/mozilla/extensions/xforms/nsXFormsUtils.cpp b/mozilla/extensions/xforms/nsXFormsUtils.cpp index 6107129d847..98940e98c2b 100644 --- a/mozilla/extensions/xforms/nsXFormsUtils.cpp +++ b/mozilla/extensions/xforms/nsXFormsUtils.cpp @@ -115,7 +115,7 @@ #define CANCELABLE 0x01 #define BUBBLES 0x02 -const EventData sXFormsEventsEntries[42] = { +const EventData sXFormsEventsEntries[43] = { { "xforms-model-construct", PR_FALSE, PR_TRUE }, { "xforms-model-construct-done", PR_FALSE, PR_TRUE }, { "xforms-ready", PR_FALSE, PR_TRUE }, @@ -157,7 +157,8 @@ const EventData sXFormsEventsEntries[42] = { { "xforms-link-exception", PR_FALSE, PR_TRUE }, { "xforms-link-error", PR_FALSE, PR_TRUE }, { "xforms-compute-exception", PR_FALSE, PR_TRUE }, - { "xforms-moz-hint-off", PR_FALSE, PR_TRUE } + { "xforms-moz-hint-off", PR_FALSE, PR_TRUE }, + { "xforms-submit-serialize", PR_FALSE, PR_TRUE } }; static const EventData sEventDefaultsEntries[] = { @@ -213,6 +214,7 @@ struct EventItem nsXFormsEvent event; nsCOMPtr eventTarget; nsCOMPtr srcElement; + nsCOMArray *contextInfo; }; PRInt32 nsXFormsUtils::waitLimit = 10; @@ -559,7 +561,21 @@ nsXFormsUtils::EvaluateXPath(const nsAString &aExpression, nsCOMPtr resolverElement = do_QueryInterface(aResolverNode); nsCOMPtr modelPriv = nsXFormsUtils::GetModel(resolverElement); nsCOMPtr model = do_QueryInterface(modelPriv); - DispatchEvent(model, eEvent_ComputeException, nsnull, resolverElement); + + // Context Info: 'error-message' + // Error message containing the expression being processed. + nsAutoString errorMsg; + errorMsg.AssignLiteral("Error evaluating expression: "); + errorMsg.Append(aExpression); + + nsCOMPtr contextInfo = + new nsXFormsContextInfo(resolverElement); + NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY); + contextInfo->SetStringValue("error-message", errorMsg); + nsCOMArray contextInfoArray; + contextInfoArray.AppendObject(contextInfo); + DispatchEvent(model, eEvent_ComputeException, nsnull, resolverElement, + &contextInfoArray); } return rv; @@ -870,7 +886,8 @@ nsXFormsUtils::GetSingleNodeBindingValue(nsIDOMElement* aElement, nsresult DispatchXFormsEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, - PRBool *aDefaultActionEnabled) + PRBool *aDefaultActionEnabled, + nsCOMArray *aContextInfo) { nsCOMPtr domDoc; aTarget->GetOwnerDocument(getter_AddRefs(domDoc)); @@ -890,8 +907,12 @@ DispatchXFormsEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, nsXFormsUtils::SetEventTrusted(event, aTarget); + // Create an nsXFormsDOMEvent with the context info for the event. + nsCOMPtr xfDOMEvent = new nsXFormsDOMEvent(event, aContextInfo); + NS_ENSURE_TRUE(xfDOMEvent, NS_ERROR_OUT_OF_MEMORY); + PRBool defaultActionEnabled = PR_TRUE; - nsresult rv = target->DispatchEvent(event, &defaultActionEnabled); + nsresult rv = target->DispatchEvent(xfDOMEvent, &defaultActionEnabled); if (NS_SUCCEEDED(rv) && aDefaultActionEnabled) *aDefaultActionEnabled = defaultActionEnabled; @@ -923,6 +944,10 @@ DispatchXFormsEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, break; } + // Clear the context info array before the next event. + if (aContextInfo) + aContextInfo->Clear(); + return rv; } @@ -945,7 +970,8 @@ DeleteVoidArray(void *aObject, nsresult DeferDispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, - nsIDOMElement *aSrcElement) + nsIDOMElement *aSrcElement, + nsCOMArray *aContextInfo) { nsCOMPtr domDoc; if (aTarget) { @@ -975,6 +1001,7 @@ DeferDispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, deferredEvent->event = aEvent; deferredEvent->eventTarget = aTarget; deferredEvent->srcElement = aSrcElement; + deferredEvent->contextInfo = aContextInfo; eventList->AppendElement(deferredEvent); return NS_OK; @@ -983,7 +1010,8 @@ DeferDispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, /* static */ nsresult nsXFormsUtils::DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, PRBool *aDefaultActionEnabled, - nsIDOMElement *aSrcElement) + nsIDOMElement *aSrcElement, + nsCOMArray *aContextInfo) { // it is valid to have aTarget be null if this is an event that must be // targeted at a model per spec and aSrcElement is non-null. Basically we @@ -1037,7 +1065,7 @@ nsXFormsUtils::DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, // model later when the DOM is finished loading if (!aTarget) { if (aSrcElement) { - DeferDispatchEvent(aTarget, aEvent, aSrcElement); + DeferDispatchEvent(aTarget, aEvent, aSrcElement, aContextInfo); return NS_OK; } return NS_ERROR_FAILURE; @@ -1049,7 +1077,7 @@ nsXFormsUtils::DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, PRBool safeToSendEvent = PR_FALSE; modelPriv->GetHasDOMContentFired(&safeToSendEvent); if (!safeToSendEvent) { - DeferDispatchEvent(aTarget, aEvent, nsnull); + DeferDispatchEvent(aTarget, aEvent, nsnull, aContextInfo); return NS_OK; } @@ -1091,7 +1119,7 @@ nsXFormsUtils::DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, PRBool safeToSendEvent = PR_FALSE; modelPriv->GetHasDOMContentFired(&safeToSendEvent); if (!safeToSendEvent) { - DeferDispatchEvent(aTarget, aEvent, nsnull); + DeferDispatchEvent(aTarget, aEvent, nsnull, aContextInfo); return NS_OK; } @@ -1101,7 +1129,7 @@ nsXFormsUtils::DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, break; } - return DispatchXFormsEvent(aTarget, aEvent, aDefaultActionEnabled); + return DispatchXFormsEvent(aTarget, aEvent, aDefaultActionEnabled, aContextInfo); } @@ -1157,7 +1185,7 @@ nsXFormsUtils::DispatchDeferredEvents(nsIDOMDocument* aDocument) NS_ENSURE_STATE(item->eventTarget); } - DispatchXFormsEvent(item->eventTarget, item->event, nsnull); + DispatchXFormsEvent(item->eventTarget, item->event, nsnull, item->contextInfo); } doc->DeleteProperty(nsXFormsAtoms::deferredEventListProperty); diff --git a/mozilla/extensions/xforms/nsXFormsUtils.h b/mozilla/extensions/xforms/nsXFormsUtils.h index ae8994d3f9f..eacbc5d3cb2 100644 --- a/mozilla/extensions/xforms/nsXFormsUtils.h +++ b/mozilla/extensions/xforms/nsXFormsUtils.h @@ -50,6 +50,7 @@ #include "nsIScriptError.h" #include "nsVoidArray.h" #include "nsIDOMWindowInternal.h" +#include "nsXFormsDOMEvent.h" class nsIDOMElement; class nsIXFormsModelElement; @@ -133,7 +134,8 @@ enum nsXFormsEvent { eEvent_LinkException, eEvent_LinkError, eEvent_ComputeException, - eEvent_MozHintOff + eEvent_MozHintOff, + eEvent_SubmitSerialize }; struct EventData @@ -143,7 +145,7 @@ struct EventData PRBool canBubble; }; -extern const EventData sXFormsEventsEntries[42]; +extern const EventData sXFormsEventsEntries[43]; // Default intrinsic state for XForms Controls extern const PRInt32 kDefaultIntrinsicState; @@ -326,7 +328,8 @@ public: static NS_HIDDEN_(nsresult) DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, PRBool *aDefaultActionEnabled = nsnull, - nsIDOMElement *aSrcElement = nsnull); + nsIDOMElement *aSrcElement = nsnull, + nsCOMArray *aContextInfo = nsnull); static NS_HIDDEN_(nsresult) DispatchDeferredEvents(nsIDOMDocument* aDocument);