dom/xbl/nsXBLService.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial