Wed, 31 Dec 2014 06:09:35 +0100
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 | } |