Bug 591198: Display element using normal layout rules if XBL binding fails for securi...
[mozilla-central.git] / content / xbl / src / nsXBLService.cpp
blob84fdd25e3e5977ced79684975e2e27d010f5ce31
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
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/
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.
15 * The Original Code is Mozilla Communicator client code.
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.
22 * Contributor(s):
23 * Original Author: David W. Hyatt (hyatt@netscape.com)
24 * - Brendan Eich (brendan@mozilla.org)
25 * - Mike Pinkerton (pinkerton@netscape.com)
26 * Mats Palmgren <mats.palmgren@bredband.net>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include "nsCOMPtr.h"
43 #include "nsNetUtil.h"
44 #include "nsXBLService.h"
45 #include "nsXBLWindowKeyHandler.h"
46 #include "nsIInputStream.h"
47 #include "nsINameSpaceManager.h"
48 #include "nsHashtable.h"
49 #include "nsIURI.h"
50 #include "nsIDOMElement.h"
51 #include "nsIURL.h"
52 #include "nsIChannel.h"
53 #include "nsXPIDLString.h"
54 #include "nsIParser.h"
55 #include "nsParserCIID.h"
56 #include "nsNetUtil.h"
57 #include "plstr.h"
58 #include "nsIContent.h"
59 #include "nsIDOMElement.h"
60 #include "nsIDocument.h"
61 #include "nsIXMLContentSink.h"
62 #include "nsContentCID.h"
63 #include "nsXMLDocument.h"
64 #include "mozilla/FunctionTimer.h"
65 #include "nsGkAtoms.h"
66 #include "nsIMemory.h"
67 #include "nsIObserverService.h"
68 #include "nsIDOMNodeList.h"
69 #include "nsXBLContentSink.h"
70 #include "nsXBLBinding.h"
71 #include "nsXBLPrototypeBinding.h"
72 #include "nsXBLDocumentInfo.h"
73 #include "nsCRT.h"
74 #include "nsContentUtils.h"
75 #include "nsSyncLoadService.h"
76 #include "nsIDOM3Node.h"
77 #include "nsContentPolicyUtils.h"
78 #include "nsTArray.h"
79 #include "nsContentErrors.h"
81 #include "nsIPresShell.h"
82 #include "nsIDocumentObserver.h"
83 #include "nsFrameManager.h"
84 #include "nsStyleContext.h"
85 #include "nsIScriptSecurityManager.h"
86 #include "nsIScriptError.h"
88 #ifdef MOZ_XUL
89 #include "nsXULPrototypeCache.h"
90 #endif
91 #include "nsIDOMLoadListener.h"
92 #include "nsIDOMEventGroup.h"
94 #define NS_MAX_XBL_BINDING_RECURSION 20
96 static PRBool IsChromeOrResourceURI(nsIURI* aURI)
98 PRBool isChrome = PR_FALSE;
99 PRBool isResource = PR_FALSE;
100 if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) &&
101 NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)))
102 return (isChrome || isResource);
103 return PR_FALSE;
106 static PRBool
107 IsAncestorBinding(nsIDocument* aDocument,
108 nsIURI* aChildBindingURI,
109 nsIContent* aChild)
111 NS_ASSERTION(aDocument, "expected a document");
112 NS_ASSERTION(aChildBindingURI, "expected a binding URI");
113 NS_ASSERTION(aChild, "expected a child content");
115 PRUint32 bindingRecursion = 0;
116 nsBindingManager* bindingManager = aDocument->BindingManager();
117 for (nsIContent *bindingParent = aChild->GetBindingParent();
118 bindingParent;
119 bindingParent = bindingParent->GetBindingParent()) {
120 nsXBLBinding* binding = bindingManager->GetBinding(bindingParent);
121 if (!binding) {
122 continue;
125 if (binding->PrototypeBinding()->CompareBindingURI(aChildBindingURI)) {
126 ++bindingRecursion;
127 if (bindingRecursion < NS_MAX_XBL_BINDING_RECURSION) {
128 continue;
130 nsCAutoString spec;
131 aChildBindingURI->GetSpec(spec);
132 NS_ConvertUTF8toUTF16 bindingURI(spec);
133 const PRUnichar* params[] = { bindingURI.get() };
134 nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
135 "TooDeepBindingRecursion",
136 params, NS_ARRAY_LENGTH(params),
137 aDocument->GetDocumentURI(),
138 EmptyString(), 0, 0,
139 nsIScriptError::warningFlag,
140 "XBL");
141 return PR_TRUE;
145 return PR_FALSE;
148 PRBool CheckTagNameWhiteList(PRInt32 aNameSpaceID, nsIAtom *aTagName)
150 static nsIContent::AttrValuesArray kValidXULTagNames[] = {
151 &nsGkAtoms::autorepeatbutton, &nsGkAtoms::box, &nsGkAtoms::browser,
152 &nsGkAtoms::button, &nsGkAtoms::hbox, &nsGkAtoms::image, &nsGkAtoms::menu,
153 &nsGkAtoms::menubar, &nsGkAtoms::menuitem, &nsGkAtoms::menupopup,
154 &nsGkAtoms::row, &nsGkAtoms::slider, &nsGkAtoms::spacer,
155 &nsGkAtoms::splitter, &nsGkAtoms::text, &nsGkAtoms::tree, nsnull};
157 PRUint32 i;
158 if (aNameSpaceID == kNameSpaceID_XUL) {
159 for (i = 0; kValidXULTagNames[i]; ++i) {
160 if (aTagName == *(kValidXULTagNames[i])) {
161 return PR_TRUE;
165 #ifdef MOZ_SVG
166 else if (aNameSpaceID == kNameSpaceID_SVG &&
167 aTagName == nsGkAtoms::generic) {
168 return PR_TRUE;
170 #endif
172 return PR_FALSE;
175 // Individual binding requests.
176 class nsXBLBindingRequest
178 public:
179 nsCOMPtr<nsIURI> mBindingURI;
180 nsCOMPtr<nsIContent> mBoundElement;
182 static nsXBLBindingRequest*
183 Create(nsFixedSizeAllocator& aPool, nsIURI* aURI, nsIContent* aBoundElement) {
184 void* place = aPool.Alloc(sizeof(nsXBLBindingRequest));
185 return place ? ::new (place) nsXBLBindingRequest(aURI, aBoundElement) : nsnull;
188 static void
189 Destroy(nsFixedSizeAllocator& aPool, nsXBLBindingRequest* aRequest) {
190 aRequest->~nsXBLBindingRequest();
191 aPool.Free(aRequest, sizeof(*aRequest));
194 void DocumentLoaded(nsIDocument* aBindingDoc)
196 // We only need the document here to cause frame construction, so
197 // we need the current doc, not the owner doc.
198 nsIDocument* doc = mBoundElement->GetCurrentDoc();
199 if (!doc)
200 return;
202 // Get the binding.
203 PRBool ready = PR_FALSE;
204 gXBLService->BindingReady(mBoundElement, mBindingURI, &ready);
206 if (!ready)
207 return;
209 // If |mBoundElement| is (in addition to having binding |mBinding|)
210 // also a descendant of another element with binding |mBinding|,
211 // then we might have just constructed it due to the
212 // notification of its parent. (We can know about both if the
213 // binding loads were triggered from the DOM rather than frame
214 // construction.) So we have to check both whether the element
215 // has a primary frame and whether it's in the undisplayed map
216 // before sending a ContentInserted notification, or bad things
217 // will happen.
218 nsIPresShell *shell = doc->GetShell();
219 if (shell) {
220 nsIFrame* childFrame = mBoundElement->GetPrimaryFrame();
221 if (!childFrame) {
222 // Check to see if it's in the undisplayed content map.
223 nsStyleContext* sc =
224 shell->FrameManager()->GetUndisplayedContent(mBoundElement);
226 if (!sc) {
227 shell->RecreateFramesFor(mBoundElement);
233 static nsIXBLService* gXBLService;
234 static int gRefCnt;
236 protected:
237 nsXBLBindingRequest(nsIURI* aURI, nsIContent* aBoundElement)
238 : mBindingURI(aURI),
239 mBoundElement(aBoundElement)
241 gRefCnt++;
242 if (gRefCnt == 1) {
243 CallGetService("@mozilla.org/xbl;1", &gXBLService);
247 ~nsXBLBindingRequest()
249 gRefCnt--;
250 if (gRefCnt == 0) {
251 NS_IF_RELEASE(gXBLService);
255 private:
256 // Hide so that only Create() and Destroy() can be used to
257 // allocate and deallocate from the heap
258 static void* operator new(size_t) CPP_THROW_NEW { return 0; }
259 static void operator delete(void*, size_t) {}
262 static const size_t kBucketSizes[] = {
263 sizeof(nsXBLBindingRequest)
266 static const PRInt32 kNumBuckets = sizeof(kBucketSizes)/sizeof(size_t);
267 static const PRInt32 kNumElements = 64;
268 static const PRInt32 kInitialSize = (NS_SIZE_IN_HEAP(sizeof(nsXBLBindingRequest))) * kNumElements;
270 nsIXBLService* nsXBLBindingRequest::gXBLService = nsnull;
271 int nsXBLBindingRequest::gRefCnt = 0;
273 // nsXBLStreamListener, a helper class used for
274 // asynchronous parsing of URLs
275 /* Header file */
276 class nsXBLStreamListener : public nsIStreamListener, public nsIDOMLoadListener
278 public:
279 NS_DECL_ISUPPORTS
280 NS_DECL_NSISTREAMLISTENER
281 NS_DECL_NSIREQUESTOBSERVER
283 NS_IMETHOD Load(nsIDOMEvent* aEvent);
284 NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent) { return NS_OK; }
285 NS_IMETHOD Unload(nsIDOMEvent* aEvent) { return NS_OK; }
286 NS_IMETHOD Abort(nsIDOMEvent* aEvent) { return NS_OK; }
287 NS_IMETHOD Error(nsIDOMEvent* aEvent) { return NS_OK; }
288 NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; }
290 nsXBLStreamListener(nsXBLService* aXBLService,
291 nsIDocument* aBoundDocument,
292 nsIXMLContentSink* aSink,
293 nsIDocument* aBindingDocument);
294 ~nsXBLStreamListener();
296 void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); }
297 PRBool HasRequest(nsIURI* aURI, nsIContent* aBoundElement);
299 private:
300 nsXBLService* mXBLService; // [WEAK]
302 nsCOMPtr<nsIStreamListener> mInner;
303 nsAutoTArray<nsXBLBindingRequest*, 8> mBindingRequests;
305 nsCOMPtr<nsIWeakReference> mBoundDocument;
306 nsCOMPtr<nsIXMLContentSink> mSink; // Only set until OnStartRequest
307 nsCOMPtr<nsIDocument> mBindingDocument; // Only set until OnStartRequest
310 /* Implementation file */
311 NS_IMPL_ISUPPORTS4(nsXBLStreamListener, nsIStreamListener, nsIRequestObserver, nsIDOMLoadListener, nsIDOMEventListener)
313 nsXBLStreamListener::nsXBLStreamListener(nsXBLService* aXBLService,
314 nsIDocument* aBoundDocument,
315 nsIXMLContentSink* aSink,
316 nsIDocument* aBindingDocument)
317 : mSink(aSink), mBindingDocument(aBindingDocument)
319 /* member initializers and constructor code */
320 mXBLService = aXBLService;
321 mBoundDocument = do_GetWeakReference(aBoundDocument);
324 nsXBLStreamListener::~nsXBLStreamListener()
326 for (PRUint32 i = 0; i < mBindingRequests.Length(); i++) {
327 nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
328 nsXBLBindingRequest::Destroy(mXBLService->mPool, req);
332 NS_IMETHODIMP
333 nsXBLStreamListener::OnDataAvailable(nsIRequest *request, nsISupports* aCtxt, nsIInputStream* aInStr,
334 PRUint32 aSourceOffset, PRUint32 aCount)
336 if (mInner)
337 return mInner->OnDataAvailable(request, aCtxt, aInStr, aSourceOffset, aCount);
338 return NS_ERROR_FAILURE;
341 NS_IMETHODIMP
342 nsXBLStreamListener::OnStartRequest(nsIRequest* request, nsISupports* aCtxt)
344 // Make sure we don't hold on to the sink and binding document past this point
345 nsCOMPtr<nsIXMLContentSink> sink;
346 mSink.swap(sink);
347 nsCOMPtr<nsIDocument> doc;
348 mBindingDocument.swap(doc);
350 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
351 NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
353 nsCOMPtr<nsILoadGroup> group;
354 request->GetLoadGroup(getter_AddRefs(group));
356 nsresult rv = doc->StartDocumentLoad("loadAsInteractiveData",
357 channel,
358 group,
359 nsnull,
360 getter_AddRefs(mInner),
361 PR_TRUE,
362 sink);
363 NS_ENSURE_SUCCESS(rv, rv);
365 // Make sure to add ourselves as a listener after StartDocumentLoad,
366 // since that resets the event listners on the document.
367 nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(doc));
368 target->AddEventListener(NS_LITERAL_STRING("load"), this, PR_FALSE);
370 return mInner->OnStartRequest(request, aCtxt);
373 NS_IMETHODIMP
374 nsXBLStreamListener::OnStopRequest(nsIRequest* request, nsISupports* aCtxt, nsresult aStatus)
376 nsresult rv = NS_OK;
377 if (mInner) {
378 rv = mInner->OnStopRequest(request, aCtxt, aStatus);
381 // Don't hold onto the inner listener; holding onto it can create a cycle
382 // with the document
383 mInner = nsnull;
385 return rv;
388 PRBool
389 nsXBLStreamListener::HasRequest(nsIURI* aURI, nsIContent* aElt)
391 // XXX Could be more efficient.
392 PRUint32 count = mBindingRequests.Length();
393 for (PRUint32 i = 0; i < count; i++) {
394 nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
395 PRBool eq;
396 if (req->mBoundElement == aElt &&
397 NS_SUCCEEDED(req->mBindingURI->Equals(aURI, &eq)) && eq)
398 return PR_TRUE;
401 return PR_FALSE;
404 nsresult
405 nsXBLStreamListener::Load(nsIDOMEvent* aEvent)
407 nsresult rv = NS_OK;
408 PRUint32 i;
409 PRUint32 count = mBindingRequests.Length();
411 // Get the binding document; note that we don't hold onto it in this object
412 // to avoid creating a cycle
413 nsCOMPtr<nsIDOMEventTarget> target;
414 aEvent->GetCurrentTarget(getter_AddRefs(target));
415 nsCOMPtr<nsIDocument> bindingDocument = do_QueryInterface(target);
416 NS_ASSERTION(bindingDocument, "Event not targeted at document?!");
418 // See if we're still alive.
419 nsCOMPtr<nsIDocument> doc(do_QueryReferent(mBoundDocument));
420 if (!doc) {
421 NS_WARNING("XBL load did not complete until after document went away! Modal dialog bug?\n");
423 else {
424 // We have to do a flush prior to notification of the document load.
425 // This has to happen since the HTML content sink can be holding on
426 // to notifications related to our children (e.g., if you bind to the
427 // <body> tag) that result in duplication of content.
428 // We need to get the sink's notifications flushed and then make the binding
429 // ready.
430 if (count > 0) {
431 nsXBLBindingRequest* req = mBindingRequests.ElementAt(0);
432 nsIDocument* document = req->mBoundElement->GetCurrentDoc();
433 if (document)
434 document->FlushPendingNotifications(Flush_ContentAndNotify);
437 // Remove ourselves from the set of pending docs.
438 nsBindingManager *bindingManager = doc->BindingManager();
439 nsIURI* documentURI = bindingDocument->GetDocumentURI();
440 bindingManager->RemoveLoadingDocListener(documentURI);
442 if (!bindingDocument->GetRootElement()) {
443 // FIXME: How about an error console warning?
444 NS_WARNING("*** XBL doc with no root element! Something went horribly wrong! ***");
445 return NS_ERROR_FAILURE;
448 // Put our doc info in the doc table.
449 nsBindingManager *xblDocBindingManager = bindingDocument->BindingManager();
450 nsRefPtr<nsXBLDocumentInfo> info =
451 xblDocBindingManager->GetXBLDocumentInfo(documentURI);
452 xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
453 if (!info) {
454 if (IsChromeOrResourceURI(documentURI)) {
455 NS_WARNING("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
457 nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
458 "MalformedXBL",
459 nsnull, 0, documentURI,
460 EmptyString(), 0, 0,
461 nsIScriptError::warningFlag,
462 "XBL");
463 return NS_ERROR_FAILURE;
466 // If the doc is a chrome URI, then we put it into the XUL cache.
467 #ifdef MOZ_XUL
468 if (IsChromeOrResourceURI(documentURI)) {
469 nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
470 if (cache && cache->IsEnabled())
471 cache->PutXBLDocumentInfo(info);
473 #endif
475 bindingManager->PutXBLDocumentInfo(info);
477 // Notify all pending requests that their bindings are
478 // ready and can be installed.
479 for (i = 0; i < count; i++) {
480 nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
481 req->DocumentLoaded(bindingDocument);
485 target->RemoveEventListener(NS_LITERAL_STRING("load"), (nsIDOMLoadListener*)this, PR_FALSE);
487 return rv;
490 // Implementation /////////////////////////////////////////////////////////////////
492 // Static member variable initialization
493 PRUint32 nsXBLService::gRefCnt = 0;
494 PRBool nsXBLService::gAllowDataURIs = PR_FALSE;
496 nsHashtable* nsXBLService::gClassTable = nsnull;
498 JSCList nsXBLService::gClassLRUList = JS_INIT_STATIC_CLIST(&nsXBLService::gClassLRUList);
499 PRUint32 nsXBLService::gClassLRUListLength = 0;
500 PRUint32 nsXBLService::gClassLRUListQuota = 64;
502 // Implement our nsISupports methods
503 NS_IMPL_ISUPPORTS3(nsXBLService, nsIXBLService, nsIObserver, nsISupportsWeakReference)
505 // Constructors/Destructors
506 nsXBLService::nsXBLService(void)
508 mPool.Init("XBL Binding Requests", kBucketSizes, kNumBuckets, kInitialSize);
510 gRefCnt++;
511 if (gRefCnt == 1) {
512 gClassTable = new nsHashtable();
515 nsContentUtils::AddBoolPrefVarCache("layout.debug.enable_data_xbl",
516 &gAllowDataURIs);
519 nsXBLService::~nsXBLService(void)
521 gRefCnt--;
522 if (gRefCnt == 0) {
523 // Walk the LRU list removing and deleting the nsXBLJSClasses.
524 FlushMemory();
526 // Any straggling nsXBLJSClass instances held by unfinalized JS objects
527 // created for bindings will be deleted when those objects are finalized
528 // (and not put on gClassLRUList, because length >= quota).
529 gClassLRUListLength = gClassLRUListQuota = 0;
531 // At this point, the only hash table entries should be for referenced
532 // XBL class structs held by unfinalized JS binding objects.
533 delete gClassTable;
534 gClassTable = nsnull;
538 // This function loads a particular XBL file and installs all of the bindings
539 // onto the element.
540 NS_IMETHODIMP
541 nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL,
542 nsIPrincipal* aOriginPrincipal, PRBool aAugmentFlag,
543 nsXBLBinding** aBinding, PRBool* aResolveStyle)
545 NS_PRECONDITION(aOriginPrincipal, "Must have an origin principal");
547 *aBinding = nsnull;
548 *aResolveStyle = PR_FALSE;
550 nsresult rv;
552 nsCOMPtr<nsIDocument> document = aContent->GetOwnerDoc();
554 // XXX document may be null if we're in the midst of paint suppression
555 if (!document)
556 return NS_OK;
558 nsCAutoString urlspec;
559 if (nsContentUtils::GetWrapperSafeScriptFilename(document, aURL, urlspec)) {
560 // Block an attempt to load a binding that has special wrapper
561 // automation needs.
563 return NS_OK;
566 nsBindingManager *bindingManager = document->BindingManager();
568 nsXBLBinding *binding = bindingManager->GetBinding(aContent);
569 if (binding && !aAugmentFlag) {
570 nsXBLBinding *styleBinding = binding->GetFirstStyleBinding();
571 if (styleBinding) {
572 if (binding->MarkedForDeath()) {
573 FlushStyleBindings(aContent);
574 binding = nsnull;
576 else {
577 // See if the URIs match.
578 if (styleBinding->PrototypeBinding()->CompareBindingURI(aURL))
579 return NS_OK;
580 FlushStyleBindings(aContent);
581 binding = nsnull;
586 PRBool ready;
587 nsRefPtr<nsXBLBinding> newBinding;
588 if (NS_FAILED(rv = GetBinding(aContent, aURL, PR_FALSE, aOriginPrincipal,
589 &ready, getter_AddRefs(newBinding)))) {
590 return rv;
593 if (!newBinding) {
594 #ifdef DEBUG
595 nsCAutoString spec;
596 aURL->GetSpec(spec);
597 nsCAutoString str(NS_LITERAL_CSTRING("Failed to locate XBL binding. XBL is now using id instead of name to reference bindings. Make sure you have switched over. The invalid binding name is: ") + spec);
598 NS_ERROR(str.get());
599 #endif
600 return NS_OK;
603 if (::IsAncestorBinding(document, aURL, aContent)) {
604 return NS_ERROR_ILLEGAL_VALUE;
607 if (aAugmentFlag) {
608 nsXBLBinding *baseBinding;
609 nsXBLBinding *nextBinding = newBinding;
610 do {
611 baseBinding = nextBinding;
612 nextBinding = baseBinding->GetBaseBinding();
613 baseBinding->SetIsStyleBinding(PR_FALSE);
614 } while (nextBinding);
616 // XXX Handle adjusting the prototype chain! We need to somehow indicate to
617 // InstallImplementation that the whole chain should just be whacked and rebuilt.
618 // We are becoming the new binding.
619 baseBinding->SetBaseBinding(binding);
620 bindingManager->SetBinding(aContent, newBinding);
622 else {
623 // We loaded a style binding. It goes on the end.
624 if (binding) {
625 // Get the last binding that is in the append layer.
626 binding->RootBinding()->SetBaseBinding(newBinding);
628 else {
629 // Install the binding on the content node.
630 bindingManager->SetBinding(aContent, newBinding);
635 nsAutoScriptBlocker scriptBlocker;
637 // Set the binding's bound element.
638 newBinding->SetBoundElement(aContent);
640 // Tell the binding to build the anonymous content.
641 newBinding->GenerateAnonymousContent();
643 // Tell the binding to install event handlers
644 newBinding->InstallEventHandlers();
646 // Set up our properties
647 rv = newBinding->InstallImplementation();
648 NS_ENSURE_SUCCESS(rv, rv);
650 // Figure out if we have any scoped sheets. If so, we do a second resolve.
651 *aResolveStyle = newBinding->HasStyleSheets();
653 newBinding.swap(*aBinding);
656 return NS_OK;
659 nsresult
660 nsXBLService::FlushStyleBindings(nsIContent* aContent)
662 nsCOMPtr<nsIDocument> document = aContent->GetOwnerDoc();
664 // XXX doc will be null if we're in the midst of paint suppression.
665 if (! document)
666 return NS_OK;
668 nsBindingManager *bindingManager = document->BindingManager();
670 nsXBLBinding *binding = bindingManager->GetBinding(aContent);
672 if (binding) {
673 nsXBLBinding *styleBinding = binding->GetFirstStyleBinding();
675 if (styleBinding) {
676 // Clear out the script references.
677 styleBinding->ChangeDocument(document, nsnull);
680 if (styleBinding == binding)
681 bindingManager->SetBinding(aContent, nsnull); // Flush old style bindings
684 return NS_OK;
687 NS_IMETHODIMP
688 nsXBLService::ResolveTag(nsIContent* aContent, PRInt32* aNameSpaceID,
689 nsIAtom** aResult)
691 nsIDocument* document = aContent->GetOwnerDoc();
692 if (document) {
693 *aResult = document->BindingManager()->ResolveTag(aContent, aNameSpaceID);
694 NS_IF_ADDREF(*aResult);
696 else {
697 *aNameSpaceID = aContent->GetNameSpaceID();
698 NS_ADDREF(*aResult = aContent->Tag());
701 return NS_OK;
706 // AttachGlobalKeyHandler
708 // Creates a new key handler and prepares to listen to key events on the given
709 // event receiver (either a document or an content node). If the receiver is content,
710 // then extra work needs to be done to hook it up to the document (XXX WHY??)
712 NS_IMETHODIMP
713 nsXBLService::AttachGlobalKeyHandler(nsPIDOMEventTarget* aTarget)
715 // check if the receiver is a content node (not a document), and hook
716 // it to the document if that is the case.
717 nsCOMPtr<nsPIDOMEventTarget> piTarget = aTarget;
718 nsCOMPtr<nsIContent> contentNode(do_QueryInterface(aTarget));
719 if (contentNode) {
720 // Only attach if we're really in a document
721 nsCOMPtr<nsIDocument> doc = contentNode->GetCurrentDoc();
722 if (doc)
723 piTarget = do_QueryInterface(doc); // We're a XUL keyset. Attach to our document.
726 if (!piTarget)
727 return NS_ERROR_FAILURE;
729 // the listener already exists, so skip this
730 if (contentNode && contentNode->GetProperty(nsGkAtoms::listener))
731 return NS_OK;
733 nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(contentNode));
735 // Create the key handler
736 nsXBLWindowKeyHandler* handler;
737 NS_NewXBLWindowKeyHandler(elt, piTarget, &handler); // This addRef's
738 if (!handler)
739 return NS_ERROR_FAILURE;
741 // listen to these events
742 nsCOMPtr<nsIDOMEventGroup> systemGroup;
743 piTarget->GetSystemEventGroup(getter_AddRefs(systemGroup));
744 nsCOMPtr<nsIDOM3EventTarget> target = do_QueryInterface(piTarget);
746 target->AddGroupedEventListener(NS_LITERAL_STRING("keydown"), handler,
747 PR_FALSE, systemGroup);
748 target->AddGroupedEventListener(NS_LITERAL_STRING("keyup"), handler,
749 PR_FALSE, systemGroup);
750 target->AddGroupedEventListener(NS_LITERAL_STRING("keypress"), handler,
751 PR_FALSE, systemGroup);
753 if (contentNode)
754 return contentNode->SetProperty(nsGkAtoms::listener, handler,
755 nsPropertyTable::SupportsDtorFunc, PR_TRUE);
757 // release the handler. The reference will be maintained by the event target,
758 // and, if there is a content node, the property.
759 NS_RELEASE(handler);
760 return NS_OK;
764 // DetachGlobalKeyHandler
766 // Removes a key handler added by DeatchGlobalKeyHandler.
768 NS_IMETHODIMP
769 nsXBLService::DetachGlobalKeyHandler(nsPIDOMEventTarget* aTarget)
771 nsCOMPtr<nsPIDOMEventTarget> piTarget = aTarget;
772 nsCOMPtr<nsIContent> contentNode(do_QueryInterface(aTarget));
773 if (!contentNode) // detaching is only supported for content nodes
774 return NS_ERROR_FAILURE;
776 // Only attach if we're really in a document
777 nsCOMPtr<nsIDocument> doc = contentNode->GetCurrentDoc();
778 if (doc)
779 piTarget = do_QueryInterface(doc);
780 if (!piTarget)
781 return NS_ERROR_FAILURE;
783 nsIDOMEventListener* handler =
784 static_cast<nsIDOMEventListener*>(contentNode->GetProperty(nsGkAtoms::listener));
785 if (!handler)
786 return NS_ERROR_FAILURE;
788 nsCOMPtr<nsIDOMEventGroup> systemGroup;
789 piTarget->GetSystemEventGroup(getter_AddRefs(systemGroup));
790 nsCOMPtr<nsIDOM3EventTarget> target = do_QueryInterface(piTarget);
792 target->RemoveGroupedEventListener(NS_LITERAL_STRING("keydown"), handler,
793 PR_FALSE, systemGroup);
794 target->RemoveGroupedEventListener(NS_LITERAL_STRING("keyup"), handler,
795 PR_FALSE, systemGroup);
796 target->RemoveGroupedEventListener(NS_LITERAL_STRING("keypress"), handler,
797 PR_FALSE, systemGroup);
799 contentNode->DeleteProperty(nsGkAtoms::listener);
801 return NS_OK;
804 NS_IMETHODIMP
805 nsXBLService::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData)
807 if (nsCRT::strcmp(aTopic, "memory-pressure") == 0)
808 FlushMemory();
810 return NS_OK;
813 nsresult
814 nsXBLService::FlushMemory()
816 while (!JS_CLIST_IS_EMPTY(&gClassLRUList)) {
817 JSCList* lru = gClassLRUList.next;
818 nsXBLJSClass* c = static_cast<nsXBLJSClass*>(lru);
820 JS_REMOVE_AND_INIT_LINK(lru);
821 delete c;
822 gClassLRUListLength--;
824 return NS_OK;
827 // Internal helper methods ////////////////////////////////////////////////////////////////
829 NS_IMETHODIMP nsXBLService::BindingReady(nsIContent* aBoundElement,
830 nsIURI* aURI,
831 PRBool* aIsReady)
833 // Don't do a security check here; we know this binding is set to go.
834 return GetBinding(aBoundElement, aURI, PR_TRUE, nsnull, aIsReady, nsnull);
837 nsresult
838 nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
839 PRBool aPeekOnly, nsIPrincipal* aOriginPrincipal,
840 PRBool* aIsReady, nsXBLBinding** aResult)
842 // More than 6 binding URIs are rare, see bug 55070 comment 18.
843 nsAutoTArray<nsIURI*, 6> uris;
844 return GetBinding(aBoundElement, aURI, aPeekOnly, aOriginPrincipal, aIsReady,
845 aResult, uris);
848 nsresult
849 nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
850 PRBool aPeekOnly, nsIPrincipal* aOriginPrincipal,
851 PRBool* aIsReady, nsXBLBinding** aResult,
852 nsTArray<nsIURI*>& aDontExtendURIs)
854 NS_ASSERTION(aPeekOnly || aResult,
855 "Must have non-null out param if not just peeking to see "
856 "whether the binding is ready");
858 if (aResult)
859 *aResult = nsnull;
861 if (!aURI)
862 return NS_ERROR_FAILURE;
864 nsCAutoString ref;
865 nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
866 if (url)
867 url->GetRef(ref);
869 nsCOMPtr<nsIDocument> boundDocument = aBoundElement->GetOwnerDoc();
871 nsRefPtr<nsXBLDocumentInfo> docInfo;
872 nsresult rv = LoadBindingDocumentInfo(aBoundElement, boundDocument, aURI,
873 aOriginPrincipal,
874 PR_FALSE, getter_AddRefs(docInfo));
875 NS_ENSURE_SUCCESS(rv, rv);
877 if (!docInfo)
878 return NS_ERROR_FAILURE;
880 // Get our doc info and determine our script access.
881 nsCOMPtr<nsIDocument> doc = docInfo->GetDocument();
882 PRBool allowScripts = docInfo->GetScriptAccess();
884 nsXBLPrototypeBinding* protoBinding = docInfo->GetPrototypeBinding(ref);
886 NS_ASSERTION(protoBinding, "Unable to locate an XBL binding.");
887 if (!protoBinding)
888 return NS_ERROR_FAILURE;
890 NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(protoBinding->BindingURI()),
891 NS_ERROR_OUT_OF_MEMORY);
892 nsCOMPtr<nsIURI> altBindingURI = protoBinding->AlternateBindingURI();
893 if (altBindingURI) {
894 NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(altBindingURI),
895 NS_ERROR_OUT_OF_MEMORY);
898 nsCOMPtr<nsIContent> child = protoBinding->GetBindingElement();
900 // Our prototype binding must have all its resources loaded.
901 PRBool ready = protoBinding->LoadResources();
902 if (!ready) {
903 // Add our bound element to the protos list of elts that should
904 // be notified when the stylesheets and scripts finish loading.
905 protoBinding->AddResourceListener(aBoundElement);
906 return NS_ERROR_FAILURE; // The binding isn't ready yet.
909 // If our prototype already has a base, then don't check for an "extends" attribute.
910 nsRefPtr<nsXBLBinding> baseBinding;
911 PRBool hasBase = protoBinding->HasBasePrototype();
912 nsXBLPrototypeBinding* baseProto = protoBinding->GetBasePrototype();
913 if (baseProto) {
914 // Use the NodePrincipal() of the <binding> element in question
915 // for the security check.
916 rv = GetBinding(aBoundElement, baseProto->BindingURI(), aPeekOnly,
917 child->NodePrincipal(), aIsReady,
918 getter_AddRefs(baseBinding), aDontExtendURIs);
919 if (NS_FAILED(rv))
920 return rv; // We aren't ready yet.
922 else if (hasBase) {
923 // Check for the presence of 'extends' and 'display' attributes
924 nsAutoString display, extends;
925 child->GetAttr(kNameSpaceID_None, nsGkAtoms::display, display);
926 child->GetAttr(kNameSpaceID_None, nsGkAtoms::extends, extends);
927 PRBool hasDisplay = !display.IsEmpty();
928 PRBool hasExtends = !extends.IsEmpty();
930 nsAutoString value(extends);
932 if (!hasExtends)
933 protoBinding->SetHasBasePrototype(PR_FALSE);
934 else {
935 // Now slice 'em up to see what we've got.
936 nsAutoString prefix;
937 PRInt32 offset;
938 if (hasDisplay) {
939 offset = display.FindChar(':');
940 if (-1 != offset) {
941 display.Left(prefix, offset);
942 display.Cut(0, offset+1);
945 else if (hasExtends) {
946 offset = extends.FindChar(':');
947 if (-1 != offset) {
948 extends.Left(prefix, offset);
949 extends.Cut(0, offset+1);
950 display = extends;
954 nsAutoString nameSpace;
956 if (!prefix.IsEmpty()) {
957 nsCOMPtr<nsIAtom> prefixAtom = do_GetAtom(prefix);
959 nsCOMPtr<nsIDOM3Node> node(do_QueryInterface(child));
961 if (node) {
962 node->LookupNamespaceURI(prefix, nameSpace);
964 if (!nameSpace.IsEmpty()) {
965 if (!hasDisplay) {
966 // We extend some widget/frame. We don't really have a
967 // base binding.
968 protoBinding->SetHasBasePrototype(PR_FALSE);
969 //child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, PR_FALSE);
972 PRInt32 nameSpaceID =
973 nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace);
975 nsCOMPtr<nsIAtom> tagName = do_GetAtom(display);
976 // Check the white list
977 if (!CheckTagNameWhiteList(nameSpaceID, tagName)) {
978 const PRUnichar* params[] = { display.get() };
979 nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
980 "InvalidExtendsBinding",
981 params, NS_ARRAY_LENGTH(params),
982 doc->GetDocumentURI(),
983 EmptyString(), 0, 0,
984 nsIScriptError::errorFlag,
985 "XBL");
986 NS_ASSERTION(!IsChromeOrResourceURI(aURI),
987 "Invalid extends value");
988 return NS_ERROR_ILLEGAL_VALUE;
991 protoBinding->SetBaseTag(nameSpaceID, tagName);
996 if (hasExtends && (hasDisplay || nameSpace.IsEmpty())) {
997 // Look up the prefix.
998 // We have a base class binding. Load it right now.
999 nsCOMPtr<nsIURI> bindingURI;
1000 rv = NS_NewURI(getter_AddRefs(bindingURI), value,
1001 doc->GetDocumentCharacterSet().get(),
1002 doc->GetDocBaseURI());
1003 NS_ENSURE_SUCCESS(rv, rv);
1005 PRUint32 count = aDontExtendURIs.Length();
1006 for (PRUint32 index = 0; index < count; ++index) {
1007 PRBool equal;
1008 rv = aDontExtendURIs[index]->Equals(bindingURI, &equal);
1009 NS_ENSURE_SUCCESS(rv, rv);
1010 if (equal) {
1011 nsCAutoString spec;
1012 protoBinding->BindingURI()->GetSpec(spec);
1013 NS_ConvertUTF8toUTF16 protoSpec(spec);
1014 const PRUnichar* params[] = { protoSpec.get(), value.get() };
1015 nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
1016 "CircularExtendsBinding",
1017 params, NS_ARRAY_LENGTH(params),
1018 boundDocument->GetDocumentURI(),
1019 EmptyString(), 0, 0,
1020 nsIScriptError::warningFlag,
1021 "XBL");
1022 return NS_ERROR_ILLEGAL_VALUE;
1026 // Use the NodePrincipal() of the <binding> element in question
1027 // for the security check.
1028 rv = GetBinding(aBoundElement, bindingURI, aPeekOnly,
1029 child->NodePrincipal(), aIsReady,
1030 getter_AddRefs(baseBinding), aDontExtendURIs);
1031 if (NS_FAILED(rv))
1032 return rv; // Binding not yet ready or an error occurred.
1033 if (!aPeekOnly) {
1034 // Make sure to set the base prototype.
1035 baseProto = baseBinding->PrototypeBinding();
1036 protoBinding->SetBasePrototype(baseProto);
1037 child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, PR_FALSE);
1038 child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::display, PR_FALSE);
1044 *aIsReady = PR_TRUE;
1045 if (!aPeekOnly) {
1046 // Make a new binding
1047 nsXBLBinding *newBinding = new nsXBLBinding(protoBinding);
1048 NS_ENSURE_TRUE(newBinding, NS_ERROR_OUT_OF_MEMORY);
1050 if (baseBinding)
1051 newBinding->SetBaseBinding(baseBinding);
1053 NS_ADDREF(*aResult = newBinding);
1056 return NS_OK;
1059 static PRBool SchemeIs(nsIURI* aURI, const char* aScheme)
1061 nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
1062 NS_ENSURE_TRUE(baseURI, PR_FALSE);
1064 PRBool isScheme = PR_FALSE;
1065 return NS_SUCCEEDED(baseURI->SchemeIs(aScheme, &isScheme)) && isScheme;
1068 static PRBool
1069 IsSystemOrChromeURLPrincipal(nsIPrincipal* aPrincipal)
1071 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
1072 return PR_TRUE;
1075 nsCOMPtr<nsIURI> uri;
1076 aPrincipal->GetURI(getter_AddRefs(uri));
1077 NS_ENSURE_TRUE(uri, PR_FALSE);
1079 PRBool isChrome = PR_FALSE;
1080 return NS_SUCCEEDED(uri->SchemeIs("chrome", &isChrome)) && isChrome;
1083 NS_IMETHODIMP
1084 nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
1085 nsIDocument* aBoundDocument,
1086 nsIURI* aBindingURI,
1087 nsIPrincipal* aOriginPrincipal,
1088 PRBool aForceSyncLoad,
1089 nsXBLDocumentInfo** aResult)
1091 NS_PRECONDITION(aBindingURI, "Must have a binding URI");
1092 NS_PRECONDITION(!aOriginPrincipal || aBoundDocument,
1093 "If we're doing a security check, we better have a document!");
1095 nsresult rv;
1096 if (aOriginPrincipal) {
1097 // Security check - Enforce same-origin policy, except to chrome.
1098 // We have to be careful to not pass aContent as the context here.
1099 // Otherwise, if there is a JS-implemented content policy, we will attempt
1100 // to wrap the content node, which will try to load XBL bindings for it, if
1101 // any. Since we're not done loading this binding yet, that will reenter
1102 // this method and we'll end up creating a binding and then immediately
1103 // clobbering it in our table. That makes things very confused, leading to
1104 // misbehavior and crashes.
1105 rv = nsContentUtils::
1106 CheckSecurityBeforeLoad(aBindingURI, aOriginPrincipal,
1107 nsIScriptSecurityManager::ALLOW_CHROME,
1108 gAllowDataURIs,
1109 nsIContentPolicy::TYPE_XBL,
1110 aBoundDocument);
1111 NS_ENSURE_SUCCESS(rv, NS_ERROR_XBL_BLOCKED);
1113 if (!IsSystemOrChromeURLPrincipal(aOriginPrincipal)) {
1114 // Also make sure that we're same-origin with the bound document
1115 // except if the stylesheet has the system principal.
1116 if (!(gAllowDataURIs && SchemeIs(aBindingURI, "data")) &&
1117 !SchemeIs(aBindingURI, "chrome")) {
1118 rv = aBoundDocument->NodePrincipal()->CheckMayLoad(aBindingURI,
1119 PR_TRUE);
1120 NS_ENSURE_SUCCESS(rv, NS_ERROR_XBL_BLOCKED);
1123 // Finally check if this document is allowed to use XBL at all.
1124 NS_ENSURE_TRUE(aBoundDocument->AllowXULXBL(),
1125 NS_ERROR_XBL_BLOCKED);
1129 *aResult = nsnull;
1130 nsRefPtr<nsXBLDocumentInfo> info;
1132 nsCOMPtr<nsIURI> documentURI;
1133 rv = aBindingURI->Clone(getter_AddRefs(documentURI));
1134 NS_ENSURE_SUCCESS(rv, rv);
1136 nsCOMPtr<nsIURL> documentURL(do_QueryInterface(documentURI));
1137 if (documentURL)
1138 documentURL->SetRef(EmptyCString());
1140 #ifdef MOZ_XUL
1141 // We've got a file. Check our XBL document cache.
1142 nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1143 PRBool useXULCache = cache && cache->IsEnabled();
1145 if (useXULCache) {
1146 // The first line of defense is the chrome cache.
1147 // This cache crosses the entire product, so that any XBL bindings that are
1148 // part of chrome will be reused across all XUL documents.
1149 info = cache->GetXBLDocumentInfo(documentURI);
1151 #endif
1153 if (!info) {
1154 // The second line of defense is the binding manager's document table.
1155 nsBindingManager *bindingManager = nsnull;
1157 if (aBoundDocument) {
1158 bindingManager = aBoundDocument->BindingManager();
1159 info = bindingManager->GetXBLDocumentInfo(documentURI);
1162 nsINodeInfo *ni = nsnull;
1163 if (aBoundElement)
1164 ni = aBoundElement->NodeInfo();
1166 if (!info && bindingManager &&
1167 (!ni || !(ni->Equals(nsGkAtoms::scrollbar, kNameSpaceID_XUL) ||
1168 ni->Equals(nsGkAtoms::thumb, kNameSpaceID_XUL) ||
1169 ((ni->Equals(nsGkAtoms::input) ||
1170 ni->Equals(nsGkAtoms::select)) &&
1171 aBoundElement->IsHTML()))) && !aForceSyncLoad) {
1172 // The third line of defense is to investigate whether or not the
1173 // document is currently being loaded asynchronously. If so, there's no
1174 // document yet, but we need to glom on our request so that it will be
1175 // processed whenever the doc does finish loading.
1176 nsCOMPtr<nsIStreamListener> listener;
1177 if (bindingManager)
1178 listener = bindingManager->GetLoadingDocListener(documentURI);
1179 if (listener) {
1180 nsXBLStreamListener* xblListener =
1181 static_cast<nsXBLStreamListener*>(listener.get());
1182 // Create a new load observer.
1183 if (!xblListener->HasRequest(aBindingURI, aBoundElement)) {
1184 nsXBLBindingRequest* req = nsXBLBindingRequest::Create(mPool, aBindingURI, aBoundElement);
1185 xblListener->AddRequest(req);
1187 return NS_OK;
1191 if (!info) {
1192 // Finally, if all lines of defense fail, we go and fetch the binding
1193 // document.
1195 // Always load chrome synchronously
1196 PRBool chrome;
1197 if (NS_SUCCEEDED(documentURI->SchemeIs("chrome", &chrome)) && chrome)
1198 aForceSyncLoad = PR_TRUE;
1200 nsCOMPtr<nsIDocument> document;
1201 FetchBindingDocument(aBoundElement, aBoundDocument, documentURI,
1202 aBindingURI, aForceSyncLoad, getter_AddRefs(document));
1204 if (document) {
1205 nsBindingManager *xblDocBindingManager = document->BindingManager();
1206 info = xblDocBindingManager->GetXBLDocumentInfo(documentURI);
1207 if (!info) {
1208 NS_ERROR("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
1209 return NS_ERROR_FAILURE;
1211 xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
1213 // If the doc is a chrome URI, then we put it into the XUL cache.
1214 #ifdef MOZ_XUL
1215 if (useXULCache && IsChromeOrResourceURI(documentURI)) {
1216 cache->PutXBLDocumentInfo(info);
1218 #endif
1220 if (bindingManager) {
1221 // Also put it in our binding manager's document table.
1222 bindingManager->PutXBLDocumentInfo(info);
1228 if (!info)
1229 return NS_OK;
1231 *aResult = info;
1232 NS_IF_ADDREF(*aResult);
1234 return NS_OK;
1237 nsresult
1238 nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
1239 nsIURI* aDocumentURI, nsIURI* aBindingURI,
1240 PRBool aForceSyncLoad, nsIDocument** aResult)
1242 NS_TIME_FUNCTION;
1244 nsresult rv = NS_OK;
1245 // Initialize our out pointer to nsnull
1246 *aResult = nsnull;
1248 // Now we have to synchronously load the binding file.
1249 // Create an XML content sink and a parser.
1250 nsCOMPtr<nsILoadGroup> loadGroup;
1251 if (aBoundDocument)
1252 loadGroup = aBoundDocument->GetDocumentLoadGroup();
1254 // We really shouldn't have to force a sync load for anything here... could
1255 // we get away with not doing that? Not sure.
1256 if (IsChromeOrResourceURI(aDocumentURI))
1257 aForceSyncLoad = PR_TRUE;
1259 // Create document and contentsink and set them up.
1260 nsCOMPtr<nsIDocument> doc;
1261 rv = NS_NewXMLDocument(getter_AddRefs(doc));
1262 NS_ENSURE_SUCCESS(rv, rv);
1264 nsCOMPtr<nsIXMLContentSink> xblSink;
1265 rv = NS_NewXBLContentSink(getter_AddRefs(xblSink), doc, aDocumentURI, nsnull);
1266 NS_ENSURE_SUCCESS(rv, rv);
1268 // Open channel
1269 nsCOMPtr<nsIChannel> channel;
1270 rv = NS_NewChannel(getter_AddRefs(channel), aDocumentURI, nsnull, loadGroup);
1271 NS_ENSURE_SUCCESS(rv, rv);
1273 nsCOMPtr<nsIInterfaceRequestor> sameOriginChecker = nsContentUtils::GetSameOriginChecker();
1274 NS_ENSURE_TRUE(sameOriginChecker, NS_ERROR_OUT_OF_MEMORY);
1276 channel->SetNotificationCallbacks(sameOriginChecker);
1278 if (!aForceSyncLoad) {
1279 // We can be asynchronous
1280 nsXBLStreamListener* xblListener =
1281 new nsXBLStreamListener(this, aBoundDocument, xblSink, doc);
1282 NS_ENSURE_TRUE(xblListener,NS_ERROR_OUT_OF_MEMORY);
1284 // Add ourselves to the list of loading docs.
1285 nsBindingManager *bindingManager;
1286 if (aBoundDocument)
1287 bindingManager = aBoundDocument->BindingManager();
1288 else
1289 bindingManager = nsnull;
1291 if (bindingManager)
1292 bindingManager->PutLoadingDocListener(aDocumentURI, xblListener);
1294 // Add our request.
1295 nsXBLBindingRequest* req = nsXBLBindingRequest::Create(mPool,
1296 aBindingURI,
1297 aBoundElement);
1298 xblListener->AddRequest(req);
1300 // Now kick off the async read.
1301 rv = channel->AsyncOpen(xblListener, nsnull);
1302 if (NS_FAILED(rv)) {
1303 // Well, we won't be getting a load. Make sure to clean up our stuff!
1304 if (bindingManager) {
1305 bindingManager->RemoveLoadingDocListener(aDocumentURI);
1308 return NS_OK;
1311 nsCOMPtr<nsIStreamListener> listener;
1312 rv = doc->StartDocumentLoad("loadAsInteractiveData",
1313 channel,
1314 loadGroup,
1315 nsnull,
1316 getter_AddRefs(listener),
1317 PR_TRUE,
1318 xblSink);
1319 NS_ENSURE_SUCCESS(rv, rv);
1321 // Now do a blocking synchronous parse of the file.
1322 nsCOMPtr<nsIInputStream> in;
1323 rv = channel->Open(getter_AddRefs(in));
1324 NS_ENSURE_SUCCESS(rv, rv);
1326 rv = nsSyncLoadService::PushSyncStreamToListener(in, listener, channel);
1327 NS_ENSURE_SUCCESS(rv, rv);
1329 doc.swap(*aResult);
1331 return NS_OK;
1334 // Creation Routine ///////////////////////////////////////////////////////////////////////
1336 nsresult NS_NewXBLService(nsIXBLService** aResult);
1338 nsresult
1339 NS_NewXBLService(nsIXBLService** aResult)
1341 nsXBLService* result = new nsXBLService;
1342 if (! result)
1343 return NS_ERROR_OUT_OF_MEMORY;
1345 NS_ADDREF(*aResult = result);
1347 // Register the first (and only) nsXBLService as a memory pressure observer
1348 // so it can flush the LRU list in low-memory situations.
1349 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1350 if (os)
1351 os->AddObserver(result, "memory-pressure", PR_TRUE);
1353 return NS_OK;