1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/xbl/nsXBLBinding.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1211 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=79: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsCOMPtr.h" 1.11 +#include "nsIAtom.h" 1.12 +#include "nsXBLDocumentInfo.h" 1.13 +#include "nsIInputStream.h" 1.14 +#include "nsNameSpaceManager.h" 1.15 +#include "nsIURI.h" 1.16 +#include "nsIURL.h" 1.17 +#include "nsIChannel.h" 1.18 +#include "nsXPIDLString.h" 1.19 +#include "nsReadableUtils.h" 1.20 +#include "nsNetUtil.h" 1.21 +#include "plstr.h" 1.22 +#include "nsIContent.h" 1.23 +#include "nsIDocument.h" 1.24 +#include "nsContentUtils.h" 1.25 +#include "ChildIterator.h" 1.26 +#include "nsCxPusher.h" 1.27 +#ifdef MOZ_XUL 1.28 +#include "nsIXULDocument.h" 1.29 +#endif 1.30 +#include "nsIXMLContentSink.h" 1.31 +#include "nsContentCID.h" 1.32 +#include "mozilla/dom/XMLDocument.h" 1.33 +#include "jsapi.h" 1.34 +#include "nsXBLService.h" 1.35 +#include "nsIXPConnect.h" 1.36 +#include "nsIScriptContext.h" 1.37 +#include "nsCRT.h" 1.38 + 1.39 +// Event listeners 1.40 +#include "mozilla/EventListenerManager.h" 1.41 +#include "nsIDOMEventListener.h" 1.42 +#include "nsAttrName.h" 1.43 + 1.44 +#include "nsGkAtoms.h" 1.45 + 1.46 +#include "nsXBLPrototypeHandler.h" 1.47 + 1.48 +#include "nsXBLPrototypeBinding.h" 1.49 +#include "nsXBLBinding.h" 1.50 +#include "nsIPrincipal.h" 1.51 +#include "nsIScriptSecurityManager.h" 1.52 +#include "mozilla/dom/XBLChildrenElement.h" 1.53 + 1.54 +#include "prprf.h" 1.55 +#include "nsNodeUtils.h" 1.56 +#include "nsJSUtils.h" 1.57 + 1.58 +// Nasty hack. Maybe we could move some of the classinfo utility methods 1.59 +// (e.g. WrapNative) over to nsContentUtils? 1.60 +#include "nsDOMClassInfo.h" 1.61 + 1.62 +#include "mozilla/dom/Element.h" 1.63 +#include "mozilla/dom/ShadowRoot.h" 1.64 + 1.65 +using namespace mozilla; 1.66 +using namespace mozilla::dom; 1.67 + 1.68 +// Helper classes 1.69 + 1.70 +/***********************************************************************/ 1.71 +// 1.72 +// The JS class for XBLBinding 1.73 +// 1.74 +static void 1.75 +XBLFinalize(JSFreeOp *fop, JSObject *obj) 1.76 +{ 1.77 + nsXBLDocumentInfo* docInfo = 1.78 + static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj)); 1.79 + nsContentUtils::DeferredFinalize(docInfo); 1.80 +} 1.81 + 1.82 +static bool 1.83 +XBLEnumerate(JSContext *cx, JS::Handle<JSObject*> obj) 1.84 +{ 1.85 + nsXBLPrototypeBinding* protoBinding = 1.86 + static_cast<nsXBLPrototypeBinding*>(::JS_GetReservedSlot(obj, 0).toPrivate()); 1.87 + MOZ_ASSERT(protoBinding); 1.88 + 1.89 + return protoBinding->ResolveAllFields(cx, obj); 1.90 +} 1.91 + 1.92 +static const JSClass gPrototypeJSClass = { 1.93 + "XBL prototype JSClass", 1.94 + JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | 1.95 + JSCLASS_NEW_RESOLVE | 1.96 + // Our one reserved slot holds the relevant nsXBLPrototypeBinding 1.97 + JSCLASS_HAS_RESERVED_SLOTS(1), 1.98 + JS_PropertyStub, JS_DeletePropertyStub, 1.99 + JS_PropertyStub, JS_StrictPropertyStub, 1.100 + XBLEnumerate, JS_ResolveStub, 1.101 + JS_ConvertStub, XBLFinalize, 1.102 + nullptr, nullptr, nullptr, nullptr 1.103 +}; 1.104 + 1.105 +// Implementation ///////////////////////////////////////////////////////////////// 1.106 + 1.107 +// Constructors/Destructors 1.108 +nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding) 1.109 + : mMarkedForDeath(false) 1.110 + , mUsingXBLScope(false) 1.111 + , mPrototypeBinding(aBinding) 1.112 +{ 1.113 + NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!"); 1.114 + // Grab a ref to the document info so the prototype binding won't die 1.115 + NS_ADDREF(mPrototypeBinding->XBLDocumentInfo()); 1.116 +} 1.117 + 1.118 +// Constructor used by web components. 1.119 +nsXBLBinding::nsXBLBinding(ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aBinding) 1.120 + : mMarkedForDeath(false), 1.121 + mUsingXBLScope(false), 1.122 + mPrototypeBinding(aBinding), 1.123 + mContent(aShadowRoot) 1.124 +{ 1.125 + NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!"); 1.126 + // Grab a ref to the document info so the prototype binding won't die 1.127 + NS_ADDREF(mPrototypeBinding->XBLDocumentInfo()); 1.128 +} 1.129 + 1.130 +nsXBLBinding::~nsXBLBinding(void) 1.131 +{ 1.132 + if (mContent) { 1.133 + nsXBLBinding::UninstallAnonymousContent(mContent->OwnerDoc(), mContent); 1.134 + } 1.135 + nsXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo(); 1.136 + NS_RELEASE(info); 1.137 +} 1.138 + 1.139 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLBinding) 1.140 + 1.141 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLBinding) 1.142 + // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because 1.143 + // mPrototypeBinding is weak. 1.144 + if (tmp->mContent) { 1.145 + nsXBLBinding::UninstallAnonymousContent(tmp->mContent->OwnerDoc(), 1.146 + tmp->mContent); 1.147 + } 1.148 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent) 1.149 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextBinding) 1.150 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDefaultInsertionPoint) 1.151 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mInsertionPoints) 1.152 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContentList) 1.153 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.154 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLBinding) 1.155 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, 1.156 + "mPrototypeBinding->XBLDocumentInfo()"); 1.157 + cb.NoteXPCOMChild(tmp->mPrototypeBinding->XBLDocumentInfo()); 1.158 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent) 1.159 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextBinding) 1.160 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultInsertionPoint) 1.161 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInsertionPoints) 1.162 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContentList) 1.163 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.164 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding, AddRef) 1.165 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding, Release) 1.166 + 1.167 +void 1.168 +nsXBLBinding::SetBaseBinding(nsXBLBinding* aBinding) 1.169 +{ 1.170 + if (mNextBinding) { 1.171 + NS_ERROR("Base XBL binding is already defined!"); 1.172 + return; 1.173 + } 1.174 + 1.175 + mNextBinding = aBinding; // Comptr handles rel/add 1.176 +} 1.177 + 1.178 +nsXBLBinding* 1.179 +nsXBLBinding::GetBindingWithContent() 1.180 +{ 1.181 + if (mContent) { 1.182 + return this; 1.183 + } 1.184 + 1.185 + return mNextBinding ? mNextBinding->GetBindingWithContent() : nullptr; 1.186 +} 1.187 + 1.188 +void 1.189 +nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement, 1.190 + bool aChromeOnlyContent) 1.191 +{ 1.192 + // We need to ensure two things. 1.193 + // (1) The anonymous content should be fooled into thinking it's in the bound 1.194 + // element's document, assuming that the bound element is in a document 1.195 + // Note that we don't change the current doc of aAnonParent here, since that 1.196 + // quite simply does not matter. aAnonParent is just a way of keeping refs 1.197 + // to all its kids, which are anonymous content from the point of view of 1.198 + // aElement. 1.199 + // (2) The children's parent back pointer should not be to this synthetic root 1.200 + // but should instead point to the enclosing parent element. 1.201 + nsIDocument* doc = aElement->GetCurrentDoc(); 1.202 + bool allowScripts = AllowScripts(); 1.203 + 1.204 + nsAutoScriptBlocker scriptBlocker; 1.205 + for (nsIContent* child = aAnonParent->GetFirstChild(); 1.206 + child; 1.207 + child = child->GetNextSibling()) { 1.208 + child->UnbindFromTree(); 1.209 + if (aChromeOnlyContent) { 1.210 + child->SetFlags(NODE_CHROME_ONLY_ACCESS | 1.211 + NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS); 1.212 + } 1.213 + nsresult rv = 1.214 + child->BindToTree(doc, aElement, mBoundElement, allowScripts); 1.215 + if (NS_FAILED(rv)) { 1.216 + // Oh, well... Just give up. 1.217 + // XXXbz This really shouldn't be a void method! 1.218 + child->UnbindFromTree(); 1.219 + return; 1.220 + } 1.221 + 1.222 + child->SetFlags(NODE_IS_ANONYMOUS_ROOT); 1.223 + 1.224 +#ifdef MOZ_XUL 1.225 + // To make XUL templates work (and other goodies that happen when 1.226 + // an element is added to a XUL document), we need to notify the 1.227 + // XUL document using its special API. 1.228 + nsCOMPtr<nsIXULDocument> xuldoc(do_QueryInterface(doc)); 1.229 + if (xuldoc) 1.230 + xuldoc->AddSubtreeToDocument(child); 1.231 +#endif 1.232 + } 1.233 +} 1.234 + 1.235 +void 1.236 +nsXBLBinding::UninstallAnonymousContent(nsIDocument* aDocument, 1.237 + nsIContent* aAnonParent) 1.238 +{ 1.239 + if (aAnonParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) { 1.240 + // It is unnecessary to uninstall anonymous content in a shadow tree 1.241 + // because the ShadowRoot itself is a DocumentFragment and does not 1.242 + // need any additional cleanup. 1.243 + return; 1.244 + } 1.245 + 1.246 + nsAutoScriptBlocker scriptBlocker; 1.247 + // Hold a strong ref while doing this, just in case. 1.248 + nsCOMPtr<nsIContent> anonParent = aAnonParent; 1.249 +#ifdef MOZ_XUL 1.250 + nsCOMPtr<nsIXULDocument> xuldoc = 1.251 + do_QueryInterface(aDocument); 1.252 +#endif 1.253 + for (nsIContent* child = aAnonParent->GetFirstChild(); 1.254 + child; 1.255 + child = child->GetNextSibling()) { 1.256 + child->UnbindFromTree(); 1.257 +#ifdef MOZ_XUL 1.258 + if (xuldoc) { 1.259 + xuldoc->RemoveSubtreeFromDocument(child); 1.260 + } 1.261 +#endif 1.262 + } 1.263 +} 1.264 + 1.265 +void 1.266 +nsXBLBinding::SetBoundElement(nsIContent* aElement) 1.267 +{ 1.268 + mBoundElement = aElement; 1.269 + if (mNextBinding) 1.270 + mNextBinding->SetBoundElement(aElement); 1.271 + 1.272 + if (!mBoundElement) { 1.273 + return; 1.274 + } 1.275 + 1.276 + // Compute whether we're using an XBL scope. 1.277 + // 1.278 + // We disable XBL scopes for remote XUL, where we care about compat more 1.279 + // than security. So we need to know whether we're using an XBL scope so that 1.280 + // we can decide what to do about untrusted events when "allowuntrusted" 1.281 + // is not given in the handler declaration. 1.282 + nsCOMPtr<nsIGlobalObject> go = mBoundElement->OwnerDoc()->GetScopeObject(); 1.283 + NS_ENSURE_TRUE_VOID(go && go->GetGlobalJSObject()); 1.284 + mUsingXBLScope = xpc::UseXBLScope(js::GetObjectCompartment(go->GetGlobalJSObject())); 1.285 +} 1.286 + 1.287 +bool 1.288 +nsXBLBinding::HasStyleSheets() const 1.289 +{ 1.290 + // Find out if we need to re-resolve style. We'll need to do this 1.291 + // if we have additional stylesheets in our binding document. 1.292 + if (mPrototypeBinding->HasStyleSheets()) 1.293 + return true; 1.294 + 1.295 + return mNextBinding ? mNextBinding->HasStyleSheets() : false; 1.296 +} 1.297 + 1.298 +void 1.299 +nsXBLBinding::GenerateAnonymousContent() 1.300 +{ 1.301 + NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), 1.302 + "Someone forgot a script blocker"); 1.303 + 1.304 + // Fetch the content element for this binding. 1.305 + nsIContent* content = 1.306 + mPrototypeBinding->GetImmediateChild(nsGkAtoms::content); 1.307 + 1.308 + if (!content) { 1.309 + // We have no anonymous content. 1.310 + if (mNextBinding) 1.311 + mNextBinding->GenerateAnonymousContent(); 1.312 + 1.313 + return; 1.314 + } 1.315 + 1.316 + // Find out if we're really building kids or if we're just 1.317 + // using the attribute-setting shorthand hack. 1.318 + uint32_t contentCount = content->GetChildCount(); 1.319 + 1.320 + // Plan to build the content by default. 1.321 + bool hasContent = (contentCount > 0); 1.322 + if (hasContent) { 1.323 + nsIDocument* doc = mBoundElement->OwnerDoc(); 1.324 + 1.325 + nsCOMPtr<nsINode> clonedNode; 1.326 + nsCOMArray<nsINode> nodesWithProperties; 1.327 + nsNodeUtils::Clone(content, true, doc->NodeInfoManager(), 1.328 + nodesWithProperties, getter_AddRefs(clonedNode)); 1.329 + mContent = clonedNode->AsElement(); 1.330 + 1.331 + // Search for <xbl:children> elements in the XBL content. In the presence 1.332 + // of multiple default insertion points, we use the last one in document 1.333 + // order. 1.334 + for (nsIContent* child = mContent; child; child = child->GetNextNode(mContent)) { 1.335 + if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { 1.336 + XBLChildrenElement* point = static_cast<XBLChildrenElement*>(child); 1.337 + if (point->IsDefaultInsertion()) { 1.338 + mDefaultInsertionPoint = point; 1.339 + } else { 1.340 + mInsertionPoints.AppendElement(point); 1.341 + } 1.342 + } 1.343 + } 1.344 + 1.345 + // Do this after looking for <children> as this messes up the parent 1.346 + // pointer which would make the GetNextNode call above fail 1.347 + InstallAnonymousContent(mContent, mBoundElement, 1.348 + mPrototypeBinding->ChromeOnlyContent()); 1.349 + 1.350 + // Insert explicit children into insertion points 1.351 + if (mDefaultInsertionPoint && mInsertionPoints.IsEmpty()) { 1.352 + ExplicitChildIterator iter(mBoundElement); 1.353 + for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { 1.354 + mDefaultInsertionPoint->AppendInsertedChild(child); 1.355 + } 1.356 + } else { 1.357 + // It is odd to come into this code if mInsertionPoints is not empty, but 1.358 + // we need to make sure to do the compatibility hack below if the bound 1.359 + // node has any non <xul:template> or <xul:observes> children. 1.360 + ExplicitChildIterator iter(mBoundElement); 1.361 + for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { 1.362 + XBLChildrenElement* point = FindInsertionPointForInternal(child); 1.363 + if (point) { 1.364 + point->AppendInsertedChild(child); 1.365 + } else { 1.366 + nsINodeInfo *ni = child->NodeInfo(); 1.367 + if (ni->NamespaceID() != kNameSpaceID_XUL || 1.368 + (!ni->Equals(nsGkAtoms::_template) && 1.369 + !ni->Equals(nsGkAtoms::observes))) { 1.370 + // Compatibility hack. For some reason the original XBL 1.371 + // implementation dropped the content of a binding if any child of 1.372 + // the bound element didn't match any of the <children> in the 1.373 + // binding. This became a pseudo-API that we have to maintain. 1.374 + 1.375 + // Undo InstallAnonymousContent 1.376 + UninstallAnonymousContent(doc, mContent); 1.377 + 1.378 + // Clear out our children elements to avoid dangling references. 1.379 + ClearInsertionPoints(); 1.380 + 1.381 + // Pretend as though there was no content in the binding. 1.382 + mContent = nullptr; 1.383 + return; 1.384 + } 1.385 + } 1.386 + } 1.387 + } 1.388 + 1.389 + // Set binding parent on default content if need 1.390 + if (mDefaultInsertionPoint) { 1.391 + mDefaultInsertionPoint->MaybeSetupDefaultContent(); 1.392 + } 1.393 + for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) { 1.394 + mInsertionPoints[i]->MaybeSetupDefaultContent(); 1.395 + } 1.396 + 1.397 + mPrototypeBinding->SetInitialAttributes(mBoundElement, mContent); 1.398 + } 1.399 + 1.400 + // Always check the content element for potential attributes. 1.401 + // This shorthand hack always happens, even when we didn't 1.402 + // build anonymous content. 1.403 + const nsAttrName* attrName; 1.404 + for (uint32_t i = 0; (attrName = content->GetAttrNameAt(i)); ++i) { 1.405 + int32_t namespaceID = attrName->NamespaceID(); 1.406 + // Hold a strong reference here so that the atom doesn't go away during 1.407 + // UnsetAttr. 1.408 + nsCOMPtr<nsIAtom> name = attrName->LocalName(); 1.409 + 1.410 + if (name != nsGkAtoms::includes) { 1.411 + if (!nsContentUtils::HasNonEmptyAttr(mBoundElement, namespaceID, name)) { 1.412 + nsAutoString value2; 1.413 + content->GetAttr(namespaceID, name, value2); 1.414 + mBoundElement->SetAttr(namespaceID, name, attrName->GetPrefix(), 1.415 + value2, false); 1.416 + } 1.417 + } 1.418 + 1.419 + // Conserve space by wiping the attributes off the clone. 1.420 + if (mContent) 1.421 + mContent->UnsetAttr(namespaceID, name, false); 1.422 + } 1.423 +} 1.424 + 1.425 +XBLChildrenElement* 1.426 +nsXBLBinding::FindInsertionPointFor(nsIContent* aChild) 1.427 +{ 1.428 + // XXX We should get rid of this function as it causes us to traverse the 1.429 + // binding chain multiple times 1.430 + if (mContent) { 1.431 + return FindInsertionPointForInternal(aChild); 1.432 + } 1.433 + 1.434 + return mNextBinding ? mNextBinding->FindInsertionPointFor(aChild) 1.435 + : nullptr; 1.436 +} 1.437 + 1.438 +XBLChildrenElement* 1.439 +nsXBLBinding::FindInsertionPointForInternal(nsIContent* aChild) 1.440 +{ 1.441 + for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) { 1.442 + XBLChildrenElement* point = mInsertionPoints[i]; 1.443 + if (point->Includes(aChild)) { 1.444 + return point; 1.445 + } 1.446 + } 1.447 + 1.448 + return mDefaultInsertionPoint; 1.449 +} 1.450 + 1.451 +void 1.452 +nsXBLBinding::ClearInsertionPoints() 1.453 +{ 1.454 + if (mDefaultInsertionPoint) { 1.455 + mDefaultInsertionPoint->ClearInsertedChildren(); 1.456 + } 1.457 + 1.458 + for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) { 1.459 + mInsertionPoints[i]->ClearInsertedChildren(); 1.460 + } 1.461 +} 1.462 + 1.463 +nsAnonymousContentList* 1.464 +nsXBLBinding::GetAnonymousNodeList() 1.465 +{ 1.466 + if (!mContent) { 1.467 + return mNextBinding ? mNextBinding->GetAnonymousNodeList() : nullptr; 1.468 + } 1.469 + 1.470 + if (!mAnonymousContentList) { 1.471 + mAnonymousContentList = new nsAnonymousContentList(mContent); 1.472 + } 1.473 + 1.474 + return mAnonymousContentList; 1.475 +} 1.476 + 1.477 +void 1.478 +nsXBLBinding::InstallEventHandlers() 1.479 +{ 1.480 + // Don't install handlers if scripts aren't allowed. 1.481 + if (AllowScripts()) { 1.482 + // Fetch the handlers prototypes for this binding. 1.483 + nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers(); 1.484 + 1.485 + if (handlerChain) { 1.486 + EventListenerManager* manager = mBoundElement->GetOrCreateListenerManager(); 1.487 + if (!manager) 1.488 + return; 1.489 + 1.490 + bool isChromeDoc = 1.491 + nsContentUtils::IsChromeDoc(mBoundElement->OwnerDoc()); 1.492 + bool isChromeBinding = mPrototypeBinding->IsChrome(); 1.493 + nsXBLPrototypeHandler* curr; 1.494 + for (curr = handlerChain; curr; curr = curr->GetNextHandler()) { 1.495 + // Fetch the event type. 1.496 + nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName(); 1.497 + if (!eventAtom || 1.498 + eventAtom == nsGkAtoms::keyup || 1.499 + eventAtom == nsGkAtoms::keydown || 1.500 + eventAtom == nsGkAtoms::keypress) 1.501 + continue; 1.502 + 1.503 + nsXBLEventHandler* handler = curr->GetEventHandler(); 1.504 + if (handler) { 1.505 + // Figure out if we're using capturing or not. 1.506 + EventListenerFlags flags; 1.507 + flags.mCapture = (curr->GetPhase() == NS_PHASE_CAPTURING); 1.508 + 1.509 + // If this is a command, add it in the system event group 1.510 + if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | 1.511 + NS_HANDLER_TYPE_SYSTEM)) && 1.512 + (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) { 1.513 + flags.mInSystemGroup = true; 1.514 + } 1.515 + 1.516 + bool hasAllowUntrustedAttr = curr->HasAllowUntrustedAttr(); 1.517 + if ((hasAllowUntrustedAttr && curr->AllowUntrustedEvents()) || 1.518 + (!hasAllowUntrustedAttr && !isChromeDoc && !mUsingXBLScope)) { 1.519 + flags.mAllowUntrustedEvents = true; 1.520 + } 1.521 + 1.522 + manager->AddEventListenerByType(handler, 1.523 + nsDependentAtomString(eventAtom), 1.524 + flags); 1.525 + } 1.526 + } 1.527 + 1.528 + const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers = 1.529 + mPrototypeBinding->GetKeyEventHandlers(); 1.530 + int32_t i; 1.531 + for (i = 0; i < keyHandlers->Count(); ++i) { 1.532 + nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i); 1.533 + handler->SetIsBoundToChrome(isChromeDoc); 1.534 + handler->SetUsingXBLScope(mUsingXBLScope); 1.535 + 1.536 + nsAutoString type; 1.537 + handler->GetEventName(type); 1.538 + 1.539 + // If this is a command, add it in the system event group, otherwise 1.540 + // add it to the standard event group. 1.541 + 1.542 + // Figure out if we're using capturing or not. 1.543 + EventListenerFlags flags; 1.544 + flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING); 1.545 + 1.546 + if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | 1.547 + NS_HANDLER_TYPE_SYSTEM)) && 1.548 + (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) { 1.549 + flags.mInSystemGroup = true; 1.550 + } 1.551 + 1.552 + // For key handlers we have to set mAllowUntrustedEvents flag. 1.553 + // Whether the handling of the event is allowed or not is handled in 1.554 + // nsXBLKeyEventHandler::HandleEvent 1.555 + flags.mAllowUntrustedEvents = true; 1.556 + 1.557 + manager->AddEventListenerByType(handler, type, flags); 1.558 + } 1.559 + } 1.560 + } 1.561 + 1.562 + if (mNextBinding) 1.563 + mNextBinding->InstallEventHandlers(); 1.564 +} 1.565 + 1.566 +nsresult 1.567 +nsXBLBinding::InstallImplementation() 1.568 +{ 1.569 + // Always install the base class properties first, so that 1.570 + // derived classes can reference the base class properties. 1.571 + 1.572 + if (mNextBinding) { 1.573 + nsresult rv = mNextBinding->InstallImplementation(); 1.574 + NS_ENSURE_SUCCESS(rv, rv); 1.575 + } 1.576 + 1.577 + // iterate through each property in the prototype's list and install the property. 1.578 + if (AllowScripts()) 1.579 + return mPrototypeBinding->InstallImplementation(this); 1.580 + 1.581 + return NS_OK; 1.582 +} 1.583 + 1.584 +nsIAtom* 1.585 +nsXBLBinding::GetBaseTag(int32_t* aNameSpaceID) 1.586 +{ 1.587 + nsIAtom *tag = mPrototypeBinding->GetBaseTag(aNameSpaceID); 1.588 + if (!tag && mNextBinding) 1.589 + return mNextBinding->GetBaseTag(aNameSpaceID); 1.590 + 1.591 + return tag; 1.592 +} 1.593 + 1.594 +void 1.595 +nsXBLBinding::AttributeChanged(nsIAtom* aAttribute, int32_t aNameSpaceID, 1.596 + bool aRemoveFlag, bool aNotify) 1.597 +{ 1.598 + // XXX Change if we ever allow multiple bindings in a chain to contribute anonymous content 1.599 + if (!mContent) { 1.600 + if (mNextBinding) 1.601 + mNextBinding->AttributeChanged(aAttribute, aNameSpaceID, 1.602 + aRemoveFlag, aNotify); 1.603 + } else { 1.604 + mPrototypeBinding->AttributeChanged(aAttribute, aNameSpaceID, aRemoveFlag, 1.605 + mBoundElement, mContent, aNotify); 1.606 + } 1.607 +} 1.608 + 1.609 +void 1.610 +nsXBLBinding::ExecuteAttachedHandler() 1.611 +{ 1.612 + if (mNextBinding) 1.613 + mNextBinding->ExecuteAttachedHandler(); 1.614 + 1.615 + if (AllowScripts()) 1.616 + mPrototypeBinding->BindingAttached(mBoundElement); 1.617 +} 1.618 + 1.619 +void 1.620 +nsXBLBinding::ExecuteDetachedHandler() 1.621 +{ 1.622 + if (AllowScripts()) 1.623 + mPrototypeBinding->BindingDetached(mBoundElement); 1.624 + 1.625 + if (mNextBinding) 1.626 + mNextBinding->ExecuteDetachedHandler(); 1.627 +} 1.628 + 1.629 +void 1.630 +nsXBLBinding::UnhookEventHandlers() 1.631 +{ 1.632 + nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers(); 1.633 + 1.634 + if (handlerChain) { 1.635 + EventListenerManager* manager = mBoundElement->GetExistingListenerManager(); 1.636 + if (!manager) { 1.637 + return; 1.638 + } 1.639 + 1.640 + bool isChromeBinding = mPrototypeBinding->IsChrome(); 1.641 + nsXBLPrototypeHandler* curr; 1.642 + for (curr = handlerChain; curr; curr = curr->GetNextHandler()) { 1.643 + nsXBLEventHandler* handler = curr->GetCachedEventHandler(); 1.644 + if (!handler) { 1.645 + continue; 1.646 + } 1.647 + 1.648 + nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName(); 1.649 + if (!eventAtom || 1.650 + eventAtom == nsGkAtoms::keyup || 1.651 + eventAtom == nsGkAtoms::keydown || 1.652 + eventAtom == nsGkAtoms::keypress) 1.653 + continue; 1.654 + 1.655 + // Figure out if we're using capturing or not. 1.656 + EventListenerFlags flags; 1.657 + flags.mCapture = (curr->GetPhase() == NS_PHASE_CAPTURING); 1.658 + 1.659 + // If this is a command, remove it from the system event group, 1.660 + // otherwise remove it from the standard event group. 1.661 + 1.662 + if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | 1.663 + NS_HANDLER_TYPE_SYSTEM)) && 1.664 + (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) { 1.665 + flags.mInSystemGroup = true; 1.666 + } 1.667 + 1.668 + manager->RemoveEventListenerByType(handler, 1.669 + nsDependentAtomString(eventAtom), 1.670 + flags); 1.671 + } 1.672 + 1.673 + const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers = 1.674 + mPrototypeBinding->GetKeyEventHandlers(); 1.675 + int32_t i; 1.676 + for (i = 0; i < keyHandlers->Count(); ++i) { 1.677 + nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i); 1.678 + 1.679 + nsAutoString type; 1.680 + handler->GetEventName(type); 1.681 + 1.682 + // Figure out if we're using capturing or not. 1.683 + EventListenerFlags flags; 1.684 + flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING); 1.685 + 1.686 + // If this is a command, remove it from the system event group, otherwise 1.687 + // remove it from the standard event group. 1.688 + 1.689 + if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) && 1.690 + (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) { 1.691 + flags.mInSystemGroup = true; 1.692 + } 1.693 + 1.694 + manager->RemoveEventListenerByType(handler, type, flags); 1.695 + } 1.696 + } 1.697 +} 1.698 + 1.699 +static void 1.700 +UpdateInsertionParent(XBLChildrenElement* aPoint, 1.701 + nsIContent* aOldBoundElement) 1.702 +{ 1.703 + if (aPoint->IsDefaultInsertion()) { 1.704 + return; 1.705 + } 1.706 + 1.707 + for (size_t i = 0; i < aPoint->InsertedChildrenLength(); ++i) { 1.708 + nsIContent* child = aPoint->mInsertedChildren[i]; 1.709 + 1.710 + MOZ_ASSERT(child->GetParentNode()); 1.711 + 1.712 + // Here, we're iterating children that we inserted. There are two cases: 1.713 + // either |child| is an explicit child of |aOldBoundElement| and is no 1.714 + // longer inserted anywhere or it's a child of a <children> element 1.715 + // parented to |aOldBoundElement|. In the former case, the child is no 1.716 + // longer inserted anywhere, so we set its insertion parent to null. In the 1.717 + // latter case, the child is now inserted into |aOldBoundElement| from some 1.718 + // binding above us, so we set its insertion parent to aOldBoundElement. 1.719 + if (child->GetParentNode() == aOldBoundElement) { 1.720 + child->SetXBLInsertionParent(nullptr); 1.721 + } else { 1.722 + child->SetXBLInsertionParent(aOldBoundElement); 1.723 + } 1.724 + } 1.725 +} 1.726 + 1.727 +void 1.728 +nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocument) 1.729 +{ 1.730 + if (aOldDocument == aNewDocument) 1.731 + return; 1.732 + 1.733 + // Now the binding dies. Unhook our prototypes. 1.734 + if (mPrototypeBinding->HasImplementation()) { 1.735 + nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface( 1.736 + aOldDocument->GetScopeObject()); 1.737 + if (global) { 1.738 + nsCOMPtr<nsIScriptContext> context = global->GetContext(); 1.739 + if (context) { 1.740 + JSContext *cx = context->GetNativeContext(); 1.741 + 1.742 + nsCxPusher pusher; 1.743 + pusher.Push(cx); 1.744 + 1.745 + // scope might be null if we've cycle-collected the global 1.746 + // object, since the Unlink phase of cycle collection happens 1.747 + // after JS GC finalization. But in that case, we don't care 1.748 + // about fixing the prototype chain, since everything's going 1.749 + // away immediately. 1.750 + JS::Rooted<JSObject*> scope(cx, global->GetGlobalJSObject()); 1.751 + JS::Rooted<JSObject*> scriptObject(cx, mBoundElement->GetWrapper()); 1.752 + if (scope && scriptObject) { 1.753 + // XXX Stay in sync! What if a layered binding has an 1.754 + // <interface>?! 1.755 + // XXXbz what does that comment mean, really? It seems to date 1.756 + // back to when there was such a thing as an <interface>, whever 1.757 + // that was... 1.758 + 1.759 + // Find the right prototype. 1.760 + JSAutoCompartment ac(cx, scriptObject); 1.761 + 1.762 + JS::Rooted<JSObject*> base(cx, scriptObject); 1.763 + JS::Rooted<JSObject*> proto(cx); 1.764 + for ( ; true; base = proto) { // Will break out on null proto 1.765 + if (!JS_GetPrototype(cx, base, &proto)) { 1.766 + return; 1.767 + } 1.768 + if (!proto) { 1.769 + break; 1.770 + } 1.771 + 1.772 + if (JS_GetClass(proto) != &gPrototypeJSClass) { 1.773 + // Clearly not the right class 1.774 + continue; 1.775 + } 1.776 + 1.777 + nsRefPtr<nsXBLDocumentInfo> docInfo = 1.778 + static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(proto)); 1.779 + if (!docInfo) { 1.780 + // Not the proto we seek 1.781 + continue; 1.782 + } 1.783 + 1.784 + JS::Value protoBinding = ::JS_GetReservedSlot(proto, 0); 1.785 + 1.786 + if (JSVAL_TO_PRIVATE(protoBinding) != mPrototypeBinding) { 1.787 + // Not the right binding 1.788 + continue; 1.789 + } 1.790 + 1.791 + // Alright! This is the right prototype. Pull it out of the 1.792 + // proto chain. 1.793 + JS::Rooted<JSObject*> grandProto(cx); 1.794 + if (!JS_GetPrototype(cx, proto, &grandProto)) { 1.795 + return; 1.796 + } 1.797 + ::JS_SetPrototype(cx, base, grandProto); 1.798 + break; 1.799 + } 1.800 + 1.801 + mPrototypeBinding->UndefineFields(cx, scriptObject); 1.802 + 1.803 + // Don't remove the reference from the document to the 1.804 + // wrapper here since it'll be removed by the element 1.805 + // itself when that's taken out of the document. 1.806 + } 1.807 + } 1.808 + } 1.809 + } 1.810 + 1.811 + // Remove our event handlers 1.812 + UnhookEventHandlers(); 1.813 + 1.814 + { 1.815 + nsAutoScriptBlocker scriptBlocker; 1.816 + 1.817 + // Then do our ancestors. This reverses the construction order, so that at 1.818 + // all times things are consistent as far as everyone is concerned. 1.819 + if (mNextBinding) { 1.820 + mNextBinding->ChangeDocument(aOldDocument, aNewDocument); 1.821 + } 1.822 + 1.823 + // Update the anonymous content. 1.824 + // XXXbz why not only for style bindings? 1.825 + if (mContent) { 1.826 + nsXBLBinding::UninstallAnonymousContent(aOldDocument, mContent); 1.827 + } 1.828 + 1.829 + // Now that we've unbound our anonymous content from the tree and updated 1.830 + // its binding parent, update the insertion parent for content inserted 1.831 + // into our <children> elements. 1.832 + if (mDefaultInsertionPoint) { 1.833 + UpdateInsertionParent(mDefaultInsertionPoint, mBoundElement); 1.834 + } 1.835 + 1.836 + for (size_t i = 0; i < mInsertionPoints.Length(); ++i) { 1.837 + UpdateInsertionParent(mInsertionPoints[i], mBoundElement); 1.838 + } 1.839 + 1.840 + // Now that our inserted children no longer think they're inserted 1.841 + // anywhere, make sure our internal state reflects that as well. 1.842 + ClearInsertionPoints(); 1.843 + } 1.844 +} 1.845 + 1.846 +bool 1.847 +nsXBLBinding::InheritsStyle() const 1.848 +{ 1.849 + // XXX Will have to change if we ever allow multiple bindings to contribute anonymous content. 1.850 + // Most derived binding with anonymous content determines style inheritance for now. 1.851 + 1.852 + // XXX What about bindings with <content> but no kids, e.g., my treecell-text binding? 1.853 + if (mContent) 1.854 + return mPrototypeBinding->InheritsStyle(); 1.855 + 1.856 + if (mNextBinding) 1.857 + return mNextBinding->InheritsStyle(); 1.858 + 1.859 + return true; 1.860 +} 1.861 + 1.862 +void 1.863 +nsXBLBinding::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc, void* aData) 1.864 +{ 1.865 + if (mNextBinding) 1.866 + mNextBinding->WalkRules(aFunc, aData); 1.867 + 1.868 + nsIStyleRuleProcessor *rules = mPrototypeBinding->GetRuleProcessor(); 1.869 + if (rules) 1.870 + (*aFunc)(rules, aData); 1.871 +} 1.872 + 1.873 +// Internal helper methods //////////////////////////////////////////////////////////////// 1.874 + 1.875 +// Get or create a WeakMap object on a given XBL-hosting global. 1.876 +// 1.877 +// The scheme is as follows. XBL-hosting globals (either privileged content 1.878 +// Windows or XBL scopes) get two lazily-defined WeakMap properties. Each 1.879 +// WeakMap is keyed by the grand-proto - i.e. the original prototype of the 1.880 +// content before it was bound, and the prototype of the class object that we 1.881 +// splice in. The values in the WeakMap are simple dictionary-style objects, 1.882 +// mapping from XBL class names to class objects. 1.883 +static JSObject* 1.884 +GetOrCreateClassObjectMap(JSContext *cx, JS::Handle<JSObject*> scope, const char *mapName) 1.885 +{ 1.886 + AssertSameCompartment(cx, scope); 1.887 + MOZ_ASSERT(JS_IsGlobalObject(scope)); 1.888 + MOZ_ASSERT(scope == xpc::GetXBLScopeOrGlobal(cx, scope)); 1.889 + 1.890 + // First, see if the map is already defined. 1.891 + JS::Rooted<JSPropertyDescriptor> desc(cx); 1.892 + if (!JS_GetOwnPropertyDescriptor(cx, scope, mapName, &desc)) { 1.893 + return nullptr; 1.894 + } 1.895 + if (desc.object() && desc.value().isObject() && 1.896 + JS::IsWeakMapObject(&desc.value().toObject())) { 1.897 + return &desc.value().toObject(); 1.898 + } 1.899 + 1.900 + // It's not there. Create and define it. 1.901 + JS::Rooted<JSObject*> map(cx, JS::NewWeakMapObject(cx)); 1.902 + if (!map || !JS_DefineProperty(cx, scope, mapName, map, 1.903 + JSPROP_PERMANENT | JSPROP_READONLY, 1.904 + JS_PropertyStub, JS_StrictPropertyStub)) 1.905 + { 1.906 + return nullptr; 1.907 + } 1.908 + return map; 1.909 +} 1.910 + 1.911 +static JSObject* 1.912 +GetOrCreateMapEntryForPrototype(JSContext *cx, JS::Handle<JSObject*> proto) 1.913 +{ 1.914 + AssertSameCompartment(cx, proto); 1.915 + // We want to hang our class objects off the XBL scope. But since we also 1.916 + // hoist anonymous content into the XBL scope, this creates the potential for 1.917 + // tricky collisions, since we can simultaneously have a bound in-content 1.918 + // node with grand-proto HTMLDivElement and a bound anonymous node whose 1.919 + // grand-proto is the XBL scope's cross-compartment wrapper to HTMLDivElement. 1.920 + // Since we have to wrap the WeakMap keys into its scope, this distinction 1.921 + // would be lost if we don't do something about it. 1.922 + // 1.923 + // So we define two maps - one class objects that live in content (prototyped 1.924 + // to content prototypes), and the other for class objects that live in the 1.925 + // XBL scope (prototyped to cross-compartment-wrapped content prototypes). 1.926 + const char* name = xpc::IsInXBLScope(proto) ? "__ContentClassObjectMap__" 1.927 + : "__XBLClassObjectMap__"; 1.928 + 1.929 + // Now, enter the XBL scope, since that's where we need to operate, and wrap 1.930 + // the proto accordingly. 1.931 + JS::Rooted<JSObject*> scope(cx, xpc::GetXBLScopeOrGlobal(cx, proto)); 1.932 + NS_ENSURE_TRUE(scope, nullptr); 1.933 + JS::Rooted<JSObject*> wrappedProto(cx, proto); 1.934 + JSAutoCompartment ac(cx, scope); 1.935 + if (!JS_WrapObject(cx, &wrappedProto)) { 1.936 + return nullptr; 1.937 + } 1.938 + 1.939 + // Grab the appropriate WeakMap. 1.940 + JS::Rooted<JSObject*> map(cx, GetOrCreateClassObjectMap(cx, scope, name)); 1.941 + if (!map) { 1.942 + return nullptr; 1.943 + } 1.944 + 1.945 + // See if we already have a map entry for that prototype. 1.946 + JS::Rooted<JS::Value> val(cx); 1.947 + if (!JS::GetWeakMapEntry(cx, map, wrappedProto, &val)) { 1.948 + return nullptr; 1.949 + } 1.950 + if (val.isObject()) { 1.951 + return &val.toObject(); 1.952 + } 1.953 + 1.954 + // We don't have an entry. Create one and stick it in the map. 1.955 + JS::Rooted<JSObject*> entry(cx); 1.956 + entry = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), scope); 1.957 + if (!entry) { 1.958 + return nullptr; 1.959 + } 1.960 + JS::Rooted<JS::Value> entryVal(cx, JS::ObjectValue(*entry)); 1.961 + if (!JS::SetWeakMapEntry(cx, map, wrappedProto, entryVal)) { 1.962 + NS_WARNING("SetWeakMapEntry failed, probably due to non-preservable WeakMap " 1.963 + "key. XBL binding will fail for this element."); 1.964 + return nullptr; 1.965 + } 1.966 + return entry; 1.967 +} 1.968 + 1.969 +// static 1.970 +nsresult 1.971 +nsXBLBinding::DoInitJSClass(JSContext *cx, 1.972 + JS::Handle<JSObject*> obj, 1.973 + const nsAFlatCString& aClassName, 1.974 + nsXBLPrototypeBinding* aProtoBinding, 1.975 + JS::MutableHandle<JSObject*> aClassObject, 1.976 + bool* aNew) 1.977 +{ 1.978 + MOZ_ASSERT(obj); 1.979 + 1.980 + // Note that, now that NAC reflectors are created in the XBL scope, the 1.981 + // reflector is not necessarily same-compartment with the document. So we'll 1.982 + // end up creating a separate instance of the oddly-named XBL class object 1.983 + // and defining it as a property on the XBL scope's global. This works fine, 1.984 + // but we need to make sure never to assume that the the reflector and 1.985 + // prototype are same-compartment with the bound document. 1.986 + JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj)); 1.987 + JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, global)); 1.988 + NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED); 1.989 + 1.990 + JS::Rooted<JSObject*> parent_proto(cx); 1.991 + if (!JS_GetPrototype(cx, obj, &parent_proto)) { 1.992 + return NS_ERROR_FAILURE; 1.993 + } 1.994 + 1.995 + // Get the map entry for the parent prototype. In the one-off case that the 1.996 + // parent prototype is null, we somewhat hackily just use the WeakMap itself 1.997 + // as a property holder. 1.998 + JS::Rooted<JSObject*> holder(cx); 1.999 + if (parent_proto) { 1.1000 + holder = GetOrCreateMapEntryForPrototype(cx, parent_proto); 1.1001 + } else { 1.1002 + JSAutoCompartment innerAC(cx, xblScope); 1.1003 + holder = GetOrCreateClassObjectMap(cx, xblScope, "__ContentClassObjectMap__"); 1.1004 + } 1.1005 + if (NS_WARN_IF(!holder)) { 1.1006 + return NS_ERROR_FAILURE; 1.1007 + } 1.1008 + js::AssertSameCompartment(holder, xblScope); 1.1009 + JSAutoCompartment ac(cx, holder); 1.1010 + 1.1011 + // Look up the class on the property holder. The only properties on the 1.1012 + // holder should be class objects. If we don't find the class object, we need 1.1013 + // to create and define it. 1.1014 + JS::Rooted<JSObject*> proto(cx); 1.1015 + JS::Rooted<JSPropertyDescriptor> desc(cx); 1.1016 + if (!JS_GetOwnPropertyDescriptor(cx, holder, aClassName.get(), &desc)) { 1.1017 + return NS_ERROR_OUT_OF_MEMORY; 1.1018 + } 1.1019 + *aNew = !desc.object(); 1.1020 + if (desc.object()) { 1.1021 + proto = &desc.value().toObject(); 1.1022 + MOZ_ASSERT(JS_GetClass(js::UncheckedUnwrap(proto)) == &gPrototypeJSClass); 1.1023 + } else { 1.1024 + 1.1025 + // We need to create the prototype. First, enter the compartment where it's 1.1026 + // going to live, and create it. 1.1027 + JSAutoCompartment ac2(cx, global); 1.1028 + proto = JS_NewObjectWithGivenProto(cx, &gPrototypeJSClass, parent_proto, global); 1.1029 + if (!proto) { 1.1030 + return NS_ERROR_OUT_OF_MEMORY; 1.1031 + } 1.1032 + 1.1033 + // Keep this proto binding alive while we're alive. Do this first so that 1.1034 + // we can guarantee that in XBLFinalize this will be non-null. 1.1035 + // Note that we can't just store aProtoBinding in the private and 1.1036 + // addref/release the nsXBLDocumentInfo through it, because cycle 1.1037 + // collection doesn't seem to work right if the private is not an 1.1038 + // nsISupports. 1.1039 + nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo(); 1.1040 + ::JS_SetPrivate(proto, docInfo); 1.1041 + NS_ADDREF(docInfo); 1.1042 + JS_SetReservedSlot(proto, 0, PRIVATE_TO_JSVAL(aProtoBinding)); 1.1043 + 1.1044 + // Next, enter the compartment of the property holder, wrap the proto, and 1.1045 + // stick it on. 1.1046 + JSAutoCompartment ac3(cx, holder); 1.1047 + if (!JS_WrapObject(cx, &proto) || 1.1048 + !JS_DefineProperty(cx, holder, aClassName.get(), proto, 1.1049 + JSPROP_READONLY | JSPROP_PERMANENT, 1.1050 + JS_PropertyStub, JS_StrictPropertyStub)) 1.1051 + { 1.1052 + return NS_ERROR_OUT_OF_MEMORY; 1.1053 + } 1.1054 + } 1.1055 + 1.1056 + // Whew. We have the proto. Wrap it back into the compartment of |obj|, 1.1057 + // splice it in, and return it. 1.1058 + JSAutoCompartment ac4(cx, obj); 1.1059 + if (!JS_WrapObject(cx, &proto) || !JS_SetPrototype(cx, obj, proto)) { 1.1060 + return NS_ERROR_FAILURE; 1.1061 + } 1.1062 + aClassObject.set(proto); 1.1063 + return NS_OK; 1.1064 +} 1.1065 + 1.1066 +bool 1.1067 +nsXBLBinding::AllowScripts() 1.1068 +{ 1.1069 + return mBoundElement && mPrototypeBinding->GetAllowScripts(); 1.1070 +} 1.1071 + 1.1072 +nsXBLBinding* 1.1073 +nsXBLBinding::RootBinding() 1.1074 +{ 1.1075 + if (mNextBinding) 1.1076 + return mNextBinding->RootBinding(); 1.1077 + 1.1078 + return this; 1.1079 +} 1.1080 + 1.1081 +bool 1.1082 +nsXBLBinding::ResolveAllFields(JSContext *cx, JS::Handle<JSObject*> obj) const 1.1083 +{ 1.1084 + if (!mPrototypeBinding->ResolveAllFields(cx, obj)) { 1.1085 + return false; 1.1086 + } 1.1087 + 1.1088 + if (mNextBinding) { 1.1089 + return mNextBinding->ResolveAllFields(cx, obj); 1.1090 + } 1.1091 + 1.1092 + return true; 1.1093 +} 1.1094 + 1.1095 +bool 1.1096 +nsXBLBinding::LookupMember(JSContext* aCx, JS::Handle<jsid> aId, 1.1097 + JS::MutableHandle<JSPropertyDescriptor> aDesc) 1.1098 +{ 1.1099 + // We should never enter this function with a pre-filled property descriptor. 1.1100 + MOZ_ASSERT(!aDesc.object()); 1.1101 + 1.1102 + // Get the string as an nsString before doing anything, so we can make 1.1103 + // convenient comparisons during our search. 1.1104 + if (!JSID_IS_STRING(aId)) { 1.1105 + return true; 1.1106 + } 1.1107 + nsDependentJSString name(aId); 1.1108 + 1.1109 + // We have a weak reference to our bound element, so make sure it's alive. 1.1110 + if (!mBoundElement || !mBoundElement->GetWrapper()) { 1.1111 + return false; 1.1112 + } 1.1113 + 1.1114 + // Get the scope of mBoundElement and the associated XBL scope. We should only 1.1115 + // be calling into this machinery if we're running in a separate XBL scope. 1.1116 + // 1.1117 + // Note that we only end up in LookupMember for XrayWrappers from XBL scopes 1.1118 + // into content. So for NAC reflectors that live in the XBL scope, we should 1.1119 + // never get here. But on the off-chance that someone adds new callsites to 1.1120 + // LookupMember, we do a release-mode assertion as belt-and-braces. 1.1121 + // We do a release-mode assertion here to be extra safe. 1.1122 + JS::Rooted<JSObject*> boundScope(aCx, 1.1123 + js::GetGlobalForObjectCrossCompartment(mBoundElement->GetWrapper())); 1.1124 + MOZ_RELEASE_ASSERT(!xpc::IsInXBLScope(boundScope)); 1.1125 + JS::Rooted<JSObject*> xblScope(aCx, xpc::GetXBLScope(aCx, boundScope)); 1.1126 + NS_ENSURE_TRUE(xblScope, false); 1.1127 + MOZ_ASSERT(boundScope != xblScope); 1.1128 + 1.1129 + // Enter the xbl scope and invoke the internal version. 1.1130 + { 1.1131 + JSAutoCompartment ac(aCx, xblScope); 1.1132 + JS::Rooted<jsid> id(aCx, aId); 1.1133 + if (!JS_WrapId(aCx, &id) || 1.1134 + !LookupMemberInternal(aCx, name, id, aDesc, xblScope)) 1.1135 + { 1.1136 + return false; 1.1137 + } 1.1138 + } 1.1139 + 1.1140 + // Wrap into the caller's scope. 1.1141 + return JS_WrapPropertyDescriptor(aCx, aDesc); 1.1142 +} 1.1143 + 1.1144 +bool 1.1145 +nsXBLBinding::LookupMemberInternal(JSContext* aCx, nsString& aName, 1.1146 + JS::Handle<jsid> aNameAsId, 1.1147 + JS::MutableHandle<JSPropertyDescriptor> aDesc, 1.1148 + JS::Handle<JSObject*> aXBLScope) 1.1149 +{ 1.1150 + // First, see if we have an implementation. If we don't, it means that this 1.1151 + // binding doesn't have a class object, and thus doesn't have any members. 1.1152 + // Skip it. 1.1153 + if (!PrototypeBinding()->HasImplementation()) { 1.1154 + if (!mNextBinding) { 1.1155 + return true; 1.1156 + } 1.1157 + return mNextBinding->LookupMemberInternal(aCx, aName, aNameAsId, 1.1158 + aDesc, aXBLScope); 1.1159 + } 1.1160 + 1.1161 + // Find our class object. It's in a protected scope and permanent just in case, 1.1162 + // so should be there no matter what. 1.1163 + JS::Rooted<JS::Value> classObject(aCx); 1.1164 + if (!JS_GetProperty(aCx, aXBLScope, PrototypeBinding()->ClassName().get(), 1.1165 + &classObject)) { 1.1166 + return false; 1.1167 + } 1.1168 + 1.1169 + // The bound element may have been adoped by a document and have a different 1.1170 + // wrapper (and different xbl scope) than when the binding was applied, in 1.1171 + // this case getting the class object will fail. Behave as if the class 1.1172 + // object did not exist. 1.1173 + if (classObject.isUndefined()) { 1.1174 + return true; 1.1175 + } 1.1176 + 1.1177 + MOZ_ASSERT(classObject.isObject()); 1.1178 + 1.1179 + // Look for the property on this binding. If it's not there, try the next 1.1180 + // binding on the chain. 1.1181 + nsXBLProtoImpl* impl = mPrototypeBinding->GetImplementation(); 1.1182 + JS::Rooted<JSObject*> object(aCx, &classObject.toObject()); 1.1183 + if (impl && !impl->LookupMember(aCx, aName, aNameAsId, aDesc, object)) { 1.1184 + return false; 1.1185 + } 1.1186 + if (aDesc.object() || !mNextBinding) { 1.1187 + return true; 1.1188 + } 1.1189 + 1.1190 + return mNextBinding->LookupMemberInternal(aCx, aName, aNameAsId, aDesc, 1.1191 + aXBLScope); 1.1192 +} 1.1193 + 1.1194 +bool 1.1195 +nsXBLBinding::HasField(nsString& aName) 1.1196 +{ 1.1197 + // See if this binding has such a field. 1.1198 + return mPrototypeBinding->FindField(aName) || 1.1199 + (mNextBinding && mNextBinding->HasField(aName)); 1.1200 +} 1.1201 + 1.1202 +void 1.1203 +nsXBLBinding::MarkForDeath() 1.1204 +{ 1.1205 + mMarkedForDeath = true; 1.1206 + ExecuteDetachedHandler(); 1.1207 +} 1.1208 + 1.1209 +bool 1.1210 +nsXBLBinding::ImplementsInterface(REFNSIID aIID) const 1.1211 +{ 1.1212 + return mPrototypeBinding->ImplementsInterface(aIID) || 1.1213 + (mNextBinding && mNextBinding->ImplementsInterface(aIID)); 1.1214 +}