Back to index

lightning-sunbird  0.9+nobinonly
nsXFormsUtilityService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla XForms support.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * IBM Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Aaron Reed <aaronr@us.ibm.com>
00024  *  Merle Sterling <msterlin@us.ibm.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsIServiceManager.h"
00041 #include "nsXFormsUtilityService.h"
00042 #include "nsXFormsUtils.h"
00043 #include "nsIXTFElement.h"
00044 #include "nsIDOMNode.h"
00045 #include "nsIDOMElement.h"
00046 #include "nsString.h"
00047 #include "nsIDOMDocument.h"
00048 #include "nsIXFormsModelElement.h"
00049 #include "nsIDOMNodeList.h"
00050 #include "nsIInstanceElementPrivate.h"
00051 #include "nsIXFormsRepeatElement.h"
00052 #include "nsISchemaValidator.h"
00053 #include "nsISchemaDuration.h"
00054 #include "nsXFormsSchemaValidator.h"
00055 #include "prdtoa.h"
00056 #include "prprf.h"
00057 #include "nsIXFormsControl.h"
00058 #include "nsIModelElementPrivate.h"
00059 #include "nsIXFormsActionModuleElement.h"
00060 #include "nsIXFormsContextInfo.h"
00061 #include "prmem.h"
00062 #include "plbase64.h"
00063 #include "nsICryptoHash.h"
00064 
00065 NS_IMPL_ISUPPORTS1(nsXFormsUtilityService, nsIXFormsUtilityService)
00066 
00067 /* I don't know why Doron didn't put this in the .idl so that it could be added
00068  * to the generated .h file.  Put it here for now
00069  */
00070 #define NS_SCHEMAVALIDATOR_CONTRACTID  "@mozilla.org/schemavalidator;1"
00071 
00072 
00073 NS_IMETHODIMP
00074 nsXFormsUtilityService::GetBuiltinTypeName(nsIDOMNode *aElement,
00075                                            nsAString& aName)
00076 {
00077   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(aElement));
00078   NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
00079 
00080   nsCOMPtr<nsIModelElementPrivate> model = nsXFormsUtils::GetModel(element);
00081   NS_ENSURE_TRUE(model, NS_ERROR_FAILURE);
00082 
00083   nsCOMPtr<nsIXFormsControl> control(do_QueryInterface(element));
00084   return model->GetBuiltinTypeNameForControl(control, aName);
00085 }
00086 
00087 NS_IMETHODIMP
00088 nsXFormsUtilityService::GetModelFromNode(nsIDOMNode *aNode, 
00089                                          nsIDOMNode **aModel)
00090 {
00091   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
00092   NS_ASSERTION(aModel, "no return buffer, we'll crash soon");
00093   *aModel = nsnull;
00094 
00095   nsAutoString namespaceURI;
00096   aNode->GetNamespaceURI(namespaceURI);
00097 
00098   // If the node is in the XForms namespace and XTF based, then it should
00099   //   be able to be handled by GetModel.  Otherwise it is probably an instance
00100   //   node in a instance document.
00101   if (!namespaceURI.EqualsLiteral(NS_NAMESPACE_XFORMS)) {
00102     return NS_ERROR_FAILURE;
00103   }
00104 
00105   nsCOMPtr<nsIModelElementPrivate> modelPriv = nsXFormsUtils::GetModel(element);
00106   nsCOMPtr<nsIDOMNode> modelElement = do_QueryInterface(modelPriv);
00107   if( modelElement ) {
00108     NS_IF_ADDREF(*aModel = modelElement);
00109   }
00110 
00111   // No model found
00112   NS_ENSURE_TRUE(*aModel, NS_ERROR_FAILURE);
00113 
00114   return NS_OK;
00115 }
00116 
00117 
00133 NS_IMETHODIMP
00134 nsXFormsUtilityService::IsNodeAssocWithModel( nsIDOMNode *aNode, 
00135                                               nsIDOMNode *aModel,
00136                                               PRBool     *aModelAssocWithNode)
00137 {
00138 
00139   // Determine if the given model contains this instance document.
00140   nsCOMPtr<nsIDOMNode> modelNode, instNode;
00141   nsresult rv =
00142     nsXFormsUtils::GetInstanceNodeForData(aNode, getter_AddRefs(instNode));
00143   if (NS_SUCCEEDED(rv) && instNode) {
00144     instNode->GetParentNode(getter_AddRefs(modelNode));
00145   }
00146 
00147   *aModelAssocWithNode = modelNode && (modelNode == aModel);
00148   return NS_OK;
00149 }
00150 
00151 NS_IMETHODIMP
00152 nsXFormsUtilityService::GetInstanceDocumentRoot(const      nsAString& aID,
00153                                                 nsIDOMNode *aModelNode,
00154                                                 nsIDOMNode **aInstanceRoot)
00155 {
00156   nsresult rv = NS_ERROR_FAILURE;
00157   NS_ASSERTION(aInstanceRoot, "no return buffer, we'll crash soon");
00158   *aInstanceRoot = nsnull;
00159 
00160   if (aID.IsEmpty()) {
00161     return rv;
00162   }
00163 
00164   nsCOMPtr<nsIXFormsModelElement> modelElement = do_QueryInterface(aModelNode);
00165   nsCOMPtr<nsIDOMDocument> doc;
00166   rv = modelElement->GetInstanceDocument(aID, getter_AddRefs(doc));
00167   NS_ENSURE_SUCCESS(rv, rv);
00168   
00169   nsCOMPtr<nsIDOMElement> element;
00170   rv = doc->GetDocumentElement(getter_AddRefs(element));
00171   NS_ENSURE_SUCCESS(rv, rv);
00172 
00173   if (element) {
00174     NS_IF_ADDREF(*aInstanceRoot = element);
00175   }
00176 
00177   return rv;
00178 }
00179 
00180 /* Gotta do this via the service since we don't want transformiix to require
00181  * any of the new extensions, like schema-validation
00182  */
00183 NS_IMETHODIMP 
00184 nsXFormsUtilityService::ValidateString(const nsAString & aValue, 
00185                                        const nsAString & aType, 
00186                                        const nsAString & aNamespace,
00187                                        PRBool *aResult)
00188 {
00189 
00190   NS_ASSERTION(aResult, "no return buffer for result so we'll crash soon");
00191   *aResult = PR_FALSE;
00192 
00193   nsXFormsSchemaValidator *validator = new nsXFormsSchemaValidator();
00194   if (validator) {
00195     *aResult = validator->ValidateString(aValue, aType, aNamespace);
00196     delete validator;
00197   }
00198   return *aResult ? NS_OK : NS_ERROR_FAILURE;
00199 }
00200 
00201 NS_IMETHODIMP
00202 nsXFormsUtilityService::GetRepeatIndexById(nsIDOMNode *aResolverNode,
00203                                            const nsAString &aId,
00204                                            PRInt32         *aIndex)
00205 {
00206   NS_ENSURE_ARG_POINTER(aIndex);
00207 
00208   nsCOMPtr<nsIDOMElement> resolverElement(do_QueryInterface(aResolverNode));
00209   NS_ENSURE_STATE(resolverElement);
00210 
00211   nsCOMPtr<nsIDOMElement> element;
00212   nsXFormsUtils::GetElementByContextId(resolverElement, aId,
00213                                        getter_AddRefs(element));
00214 
00215   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(element));
00216 
00217   nsCOMPtr<nsIXFormsRepeatElement> repeat = do_QueryInterface(node);
00218   if (!repeat) {
00219     // if aRepeat isn't a repeat element, then setting aIndex to -1 to tell
00220     // XPath to return NaN.  Per 7.8.5 in the spec (1.0, 2nd edition)
00221     *aIndex = -1;
00222     return NS_OK;
00223   }
00224 
00225   PRUint32 retIndex = 0;
00226   nsresult rv = repeat->GetIndex(&retIndex);
00227   NS_ENSURE_SUCCESS(rv, rv);
00228   *aIndex = retIndex;
00229 
00230   return NS_OK;
00231 }
00232 
00233 NS_IMETHODIMP
00234 nsXFormsUtilityService::GetMonths(const nsAString & aValue, 
00235                                   PRInt32         * aMonths)
00236 {
00237   NS_ASSERTION(aMonths, "no return buffer for months, we'll crash soon");
00238 
00239   *aMonths = 0;
00240   nsCOMPtr<nsISchemaDuration> duration;
00241   nsCOMPtr<nsISchemaValidator> schemaValidator = 
00242     do_CreateInstance(NS_SCHEMAVALIDATOR_CONTRACTID);
00243   NS_ENSURE_TRUE(schemaValidator, NS_ERROR_FAILURE);
00244 
00245   nsresult rv = schemaValidator->ValidateBuiltinTypeDuration(aValue, 
00246                                                     getter_AddRefs(duration));
00247   NS_ENSURE_SUCCESS(rv, rv);
00248 
00249   PRInt32 sumMonths;
00250   PRUint32 years;
00251   PRUint32 months;
00252 
00253   duration->GetYears(&years);
00254   duration->GetMonths(&months);
00255 
00256   sumMonths = months + years*12;
00257   PRBool negative;
00258   duration->GetNegative(&negative);
00259   if (negative) {
00260     // according to the spec, "the sign of the result will match the sign
00261     // of the duration"
00262     sumMonths *= -1;
00263   }
00264   
00265   *aMonths = sumMonths;
00266 
00267   return NS_OK;
00268 }
00269 
00270 NS_IMETHODIMP
00271 nsXFormsUtilityService::GetSeconds(const nsAString & aValue, 
00272                                    double          * aSeconds)
00273 {
00274   nsCOMPtr<nsISchemaDuration> duration;
00275   nsCOMPtr<nsISchemaValidator> schemaValidator = 
00276     do_CreateInstance(NS_SCHEMAVALIDATOR_CONTRACTID);
00277   NS_ENSURE_TRUE(schemaValidator, NS_ERROR_FAILURE);
00278 
00279   nsresult rv = schemaValidator->ValidateBuiltinTypeDuration(aValue, 
00280                                                     getter_AddRefs(duration));
00281   NS_ENSURE_SUCCESS(rv, rv);
00282   double sumSeconds;
00283   PRUint32 days;
00284   PRUint32 hours;
00285   PRUint32 minutes;
00286   PRUint32 seconds;
00287   double fractSecs;
00288 
00289   duration->GetDays(&days);
00290   duration->GetHours(&hours);
00291   duration->GetMinutes(&minutes);
00292   duration->GetSeconds(&seconds);
00293   duration->GetFractionSeconds(&fractSecs);
00294 
00295   sumSeconds = seconds + minutes*60 + hours*3600 + days*24*3600 + fractSecs;
00296 
00297   PRBool negative;
00298   duration->GetNegative(&negative);
00299   if (negative) {
00300     // according to the spec, "the sign of the result will match the sign
00301     // of the duration"
00302     sumSeconds *= -1;
00303   }
00304 
00305   *aSeconds = sumSeconds;
00306   return NS_OK;
00307 }
00308 
00309 NS_IMETHODIMP
00310 nsXFormsUtilityService::GetSecondsFromDateTime(const nsAString & aValue, 
00311                                                double          * aSeconds)
00312 {
00313   PRTime dateTime;
00314   nsCOMPtr<nsISchemaValidator> schemaValidator = 
00315     do_CreateInstance(NS_SCHEMAVALIDATOR_CONTRACTID);
00316   NS_ENSURE_TRUE(schemaValidator, NS_ERROR_FAILURE);
00317 
00318   nsresult rv = schemaValidator->ValidateBuiltinTypeDateTime(aValue, &dateTime); 
00319   NS_ENSURE_SUCCESS(rv, rv);
00320                                                
00321   PRTime secs64 = dateTime, remain64;
00322   PRInt64 usecPerSec;
00323   PRInt32 secs32, remain32;
00324 
00325   // convert from PRTime (microseconds from epoch) to seconds.
00326   LL_I2L(usecPerSec, PR_USEC_PER_SEC);
00327   LL_MOD(remain64, secs64, usecPerSec);   /* remainder after conversion */
00328   LL_DIV(secs64, secs64, usecPerSec);     /* Conversion in whole seconds */
00329 
00330   // convert whole seconds and remainder to PRInt32
00331   LL_L2I(secs32, secs64);
00332   LL_L2I(remain32, remain64);
00333 
00334   // ready the result to send back to transformiix land now in case there are
00335   // no fractional seconds or we end up having a problem parsing them out.  If
00336   // we do, we'll just ignore the fractional seconds.
00337   double totalSeconds = secs32;
00338   *aSeconds = totalSeconds;
00339 
00340   // We're not getting fractional seconds back in the PRTime we get from
00341   // the schemaValidator.  We'll have to figure out the fractions from
00342   // the original value.  Since ValidateBuiltinTypeDateTime returned
00343   // successful for us to get this far, we know that the value is in
00344   // the proper format.
00345   int findFractionalSeconds = aValue.FindChar('.');
00346   if (findFractionalSeconds < 0) {
00347     // no fractions of seconds, so we are good to go as we are
00348     return NS_OK;
00349   }
00350 
00351   const nsAString& fraction = Substring(aValue, findFractionalSeconds+1, 
00352                                         aValue.Length());
00353 
00354   PRBool done = PR_FALSE;
00355   PRUnichar currentChar;
00356   nsCAutoString fractionResult;
00357   nsAString::const_iterator start, end, buffStart;
00358   fraction.BeginReading(start);
00359   fraction.BeginReading(buffStart);
00360   fraction.EndReading(end);
00361 
00362   while ((start != end) && !done) {
00363     currentChar = *start++;
00364 
00365     // Time is usually terminated with Z or followed by a time zone
00366     // (i.e. -05:00).  Time can also be terminated by the end of the string, so
00367     // test for that as well.  All of this specified at:
00368     // http://www.w3.org/TR/xmlschema-2/#dateTime
00369     if ((currentChar == 'Z') || (currentChar == '+') || (currentChar == '-') ||
00370         (start == end)) {
00371       fractionResult.AssignLiteral("0.");
00372       AppendUTF16toUTF8(Substring(buffStart.get(), start.get()-1), 
00373                         fractionResult);
00374     } else if ((currentChar > '9') || (currentChar < '0')) {
00375       // has to be a numerical character or else abort.  This should have been
00376       // caught by the schemavalidator, but it is worth double checking.
00377       done = PR_TRUE;
00378     }
00379   }
00380 
00381   if (fractionResult.IsEmpty()) {
00382     // couldn't successfully parse the fractional seconds, so we'll just return
00383     // without them.
00384     return NS_OK;
00385   }
00386 
00387   // convert the result string that we have to a double and add it to the total
00388   totalSeconds += PR_strtod(fractionResult.get(), nsnull);
00389   *aSeconds = totalSeconds;
00390 
00391   return NS_OK;
00392 }
00393 
00394 NS_IMETHODIMP
00395 nsXFormsUtilityService::GetDaysFromDateTime(const nsAString & aValue,
00396                                             PRInt32         * aDays)
00397 {
00398   NS_ASSERTION(aDays, "no return buffer for days, we'll crash soon");
00399   *aDays = 0;
00400 
00401   PRTime date;
00402   nsCOMPtr<nsISchemaValidator> schemaValidator =
00403     do_CreateInstance(NS_SCHEMAVALIDATOR_CONTRACTID);
00404   NS_ENSURE_TRUE(schemaValidator, NS_ERROR_FAILURE);
00405 
00406   // aValue could be a xsd:date or a xsd:dateTime.  If it is a xsd:dateTime,
00407   // there will be a 'T' separating the date portion of the string from the time
00408   // portion http://www.w3.org/TR/xmlschema-2/#dateTime
00409   PRInt32 timeSeparator = aValue.FindChar('T');
00410 
00411   nsresult rv;
00412   if (timeSeparator >= 0) {
00413     rv = schemaValidator->ValidateBuiltinTypeDateTime(aValue, &date);
00414   } else {
00415     rv = schemaValidator->ValidateBuiltinTypeDate(aValue, &date);
00416   }
00417   NS_ENSURE_SUCCESS(rv, rv);
00418 
00419   PRTime secs64 = date;
00420   PRInt64 usecPerSec;
00421   PRInt32 secs32;
00422 
00423   // convert from PRTime (microseconds from epoch) to seconds.  Shouldn't
00424   // have to worry about remainders since input is a date.  Smallest value
00425   // is in whole days.
00426   LL_I2L(usecPerSec, PR_USEC_PER_SEC);
00427   LL_DIV(secs64, secs64, usecPerSec);
00428 
00429   // convert whole seconds to PRInt32
00430   LL_L2I(secs32, secs64);
00431 
00432   // If aValue was a dateTime, "Hour, minute, and second components are ignored
00433   // after normalization" according to 7.10.2 in the spec.  So according to spec
00434   // we should strip off the fraction of a day after normalizing and before
00435   // figuring out its distance from the epoch.  But secs32 already has been
00436   // normalized and contains the distance from the epoch.  So now we might have
00437   // to alter aDays to account for the fact that we didn't remove any fraction
00438   // before.  For example, if aValue is 1970-01-02T12:00:00, then this is
00439   // 1.5 days after the epoch.  But if we removed the fraction before the
00440   // calculation we'd have 1970-01-02, which is 1 day from the epoch.  So
00441   // GetDaysFromDateTime would return 1.  So we see that if aValue is on or
00442   // after the epoch, we can ignore the remainder.  However, if aValue is
00443   // 1969-12-31T12:00:00, this would be -0.5 days from the epoch.  Applying
00444   // the spec rule of dropping the fractional day, we would be calculating
00445   // using 1969-12-31 which would give us -1 days from the epoch (negative
00446   // because it is before the epoch).  So we can't simply ignore the remainder.
00447   // If we have a negative value with a remainder, we need to round down to
00448   // the next whole day value.  So that is what we will do below.
00449 
00450   // Convert seconds to days.  86400 seconds in a day.
00451   *aDays = secs32/86400;
00452 
00453   // Apply the rule from above to simulate having removed the fractional day
00454   // prior to calculating the distance from the epoch.  If secs32 is negative
00455   // then if there was a fraction of a day, round down a day.
00456   if (secs32 < 0) {
00457     PRInt32 remainder = secs32%86400;
00458     if (remainder) {
00459       --*aDays;
00460     }
00461   }
00462 
00463 
00464   return NS_OK;
00465 }
00466 
00467 
00468 /* static */ nsresult
00469 nsXFormsUtilityService::GetTime(nsAString & aResult, PRBool aUTC)
00470 {
00471     PRExplodedTime time;
00472     char ctime[60];
00473 
00474     PR_ExplodeTime(PR_Now(),
00475                    aUTC ? PR_GMTParameters : PR_LocalTimeParameters, &time);
00476 
00477     PR_FormatTime(ctime, sizeof(ctime), "%Y-%m-%dT%H:%M:%S\0", &time);
00478 
00479     aResult.Assign(NS_ConvertASCIItoUTF16(ctime));
00480 
00481     if (aUTC) {
00482       aResult.AppendLiteral("Z");
00483       return NS_OK;
00484     }
00485 
00486     int gmtoffsethour = time.tm_params.tp_gmt_offset < 0 ?
00487                         -1*time.tm_params.tp_gmt_offset / 3600 :
00488                         time.tm_params.tp_gmt_offset / 3600;
00489     int remainder = time.tm_params.tp_gmt_offset%3600;
00490     int gmtoffsetminute = remainder ? remainder/60 : 00;
00491   
00492     char zone_location[40];
00493     const int zoneBufSize = sizeof(zone_location);
00494     PR_snprintf(zone_location, zoneBufSize, "%c%02d:%02d\0",
00495                 time.tm_params.tp_gmt_offset < 0 ? '-' : '+',
00496                 gmtoffsethour, gmtoffsetminute);
00497 
00498     aResult.Append(NS_ConvertASCIItoUTF16(zone_location));
00499 
00500     return NS_OK;
00501 }
00502 
00503 
00504 NS_IMETHODIMP
00505 nsXFormsUtilityService::GetEventContextInfo(const nsAString & aContextName,
00506                                        nsIDOMNode           * aNode,
00507                                        nsCOMArray<nsIDOMNode> *aResult)
00508 {
00509   nsresult rv;
00510 
00511   nsCOMPtr<nsIXFormsContextInfo> contextInfo;
00512   nsCOMPtr<nsIXFormsActionModuleElement> actionElt(do_QueryInterface(aNode));
00513   if (!actionElt)
00514     return NS_OK;
00515 
00516   nsCOMPtr<nsIDOMEvent> domEvent;
00517   actionElt->GetCurrentEvent(getter_AddRefs(domEvent));
00518   nsCOMPtr<nsIXFormsDOMEvent> xfEvent(do_QueryInterface(domEvent));
00519   if (!xfEvent) {
00520     // Event being called for an nsIDOMEvent that is not an
00521     // nsIXFormsDOMEvent.
00522     return NS_OK;
00523   }
00524   xfEvent->GetContextInfo(aContextName, getter_AddRefs(contextInfo));
00525   if (!contextInfo) {
00526     // The requested context info property does not exist.
00527     return NS_OK;
00528   }
00529 
00530   // Determine the type of context info property.
00531   PRInt32 resultType;
00532   contextInfo->GetType(&resultType);
00533 
00534   if (resultType == nsIXFormsContextInfo::NODESET_TYPE) {
00535     // The context property is a nodeset. Snapshot each individual node
00536     // in the nodeset and add them one at a time to the context info array.
00537     nsCOMPtr<nsIDOMXPathResult> nodeset;
00538     contextInfo->GetNodesetValue(getter_AddRefs(nodeset));
00539     if (nodeset) {
00540       PRUint32 nodesetSize;
00541       rv = nodeset->GetSnapshotLength(&nodesetSize);
00542       NS_ENSURE_SUCCESS(rv, rv);
00543       for (PRUint32 i=0; i < nodesetSize; ++i) {
00544         nsCOMPtr<nsIDOMNode> node;
00545         nodeset->SnapshotItem(i, getter_AddRefs(node));
00546         aResult->AppendObject(node);
00547       }
00548     }
00549   } else {
00550     // The type is a dom node, string, or number. Strings and numbers
00551     // are encapsulated in a text node.
00552     nsCOMPtr<nsIDOMNode> node;
00553     contextInfo->GetNodeValue(getter_AddRefs(node));
00554     if (node) {
00555       aResult->AppendObject(node);
00556     }
00557 #ifdef DEBUG
00558     PRInt32 type;
00559     contextInfo->GetType(&type);
00560     if (type == nsXFormsContextInfo::STRING_TYPE) {
00561       nsAutoString str;
00562       contextInfo->GetStringValue(str);
00563     } else if (type == nsXFormsContextInfo::NUMBER_TYPE) {
00564       PRInt32 number;
00565       contextInfo->GetNumberValue(&number);
00566     }
00567 #endif
00568   }
00569 
00570   return NS_OK;
00571 }
00572 
00573 NS_IMETHODIMP
00574 nsXFormsUtilityService::Context(nsIDOMNode  *aResolverNode,
00575                                 nsIDOMNode **aResult)
00576 {
00577 
00578   nsCOMPtr<nsIDOMNode> contextNode;
00579   PRUint32 contextNodesetSize = 0;
00580   PRInt32 contextPosition;
00581   nsCOMPtr<nsIModelElementPrivate> model;
00582   nsCOMPtr<nsIDOMElement> bindElement;
00583   nsCOMPtr<nsIXFormsControl> parentControl;
00584   PRBool outerBind;
00585 
00586   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(aResolverNode));
00587   if (!element) {
00588     contextNode.swap(*aResult);
00589     return NS_OK;
00590   }
00591 
00592   nsresult rv =
00593     nsXFormsUtils::GetNodeContext(element,
00594                                   nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
00595                                   getter_AddRefs(model),
00596                                   getter_AddRefs(bindElement),
00597                                   &outerBind,
00598                                   getter_AddRefs(parentControl),
00599                                   getter_AddRefs(contextNode),
00600                                   &contextPosition,
00601                                   (PRInt32*)&contextNodesetSize,
00602                                   PR_FALSE);
00603 
00604   NS_ENSURE_SUCCESS(rv, rv);
00605 
00606   contextNode.swap(*aResult);
00607 
00608   return NS_OK;
00609 }
00610 
00611 NS_IMETHODIMP
00612 nsXFormsUtilityService::IsCardNumber(const nsAString& aNumber, PRBool *aResult)
00613 {
00614   nsAutoString number(aNumber);
00615 
00616   nsXFormsSchemaValidator validator;
00617   if (!validator.ValidateString(number,
00618                                 NS_LITERAL_STRING("card-number"),
00619                                 NS_LITERAL_STRING(NS_NAMESPACE_XFORMS))) {
00620     *aResult = PR_FALSE;
00621     return NS_OK;
00622   }
00623 
00624   // Now check if the card number is a valid Luhn number.
00625   PRInt32 sum = 0;
00626   PRBool alt = false;
00627   for (PRInt32 i = number.Length() - 1; i >= 0; --i) {
00628     PRUnichar currentChar = number.CharAt(i);
00629     PRInt32 digit = abs(currentChar - '0');
00630 
00631     if (alt) {
00632       digit *= 2;
00633       if (digit > 9) {
00634         digit -= 9;
00635       }
00636     }
00637     sum += digit;
00638     alt = !alt;
00639   }
00640 
00641   *aResult = (sum % 10 == 0);
00642 
00643   return NS_OK;
00644 }
00645 
00646 NS_IMETHODIMP
00647 nsXFormsUtilityService::Digest(const nsAString &aData,
00648                                const nsAString &aAlgorithm,
00649                                const nsAString &aEncoding, 
00650                                nsIDOMNode *aResolverNode,
00651                                nsAString &aResult)
00652 {
00653   aResult.Truncate();
00654 
00655   PRBool throwException = PR_FALSE;
00656   
00657   // Determine the hash algorithm to use.
00658   PRUint32 hashAlg = 0;
00659 
00660   if (aAlgorithm.EqualsLiteral("MD5")) {
00661     hashAlg = nsICryptoHash::MD5;
00662   } else if (aAlgorithm.EqualsLiteral("SHA-1")) {
00663     hashAlg = nsICryptoHash::SHA1;
00664   } else if (aAlgorithm.EqualsLiteral("SHA-256")) {
00665     hashAlg = nsICryptoHash::SHA256;
00666   } else if (aAlgorithm.EqualsLiteral("SHA-384")) {
00667     hashAlg = nsICryptoHash::SHA384;
00668   } else if (aAlgorithm.EqualsLiteral("SHA-512")) {
00669     hashAlg = nsICryptoHash::SHA512;
00670   } else {
00671     // Throw exception.
00672     throwException = PR_TRUE;
00673   }
00674 
00675   if (!throwException) {
00676     // Perform the hash.
00677     nsresult rv;
00678   
00679     nsCOMPtr<nsICryptoHash> hash =
00680       do_CreateInstance("@mozilla.org/security/hash;1", &rv);
00681     NS_ENSURE_SUCCESS(rv, rv);
00682   
00683     rv = hash->Init(hashAlg);
00684     NS_ENSURE_SUCCESS(rv, rv);
00685   
00686     nsCAutoString data = NS_LossyConvertUTF16toASCII(aData);
00687     rv = hash->Update(reinterpret_cast<const PRUint8*>(data.get()),
00688                       data.Length());
00689     NS_ENSURE_SUCCESS(rv, rv);
00690  
00691     // PR_FALSE means return the raw binary data.
00692     nsCAutoString result;
00693     rv = hash->Finish(PR_FALSE, result);
00694     NS_ENSURE_SUCCESS(rv, rv);
00695 
00696     // Encode the result.
00697     if (aEncoding.IsEmpty() || aEncoding.EqualsLiteral("base64")) {
00698       char *buffer = PL_Base64Encode((char *)result.get(),
00699                                      result.Length(), nsnull);
00700       if (buffer) {
00701         aResult = ToNewUnicode(NS_ConvertASCIItoUTF16(buffer));
00702         PR_Free(buffer);
00703       }
00704     } else if (aEncoding.EqualsLiteral("hex")) {
00705       PRUint32 length = result.Length() * 2 + 1;
00706       PRUnichar *hexBuffer =
00707         NS_STATIC_CAST(PRUnichar*, nsMemory::Alloc(length * sizeof(PRUnichar)));
00708       NS_ENSURE_TRUE(hexBuffer, NS_ERROR_OUT_OF_MEMORY);
00709       nsXFormsUtils::BinaryToHex(result.get(), result.Length(), &hexBuffer);
00710       hexBuffer [result.Length() * 2] = 0;
00711 
00712       nsAutoString hexResult(hexBuffer);
00713       nsCAutoString hexLower = NS_LossyConvertUTF16toASCII(hexResult);
00714       ToLowerCase(hexLower);
00715       aResult = NS_ConvertASCIItoUTF16(hexLower);
00716 
00717       nsMemory::Free(hexBuffer);
00718     } else {
00719       // Throw exception.
00720       throwException = PR_TRUE;
00721     }
00722   }
00723 
00724   if (throwException) {
00725     // If the digest function appears in a computed expression (An XPath
00726     // expression used by model item properties such as relevant and
00727     // calculate to include dynamic functionality in XForms), an
00728     // xforms-compute-exception occurs. If the digest function appears
00729     // in any other attribute that contains an XPath function, an
00730     // xforms-binding-exception occurs.
00731     nsXFormsEvent event = eEvent_BindingException;
00732     nsAutoString localName, namespaceURI;
00733     aResolverNode->GetLocalName(localName);
00734     if (localName.EqualsLiteral("bind")) {
00735       aResolverNode->GetNamespaceURI(namespaceURI);
00736       if (namespaceURI.EqualsLiteral(NS_NAMESPACE_XFORMS)) {
00737         event = eEvent_ComputeException;
00738       }
00739     }
00740 
00741     // Dispatch the event.
00742     nsCOMPtr<nsIDOMElement> resolverElement = do_QueryInterface(aResolverNode);
00743     nsCOMPtr<nsIModelElementPrivate> modelPriv =
00744       nsXFormsUtils::GetModel(resolverElement);
00745     nsCOMPtr<nsIDOMNode> model = do_QueryInterface(modelPriv);
00746     nsXFormsUtils::DispatchEvent(model, event, nsnull, resolverElement,
00747                                  nsnull);
00748     return NS_ERROR_FAILURE;
00749   }
00750 
00751   return NS_OK;
00752 }
00753 
00754 NS_IMETHODIMP
00755 nsXFormsUtilityService::AdjustDateTimeToTimezone(const nsAString &aDateTime,
00756                                                  nsAString &aResult)
00757 {
00758   // We have three cases to deal with:
00759   //
00760   // 1. aDateTime does not include a timezone indicator: 2007-10-07T02:22:00
00761   //    The schema validator will return a PRTime that was calculated using
00762   //    PR_LocalTimeParameters and that PRTime value formatted as an
00763   //    xsd:dateTime is the same 2007-10-07T02:22:00 that was passed in.
00764   //    We just need to append the local time zone.
00765   //
00766   // 2. aDateTime is a UTC (aka GMT) time: 2007-10-07T21:26:43Z
00767   //    The schema validator will treat aDateTime as GMT and return that as a
00768   //    PRTime. We convert the GMT time to a time in the local time zone and
00769   //    append the local time zone.
00770   //
00771   // 3. aDateTime includes a time zone component: 2007-10-07T02:22:00-07:00
00772   //    The schema validator checks if the time zone component is valid, but
00773   //    does not use it when calculating the PRTime value so this case is
00774   //    similar to case 1 except that we have to add the given time zone
00775   //    offset (to get the GMT time of the input aDateTime), convert the GMT
00776   //    time to the local time zone, and append the local time zone.
00777   aResult.Truncate();
00778 
00779   nsCOMPtr<nsISchemaValidator> schemaValidator =
00780     do_CreateInstance("@mozilla.org/schemavalidator;1");
00781   NS_ENSURE_TRUE(schemaValidator, NS_ERROR_FAILURE);
00782 
00783   PRTime t_dateTime;
00784   nsresult rv = schemaValidator->ValidateBuiltinTypeDateTime(aDateTime,
00785                                                              &t_dateTime);
00786 
00787   if (NS_FAILED(rv)) {
00788     return NS_OK;
00789   }
00790 
00791   // The dateTime is valid, so get the timeZone information. If there is time
00792   // zone information we are dealing with case 3 and have a bit more work to
00793   // do to convert aDateTime to the local time zone.
00794   nsAutoString timeString, timeZoneString;
00795   PRInt32 timeSeparator = aDateTime.FindChar(PRUnichar('T'));
00796   timeString.Append(Substring(aDateTime,
00797                               timeSeparator + 1,
00798                               aDateTime.Length() - timeSeparator));
00799   nsXFormsUtils::GetTimeZone(timeString, timeZoneString);
00800 
00801   PRExplodedTime time;
00802   char ctime[60];
00803 
00804   if (!timeZoneString.IsEmpty()) {
00805     // The time zone string will be of the form ('+' | '-') hh ':' mm
00806     // For example: +05:00, -07:00
00807     nsAutoString hoursString, minutesString;
00808     hoursString.Append(Substring(timeZoneString, 1, 2));
00809     minutesString.Append(Substring(timeZoneString, 4, 2));
00810 
00811     PRInt32 errCode;
00812     PRInt32 hours = hoursString.ToInteger(&errCode);
00813     NS_ENSURE_TRUE(errCode == 0, NS_ERROR_FAILURE);
00814     PRInt32 minutes = minutesString.ToInteger(&errCode);
00815     NS_ENSURE_TRUE(errCode == 0, NS_ERROR_FAILURE);
00816     PRInt32 tzSecs = (hours * 3600) + (minutes * 60);
00817 
00818     if (timeZoneString.CharAt(0) == '+') {
00819       // The time zone is relative to GMT so if it is positive, we need to
00820       // subtract the total number of seconds represented by the time zone;
00821       // likewise, we add if the time zone is negative.
00822       tzSecs *= -1;
00823     }
00824 
00825     PR_ExplodeTime(t_dateTime, PR_LocalTimeParameters, &time);
00826     // Zero out the gmt and dst information because we don't want
00827     // PR_NormalizeTime to use the local time zone to get back to
00828     // GMT before it normalizes (because it would calculate the GMT
00829     // time relative to the time zone that was part of the input dateTime).
00830     time.tm_params.tp_gmt_offset = 0;
00831     time.tm_params.tp_dst_offset = 0;
00832     // Adjust the total seconds.
00833     time.tm_sec += tzSecs;
00834     // Normalize the fields and apply the local time parameters to convert
00835     // the time to the local time zone.
00836     PR_NormalizeTime(&time, PR_LocalTimeParameters);
00837     PR_FormatTime(ctime, sizeof(ctime), "%Y-%m-%dT%H:%M:%S\0", &time);
00838 
00839   } else {
00840     // This is either a GMT time or no time zone information is available.
00841     PR_ExplodeTime(t_dateTime, PR_LocalTimeParameters, &time);
00842     PR_FormatTime(ctime, sizeof(ctime), "%Y-%m-%dT%H:%M:%S\0", &time);
00843   }
00844 
00845   // Calculate local time zone to append to the result.
00846   int gmtoffsethour = time.tm_params.tp_gmt_offset / 3600;
00847   int remainder = time.tm_params.tp_gmt_offset % 3600;
00848   int gmtoffsetminute = remainder ? remainder / 60 : 0;
00849   // adjust gmtoffsethour for daylight savings time.
00850   int dstoffset = time.tm_params.tp_dst_offset / 3600;
00851   gmtoffsethour += dstoffset;
00852   if (gmtoffsethour < 0) {
00853     // Make the gmtoffsethour positive; we'll add the plus or minus
00854     // to the time zone string.
00855     gmtoffsethour *= -1;
00856   }
00857   
00858   char zone_location[40];
00859   const int zoneBufSize = sizeof(zone_location);
00860   PR_snprintf(zone_location, zoneBufSize, "%c%02d:%02d\0",
00861               time.tm_params.tp_gmt_offset < 0 ? '-' : '+',
00862               gmtoffsethour, gmtoffsetminute);
00863 
00864   aResult.AppendLiteral(ctime);
00865   aResult.Append(NS_ConvertASCIItoUTF16(zone_location));
00866 
00867   return NS_OK;
00868 }