Back to index

lightning-sunbird  0.9+nobinonly
nsXPointer.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002 */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2003
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Heikki Toivonen <heikki@netscape.com> (original author)
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 
00057 // TODO:
00058 // - xpointer scheme
00059 
00060 
00061 #include "nsIDOMNode.h"
00062 #include "nsIDOMNodeList.h"
00063 #include "nsIDOMElement.h"
00064 #include "nsIDOMDocument.h"
00065 #include "nsIDOMClassInfo.h"
00066 #include "nsCOMPtr.h"
00067 #include "nsXPointer.h"
00068 #include "nsIModifyableXPointer.h"
00069 #include "nsISupports.h"
00070 #include "nsISupportsUtils.h"
00071 #include "nsIXPointer.h"
00072 #include "nsFIXptr.h"
00073 #include "nsIServiceManager.h"
00074 #include "nsReadableUtils.h"
00075 
00076 #include "nsContentCID.h"
00077 static NS_DEFINE_IID(kRangeCID,     NS_RANGE_CID);
00078 
00079 nsXPointerResult::nsXPointerResult()
00080 {
00081 }
00082 
00083 nsXPointerResult::~nsXPointerResult()
00084 {
00085 }
00086 
00087 NS_INTERFACE_MAP_BEGIN(nsXPointerResult)
00088   NS_INTERFACE_MAP_ENTRY(nsIXPointerResult)
00089   NS_INTERFACE_MAP_ENTRY(nsIModifyableXPointerResult)
00090   NS_INTERFACE_MAP_ENTRY(nsISupports)
00091   NS_INTERFACE_MAP_ENTRY_EXTERNAL_DOM_CLASSINFO(XPointerResult)
00092 NS_INTERFACE_MAP_END
00093 
00094 NS_IMPL_ADDREF(nsXPointerResult)
00095 NS_IMPL_RELEASE(nsXPointerResult)
00096 
00097 NS_IMETHODIMP
00098 nsXPointerResult::AppendRange(nsIDOMRange* aRange)
00099 {
00100   NS_ENSURE_ARG(aRange);
00101   
00102   if (!mArray.AppendObject(aRange)) {
00103     return NS_ERROR_OUT_OF_MEMORY;
00104   }
00105 
00106   return NS_OK;
00107 }
00108 
00109 NS_IMETHODIMP
00110 nsXPointerResult::Item(PRUint32 aIndex, nsIDOMRange** aReturn)
00111 {
00112   NS_ENSURE_ARG_POINTER(aReturn);
00113 
00114   if (aIndex >= (PRUint32)mArray.Count()) {
00115     return NS_ERROR_FAILURE;
00116   }
00117 
00118   *aReturn = mArray.ObjectAt(aIndex);
00119   NS_IF_ADDREF(*aReturn);
00120   
00121   return NS_OK;
00122 }
00123 
00124 NS_IMETHODIMP
00125 nsXPointerResult::GetLength(PRUint32* aLength)
00126 {
00127   NS_ENSURE_ARG_POINTER(aLength);
00128 
00129   *aLength = mArray.Count();
00130 
00131   return NS_OK;
00132 }
00133 
00134 nsresult NS_NewXPointerResult(nsIXPointerResult **aResult)
00135 {
00136   NS_ENSURE_ARG_POINTER(aResult);
00137 
00138   *aResult = new nsXPointerResult();
00139   if (!*aResult) {
00140     return NS_ERROR_OUT_OF_MEMORY;
00141   }
00142   
00143   NS_ADDREF(*aResult);
00144 
00145   return NS_OK;
00146 }
00147 
00148 static nsresult NS_NewXPointerResult(nsIDOMRange *aRange,
00149                                      nsIXPointerResult **aResult)
00150 {
00151   NS_ENSURE_ARG(aRange);
00152   NS_ENSURE_ARG_POINTER(aResult);
00153 
00154   nsCOMPtr<nsXPointerResult> result(new nsXPointerResult());
00155   if (!result) {
00156     return NS_ERROR_OUT_OF_MEMORY;
00157   }
00158   
00159   nsresult rv = result->AppendRange(aRange);
00160   if (NS_FAILED(rv)) {
00161     return rv;
00162   }
00163 
00164   *aResult = result.get();
00165   NS_ADDREF(*aResult);
00166 
00167   return NS_OK;
00168 }
00169 
00170 static nsresult NS_NewXPointerResult(nsIDOMNode *aNode,
00171                                      nsIXPointerResult **aResult)
00172 {
00173   NS_ENSURE_ARG(aNode);
00174   NS_ENSURE_ARG_POINTER(aResult);
00175 
00176   nsCOMPtr<nsIDOMRange> range(do_CreateInstance(kRangeCID));
00177   if (!range) {
00178     return NS_ERROR_OUT_OF_MEMORY;
00179   }
00180 
00181   nsresult rv = range->SelectNode(aNode);
00182   if (NS_FAILED(rv)) {
00183     return rv;
00184   }
00185 
00186   return NS_NewXPointerResult(range, aResult);
00187 }
00188 
00189 
00190 // nsXPointerSchemeContext
00191 
00192 class nsXPointerSchemeContext : public nsIXPointerSchemeContext
00193 {
00194 public:
00195   nsXPointerSchemeContext() {};
00196   virtual ~nsXPointerSchemeContext() {};
00197   
00198   NS_DECL_ISUPPORTS
00199   NS_DECL_NSIXPOINTERSCHEMECONTEXT
00200 
00201   nsresult Append(const nsAString &aScheme, const nsAString &aData);
00202 
00203 private:
00204   nsStringArray mSchemes;
00205   nsStringArray mDatas;
00206 };
00207 
00208 NS_IMPL_ISUPPORTS1(nsXPointerSchemeContext, nsIXPointerSchemeContext)
00209 
00210 NS_IMETHODIMP
00211 nsXPointerSchemeContext::GetCount(PRUint32 *aCount)
00212 {
00213   NS_ENSURE_ARG_POINTER(aCount);
00214 
00215   *aCount = mSchemes.Count();
00216 
00217   return NS_OK;
00218 }
00219 
00220 
00221 nsresult
00222 nsXPointerSchemeContext::Append(const nsAString &aScheme,
00223                                 const nsAString &aData)
00224 {
00225   if (!mSchemes.AppendString(aScheme)) {
00226     return NS_ERROR_OUT_OF_MEMORY;
00227   }
00228 
00229   if (!mDatas.AppendString(aData)) {
00230     // Keep mDatas and mSchemes in sync
00231     mSchemes.RemoveStringAt(mSchemes.Count() - 1);
00232     return NS_ERROR_OUT_OF_MEMORY;
00233   }
00234 
00235   return NS_OK;
00236 }
00237 
00238 NS_IMETHODIMP
00239 nsXPointerSchemeContext::GetSchemeData(PRUint32 aIndex,
00240                                        nsAString &aScheme,
00241                                        nsAString &aData)
00242 {
00243   if (aIndex >= (PRUint32)mSchemes.Count()) {
00244     aScheme.Truncate();
00245     aData.Truncate();
00246 
00247     return NS_ERROR_FAILURE;
00248   }
00249 
00250   mSchemes.StringAt(aIndex, aScheme);
00251   mDatas.StringAt(aIndex, aData);
00252 
00253   return NS_OK;
00254 }
00255 
00256 // XPointer
00257 
00258 nsXPointer::nsXPointer()
00259 {
00260 }
00261 
00262 nsXPointer::~nsXPointer()
00263 {
00264 }
00265 
00266 NS_IMPL_ISUPPORTS1(nsXPointer, nsIXPointerEvaluator)
00267 
00268 static nsresult GetNextSchemeNameAndData(nsString& aExpression,
00269                                          nsString &aScheme,
00270                                          nsString& aData)
00271 {
00272   aScheme.Truncate();
00273   aData.Truncate();
00274 
00275   PRInt32 lp = aExpression.FindChar('(');
00276   if (lp < 1) {
00277     return NS_ERROR_FAILURE; // format |scheme + '(' [ + data + ] + ')'| required
00278   }
00279 
00280   PRInt32 i = lp + 1, len = aExpression.Length();
00281   if (i >= len) {
00282     return NS_ERROR_FAILURE; // format |scheme + '(' [ + data + ] + ')'| required
00283   }
00284 
00285   aScheme = Substring(aExpression, 0, lp);
00286   aScheme.CompressWhitespace(PR_TRUE, PR_FALSE);
00287   if (aScheme.FindCharInSet(" \t\r\n") > 0) {
00288     return NS_ERROR_FAILURE; // scheme name can't contain ws (we'd really need to check a lot more...)
00289   }
00290 
00291   // XXX perf: Switch to string iterators
00292   PRBool escapeOn = PR_FALSE;
00293   PRInt32 balance = 1;
00294   for (; i < len; ++i) {
00295     // Circumflex is the escape character that can precede ^, ( and ) only
00296     if (aExpression[i] == '^') {
00297       if (!escapeOn) {
00298         escapeOn = PR_TRUE;
00299         continue;
00300       }
00301     } else if (escapeOn) {
00302       if ((aExpression[i] != '(') && (aExpression[i] != ')')) {
00303         return NS_ERROR_FAILURE; // illegal use of ^
00304       }
00305     } else if (aExpression[i] == '(') {
00306       ++balance;
00307     } else if (aExpression[i] == ')') {
00308       if (--balance == 0) {
00309         aExpression.Cut(0, i + 1);
00310         break;
00311       }
00312     }
00313 
00314     aData.Append(aExpression[i]);
00315     escapeOn = PR_FALSE;
00316   }
00317 
00318   if (balance != 0) {
00319     return NS_ERROR_FAILURE; // format |scheme + '(' [ + data + ] + ')'| required
00320   }
00321 
00322   return NS_OK;
00323 }
00324 
00325 NS_IMETHODIMP
00326 nsXPointer::Evaluate(nsIDOMDocument *aDocument,
00327                      const nsAString& aExpression,
00328                      nsIXPointerResult **aResult)
00329 {
00330   NS_ENSURE_ARG_POINTER(aDocument);
00331   NS_ENSURE_ARG_POINTER(aResult);
00332   *aResult = nsnull;
00333 
00334   nsresult rv = NS_OK;
00335 
00336   if (aExpression.FindChar('(') < 0) {
00337     // Must be shorthand, i.e. plain id
00338     nsCOMPtr<nsIDOMElement> element;
00339     aDocument->GetElementById(aExpression, getter_AddRefs(element));
00340     if (element) {
00341       rv = NS_NewXPointerResult(element, aResult);
00342     }
00343     return rv;
00344   }
00345 
00346   nsAutoString expression(aExpression), scheme, data;
00347 
00348   NS_NAMED_LITERAL_STRING(element, "element");
00349   NS_NAMED_LITERAL_STRING(fixptr, "fixptr");
00350   NS_NAMED_LITERAL_CSTRING(baseSchemeProgID, NS_XPOINTER_SCHEME_PROCESSOR_BASE);
00351   nsCOMPtr<nsXPointerSchemeContext> contextSchemeDataArray(new nsXPointerSchemeContext());
00352   if (!contextSchemeDataArray) {
00353     return NS_ERROR_OUT_OF_MEMORY;
00354   }
00355 
00356   // Keep trying the schemes from left to right until one finds a subresource
00357   while (!expression.IsEmpty()) {
00358     rv = GetNextSchemeNameAndData(expression, scheme, data);
00359     if (NS_FAILED(rv))
00360       break;
00361 
00362     // Built in schemes
00363     if (scheme.Equals(element)) {
00364       // We implement element scheme by using the FIXptr processor.
00365       // Check there are no parenthesis (legal in FIXptr data).
00366       if (data.FindChar('(') < 0) {
00367         nsCOMPtr<nsIDOMRange> range;
00368         nsCOMPtr<nsIFIXptrEvaluator> e(new nsFIXptr());
00369         if (!e)
00370           return NS_ERROR_OUT_OF_MEMORY;
00371         rv = e->Evaluate(aDocument, data, getter_AddRefs(range));
00372         if (NS_FAILED(rv))
00373           break;
00374         if (range) {
00375           return NS_NewXPointerResult(range, aResult);
00376         }
00377       }
00378     } else if (scheme.Equals(fixptr)) {
00379       nsCOMPtr<nsIDOMRange> range;
00380       nsCOMPtr<nsIFIXptrEvaluator> e(new nsFIXptr());
00381       if (!e)
00382         return NS_ERROR_OUT_OF_MEMORY;
00383       rv = e->Evaluate(aDocument, data, getter_AddRefs(range));
00384       if (NS_FAILED(rv))
00385         break;
00386       if (range) {
00387         return NS_NewXPointerResult(range, aResult);
00388       }
00389     } else {
00390       // Add-on schemes
00391       nsCAutoString progid(baseSchemeProgID);
00392       AppendUTF16toUTF8(scheme, progid);
00393       nsCOMPtr<nsIXPointerSchemeProcessor> p(do_CreateInstance(progid.get()));
00394       if (p) {
00395         rv = p->Evaluate(aDocument, contextSchemeDataArray, data, aResult);
00396         if (NS_FAILED(rv))
00397           break;
00398         if (*aResult)
00399           return NS_OK;
00400       }
00401     }
00402 
00403     rv = contextSchemeDataArray->Append(scheme, data);
00404     if (NS_FAILED(rv))
00405       break;
00406 
00407   }
00408 
00409   return rv;
00410 }
00411