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 : * 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>
27 : *
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.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include "mozilla/Util.h"
43 :
44 : #include "nsCOMPtr.h"
45 : #include "nsNetUtil.h"
46 : #include "nsXBLService.h"
47 : #include "nsXBLWindowKeyHandler.h"
48 : #include "nsIInputStream.h"
49 : #include "nsINameSpaceManager.h"
50 : #include "nsHashtable.h"
51 : #include "nsIURI.h"
52 : #include "nsIDOMElement.h"
53 : #include "nsIURL.h"
54 : #include "nsIChannel.h"
55 : #include "nsXPIDLString.h"
56 : #include "nsIParser.h"
57 : #include "nsParserCIID.h"
58 : #include "nsNetUtil.h"
59 : #include "plstr.h"
60 : #include "nsIContent.h"
61 : #include "nsIDOMElement.h"
62 : #include "nsIDocument.h"
63 : #include "nsIXMLContentSink.h"
64 : #include "nsContentCID.h"
65 : #include "nsXMLDocument.h"
66 : #include "mozilla/FunctionTimer.h"
67 : #include "nsGkAtoms.h"
68 : #include "nsIMemory.h"
69 : #include "nsIObserverService.h"
70 : #include "nsIDOMNodeList.h"
71 : #include "nsXBLContentSink.h"
72 : #include "nsXBLBinding.h"
73 : #include "nsXBLPrototypeBinding.h"
74 : #include "nsXBLDocumentInfo.h"
75 : #include "nsCRT.h"
76 : #include "nsContentUtils.h"
77 : #include "nsSyncLoadService.h"
78 : #include "nsContentPolicyUtils.h"
79 : #include "nsTArray.h"
80 : #include "nsContentErrors.h"
81 :
82 : #include "nsIPresShell.h"
83 : #include "nsIDocumentObserver.h"
84 : #include "nsFrameManager.h"
85 : #include "nsStyleContext.h"
86 : #include "nsIScriptSecurityManager.h"
87 : #include "nsIScriptError.h"
88 : #include "nsXBLSerialize.h"
89 :
90 : #ifdef MOZ_XUL
91 : #include "nsXULPrototypeCache.h"
92 : #endif
93 : #include "nsIDOMEventListener.h"
94 : #include "mozilla/Preferences.h"
95 : #include "mozilla/dom/Element.h"
96 :
97 : using namespace mozilla;
98 :
99 : #define NS_MAX_XBL_BINDING_RECURSION 20
100 :
101 : static bool
102 0 : IsAncestorBinding(nsIDocument* aDocument,
103 : nsIURI* aChildBindingURI,
104 : nsIContent* aChild)
105 : {
106 0 : NS_ASSERTION(aDocument, "expected a document");
107 0 : NS_ASSERTION(aChildBindingURI, "expected a binding URI");
108 0 : NS_ASSERTION(aChild, "expected a child content");
109 :
110 0 : PRUint32 bindingRecursion = 0;
111 0 : nsBindingManager* bindingManager = aDocument->BindingManager();
112 0 : for (nsIContent *bindingParent = aChild->GetBindingParent();
113 : bindingParent;
114 0 : bindingParent = bindingParent->GetBindingParent()) {
115 0 : nsXBLBinding* binding = bindingManager->GetBinding(bindingParent);
116 0 : if (!binding) {
117 0 : continue;
118 : }
119 :
120 0 : if (binding->PrototypeBinding()->CompareBindingURI(aChildBindingURI)) {
121 0 : ++bindingRecursion;
122 0 : if (bindingRecursion < NS_MAX_XBL_BINDING_RECURSION) {
123 0 : continue;
124 : }
125 0 : nsCAutoString spec;
126 0 : aChildBindingURI->GetSpec(spec);
127 0 : NS_ConvertUTF8toUTF16 bindingURI(spec);
128 0 : const PRUnichar* params[] = { bindingURI.get() };
129 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
130 : "XBL", aDocument,
131 : nsContentUtils::eXBL_PROPERTIES,
132 : "TooDeepBindingRecursion",
133 0 : params, ArrayLength(params));
134 0 : return true;
135 : }
136 : }
137 :
138 0 : return false;
139 : }
140 :
141 : // Individual binding requests.
142 : class nsXBLBindingRequest
143 : {
144 : public:
145 : nsCOMPtr<nsIURI> mBindingURI;
146 : nsCOMPtr<nsIContent> mBoundElement;
147 :
148 : static nsXBLBindingRequest*
149 0 : Create(nsFixedSizeAllocator& aPool, nsIURI* aURI, nsIContent* aBoundElement) {
150 0 : void* place = aPool.Alloc(sizeof(nsXBLBindingRequest));
151 0 : return place ? ::new (place) nsXBLBindingRequest(aURI, aBoundElement) : nsnull;
152 : }
153 :
154 : static void
155 0 : Destroy(nsFixedSizeAllocator& aPool, nsXBLBindingRequest* aRequest) {
156 0 : aRequest->~nsXBLBindingRequest();
157 0 : aPool.Free(aRequest, sizeof(*aRequest));
158 0 : }
159 :
160 0 : void DocumentLoaded(nsIDocument* aBindingDoc)
161 : {
162 : // We only need the document here to cause frame construction, so
163 : // we need the current doc, not the owner doc.
164 0 : nsIDocument* doc = mBoundElement->GetCurrentDoc();
165 0 : if (!doc)
166 0 : return;
167 :
168 : // Get the binding.
169 0 : bool ready = false;
170 0 : gXBLService->BindingReady(mBoundElement, mBindingURI, &ready);
171 :
172 0 : if (!ready)
173 0 : return;
174 :
175 : // If |mBoundElement| is (in addition to having binding |mBinding|)
176 : // also a descendant of another element with binding |mBinding|,
177 : // then we might have just constructed it due to the
178 : // notification of its parent. (We can know about both if the
179 : // binding loads were triggered from the DOM rather than frame
180 : // construction.) So we have to check both whether the element
181 : // has a primary frame and whether it's in the undisplayed map
182 : // before sending a ContentInserted notification, or bad things
183 : // will happen.
184 0 : nsIPresShell *shell = doc->GetShell();
185 0 : if (shell) {
186 0 : nsIFrame* childFrame = mBoundElement->GetPrimaryFrame();
187 0 : if (!childFrame) {
188 : // Check to see if it's in the undisplayed content map.
189 : nsStyleContext* sc =
190 0 : shell->FrameManager()->GetUndisplayedContent(mBoundElement);
191 :
192 0 : if (!sc) {
193 0 : shell->RecreateFramesFor(mBoundElement);
194 : }
195 : }
196 : }
197 : }
198 :
199 : static nsIXBLService* gXBLService;
200 : static int gRefCnt;
201 :
202 : protected:
203 0 : nsXBLBindingRequest(nsIURI* aURI, nsIContent* aBoundElement)
204 : : mBindingURI(aURI),
205 0 : mBoundElement(aBoundElement)
206 : {
207 0 : gRefCnt++;
208 0 : if (gRefCnt == 1) {
209 0 : CallGetService("@mozilla.org/xbl;1", &gXBLService);
210 : }
211 0 : }
212 :
213 0 : ~nsXBLBindingRequest()
214 0 : {
215 0 : gRefCnt--;
216 0 : if (gRefCnt == 0) {
217 0 : NS_IF_RELEASE(gXBLService);
218 : }
219 0 : }
220 :
221 : private:
222 : // Hide so that only Create() and Destroy() can be used to
223 : // allocate and deallocate from the heap
224 : static void* operator new(size_t) CPP_THROW_NEW { return 0; }
225 : static void operator delete(void*, size_t) {}
226 : };
227 :
228 : static const size_t kBucketSizes[] = {
229 : sizeof(nsXBLBindingRequest)
230 : };
231 :
232 : static const PRInt32 kNumBuckets = sizeof(kBucketSizes)/sizeof(size_t);
233 : static const PRInt32 kNumElements = 64;
234 : static const PRInt32 kInitialSize = (NS_SIZE_IN_HEAP(sizeof(nsXBLBindingRequest))) * kNumElements;
235 :
236 : nsIXBLService* nsXBLBindingRequest::gXBLService = nsnull;
237 : int nsXBLBindingRequest::gRefCnt = 0;
238 :
239 : // nsXBLStreamListener, a helper class used for
240 : // asynchronous parsing of URLs
241 : /* Header file */
242 : class nsXBLStreamListener : public nsIStreamListener, public nsIDOMEventListener
243 : {
244 : public:
245 : NS_DECL_ISUPPORTS
246 : NS_DECL_NSISTREAMLISTENER
247 : NS_DECL_NSIREQUESTOBSERVER
248 : NS_DECL_NSIDOMEVENTLISTENER
249 :
250 : nsXBLStreamListener(nsXBLService* aXBLService,
251 : nsIDocument* aBoundDocument,
252 : nsIXMLContentSink* aSink,
253 : nsIDocument* aBindingDocument);
254 : ~nsXBLStreamListener();
255 :
256 0 : void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); }
257 : bool HasRequest(nsIURI* aURI, nsIContent* aBoundElement);
258 :
259 : private:
260 : nsXBLService* mXBLService; // [WEAK]
261 :
262 : nsCOMPtr<nsIStreamListener> mInner;
263 : nsAutoTArray<nsXBLBindingRequest*, 8> mBindingRequests;
264 :
265 : nsCOMPtr<nsIWeakReference> mBoundDocument;
266 : nsCOMPtr<nsIXMLContentSink> mSink; // Only set until OnStartRequest
267 : nsCOMPtr<nsIDocument> mBindingDocument; // Only set until OnStartRequest
268 : };
269 :
270 : /* Implementation file */
271 0 : NS_IMPL_ISUPPORTS3(nsXBLStreamListener,
272 : nsIStreamListener,
273 : nsIRequestObserver,
274 : nsIDOMEventListener)
275 :
276 0 : nsXBLStreamListener::nsXBLStreamListener(nsXBLService* aXBLService,
277 : nsIDocument* aBoundDocument,
278 : nsIXMLContentSink* aSink,
279 : nsIDocument* aBindingDocument)
280 0 : : mSink(aSink), mBindingDocument(aBindingDocument)
281 : {
282 : /* member initializers and constructor code */
283 0 : mXBLService = aXBLService;
284 0 : mBoundDocument = do_GetWeakReference(aBoundDocument);
285 0 : }
286 :
287 0 : nsXBLStreamListener::~nsXBLStreamListener()
288 : {
289 0 : for (PRUint32 i = 0; i < mBindingRequests.Length(); i++) {
290 0 : nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
291 0 : nsXBLBindingRequest::Destroy(mXBLService->mPool, req);
292 : }
293 0 : }
294 :
295 : NS_IMETHODIMP
296 0 : nsXBLStreamListener::OnDataAvailable(nsIRequest *request, nsISupports* aCtxt, nsIInputStream* aInStr,
297 : PRUint32 aSourceOffset, PRUint32 aCount)
298 : {
299 0 : if (mInner)
300 0 : return mInner->OnDataAvailable(request, aCtxt, aInStr, aSourceOffset, aCount);
301 0 : return NS_ERROR_FAILURE;
302 : }
303 :
304 : NS_IMETHODIMP
305 0 : nsXBLStreamListener::OnStartRequest(nsIRequest* request, nsISupports* aCtxt)
306 : {
307 : // Make sure we don't hold on to the sink and binding document past this point
308 0 : nsCOMPtr<nsIXMLContentSink> sink;
309 0 : mSink.swap(sink);
310 0 : nsCOMPtr<nsIDocument> doc;
311 0 : mBindingDocument.swap(doc);
312 :
313 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
314 0 : NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
315 :
316 0 : nsCOMPtr<nsILoadGroup> group;
317 0 : request->GetLoadGroup(getter_AddRefs(group));
318 :
319 0 : nsresult rv = doc->StartDocumentLoad("loadAsInteractiveData",
320 : channel,
321 : group,
322 : nsnull,
323 0 : getter_AddRefs(mInner),
324 : true,
325 0 : sink);
326 0 : NS_ENSURE_SUCCESS(rv, rv);
327 :
328 : // Make sure to add ourselves as a listener after StartDocumentLoad,
329 : // since that resets the event listners on the document.
330 0 : nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(doc));
331 0 : target->AddEventListener(NS_LITERAL_STRING("load"), this, false);
332 :
333 0 : return mInner->OnStartRequest(request, aCtxt);
334 : }
335 :
336 : NS_IMETHODIMP
337 0 : nsXBLStreamListener::OnStopRequest(nsIRequest* request, nsISupports* aCtxt, nsresult aStatus)
338 : {
339 0 : nsresult rv = NS_OK;
340 0 : if (mInner) {
341 0 : rv = mInner->OnStopRequest(request, aCtxt, aStatus);
342 : }
343 :
344 : // Don't hold onto the inner listener; holding onto it can create a cycle
345 : // with the document
346 0 : mInner = nsnull;
347 :
348 0 : return rv;
349 : }
350 :
351 : bool
352 0 : nsXBLStreamListener::HasRequest(nsIURI* aURI, nsIContent* aElt)
353 : {
354 : // XXX Could be more efficient.
355 0 : PRUint32 count = mBindingRequests.Length();
356 0 : for (PRUint32 i = 0; i < count; i++) {
357 0 : nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
358 : bool eq;
359 0 : if (req->mBoundElement == aElt &&
360 0 : NS_SUCCEEDED(req->mBindingURI->Equals(aURI, &eq)) && eq)
361 0 : return true;
362 : }
363 :
364 0 : return false;
365 : }
366 :
367 : nsresult
368 0 : nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent)
369 : {
370 0 : nsresult rv = NS_OK;
371 : PRUint32 i;
372 0 : PRUint32 count = mBindingRequests.Length();
373 :
374 : // Get the binding document; note that we don't hold onto it in this object
375 : // to avoid creating a cycle
376 0 : nsCOMPtr<nsIDOMEventTarget> target;
377 0 : aEvent->GetCurrentTarget(getter_AddRefs(target));
378 0 : nsCOMPtr<nsIDocument> bindingDocument = do_QueryInterface(target);
379 0 : NS_ASSERTION(bindingDocument, "Event not targeted at document?!");
380 :
381 : // See if we're still alive.
382 0 : nsCOMPtr<nsIDocument> doc(do_QueryReferent(mBoundDocument));
383 0 : if (!doc) {
384 0 : NS_WARNING("XBL load did not complete until after document went away! Modal dialog bug?\n");
385 : }
386 : else {
387 : // We have to do a flush prior to notification of the document load.
388 : // This has to happen since the HTML content sink can be holding on
389 : // to notifications related to our children (e.g., if you bind to the
390 : // <body> tag) that result in duplication of content.
391 : // We need to get the sink's notifications flushed and then make the binding
392 : // ready.
393 0 : if (count > 0) {
394 0 : nsXBLBindingRequest* req = mBindingRequests.ElementAt(0);
395 0 : nsIDocument* document = req->mBoundElement->GetCurrentDoc();
396 0 : if (document)
397 0 : document->FlushPendingNotifications(Flush_ContentAndNotify);
398 : }
399 :
400 : // Remove ourselves from the set of pending docs.
401 0 : nsBindingManager *bindingManager = doc->BindingManager();
402 0 : nsIURI* documentURI = bindingDocument->GetDocumentURI();
403 0 : bindingManager->RemoveLoadingDocListener(documentURI);
404 :
405 0 : if (!bindingDocument->GetRootElement()) {
406 : // FIXME: How about an error console warning?
407 0 : NS_WARNING("*** XBL doc with no root element! Something went horribly wrong! ***");
408 0 : return NS_ERROR_FAILURE;
409 : }
410 :
411 : // Put our doc info in the doc table.
412 0 : nsBindingManager *xblDocBindingManager = bindingDocument->BindingManager();
413 : nsRefPtr<nsXBLDocumentInfo> info =
414 0 : xblDocBindingManager->GetXBLDocumentInfo(documentURI);
415 0 : xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
416 0 : if (!info) {
417 0 : if (nsXBLService::IsChromeOrResourceURI(documentURI)) {
418 0 : NS_WARNING("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
419 : }
420 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
421 : "XBL", nsnull,
422 : nsContentUtils::eXBL_PROPERTIES,
423 : "MalformedXBL",
424 0 : nsnull, 0, documentURI);
425 0 : return NS_ERROR_FAILURE;
426 : }
427 :
428 : // If the doc is a chrome URI, then we put it into the XUL cache.
429 : #ifdef MOZ_XUL
430 0 : if (nsXBLService::IsChromeOrResourceURI(documentURI)) {
431 0 : nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
432 0 : if (cache && cache->IsEnabled())
433 0 : cache->PutXBLDocumentInfo(info);
434 : }
435 : #endif
436 :
437 0 : bindingManager->PutXBLDocumentInfo(info);
438 :
439 : // Notify all pending requests that their bindings are
440 : // ready and can be installed.
441 0 : for (i = 0; i < count; i++) {
442 0 : nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
443 0 : req->DocumentLoaded(bindingDocument);
444 : }
445 : }
446 :
447 0 : target->RemoveEventListener(NS_LITERAL_STRING("load"), this, false);
448 :
449 0 : return rv;
450 : }
451 :
452 : // Implementation /////////////////////////////////////////////////////////////////
453 :
454 : // Static member variable initialization
455 : PRUint32 nsXBLService::gRefCnt = 0;
456 : bool nsXBLService::gAllowDataURIs = false;
457 :
458 : nsHashtable* nsXBLService::gClassTable = nsnull;
459 :
460 : JSCList nsXBLService::gClassLRUList = JS_INIT_STATIC_CLIST(&nsXBLService::gClassLRUList);
461 : PRUint32 nsXBLService::gClassLRUListLength = 0;
462 : PRUint32 nsXBLService::gClassLRUListQuota = 64;
463 :
464 : // Implement our nsISupports methods
465 0 : NS_IMPL_ISUPPORTS3(nsXBLService, nsIXBLService, nsIObserver, nsISupportsWeakReference)
466 :
467 : // Constructors/Destructors
468 0 : nsXBLService::nsXBLService(void)
469 : {
470 0 : mPool.Init("XBL Binding Requests", kBucketSizes, kNumBuckets, kInitialSize);
471 :
472 0 : gRefCnt++;
473 0 : if (gRefCnt == 1) {
474 0 : gClassTable = new nsHashtable();
475 : }
476 :
477 0 : Preferences::AddBoolVarCache(&gAllowDataURIs, "layout.debug.enable_data_xbl");
478 0 : }
479 :
480 0 : nsXBLService::~nsXBLService(void)
481 : {
482 0 : gRefCnt--;
483 0 : if (gRefCnt == 0) {
484 : // Walk the LRU list removing and deleting the nsXBLJSClasses.
485 0 : FlushMemory();
486 :
487 : // Any straggling nsXBLJSClass instances held by unfinalized JS objects
488 : // created for bindings will be deleted when those objects are finalized
489 : // (and not put on gClassLRUList, because length >= quota).
490 0 : gClassLRUListLength = gClassLRUListQuota = 0;
491 :
492 : // At this point, the only hash table entries should be for referenced
493 : // XBL class structs held by unfinalized JS binding objects.
494 0 : delete gClassTable;
495 0 : gClassTable = nsnull;
496 : }
497 0 : }
498 :
499 : // static
500 : bool
501 0 : nsXBLService::IsChromeOrResourceURI(nsIURI* aURI)
502 : {
503 0 : bool isChrome = false;
504 0 : bool isResource = false;
505 0 : if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) &&
506 0 : NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)))
507 0 : return (isChrome || isResource);
508 0 : return false;
509 : }
510 :
511 :
512 : // This function loads a particular XBL file and installs all of the bindings
513 : // onto the element.
514 : NS_IMETHODIMP
515 0 : nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL,
516 : nsIPrincipal* aOriginPrincipal, bool aAugmentFlag,
517 : nsXBLBinding** aBinding, bool* aResolveStyle)
518 : {
519 0 : NS_PRECONDITION(aOriginPrincipal, "Must have an origin principal");
520 :
521 0 : *aBinding = nsnull;
522 0 : *aResolveStyle = false;
523 :
524 : nsresult rv;
525 :
526 0 : nsCOMPtr<nsIDocument> document = aContent->OwnerDoc();
527 :
528 0 : nsCAutoString urlspec;
529 0 : if (nsContentUtils::GetWrapperSafeScriptFilename(document, aURL, urlspec)) {
530 : // Block an attempt to load a binding that has special wrapper
531 : // automation needs.
532 :
533 0 : return NS_OK;
534 : }
535 :
536 0 : nsBindingManager *bindingManager = document->BindingManager();
537 :
538 0 : nsXBLBinding *binding = bindingManager->GetBinding(aContent);
539 0 : if (binding && !aAugmentFlag) {
540 0 : nsXBLBinding *styleBinding = binding->GetFirstStyleBinding();
541 0 : if (styleBinding) {
542 0 : if (binding->MarkedForDeath()) {
543 0 : FlushStyleBindings(aContent);
544 0 : binding = nsnull;
545 : }
546 : else {
547 : // See if the URIs match.
548 0 : if (styleBinding->PrototypeBinding()->CompareBindingURI(aURL))
549 0 : return NS_OK;
550 0 : FlushStyleBindings(aContent);
551 0 : binding = nsnull;
552 : }
553 : }
554 : }
555 :
556 : bool ready;
557 0 : nsRefPtr<nsXBLBinding> newBinding;
558 0 : if (NS_FAILED(rv = GetBinding(aContent, aURL, false, aOriginPrincipal,
559 : &ready, getter_AddRefs(newBinding)))) {
560 0 : return rv;
561 : }
562 :
563 0 : if (!newBinding) {
564 : #ifdef DEBUG
565 0 : nsCAutoString spec;
566 0 : aURL->GetSpec(spec);
567 0 : 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);
568 0 : NS_ERROR(str.get());
569 : #endif
570 0 : return NS_OK;
571 : }
572 :
573 0 : if (::IsAncestorBinding(document, aURL, aContent)) {
574 0 : return NS_ERROR_ILLEGAL_VALUE;
575 : }
576 :
577 0 : if (aAugmentFlag) {
578 : nsXBLBinding *baseBinding;
579 0 : nsXBLBinding *nextBinding = newBinding;
580 0 : do {
581 0 : baseBinding = nextBinding;
582 0 : nextBinding = baseBinding->GetBaseBinding();
583 0 : baseBinding->SetIsStyleBinding(false);
584 : } while (nextBinding);
585 :
586 : // XXX Handle adjusting the prototype chain! We need to somehow indicate to
587 : // InstallImplementation that the whole chain should just be whacked and rebuilt.
588 : // We are becoming the new binding.
589 0 : baseBinding->SetBaseBinding(binding);
590 0 : bindingManager->SetBinding(aContent, newBinding);
591 : }
592 : else {
593 : // We loaded a style binding. It goes on the end.
594 0 : if (binding) {
595 : // Get the last binding that is in the append layer.
596 0 : binding->RootBinding()->SetBaseBinding(newBinding);
597 : }
598 : else {
599 : // Install the binding on the content node.
600 0 : bindingManager->SetBinding(aContent, newBinding);
601 : }
602 : }
603 :
604 : {
605 0 : nsAutoScriptBlocker scriptBlocker;
606 :
607 : // Set the binding's bound element.
608 0 : newBinding->SetBoundElement(aContent);
609 :
610 : // Tell the binding to build the anonymous content.
611 0 : newBinding->GenerateAnonymousContent();
612 :
613 : // Tell the binding to install event handlers
614 0 : newBinding->InstallEventHandlers();
615 :
616 : // Set up our properties
617 0 : rv = newBinding->InstallImplementation();
618 0 : NS_ENSURE_SUCCESS(rv, rv);
619 :
620 : // Figure out if we have any scoped sheets. If so, we do a second resolve.
621 0 : *aResolveStyle = newBinding->HasStyleSheets();
622 :
623 0 : newBinding.swap(*aBinding);
624 : }
625 :
626 0 : return NS_OK;
627 : }
628 :
629 : nsresult
630 0 : nsXBLService::FlushStyleBindings(nsIContent* aContent)
631 : {
632 0 : nsCOMPtr<nsIDocument> document = aContent->OwnerDoc();
633 :
634 0 : nsBindingManager *bindingManager = document->BindingManager();
635 :
636 0 : nsXBLBinding *binding = bindingManager->GetBinding(aContent);
637 :
638 0 : if (binding) {
639 0 : nsXBLBinding *styleBinding = binding->GetFirstStyleBinding();
640 :
641 0 : if (styleBinding) {
642 : // Clear out the script references.
643 0 : styleBinding->ChangeDocument(document, nsnull);
644 : }
645 :
646 0 : if (styleBinding == binding)
647 0 : bindingManager->SetBinding(aContent, nsnull); // Flush old style bindings
648 : }
649 :
650 0 : return NS_OK;
651 : }
652 :
653 : NS_IMETHODIMP
654 0 : nsXBLService::ResolveTag(nsIContent* aContent, PRInt32* aNameSpaceID,
655 : nsIAtom** aResult)
656 : {
657 0 : nsIDocument* document = aContent->OwnerDoc();
658 0 : *aResult = document->BindingManager()->ResolveTag(aContent, aNameSpaceID);
659 0 : NS_IF_ADDREF(*aResult);
660 :
661 0 : return NS_OK;
662 : }
663 :
664 :
665 : //
666 : // AttachGlobalKeyHandler
667 : //
668 : // Creates a new key handler and prepares to listen to key events on the given
669 : // event receiver (either a document or an content node). If the receiver is content,
670 : // then extra work needs to be done to hook it up to the document (XXX WHY??)
671 : //
672 : NS_IMETHODIMP
673 0 : nsXBLService::AttachGlobalKeyHandler(nsIDOMEventTarget* aTarget)
674 : {
675 : // check if the receiver is a content node (not a document), and hook
676 : // it to the document if that is the case.
677 0 : nsCOMPtr<nsIDOMEventTarget> piTarget = aTarget;
678 0 : nsCOMPtr<nsIContent> contentNode(do_QueryInterface(aTarget));
679 0 : if (contentNode) {
680 : // Only attach if we're really in a document
681 0 : nsCOMPtr<nsIDocument> doc = contentNode->GetCurrentDoc();
682 0 : if (doc)
683 0 : piTarget = doc; // We're a XUL keyset. Attach to our document.
684 : }
685 :
686 0 : nsEventListenerManager* manager = piTarget->GetListenerManager(true);
687 :
688 0 : if (!piTarget || !manager)
689 0 : return NS_ERROR_FAILURE;
690 :
691 : // the listener already exists, so skip this
692 0 : if (contentNode && contentNode->GetProperty(nsGkAtoms::listener))
693 0 : return NS_OK;
694 :
695 0 : nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(contentNode));
696 :
697 : // Create the key handler
698 : nsXBLWindowKeyHandler* handler;
699 0 : NS_NewXBLWindowKeyHandler(elt, piTarget, &handler); // This addRef's
700 0 : if (!handler)
701 0 : return NS_ERROR_FAILURE;
702 :
703 : // listen to these events
704 0 : manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keydown"),
705 : NS_EVENT_FLAG_BUBBLE |
706 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
707 0 : manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keyup"),
708 : NS_EVENT_FLAG_BUBBLE |
709 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
710 0 : manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
711 : NS_EVENT_FLAG_BUBBLE |
712 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
713 :
714 0 : if (contentNode)
715 0 : return contentNode->SetProperty(nsGkAtoms::listener, handler,
716 0 : nsPropertyTable::SupportsDtorFunc, true);
717 :
718 : // release the handler. The reference will be maintained by the event target,
719 : // and, if there is a content node, the property.
720 0 : NS_RELEASE(handler);
721 0 : return NS_OK;
722 : }
723 :
724 : //
725 : // DetachGlobalKeyHandler
726 : //
727 : // Removes a key handler added by DeatchGlobalKeyHandler.
728 : //
729 : NS_IMETHODIMP
730 0 : nsXBLService::DetachGlobalKeyHandler(nsIDOMEventTarget* aTarget)
731 : {
732 0 : nsCOMPtr<nsIDOMEventTarget> piTarget = aTarget;
733 0 : nsCOMPtr<nsIContent> contentNode(do_QueryInterface(aTarget));
734 0 : if (!contentNode) // detaching is only supported for content nodes
735 0 : return NS_ERROR_FAILURE;
736 :
737 : // Only attach if we're really in a document
738 0 : nsCOMPtr<nsIDocument> doc = contentNode->GetCurrentDoc();
739 0 : if (doc)
740 0 : piTarget = do_QueryInterface(doc);
741 :
742 0 : nsEventListenerManager* manager = piTarget->GetListenerManager(true);
743 :
744 0 : if (!piTarget || !manager)
745 0 : return NS_ERROR_FAILURE;
746 :
747 : nsIDOMEventListener* handler =
748 0 : static_cast<nsIDOMEventListener*>(contentNode->GetProperty(nsGkAtoms::listener));
749 0 : if (!handler)
750 0 : return NS_ERROR_FAILURE;
751 :
752 0 : manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keydown"),
753 : NS_EVENT_FLAG_BUBBLE |
754 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
755 0 : manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keyup"),
756 : NS_EVENT_FLAG_BUBBLE |
757 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
758 0 : manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
759 : NS_EVENT_FLAG_BUBBLE |
760 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
761 :
762 0 : contentNode->DeleteProperty(nsGkAtoms::listener);
763 :
764 0 : return NS_OK;
765 : }
766 :
767 : NS_IMETHODIMP
768 0 : nsXBLService::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData)
769 : {
770 0 : if (nsCRT::strcmp(aTopic, "memory-pressure") == 0)
771 0 : FlushMemory();
772 :
773 0 : return NS_OK;
774 : }
775 :
776 : nsresult
777 0 : nsXBLService::FlushMemory()
778 : {
779 0 : while (!JS_CLIST_IS_EMPTY(&gClassLRUList)) {
780 0 : JSCList* lru = gClassLRUList.next;
781 0 : nsXBLJSClass* c = static_cast<nsXBLJSClass*>(lru);
782 :
783 0 : JS_REMOVE_AND_INIT_LINK(lru);
784 0 : delete c;
785 0 : gClassLRUListLength--;
786 : }
787 0 : return NS_OK;
788 : }
789 :
790 : // Internal helper methods ////////////////////////////////////////////////////////////////
791 :
792 0 : NS_IMETHODIMP nsXBLService::BindingReady(nsIContent* aBoundElement,
793 : nsIURI* aURI,
794 : bool* aIsReady)
795 : {
796 : // Don't do a security check here; we know this binding is set to go.
797 0 : return GetBinding(aBoundElement, aURI, true, nsnull, aIsReady, nsnull);
798 : }
799 :
800 : nsresult
801 0 : nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
802 : bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
803 : bool* aIsReady, nsXBLBinding** aResult)
804 : {
805 : // More than 6 binding URIs are rare, see bug 55070 comment 18.
806 0 : nsAutoTArray<nsIURI*, 6> uris;
807 : return GetBinding(aBoundElement, aURI, aPeekOnly, aOriginPrincipal, aIsReady,
808 0 : aResult, uris);
809 : }
810 :
811 : nsresult
812 0 : nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
813 : bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
814 : bool* aIsReady, nsXBLBinding** aResult,
815 : nsTArray<nsIURI*>& aDontExtendURIs)
816 : {
817 0 : NS_ASSERTION(aPeekOnly || aResult,
818 : "Must have non-null out param if not just peeking to see "
819 : "whether the binding is ready");
820 :
821 0 : if (aResult)
822 0 : *aResult = nsnull;
823 :
824 0 : if (!aURI)
825 0 : return NS_ERROR_FAILURE;
826 :
827 0 : nsCAutoString ref;
828 0 : aURI->GetRef(ref);
829 :
830 0 : nsCOMPtr<nsIDocument> boundDocument = aBoundElement->OwnerDoc();
831 :
832 0 : nsRefPtr<nsXBLDocumentInfo> docInfo;
833 : nsresult rv = LoadBindingDocumentInfo(aBoundElement, boundDocument, aURI,
834 : aOriginPrincipal,
835 0 : false, getter_AddRefs(docInfo));
836 0 : NS_ENSURE_SUCCESS(rv, rv);
837 :
838 0 : if (!docInfo)
839 0 : return NS_ERROR_FAILURE;
840 :
841 0 : nsXBLPrototypeBinding* protoBinding = docInfo->GetPrototypeBinding(ref);
842 :
843 0 : NS_WARN_IF_FALSE(protoBinding, "Unable to locate an XBL binding");
844 0 : if (!protoBinding)
845 0 : return NS_ERROR_FAILURE;
846 :
847 0 : NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(protoBinding->BindingURI()),
848 : NS_ERROR_OUT_OF_MEMORY);
849 0 : nsCOMPtr<nsIURI> altBindingURI = protoBinding->AlternateBindingURI();
850 0 : if (altBindingURI) {
851 0 : NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(altBindingURI),
852 : NS_ERROR_OUT_OF_MEMORY);
853 : }
854 :
855 : // Our prototype binding must have all its resources loaded.
856 0 : bool ready = protoBinding->LoadResources();
857 0 : if (!ready) {
858 : // Add our bound element to the protos list of elts that should
859 : // be notified when the stylesheets and scripts finish loading.
860 0 : protoBinding->AddResourceListener(aBoundElement);
861 0 : return NS_ERROR_FAILURE; // The binding isn't ready yet.
862 : }
863 :
864 0 : rv = protoBinding->ResolveBaseBinding();
865 0 : NS_ENSURE_SUCCESS(rv, rv);
866 :
867 : nsIURI* baseBindingURI;
868 0 : nsXBLPrototypeBinding* baseProto = protoBinding->GetBasePrototype();
869 0 : if (baseProto) {
870 0 : baseBindingURI = baseProto->BindingURI();
871 : }
872 : else {
873 0 : baseBindingURI = protoBinding->GetBaseBindingURI();
874 0 : if (baseBindingURI) {
875 0 : PRUint32 count = aDontExtendURIs.Length();
876 0 : for (PRUint32 index = 0; index < count; ++index) {
877 : bool equal;
878 0 : rv = aDontExtendURIs[index]->Equals(baseBindingURI, &equal);
879 0 : NS_ENSURE_SUCCESS(rv, rv);
880 0 : if (equal) {
881 0 : nsCAutoString spec, basespec;
882 0 : protoBinding->BindingURI()->GetSpec(spec);
883 0 : NS_ConvertUTF8toUTF16 protoSpec(spec);
884 0 : baseBindingURI->GetSpec(basespec);
885 0 : NS_ConvertUTF8toUTF16 baseSpecUTF16(basespec);
886 0 : const PRUnichar* params[] = { protoSpec.get(), baseSpecUTF16.get() };
887 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
888 : "XBL", nsnull,
889 : nsContentUtils::eXBL_PROPERTIES,
890 : "CircularExtendsBinding",
891 : params, ArrayLength(params),
892 0 : boundDocument->GetDocumentURI());
893 0 : return NS_ERROR_ILLEGAL_VALUE;
894 : }
895 : }
896 : }
897 : }
898 :
899 0 : nsRefPtr<nsXBLBinding> baseBinding;
900 0 : if (baseBindingURI) {
901 0 : nsCOMPtr<nsIContent> child = protoBinding->GetBindingElement();
902 : rv = GetBinding(aBoundElement, baseBindingURI, aPeekOnly,
903 0 : child->NodePrincipal(), aIsReady,
904 0 : getter_AddRefs(baseBinding), aDontExtendURIs);
905 0 : if (NS_FAILED(rv))
906 0 : return rv; // We aren't ready yet.
907 : }
908 :
909 0 : *aIsReady = true;
910 :
911 0 : if (!aPeekOnly) {
912 : // Make a new binding
913 0 : nsXBLBinding *newBinding = new nsXBLBinding(protoBinding);
914 0 : NS_ENSURE_TRUE(newBinding, NS_ERROR_OUT_OF_MEMORY);
915 :
916 0 : if (baseBinding) {
917 0 : if (!baseProto) {
918 0 : protoBinding->SetBasePrototype(baseBinding->PrototypeBinding());
919 : }
920 0 : newBinding->SetBaseBinding(baseBinding);
921 : }
922 :
923 0 : NS_ADDREF(*aResult = newBinding);
924 : }
925 :
926 0 : return NS_OK;
927 : }
928 :
929 0 : static bool SchemeIs(nsIURI* aURI, const char* aScheme)
930 : {
931 0 : nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
932 0 : NS_ENSURE_TRUE(baseURI, false);
933 :
934 0 : bool isScheme = false;
935 0 : return NS_SUCCEEDED(baseURI->SchemeIs(aScheme, &isScheme)) && isScheme;
936 : }
937 :
938 : static bool
939 0 : IsSystemOrChromeURLPrincipal(nsIPrincipal* aPrincipal)
940 : {
941 0 : if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
942 0 : return true;
943 : }
944 :
945 0 : nsCOMPtr<nsIURI> uri;
946 0 : aPrincipal->GetURI(getter_AddRefs(uri));
947 0 : NS_ENSURE_TRUE(uri, false);
948 :
949 0 : bool isChrome = false;
950 0 : return NS_SUCCEEDED(uri->SchemeIs("chrome", &isChrome)) && isChrome;
951 : }
952 :
953 : NS_IMETHODIMP
954 0 : nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
955 : nsIDocument* aBoundDocument,
956 : nsIURI* aBindingURI,
957 : nsIPrincipal* aOriginPrincipal,
958 : bool aForceSyncLoad,
959 : nsXBLDocumentInfo** aResult)
960 : {
961 0 : NS_PRECONDITION(aBindingURI, "Must have a binding URI");
962 0 : NS_PRECONDITION(!aOriginPrincipal || aBoundDocument,
963 : "If we're doing a security check, we better have a document!");
964 :
965 : nsresult rv;
966 0 : if (aOriginPrincipal) {
967 : // Security check - Enforce same-origin policy, except to chrome.
968 : // We have to be careful to not pass aContent as the context here.
969 : // Otherwise, if there is a JS-implemented content policy, we will attempt
970 : // to wrap the content node, which will try to load XBL bindings for it, if
971 : // any. Since we're not done loading this binding yet, that will reenter
972 : // this method and we'll end up creating a binding and then immediately
973 : // clobbering it in our table. That makes things very confused, leading to
974 : // misbehavior and crashes.
975 : rv = nsContentUtils::
976 : CheckSecurityBeforeLoad(aBindingURI, aOriginPrincipal,
977 : nsIScriptSecurityManager::ALLOW_CHROME,
978 : gAllowDataURIs,
979 : nsIContentPolicy::TYPE_XBL,
980 0 : aBoundDocument);
981 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_XBL_BLOCKED);
982 :
983 0 : if (!IsSystemOrChromeURLPrincipal(aOriginPrincipal)) {
984 : // Also make sure that we're same-origin with the bound document
985 : // except if the stylesheet has the system principal.
986 0 : if (!(gAllowDataURIs && SchemeIs(aBindingURI, "data")) &&
987 0 : !SchemeIs(aBindingURI, "chrome")) {
988 0 : rv = aBoundDocument->NodePrincipal()->CheckMayLoad(aBindingURI,
989 0 : true);
990 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_XBL_BLOCKED);
991 : }
992 :
993 : // Finally check if this document is allowed to use XBL at all.
994 0 : NS_ENSURE_TRUE(aBoundDocument->AllowXULXBL(),
995 : NS_ERROR_XBL_BLOCKED);
996 : }
997 : }
998 :
999 0 : *aResult = nsnull;
1000 0 : nsRefPtr<nsXBLDocumentInfo> info;
1001 :
1002 0 : nsCOMPtr<nsIURI> documentURI;
1003 0 : rv = aBindingURI->CloneIgnoringRef(getter_AddRefs(documentURI));
1004 0 : NS_ENSURE_SUCCESS(rv, rv);
1005 :
1006 : #ifdef MOZ_XUL
1007 : // We've got a file. Check our XBL document cache.
1008 0 : nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1009 0 : bool useXULCache = cache && cache->IsEnabled();
1010 :
1011 0 : if (useXULCache) {
1012 : // The first line of defense is the chrome cache.
1013 : // This cache crosses the entire product, so that any XBL bindings that are
1014 : // part of chrome will be reused across all XUL documents.
1015 0 : info = cache->GetXBLDocumentInfo(documentURI);
1016 : }
1017 : #endif
1018 :
1019 0 : if (!info) {
1020 : // The second line of defense is the binding manager's document table.
1021 0 : nsBindingManager *bindingManager = nsnull;
1022 :
1023 0 : if (aBoundDocument) {
1024 0 : bindingManager = aBoundDocument->BindingManager();
1025 0 : info = bindingManager->GetXBLDocumentInfo(documentURI);
1026 : }
1027 :
1028 0 : nsINodeInfo *ni = nsnull;
1029 0 : if (aBoundElement)
1030 0 : ni = aBoundElement->NodeInfo();
1031 :
1032 0 : if (!info && bindingManager &&
1033 0 : (!ni || !(ni->Equals(nsGkAtoms::scrollbar, kNameSpaceID_XUL) ||
1034 0 : ni->Equals(nsGkAtoms::thumb, kNameSpaceID_XUL) ||
1035 0 : ((ni->Equals(nsGkAtoms::input) ||
1036 0 : ni->Equals(nsGkAtoms::select)) &&
1037 0 : aBoundElement->IsHTML()))) && !aForceSyncLoad) {
1038 : // The third line of defense is to investigate whether or not the
1039 : // document is currently being loaded asynchronously. If so, there's no
1040 : // document yet, but we need to glom on our request so that it will be
1041 : // processed whenever the doc does finish loading.
1042 0 : nsCOMPtr<nsIStreamListener> listener;
1043 0 : if (bindingManager)
1044 0 : listener = bindingManager->GetLoadingDocListener(documentURI);
1045 0 : if (listener) {
1046 : nsXBLStreamListener* xblListener =
1047 0 : static_cast<nsXBLStreamListener*>(listener.get());
1048 : // Create a new load observer.
1049 0 : if (!xblListener->HasRequest(aBindingURI, aBoundElement)) {
1050 0 : nsXBLBindingRequest* req = nsXBLBindingRequest::Create(mPool, aBindingURI, aBoundElement);
1051 0 : xblListener->AddRequest(req);
1052 : }
1053 0 : return NS_OK;
1054 : }
1055 : }
1056 :
1057 : #ifdef MOZ_XUL
1058 : // Next, look in the startup cache
1059 0 : bool useStartupCache = useXULCache && IsChromeOrResourceURI(documentURI);
1060 0 : if (!info && useStartupCache) {
1061 0 : rv = nsXBLDocumentInfo::ReadPrototypeBindings(documentURI, getter_AddRefs(info));
1062 0 : if (NS_SUCCEEDED(rv)) {
1063 0 : cache->PutXBLDocumentInfo(info);
1064 :
1065 0 : if (bindingManager) {
1066 : // Cache it in our binding manager's document table.
1067 0 : bindingManager->PutXBLDocumentInfo(info);
1068 : }
1069 : }
1070 : }
1071 : #endif
1072 :
1073 0 : if (!info) {
1074 : // Finally, if all lines of defense fail, we go and fetch the binding
1075 : // document.
1076 :
1077 : // Always load chrome synchronously
1078 : bool chrome;
1079 0 : if (NS_SUCCEEDED(documentURI->SchemeIs("chrome", &chrome)) && chrome)
1080 0 : aForceSyncLoad = true;
1081 :
1082 0 : nsCOMPtr<nsIDocument> document;
1083 : FetchBindingDocument(aBoundElement, aBoundDocument, documentURI,
1084 0 : aBindingURI, aForceSyncLoad, getter_AddRefs(document));
1085 :
1086 0 : if (document) {
1087 0 : nsBindingManager *xblDocBindingManager = document->BindingManager();
1088 0 : info = xblDocBindingManager->GetXBLDocumentInfo(documentURI);
1089 0 : if (!info) {
1090 0 : NS_ERROR("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
1091 0 : return NS_ERROR_FAILURE;
1092 : }
1093 0 : xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
1094 :
1095 : // If the doc is a chrome URI, then we put it into the XUL cache.
1096 : #ifdef MOZ_XUL
1097 0 : if (useStartupCache) {
1098 0 : cache->PutXBLDocumentInfo(info);
1099 :
1100 : // now write the bindings into the startup cache
1101 0 : info->WritePrototypeBindings();
1102 : }
1103 : #endif
1104 :
1105 0 : if (bindingManager) {
1106 : // Also put it in our binding manager's document table.
1107 0 : bindingManager->PutXBLDocumentInfo(info);
1108 : }
1109 : }
1110 : }
1111 : }
1112 :
1113 0 : if (!info)
1114 0 : return NS_OK;
1115 :
1116 0 : *aResult = info;
1117 0 : NS_IF_ADDREF(*aResult);
1118 :
1119 0 : return NS_OK;
1120 : }
1121 :
1122 : nsresult
1123 0 : nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
1124 : nsIURI* aDocumentURI, nsIURI* aBindingURI,
1125 : bool aForceSyncLoad, nsIDocument** aResult)
1126 : {
1127 : NS_TIME_FUNCTION;
1128 :
1129 0 : nsresult rv = NS_OK;
1130 : // Initialize our out pointer to nsnull
1131 0 : *aResult = nsnull;
1132 :
1133 : // Now we have to synchronously load the binding file.
1134 : // Create an XML content sink and a parser.
1135 0 : nsCOMPtr<nsILoadGroup> loadGroup;
1136 0 : if (aBoundDocument)
1137 0 : loadGroup = aBoundDocument->GetDocumentLoadGroup();
1138 :
1139 : // We really shouldn't have to force a sync load for anything here... could
1140 : // we get away with not doing that? Not sure.
1141 0 : if (IsChromeOrResourceURI(aDocumentURI))
1142 0 : aForceSyncLoad = true;
1143 :
1144 : // Create document and contentsink and set them up.
1145 0 : nsCOMPtr<nsIDocument> doc;
1146 0 : rv = NS_NewXMLDocument(getter_AddRefs(doc));
1147 0 : NS_ENSURE_SUCCESS(rv, rv);
1148 :
1149 0 : nsCOMPtr<nsIXMLContentSink> xblSink;
1150 0 : rv = NS_NewXBLContentSink(getter_AddRefs(xblSink), doc, aDocumentURI, nsnull);
1151 0 : NS_ENSURE_SUCCESS(rv, rv);
1152 :
1153 : // Open channel
1154 0 : nsCOMPtr<nsIChannel> channel;
1155 0 : rv = NS_NewChannel(getter_AddRefs(channel), aDocumentURI, nsnull, loadGroup);
1156 0 : NS_ENSURE_SUCCESS(rv, rv);
1157 :
1158 0 : nsCOMPtr<nsIInterfaceRequestor> sameOriginChecker = nsContentUtils::GetSameOriginChecker();
1159 0 : NS_ENSURE_TRUE(sameOriginChecker, NS_ERROR_OUT_OF_MEMORY);
1160 :
1161 0 : channel->SetNotificationCallbacks(sameOriginChecker);
1162 :
1163 0 : if (!aForceSyncLoad) {
1164 : // We can be asynchronous
1165 : nsXBLStreamListener* xblListener =
1166 0 : new nsXBLStreamListener(this, aBoundDocument, xblSink, doc);
1167 0 : NS_ENSURE_TRUE(xblListener,NS_ERROR_OUT_OF_MEMORY);
1168 :
1169 : // Add ourselves to the list of loading docs.
1170 : nsBindingManager *bindingManager;
1171 0 : if (aBoundDocument)
1172 0 : bindingManager = aBoundDocument->BindingManager();
1173 : else
1174 0 : bindingManager = nsnull;
1175 :
1176 0 : if (bindingManager)
1177 0 : bindingManager->PutLoadingDocListener(aDocumentURI, xblListener);
1178 :
1179 : // Add our request.
1180 : nsXBLBindingRequest* req = nsXBLBindingRequest::Create(mPool,
1181 : aBindingURI,
1182 0 : aBoundElement);
1183 0 : xblListener->AddRequest(req);
1184 :
1185 : // Now kick off the async read.
1186 0 : rv = channel->AsyncOpen(xblListener, nsnull);
1187 0 : if (NS_FAILED(rv)) {
1188 : // Well, we won't be getting a load. Make sure to clean up our stuff!
1189 0 : if (bindingManager) {
1190 0 : bindingManager->RemoveLoadingDocListener(aDocumentURI);
1191 : }
1192 : }
1193 0 : return NS_OK;
1194 : }
1195 :
1196 0 : nsCOMPtr<nsIStreamListener> listener;
1197 0 : rv = doc->StartDocumentLoad("loadAsInteractiveData",
1198 : channel,
1199 : loadGroup,
1200 : nsnull,
1201 0 : getter_AddRefs(listener),
1202 : true,
1203 0 : xblSink);
1204 0 : NS_ENSURE_SUCCESS(rv, rv);
1205 :
1206 : // Now do a blocking synchronous parse of the file.
1207 0 : nsCOMPtr<nsIInputStream> in;
1208 0 : rv = channel->Open(getter_AddRefs(in));
1209 0 : NS_ENSURE_SUCCESS(rv, rv);
1210 :
1211 0 : rv = nsSyncLoadService::PushSyncStreamToListener(in, listener, channel);
1212 0 : NS_ENSURE_SUCCESS(rv, rv);
1213 :
1214 0 : doc.swap(*aResult);
1215 :
1216 0 : return NS_OK;
1217 : }
1218 :
1219 : // Creation Routine ///////////////////////////////////////////////////////////////////////
1220 :
1221 : nsresult NS_NewXBLService(nsIXBLService** aResult);
1222 :
1223 : nsresult
1224 0 : NS_NewXBLService(nsIXBLService** aResult)
1225 : {
1226 0 : nsXBLService* result = new nsXBLService;
1227 0 : if (! result)
1228 0 : return NS_ERROR_OUT_OF_MEMORY;
1229 :
1230 0 : NS_ADDREF(*aResult = result);
1231 :
1232 : // Register the first (and only) nsXBLService as a memory pressure observer
1233 : // so it can flush the LRU list in low-memory situations.
1234 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1235 0 : if (os)
1236 0 : os->AddObserver(result, "memory-pressure", true);
1237 :
1238 0 : return NS_OK;
1239 : }
1240 :
|