1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * David Hyatt <hyatt@netscape.com> (Original Author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "mozilla/Util.h"
40 :
41 : #include "nsXBLContentSink.h"
42 : #include "nsIDocument.h"
43 : #include "nsBindingManager.h"
44 : #include "nsIDOMNode.h"
45 : #include "nsIParser.h"
46 : #include "nsGkAtoms.h"
47 : #include "nsINameSpaceManager.h"
48 : #include "nsHTMLTokens.h"
49 : #include "nsIURI.h"
50 : #include "nsTextFragment.h"
51 : #ifdef MOZ_XUL
52 : #include "nsXULElement.h"
53 : #endif
54 : #include "nsXBLProtoImplProperty.h"
55 : #include "nsXBLProtoImplMethod.h"
56 : #include "nsXBLProtoImplField.h"
57 : #include "nsXBLPrototypeBinding.h"
58 : #include "nsContentUtils.h"
59 : #include "nsIConsoleService.h"
60 : #include "nsIScriptError.h"
61 : #include "nsNodeInfoManager.h"
62 : #include "nsINodeInfo.h"
63 : #include "nsIPrincipal.h"
64 : #include "mozilla/dom/Element.h"
65 :
66 : using namespace mozilla;
67 : using namespace mozilla::dom;
68 :
69 : nsresult
70 0 : NS_NewXBLContentSink(nsIXMLContentSink** aResult,
71 : nsIDocument* aDoc,
72 : nsIURI* aURI,
73 : nsISupports* aContainer)
74 : {
75 0 : NS_ENSURE_ARG_POINTER(aResult);
76 :
77 0 : nsXBLContentSink* it = new nsXBLContentSink();
78 0 : NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY);
79 :
80 0 : nsCOMPtr<nsIXMLContentSink> kungFuDeathGrip = it;
81 0 : nsresult rv = it->Init(aDoc, aURI, aContainer);
82 0 : NS_ENSURE_SUCCESS(rv, rv);
83 :
84 0 : return CallQueryInterface(it, aResult);
85 : }
86 :
87 0 : nsXBLContentSink::nsXBLContentSink()
88 : : mState(eXBL_InDocument),
89 : mSecondaryState(eXBL_None),
90 : mDocInfo(nsnull),
91 : mIsChromeOrResource(false),
92 : mFoundFirstBinding(false),
93 : mBinding(nsnull),
94 : mHandler(nsnull),
95 : mImplementation(nsnull),
96 : mImplMember(nsnull),
97 : mImplField(nsnull),
98 : mProperty(nsnull),
99 : mMethod(nsnull),
100 0 : mField(nsnull)
101 : {
102 0 : mPrettyPrintXML = false;
103 0 : }
104 :
105 0 : nsXBLContentSink::~nsXBLContentSink()
106 : {
107 0 : }
108 :
109 : nsresult
110 0 : nsXBLContentSink::Init(nsIDocument* aDoc,
111 : nsIURI* aURI,
112 : nsISupports* aContainer)
113 : {
114 : nsresult rv;
115 0 : rv = nsXMLContentSink::Init(aDoc, aURI, aContainer, nsnull);
116 0 : return rv;
117 : }
118 :
119 : void
120 0 : nsXBLContentSink::MaybeStartLayout(bool aIgnorePendingSheets)
121 : {
122 : return;
123 : }
124 :
125 : nsresult
126 0 : nsXBLContentSink::FlushText(bool aReleaseTextNode)
127 : {
128 0 : if (mTextLength != 0) {
129 0 : const nsASingleFragmentString& text = Substring(mText, mText+mTextLength);
130 0 : if (mState == eXBL_InHandlers) {
131 0 : NS_ASSERTION(mBinding, "Must have binding here");
132 : // Get the text and add it to the event handler.
133 0 : if (mSecondaryState == eXBL_InHandler)
134 0 : mHandler->AppendHandlerText(text);
135 0 : mTextLength = 0;
136 0 : return NS_OK;
137 : }
138 0 : else if (mState == eXBL_InImplementation) {
139 0 : NS_ASSERTION(mBinding, "Must have binding here");
140 0 : if (mSecondaryState == eXBL_InConstructor ||
141 : mSecondaryState == eXBL_InDestructor) {
142 : // Construct a method for the constructor/destructor.
143 : nsXBLProtoImplMethod* method;
144 0 : if (mSecondaryState == eXBL_InConstructor)
145 0 : method = mBinding->GetConstructor();
146 : else
147 0 : method = mBinding->GetDestructor();
148 :
149 : // Get the text and add it to the constructor/destructor.
150 0 : method->AppendBodyText(text);
151 : }
152 0 : else if (mSecondaryState == eXBL_InGetter ||
153 : mSecondaryState == eXBL_InSetter) {
154 : // Get the text and add it to the getter/setter
155 0 : if (mSecondaryState == eXBL_InGetter)
156 0 : mProperty->AppendGetterText(text);
157 : else
158 0 : mProperty->AppendSetterText(text);
159 : }
160 0 : else if (mSecondaryState == eXBL_InBody) {
161 : // Get the text and add it to the method
162 0 : if (mMethod)
163 0 : mMethod->AppendBodyText(text);
164 : }
165 0 : else if (mSecondaryState == eXBL_InField) {
166 : // Get the text and add it to the method
167 0 : if (mField)
168 0 : mField->AppendFieldText(text);
169 : }
170 0 : mTextLength = 0;
171 0 : return NS_OK;
172 : }
173 :
174 0 : nsIContent* content = GetCurrentContent();
175 0 : if (content &&
176 0 : (content->NodeInfo()->NamespaceEquals(kNameSpaceID_XBL) ||
177 0 : (content->NodeInfo()->NamespaceEquals(kNameSpaceID_XUL) &&
178 0 : content->Tag() != nsGkAtoms::label &&
179 0 : content->Tag() != nsGkAtoms::description))) {
180 :
181 0 : bool isWS = true;
182 0 : if (mTextLength > 0) {
183 0 : const PRUnichar* cp = mText;
184 0 : const PRUnichar* end = mText + mTextLength;
185 0 : while (cp < end) {
186 0 : PRUnichar ch = *cp++;
187 0 : if (!XP_IS_SPACE(ch)) {
188 0 : isWS = false;
189 0 : break;
190 : }
191 : }
192 : }
193 :
194 0 : if (isWS && mTextLength > 0) {
195 0 : mTextLength = 0;
196 : // Make sure to drop the textnode, if any
197 0 : return nsXMLContentSink::FlushText(aReleaseTextNode);
198 : }
199 : }
200 : }
201 :
202 0 : return nsXMLContentSink::FlushText(aReleaseTextNode);
203 : }
204 :
205 : NS_IMETHODIMP
206 0 : nsXBLContentSink::ReportError(const PRUnichar* aErrorText,
207 : const PRUnichar* aSourceText,
208 : nsIScriptError *aError,
209 : bool *_retval)
210 : {
211 0 : NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!");
212 :
213 : // XXX FIXME This function overrides and calls on
214 : // nsXMLContentSink::ReportError, and probably should die. See bug 347826.
215 :
216 : // XXX We should make sure the binding has no effect, but that it also
217 : // gets destroyed properly without leaking. Perhaps we should even
218 : // ensure that the content that was bound is displayed with no
219 : // binding.
220 :
221 : #ifdef DEBUG
222 : // Report the error to stderr.
223 : fprintf(stderr,
224 : "\n%s\n%s\n\n",
225 0 : NS_LossyConvertUTF16toASCII(aErrorText).get(),
226 0 : NS_LossyConvertUTF16toASCII(aSourceText).get());
227 : #endif
228 :
229 : // Most of what this does won't be too useful, but whatever...
230 : // nsXMLContentSink::ReportError will handle the console logging.
231 : return nsXMLContentSink::ReportError(aErrorText,
232 : aSourceText,
233 : aError,
234 0 : _retval);
235 : }
236 :
237 : nsresult
238 0 : nsXBLContentSink::ReportUnexpectedElement(nsIAtom* aElementName,
239 : PRUint32 aLineNumber)
240 : {
241 : // XXX we should really somehow stop the parse and drop the binding
242 : // instead of just letting the XML sink build the content model like
243 : // we do...
244 0 : mState = eXBL_Error;
245 0 : nsAutoString elementName;
246 0 : aElementName->ToString(elementName);
247 :
248 0 : const PRUnichar* params[] = { elementName.get() };
249 :
250 : return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
251 : "XBL Content Sink",
252 : mDocument,
253 : nsContentUtils::eXBL_PROPERTIES,
254 : "UnexpectedElement",
255 : params, ArrayLength(params),
256 : nsnull,
257 0 : EmptyString() /* source line */,
258 0 : aLineNumber);
259 : }
260 :
261 : void
262 0 : nsXBLContentSink::AddMember(nsXBLProtoImplMember* aMember)
263 : {
264 : // Add this member to our chain.
265 0 : if (mImplMember)
266 0 : mImplMember->SetNext(aMember); // Already have a chain. Just append to the end.
267 : else
268 0 : mImplementation->SetMemberList(aMember); // We're the first member in the chain.
269 :
270 0 : mImplMember = aMember; // Adjust our pointer to point to the new last member in the chain.
271 0 : }
272 :
273 : void
274 0 : nsXBLContentSink::AddField(nsXBLProtoImplField* aField)
275 : {
276 : // Add this field to our chain.
277 0 : if (mImplField)
278 0 : mImplField->SetNext(aField); // Already have a chain. Just append to the end.
279 : else
280 0 : mImplementation->SetFieldList(aField); // We're the first member in the chain.
281 :
282 0 : mImplField = aField; // Adjust our pointer to point to the new last field in the chain.
283 0 : }
284 :
285 : NS_IMETHODIMP
286 0 : nsXBLContentSink::HandleStartElement(const PRUnichar *aName,
287 : const PRUnichar **aAtts,
288 : PRUint32 aAttsCount,
289 : PRInt32 aIndex,
290 : PRUint32 aLineNumber)
291 : {
292 0 : nsresult rv = nsXMLContentSink::HandleStartElement(aName,aAtts,aAttsCount,aIndex,aLineNumber);
293 0 : if (NS_FAILED(rv))
294 0 : return rv;
295 :
296 0 : if (mState == eXBL_InBinding && !mBinding) {
297 0 : rv = ConstructBinding(aLineNumber);
298 0 : if (NS_FAILED(rv))
299 0 : return rv;
300 :
301 : // mBinding may still be null, if the binding had no id. If so,
302 : // we'll deal with that later in the sink.
303 : }
304 :
305 0 : return rv;
306 : }
307 :
308 : NS_IMETHODIMP
309 0 : nsXBLContentSink::HandleEndElement(const PRUnichar *aName)
310 : {
311 0 : FlushText();
312 :
313 0 : if (mState != eXBL_InDocument) {
314 : PRInt32 nameSpaceID;
315 0 : nsCOMPtr<nsIAtom> prefix, localName;
316 0 : nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
317 0 : getter_AddRefs(localName), &nameSpaceID);
318 :
319 0 : if (nameSpaceID == kNameSpaceID_XBL) {
320 0 : if (mState == eXBL_Error) {
321 : // Check whether we've opened this tag before; we may not have if
322 : // it was a real XBL tag before the error occurred.
323 0 : if (!GetCurrentContent()->NodeInfo()->Equals(localName,
324 0 : nameSpaceID)) {
325 : // OK, this tag was never opened as far as the XML sink is
326 : // concerned. Just drop the HandleEndElement
327 0 : return NS_OK;
328 : }
329 : }
330 0 : else if (mState == eXBL_InHandlers) {
331 0 : if (localName == nsGkAtoms::handlers) {
332 0 : mState = eXBL_InBinding;
333 0 : mHandler = nsnull;
334 : }
335 0 : else if (localName == nsGkAtoms::handler)
336 0 : mSecondaryState = eXBL_None;
337 0 : return NS_OK;
338 : }
339 0 : else if (mState == eXBL_InResources) {
340 0 : if (localName == nsGkAtoms::resources)
341 0 : mState = eXBL_InBinding;
342 0 : return NS_OK;
343 : }
344 0 : else if (mState == eXBL_InImplementation) {
345 0 : if (localName == nsGkAtoms::implementation)
346 0 : mState = eXBL_InBinding;
347 0 : else if (localName == nsGkAtoms::property) {
348 0 : mSecondaryState = eXBL_None;
349 0 : mProperty = nsnull;
350 : }
351 0 : else if (localName == nsGkAtoms::method) {
352 0 : mSecondaryState = eXBL_None;
353 0 : mMethod = nsnull;
354 : }
355 0 : else if (localName == nsGkAtoms::field) {
356 0 : mSecondaryState = eXBL_None;
357 0 : mField = nsnull;
358 : }
359 0 : else if (localName == nsGkAtoms::constructor ||
360 0 : localName == nsGkAtoms::destructor)
361 0 : mSecondaryState = eXBL_None;
362 0 : else if (localName == nsGkAtoms::getter ||
363 0 : localName == nsGkAtoms::setter)
364 0 : mSecondaryState = eXBL_InProperty;
365 0 : else if (localName == nsGkAtoms::parameter ||
366 0 : localName == nsGkAtoms::body)
367 0 : mSecondaryState = eXBL_InMethod;
368 0 : return NS_OK;
369 : }
370 0 : else if (mState == eXBL_InBindings &&
371 0 : localName == nsGkAtoms::bindings) {
372 0 : mState = eXBL_InDocument;
373 : }
374 :
375 0 : nsresult rv = nsXMLContentSink::HandleEndElement(aName);
376 0 : if (NS_FAILED(rv))
377 0 : return rv;
378 :
379 0 : if (mState == eXBL_InBinding && localName == nsGkAtoms::binding) {
380 0 : mState = eXBL_InBindings;
381 0 : if (mBinding) { // See comment in HandleStartElement()
382 0 : mBinding->Initialize();
383 0 : mBinding = nsnull; // Clear our current binding ref.
384 : }
385 : }
386 :
387 0 : return NS_OK;
388 : }
389 : }
390 :
391 0 : return nsXMLContentSink::HandleEndElement(aName);
392 : }
393 :
394 : NS_IMETHODIMP
395 0 : nsXBLContentSink::HandleCDataSection(const PRUnichar *aData,
396 : PRUint32 aLength)
397 : {
398 0 : if (mState == eXBL_InHandlers || mState == eXBL_InImplementation)
399 0 : return AddText(aData, aLength);
400 0 : return nsXMLContentSink::HandleCDataSection(aData, aLength);
401 : }
402 :
403 : #define ENSURE_XBL_STATE(_cond) \
404 : PR_BEGIN_MACRO \
405 : if (!(_cond)) { ReportUnexpectedElement(aTagName, aLineNumber); return true; } \
406 : PR_END_MACRO
407 :
408 : bool
409 0 : nsXBLContentSink::OnOpenContainer(const PRUnichar **aAtts,
410 : PRUint32 aAttsCount,
411 : PRInt32 aNameSpaceID,
412 : nsIAtom* aTagName,
413 : PRUint32 aLineNumber)
414 : {
415 0 : if (mState == eXBL_Error) {
416 0 : return true;
417 : }
418 :
419 0 : if (aNameSpaceID != kNameSpaceID_XBL) {
420 : // Construct non-XBL nodes
421 0 : return true;
422 : }
423 :
424 0 : bool ret = true;
425 0 : if (aTagName == nsGkAtoms::bindings) {
426 0 : ENSURE_XBL_STATE(mState == eXBL_InDocument);
427 :
428 0 : mDocInfo = NS_NewXBLDocumentInfo(mDocument);
429 0 : if (!mDocInfo) {
430 0 : mState = eXBL_Error;
431 0 : return true;
432 : }
433 :
434 0 : mDocument->BindingManager()->PutXBLDocumentInfo(mDocInfo);
435 :
436 0 : nsIURI *uri = mDocument->GetDocumentURI();
437 :
438 0 : bool isChrome = false;
439 0 : bool isRes = false;
440 :
441 0 : uri->SchemeIs("chrome", &isChrome);
442 0 : uri->SchemeIs("resource", &isRes);
443 0 : mIsChromeOrResource = isChrome || isRes;
444 :
445 0 : nsXBLDocumentInfo* info = mDocInfo;
446 0 : NS_RELEASE(info); // We keep a weak ref. We've created a cycle between doc/binding manager/doc info.
447 0 : mState = eXBL_InBindings;
448 : }
449 0 : else if (aTagName == nsGkAtoms::binding) {
450 0 : ENSURE_XBL_STATE(mState == eXBL_InBindings);
451 0 : mState = eXBL_InBinding;
452 : }
453 0 : else if (aTagName == nsGkAtoms::handlers) {
454 0 : ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
455 0 : mState = eXBL_InHandlers;
456 0 : ret = false;
457 : }
458 0 : else if (aTagName == nsGkAtoms::handler) {
459 0 : ENSURE_XBL_STATE(mState == eXBL_InHandlers);
460 0 : mSecondaryState = eXBL_InHandler;
461 0 : ConstructHandler(aAtts, aLineNumber);
462 0 : ret = false;
463 : }
464 0 : else if (aTagName == nsGkAtoms::resources) {
465 0 : ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
466 0 : mState = eXBL_InResources;
467 : // Note that this mState will cause us to return false, so no need
468 : // to set ret to false.
469 : }
470 0 : else if (aTagName == nsGkAtoms::stylesheet || aTagName == nsGkAtoms::image) {
471 0 : ENSURE_XBL_STATE(mState == eXBL_InResources);
472 0 : NS_ASSERTION(mBinding, "Must have binding here");
473 0 : ConstructResource(aAtts, aTagName);
474 : }
475 0 : else if (aTagName == nsGkAtoms::implementation) {
476 0 : ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
477 0 : mState = eXBL_InImplementation;
478 0 : ConstructImplementation(aAtts);
479 : // Note that this mState will cause us to return false, so no need
480 : // to set ret to false.
481 : }
482 0 : else if (aTagName == nsGkAtoms::constructor) {
483 0 : ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
484 : mSecondaryState == eXBL_None);
485 0 : NS_ASSERTION(mBinding, "Must have binding here");
486 :
487 0 : mSecondaryState = eXBL_InConstructor;
488 : nsXBLProtoImplAnonymousMethod* newMethod =
489 0 : new nsXBLProtoImplAnonymousMethod();
490 0 : if (newMethod) {
491 0 : newMethod->SetLineNumber(aLineNumber);
492 0 : mBinding->SetConstructor(newMethod);
493 0 : AddMember(newMethod);
494 : }
495 : }
496 0 : else if (aTagName == nsGkAtoms::destructor) {
497 0 : ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
498 : mSecondaryState == eXBL_None);
499 0 : NS_ASSERTION(mBinding, "Must have binding here");
500 0 : mSecondaryState = eXBL_InDestructor;
501 : nsXBLProtoImplAnonymousMethod* newMethod =
502 0 : new nsXBLProtoImplAnonymousMethod();
503 0 : if (newMethod) {
504 0 : newMethod->SetLineNumber(aLineNumber);
505 0 : mBinding->SetDestructor(newMethod);
506 0 : AddMember(newMethod);
507 : }
508 : }
509 0 : else if (aTagName == nsGkAtoms::field) {
510 0 : ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
511 : mSecondaryState == eXBL_None);
512 0 : NS_ASSERTION(mBinding, "Must have binding here");
513 0 : mSecondaryState = eXBL_InField;
514 0 : ConstructField(aAtts, aLineNumber);
515 : }
516 0 : else if (aTagName == nsGkAtoms::property) {
517 0 : ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
518 : mSecondaryState == eXBL_None);
519 0 : NS_ASSERTION(mBinding, "Must have binding here");
520 0 : mSecondaryState = eXBL_InProperty;
521 0 : ConstructProperty(aAtts);
522 : }
523 0 : else if (aTagName == nsGkAtoms::getter) {
524 0 : ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty);
525 0 : NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
526 0 : mProperty->SetGetterLineNumber(aLineNumber);
527 0 : mSecondaryState = eXBL_InGetter;
528 : }
529 0 : else if (aTagName == nsGkAtoms::setter) {
530 0 : ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty);
531 0 : NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
532 0 : mProperty->SetSetterLineNumber(aLineNumber);
533 0 : mSecondaryState = eXBL_InSetter;
534 : }
535 0 : else if (aTagName == nsGkAtoms::method) {
536 0 : ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
537 : mSecondaryState == eXBL_None);
538 0 : NS_ASSERTION(mBinding, "Must have binding here");
539 0 : mSecondaryState = eXBL_InMethod;
540 0 : ConstructMethod(aAtts);
541 : }
542 0 : else if (aTagName == nsGkAtoms::parameter) {
543 0 : ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod);
544 0 : NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
545 0 : ConstructParameter(aAtts);
546 : }
547 0 : else if (aTagName == nsGkAtoms::body) {
548 0 : ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod);
549 0 : NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
550 : // stash away the line number
551 0 : mMethod->SetLineNumber(aLineNumber);
552 0 : mSecondaryState = eXBL_InBody;
553 : }
554 :
555 0 : return ret && mState != eXBL_InResources && mState != eXBL_InImplementation;
556 : }
557 :
558 : #undef ENSURE_XBL_STATE
559 :
560 : nsresult
561 0 : nsXBLContentSink::ConstructBinding(PRUint32 aLineNumber)
562 : {
563 0 : nsCOMPtr<nsIContent> binding = GetCurrentContent();
564 0 : nsAutoString id;
565 0 : binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
566 0 : NS_ConvertUTF16toUTF8 cid(id);
567 :
568 0 : nsresult rv = NS_OK;
569 :
570 : // Don't create a binding with no id. nsXBLPrototypeBinding::Read also
571 : // performs this check.
572 0 : if (!cid.IsEmpty()) {
573 0 : mBinding = new nsXBLPrototypeBinding();
574 0 : if (!mBinding)
575 0 : return NS_ERROR_OUT_OF_MEMORY;
576 :
577 0 : rv = mBinding->Init(cid, mDocInfo, binding, !mFoundFirstBinding);
578 0 : if (NS_SUCCEEDED(rv) &&
579 0 : NS_SUCCEEDED(mDocInfo->SetPrototypeBinding(cid, mBinding))) {
580 0 : if (!mFoundFirstBinding) {
581 0 : mFoundFirstBinding = true;
582 0 : mDocInfo->SetFirstPrototypeBinding(mBinding);
583 : }
584 0 : binding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::id, false);
585 : } else {
586 0 : delete mBinding;
587 0 : mBinding = nsnull;
588 : }
589 : } else {
590 : nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
591 : "XBL Content Sink", nsnull,
592 : nsContentUtils::eXBL_PROPERTIES,
593 : "MissingIdAttr", nsnull, 0,
594 : mDocumentURI,
595 0 : EmptyString(),
596 0 : aLineNumber);
597 : }
598 :
599 0 : return rv;
600 : }
601 :
602 : static bool
603 0 : FindValue(const PRUnichar **aAtts, nsIAtom *aAtom, const PRUnichar **aResult)
604 : {
605 0 : nsCOMPtr<nsIAtom> prefix, localName;
606 0 : for (; *aAtts; aAtts += 2) {
607 : PRInt32 nameSpaceID;
608 0 : nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
609 0 : getter_AddRefs(localName), &nameSpaceID);
610 :
611 : // Is this attribute one of the ones we care about?
612 0 : if (nameSpaceID == kNameSpaceID_None && localName == aAtom) {
613 0 : *aResult = aAtts[1];
614 :
615 0 : return true;
616 : }
617 : }
618 :
619 0 : return false;
620 : }
621 :
622 : void
623 0 : nsXBLContentSink::ConstructHandler(const PRUnichar **aAtts, PRUint32 aLineNumber)
624 : {
625 0 : const PRUnichar* event = nsnull;
626 0 : const PRUnichar* modifiers = nsnull;
627 0 : const PRUnichar* button = nsnull;
628 0 : const PRUnichar* clickcount = nsnull;
629 0 : const PRUnichar* keycode = nsnull;
630 0 : const PRUnichar* charcode = nsnull;
631 0 : const PRUnichar* phase = nsnull;
632 0 : const PRUnichar* command = nsnull;
633 0 : const PRUnichar* action = nsnull;
634 0 : const PRUnichar* group = nsnull;
635 0 : const PRUnichar* preventdefault = nsnull;
636 0 : const PRUnichar* allowuntrusted = nsnull;
637 :
638 0 : nsCOMPtr<nsIAtom> prefix, localName;
639 0 : for (; *aAtts; aAtts += 2) {
640 : PRInt32 nameSpaceID;
641 0 : nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
642 0 : getter_AddRefs(localName), &nameSpaceID);
643 :
644 0 : if (nameSpaceID != kNameSpaceID_None) {
645 0 : continue;
646 : }
647 :
648 : // Is this attribute one of the ones we care about?
649 0 : if (localName == nsGkAtoms::event)
650 0 : event = aAtts[1];
651 0 : else if (localName == nsGkAtoms::modifiers)
652 0 : modifiers = aAtts[1];
653 0 : else if (localName == nsGkAtoms::button)
654 0 : button = aAtts[1];
655 0 : else if (localName == nsGkAtoms::clickcount)
656 0 : clickcount = aAtts[1];
657 0 : else if (localName == nsGkAtoms::keycode)
658 0 : keycode = aAtts[1];
659 0 : else if (localName == nsGkAtoms::key || localName == nsGkAtoms::charcode)
660 0 : charcode = aAtts[1];
661 0 : else if (localName == nsGkAtoms::phase)
662 0 : phase = aAtts[1];
663 0 : else if (localName == nsGkAtoms::command)
664 0 : command = aAtts[1];
665 0 : else if (localName == nsGkAtoms::action)
666 0 : action = aAtts[1];
667 0 : else if (localName == nsGkAtoms::group)
668 0 : group = aAtts[1];
669 0 : else if (localName == nsGkAtoms::preventdefault)
670 0 : preventdefault = aAtts[1];
671 0 : else if (localName == nsGkAtoms::allowuntrusted)
672 0 : allowuntrusted = aAtts[1];
673 : }
674 :
675 0 : if (command && !mIsChromeOrResource) {
676 : // Make sure the XBL doc is chrome or resource if we have a command
677 : // shorthand syntax.
678 0 : mState = eXBL_Error;
679 : nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
680 : "XBL Content Sink",
681 : mDocument,
682 : nsContentUtils::eXBL_PROPERTIES,
683 : "CommandNotInChrome", nsnull, 0,
684 : nsnull,
685 0 : EmptyString() /* source line */,
686 0 : aLineNumber);
687 : return; // Don't even make this handler.
688 : }
689 :
690 : // All of our pointers are now filled in. Construct our handler with all of
691 : // these parameters.
692 : nsXBLPrototypeHandler* newHandler;
693 : newHandler = new nsXBLPrototypeHandler(event, phase, action, command,
694 : keycode, charcode, modifiers, button,
695 : clickcount, group, preventdefault,
696 0 : allowuntrusted, mBinding, aLineNumber);
697 :
698 0 : if (newHandler) {
699 : // Add this handler to our chain of handlers.
700 0 : if (mHandler) {
701 : // Already have a chain. Just append to the end.
702 0 : mHandler->SetNextHandler(newHandler);
703 : }
704 : else {
705 : // We're the first handler in the chain.
706 0 : mBinding->SetPrototypeHandlers(newHandler);
707 : }
708 : // Adjust our mHandler pointer to point to the new last handler in the
709 : // chain.
710 0 : mHandler = newHandler;
711 : } else {
712 0 : mState = eXBL_Error;
713 : }
714 : }
715 :
716 : void
717 0 : nsXBLContentSink::ConstructResource(const PRUnichar **aAtts,
718 : nsIAtom* aResourceType)
719 : {
720 0 : if (!mBinding)
721 0 : return;
722 :
723 0 : const PRUnichar* src = nsnull;
724 0 : if (FindValue(aAtts, nsGkAtoms::src, &src)) {
725 0 : mBinding->AddResource(aResourceType, nsDependentString(src));
726 : }
727 : }
728 :
729 : void
730 0 : nsXBLContentSink::ConstructImplementation(const PRUnichar **aAtts)
731 : {
732 0 : mImplementation = nsnull;
733 0 : mImplMember = nsnull;
734 0 : mImplField = nsnull;
735 :
736 0 : if (!mBinding)
737 0 : return;
738 :
739 0 : const PRUnichar* name = nsnull;
740 :
741 0 : nsCOMPtr<nsIAtom> prefix, localName;
742 0 : for (; *aAtts; aAtts += 2) {
743 : PRInt32 nameSpaceID;
744 0 : nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
745 0 : getter_AddRefs(localName), &nameSpaceID);
746 :
747 0 : if (nameSpaceID != kNameSpaceID_None) {
748 0 : continue;
749 : }
750 :
751 : // Is this attribute one of the ones we care about?
752 0 : if (localName == nsGkAtoms::name) {
753 0 : name = aAtts[1];
754 : }
755 0 : else if (localName == nsGkAtoms::implements) {
756 : // Only allow implementation of interfaces via XBL if the principal of
757 : // our XBL document has UniversalXPConnect privileges. No principal
758 : // means no privs!
759 :
760 : // XXX this api is so badly tied to JS it's not even funny. We don't
761 : // have a concept of enabling capabilities on a per-principal basis,
762 : // but only on a per-principal-and-JS-stackframe basis! So for now
763 : // this is basically equivalent to testing that we have the system
764 : // principal, since there is no JS stackframe in sight here...
765 : bool hasUniversalXPConnect;
766 0 : nsresult rv = mDocument->NodePrincipal()->
767 : IsCapabilityEnabled("UniversalXPConnect", nsnull,
768 0 : &hasUniversalXPConnect);
769 0 : if (NS_SUCCEEDED(rv) && hasUniversalXPConnect) {
770 0 : mBinding->ConstructInterfaceTable(nsDependentString(aAtts[1]));
771 : }
772 : }
773 : }
774 :
775 0 : NS_NewXBLProtoImpl(mBinding, name, &mImplementation);
776 : }
777 :
778 : void
779 0 : nsXBLContentSink::ConstructField(const PRUnichar **aAtts, PRUint32 aLineNumber)
780 : {
781 0 : const PRUnichar* name = nsnull;
782 0 : const PRUnichar* readonly = nsnull;
783 :
784 0 : nsCOMPtr<nsIAtom> prefix, localName;
785 0 : for (; *aAtts; aAtts += 2) {
786 : PRInt32 nameSpaceID;
787 0 : nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
788 0 : getter_AddRefs(localName), &nameSpaceID);
789 :
790 0 : if (nameSpaceID != kNameSpaceID_None) {
791 0 : continue;
792 : }
793 :
794 : // Is this attribute one of the ones we care about?
795 0 : if (localName == nsGkAtoms::name) {
796 0 : name = aAtts[1];
797 : }
798 0 : else if (localName == nsGkAtoms::readonly) {
799 0 : readonly = aAtts[1];
800 : }
801 : }
802 :
803 0 : if (name) {
804 : // All of our pointers are now filled in. Construct our field with all of
805 : // these parameters.
806 0 : mField = new nsXBLProtoImplField(name, readonly);
807 0 : if (mField) {
808 0 : mField->SetLineNumber(aLineNumber);
809 0 : AddField(mField);
810 : }
811 : }
812 0 : }
813 :
814 : void
815 0 : nsXBLContentSink::ConstructProperty(const PRUnichar **aAtts)
816 : {
817 0 : const PRUnichar* name = nsnull;
818 0 : const PRUnichar* readonly = nsnull;
819 0 : const PRUnichar* onget = nsnull;
820 0 : const PRUnichar* onset = nsnull;
821 :
822 0 : nsCOMPtr<nsIAtom> prefix, localName;
823 0 : for (; *aAtts; aAtts += 2) {
824 : PRInt32 nameSpaceID;
825 0 : nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
826 0 : getter_AddRefs(localName), &nameSpaceID);
827 :
828 0 : if (nameSpaceID != kNameSpaceID_None) {
829 0 : continue;
830 : }
831 :
832 : // Is this attribute one of the ones we care about?
833 0 : if (localName == nsGkAtoms::name) {
834 0 : name = aAtts[1];
835 : }
836 0 : else if (localName == nsGkAtoms::readonly) {
837 0 : readonly = aAtts[1];
838 : }
839 0 : else if (localName == nsGkAtoms::onget) {
840 0 : onget = aAtts[1];
841 : }
842 0 : else if (localName == nsGkAtoms::onset) {
843 0 : onset = aAtts[1];
844 : }
845 : }
846 :
847 0 : if (name) {
848 : // All of our pointers are now filled in. Construct our property with all of
849 : // these parameters.
850 0 : mProperty = new nsXBLProtoImplProperty(name, onget, onset, readonly);
851 0 : if (mProperty) {
852 0 : AddMember(mProperty);
853 : }
854 : }
855 0 : }
856 :
857 : void
858 0 : nsXBLContentSink::ConstructMethod(const PRUnichar **aAtts)
859 : {
860 0 : mMethod = nsnull;
861 :
862 0 : const PRUnichar* name = nsnull;
863 0 : if (FindValue(aAtts, nsGkAtoms::name, &name)) {
864 0 : mMethod = new nsXBLProtoImplMethod(name);
865 : }
866 :
867 0 : if (mMethod) {
868 0 : AddMember(mMethod);
869 : }
870 0 : }
871 :
872 : void
873 0 : nsXBLContentSink::ConstructParameter(const PRUnichar **aAtts)
874 : {
875 0 : if (!mMethod)
876 0 : return;
877 :
878 0 : const PRUnichar* name = nsnull;
879 0 : if (FindValue(aAtts, nsGkAtoms::name, &name)) {
880 0 : mMethod->AddParameter(nsDependentString(name));
881 : }
882 : }
883 :
884 : nsresult
885 0 : nsXBLContentSink::CreateElement(const PRUnichar** aAtts, PRUint32 aAttsCount,
886 : nsINodeInfo* aNodeInfo, PRUint32 aLineNumber,
887 : nsIContent** aResult, bool* aAppendContent,
888 : FromParser aFromParser)
889 : {
890 : #ifdef MOZ_XUL
891 0 : if (!aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
892 : #endif
893 : return nsXMLContentSink::CreateElement(aAtts, aAttsCount, aNodeInfo,
894 : aLineNumber, aResult,
895 0 : aAppendContent, aFromParser);
896 : #ifdef MOZ_XUL
897 : }
898 :
899 : // Note that this needs to match the code in nsXBLPrototypeBinding::ReadContentNode.
900 :
901 0 : *aAppendContent = true;
902 0 : nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
903 0 : if (!prototype)
904 0 : return NS_ERROR_OUT_OF_MEMORY;
905 :
906 0 : prototype->mNodeInfo = aNodeInfo;
907 : // XXX - we need to do exactly what the XUL content-sink does (eg,
908 : // look for 'type', 'version' etc attributes)
909 0 : prototype->mScriptTypeID = nsIProgrammingLanguage::JAVASCRIPT;
910 :
911 0 : AddAttributesToXULPrototype(aAtts, aAttsCount, prototype);
912 :
913 : Element* result;
914 0 : nsresult rv = nsXULElement::Create(prototype, mDocument, false, &result);
915 0 : *aResult = result;
916 0 : return rv;
917 : #endif
918 : }
919 :
920 : nsresult
921 0 : nsXBLContentSink::AddAttributes(const PRUnichar** aAtts,
922 : nsIContent* aContent)
923 : {
924 0 : if (aContent->IsXUL())
925 0 : return NS_OK; // Nothing to do, since the proto already has the attrs.
926 :
927 0 : return nsXMLContentSink::AddAttributes(aAtts, aContent);
928 : }
929 :
930 : #ifdef MOZ_XUL
931 : nsresult
932 0 : nsXBLContentSink::AddAttributesToXULPrototype(const PRUnichar **aAtts,
933 : PRUint32 aAttsCount,
934 : nsXULPrototypeElement* aElement)
935 : {
936 : // Add tag attributes to the element
937 : nsresult rv;
938 :
939 : // Create storage for the attributes
940 0 : nsXULPrototypeAttribute* attrs = nsnull;
941 0 : if (aAttsCount > 0) {
942 0 : attrs = new nsXULPrototypeAttribute[aAttsCount];
943 0 : if (!attrs)
944 0 : return NS_ERROR_OUT_OF_MEMORY;
945 : }
946 :
947 0 : aElement->mAttributes = attrs;
948 0 : aElement->mNumAttributes = aAttsCount;
949 :
950 : // Copy the attributes into the prototype
951 0 : nsCOMPtr<nsIAtom> prefix, localName;
952 :
953 : PRUint32 i;
954 0 : for (i = 0; i < aAttsCount; ++i) {
955 : PRInt32 nameSpaceID;
956 0 : nsContentUtils::SplitExpatName(aAtts[i * 2], getter_AddRefs(prefix),
957 0 : getter_AddRefs(localName), &nameSpaceID);
958 :
959 0 : if (nameSpaceID == kNameSpaceID_None) {
960 0 : attrs[i].mName.SetTo(localName);
961 : }
962 : else {
963 0 : nsCOMPtr<nsINodeInfo> ni;
964 : ni = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
965 0 : nsIDOMNode::ATTRIBUTE_NODE);
966 0 : attrs[i].mName.SetTo(ni);
967 : }
968 :
969 0 : rv = aElement->SetAttrAt(i, nsDependentString(aAtts[i * 2 + 1]),
970 0 : mDocumentURI);
971 0 : NS_ENSURE_SUCCESS(rv, rv);
972 : }
973 :
974 0 : return NS_OK;
975 : }
976 : #endif
|