1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 2003
21 * the Initial Developer. All Rights Reserved.
24 * Heikki Toivonen <heikki@netscape.com> (original author)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 * Implementation for the XPointer family of specifications, practically the
42 * XPointer Processor. The processor can call optional modules that implement
43 * some XPointer schemes that were not implemented in this file. Please note
44 * that implementation of the xmlns scheme is left to the optional scheme
45 * implementations - all the information they need will be passed in.
48 * http://www.w3.org/TR/xptr-framework/
50 * http://www.w3.org/TR/xptr-element/
52 * Additionally this module implements 'fixptr' scheme for the FIXptr
54 * http://lists.w3.org/Archives/Public/www-xml-linking-comments/2001AprJun/att-0074/01-NOTE-FIXptr-20010425.htm
61 #include "nsIDOMNode.h"
62 #include "nsIDOMNodeList.h"
63 #include "nsIDOMElement.h"
64 #include "nsIDOMDocument.h"
65 #include "nsIDOMClassInfo.h"
67 #include "nsXPointer.h"
68 #include "nsIModifyableXPointer.h"
69 #include "nsISupports.h"
70 #include "nsISupportsUtils.h"
71 #include "nsIXPointer.h"
73 #include "nsIServiceManager.h"
74 #include "nsReadableUtils.h"
76 #include "nsContentCID.h"
77 static NS_DEFINE_IID(kRangeCID
, NS_RANGE_CID
);
79 nsXPointerResult::nsXPointerResult()
83 nsXPointerResult::~nsXPointerResult()
87 NS_INTERFACE_MAP_BEGIN(nsXPointerResult
)
88 NS_INTERFACE_MAP_ENTRY(nsIXPointerResult
)
89 NS_INTERFACE_MAP_ENTRY(nsIModifyableXPointerResult
)
90 NS_INTERFACE_MAP_ENTRY(nsISupports
)
91 NS_INTERFACE_MAP_ENTRY_EXTERNAL_DOM_CLASSINFO(XPointerResult
)
94 NS_IMPL_ADDREF(nsXPointerResult
)
95 NS_IMPL_RELEASE(nsXPointerResult
)
98 nsXPointerResult::AppendRange(nsIDOMRange
* aRange
)
100 NS_ENSURE_ARG(aRange
);
102 if (!mArray
.AppendObject(aRange
)) {
103 return NS_ERROR_OUT_OF_MEMORY
;
110 nsXPointerResult::Item(PRUint32 aIndex
, nsIDOMRange
** aReturn
)
112 NS_ENSURE_ARG_POINTER(aReturn
);
114 if (aIndex
>= (PRUint32
)mArray
.Count()) {
115 return NS_ERROR_FAILURE
;
118 *aReturn
= mArray
.ObjectAt(aIndex
);
119 NS_IF_ADDREF(*aReturn
);
125 nsXPointerResult::GetLength(PRUint32
* aLength
)
127 NS_ENSURE_ARG_POINTER(aLength
);
129 *aLength
= mArray
.Count();
134 nsresult
NS_NewXPointerResult(nsIXPointerResult
**aResult
)
136 NS_ENSURE_ARG_POINTER(aResult
);
138 *aResult
= new nsXPointerResult();
140 return NS_ERROR_OUT_OF_MEMORY
;
148 static nsresult
NS_NewXPointerResult(nsIDOMRange
*aRange
,
149 nsIXPointerResult
**aResult
)
151 NS_ENSURE_ARG(aRange
);
152 NS_ENSURE_ARG_POINTER(aResult
);
154 nsCOMPtr
<nsXPointerResult
> result(new nsXPointerResult());
156 return NS_ERROR_OUT_OF_MEMORY
;
159 nsresult rv
= result
->AppendRange(aRange
);
164 *aResult
= result
.get();
170 static nsresult
NS_NewXPointerResult(nsIDOMNode
*aNode
,
171 nsIXPointerResult
**aResult
)
173 NS_ENSURE_ARG(aNode
);
174 NS_ENSURE_ARG_POINTER(aResult
);
176 nsCOMPtr
<nsIDOMRange
> range(do_CreateInstance(kRangeCID
));
178 return NS_ERROR_OUT_OF_MEMORY
;
181 nsresult rv
= range
->SelectNode(aNode
);
186 return NS_NewXPointerResult(range
, aResult
);
190 // nsXPointerSchemeContext
192 class nsXPointerSchemeContext
: public nsIXPointerSchemeContext
195 nsXPointerSchemeContext() {}
196 virtual ~nsXPointerSchemeContext() {}
199 NS_DECL_NSIXPOINTERSCHEMECONTEXT
201 nsresult
Append(const nsAString
&aScheme
, const nsAString
&aData
);
204 nsStringArray mSchemes
;
205 nsStringArray mDatas
;
208 NS_IMPL_ISUPPORTS1(nsXPointerSchemeContext
, nsIXPointerSchemeContext
)
211 nsXPointerSchemeContext::GetCount(PRUint32
*aCount
)
213 NS_ENSURE_ARG_POINTER(aCount
);
215 *aCount
= mSchemes
.Count();
222 nsXPointerSchemeContext::Append(const nsAString
&aScheme
,
223 const nsAString
&aData
)
225 if (!mSchemes
.AppendString(aScheme
)) {
226 return NS_ERROR_OUT_OF_MEMORY
;
229 if (!mDatas
.AppendString(aData
)) {
230 // Keep mDatas and mSchemes in sync
231 mSchemes
.RemoveStringAt(mSchemes
.Count() - 1);
232 return NS_ERROR_OUT_OF_MEMORY
;
239 nsXPointerSchemeContext::GetSchemeData(PRUint32 aIndex
,
243 if (aIndex
>= (PRUint32
)mSchemes
.Count()) {
247 return NS_ERROR_FAILURE
;
250 mSchemes
.StringAt(aIndex
, aScheme
);
251 mDatas
.StringAt(aIndex
, aData
);
258 nsXPointer::nsXPointer()
262 nsXPointer::~nsXPointer()
266 NS_IMPL_ISUPPORTS1(nsXPointer
, nsIXPointerEvaluator
)
268 static nsresult
GetNextSchemeNameAndData(nsString
& aExpression
,
275 PRInt32 lp
= aExpression
.FindChar('(');
277 return NS_ERROR_FAILURE
; // format |scheme + '(' [ + data + ] + ')'| required
280 PRInt32 i
= lp
+ 1, len
= aExpression
.Length();
282 return NS_ERROR_FAILURE
; // format |scheme + '(' [ + data + ] + ')'| required
285 aScheme
= Substring(aExpression
, 0, lp
);
286 aScheme
.CompressWhitespace(PR_TRUE
, PR_FALSE
);
287 if (aScheme
.FindCharInSet(" \t\r\n") > 0) {
288 return NS_ERROR_FAILURE
; // scheme name can't contain ws (we'd really need to check a lot more...)
291 // XXX perf: Switch to string iterators
292 PRBool escapeOn
= PR_FALSE
;
294 for (; i
< len
; ++i
) {
295 // Circumflex is the escape character that can precede ^, ( and ) only
296 if (aExpression
[i
] == '^') {
301 } else if (escapeOn
) {
302 if ((aExpression
[i
] != '(') && (aExpression
[i
] != ')')) {
303 return NS_ERROR_FAILURE
; // illegal use of ^
305 } else if (aExpression
[i
] == '(') {
307 } else if (aExpression
[i
] == ')') {
308 if (--balance
== 0) {
309 aExpression
.Cut(0, i
+ 1);
314 aData
.Append(aExpression
[i
]);
319 return NS_ERROR_FAILURE
; // format |scheme + '(' [ + data + ] + ')'| required
326 nsXPointer::Evaluate(nsIDOMDocument
*aDocument
,
327 const nsAString
& aExpression
,
328 nsIXPointerResult
**aResult
)
330 NS_ENSURE_ARG_POINTER(aDocument
);
331 NS_ENSURE_ARG_POINTER(aResult
);
336 if (aExpression
.FindChar('(') < 0) {
337 // Must be shorthand, i.e. plain id
338 nsCOMPtr
<nsIDOMElement
> element
;
339 aDocument
->GetElementById(aExpression
, getter_AddRefs(element
));
341 rv
= NS_NewXPointerResult(element
, aResult
);
346 nsAutoString
expression(aExpression
), scheme
, data
;
348 NS_NAMED_LITERAL_STRING(element
, "element");
349 NS_NAMED_LITERAL_STRING(fixptr
, "fixptr");
350 NS_NAMED_LITERAL_CSTRING(baseSchemeProgID
, NS_XPOINTER_SCHEME_PROCESSOR_BASE
);
351 nsCOMPtr
<nsXPointerSchemeContext
> contextSchemeDataArray(new nsXPointerSchemeContext());
352 if (!contextSchemeDataArray
) {
353 return NS_ERROR_OUT_OF_MEMORY
;
356 // Keep trying the schemes from left to right until one finds a subresource
357 while (!expression
.IsEmpty()) {
358 rv
= GetNextSchemeNameAndData(expression
, scheme
, data
);
363 if (scheme
.Equals(element
)) {
364 // We implement element scheme by using the FIXptr processor.
365 // Check there are no parenthesis (legal in FIXptr data).
366 if (data
.FindChar('(') < 0) {
367 nsCOMPtr
<nsIDOMRange
> range
;
368 nsCOMPtr
<nsIFIXptrEvaluator
> e(new nsFIXptr());
370 return NS_ERROR_OUT_OF_MEMORY
;
371 rv
= e
->Evaluate(aDocument
, data
, getter_AddRefs(range
));
375 return NS_NewXPointerResult(range
, aResult
);
378 } else if (scheme
.Equals(fixptr
)) {
379 nsCOMPtr
<nsIDOMRange
> range
;
380 nsCOMPtr
<nsIFIXptrEvaluator
> e(new nsFIXptr());
382 return NS_ERROR_OUT_OF_MEMORY
;
383 rv
= e
->Evaluate(aDocument
, data
, getter_AddRefs(range
));
387 return NS_NewXPointerResult(range
, aResult
);
391 nsCAutoString
progid(baseSchemeProgID
);
392 AppendUTF16toUTF8(scheme
, progid
);
393 nsCOMPtr
<nsIXPointerSchemeProcessor
> p(do_CreateInstance(progid
.get()));
395 rv
= p
->Evaluate(aDocument
, contextSchemeDataArray
, data
, aResult
);
403 rv
= contextSchemeDataArray
->Append(scheme
, data
);