Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=2 sw=2 et tw=79: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "nsCOMPtr.h" |
michael@0 | 8 | #include "nsIAtom.h" |
michael@0 | 9 | #include "nsXBLDocumentInfo.h" |
michael@0 | 10 | #include "nsIInputStream.h" |
michael@0 | 11 | #include "nsNameSpaceManager.h" |
michael@0 | 12 | #include "nsIURI.h" |
michael@0 | 13 | #include "nsIURL.h" |
michael@0 | 14 | #include "nsIChannel.h" |
michael@0 | 15 | #include "nsXPIDLString.h" |
michael@0 | 16 | #include "nsReadableUtils.h" |
michael@0 | 17 | #include "nsNetUtil.h" |
michael@0 | 18 | #include "plstr.h" |
michael@0 | 19 | #include "nsIContent.h" |
michael@0 | 20 | #include "nsIDocument.h" |
michael@0 | 21 | #include "nsContentUtils.h" |
michael@0 | 22 | #include "ChildIterator.h" |
michael@0 | 23 | #include "nsCxPusher.h" |
michael@0 | 24 | #ifdef MOZ_XUL |
michael@0 | 25 | #include "nsIXULDocument.h" |
michael@0 | 26 | #endif |
michael@0 | 27 | #include "nsIXMLContentSink.h" |
michael@0 | 28 | #include "nsContentCID.h" |
michael@0 | 29 | #include "mozilla/dom/XMLDocument.h" |
michael@0 | 30 | #include "jsapi.h" |
michael@0 | 31 | #include "nsXBLService.h" |
michael@0 | 32 | #include "nsIXPConnect.h" |
michael@0 | 33 | #include "nsIScriptContext.h" |
michael@0 | 34 | #include "nsCRT.h" |
michael@0 | 35 | |
michael@0 | 36 | // Event listeners |
michael@0 | 37 | #include "mozilla/EventListenerManager.h" |
michael@0 | 38 | #include "nsIDOMEventListener.h" |
michael@0 | 39 | #include "nsAttrName.h" |
michael@0 | 40 | |
michael@0 | 41 | #include "nsGkAtoms.h" |
michael@0 | 42 | |
michael@0 | 43 | #include "nsXBLPrototypeHandler.h" |
michael@0 | 44 | |
michael@0 | 45 | #include "nsXBLPrototypeBinding.h" |
michael@0 | 46 | #include "nsXBLBinding.h" |
michael@0 | 47 | #include "nsIPrincipal.h" |
michael@0 | 48 | #include "nsIScriptSecurityManager.h" |
michael@0 | 49 | #include "mozilla/dom/XBLChildrenElement.h" |
michael@0 | 50 | |
michael@0 | 51 | #include "prprf.h" |
michael@0 | 52 | #include "nsNodeUtils.h" |
michael@0 | 53 | #include "nsJSUtils.h" |
michael@0 | 54 | |
michael@0 | 55 | // Nasty hack. Maybe we could move some of the classinfo utility methods |
michael@0 | 56 | // (e.g. WrapNative) over to nsContentUtils? |
michael@0 | 57 | #include "nsDOMClassInfo.h" |
michael@0 | 58 | |
michael@0 | 59 | #include "mozilla/dom/Element.h" |
michael@0 | 60 | #include "mozilla/dom/ShadowRoot.h" |
michael@0 | 61 | |
michael@0 | 62 | using namespace mozilla; |
michael@0 | 63 | using namespace mozilla::dom; |
michael@0 | 64 | |
michael@0 | 65 | // Helper classes |
michael@0 | 66 | |
michael@0 | 67 | /***********************************************************************/ |
michael@0 | 68 | // |
michael@0 | 69 | // The JS class for XBLBinding |
michael@0 | 70 | // |
michael@0 | 71 | static void |
michael@0 | 72 | XBLFinalize(JSFreeOp *fop, JSObject *obj) |
michael@0 | 73 | { |
michael@0 | 74 | nsXBLDocumentInfo* docInfo = |
michael@0 | 75 | static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj)); |
michael@0 | 76 | nsContentUtils::DeferredFinalize(docInfo); |
michael@0 | 77 | } |
michael@0 | 78 | |
michael@0 | 79 | static bool |
michael@0 | 80 | XBLEnumerate(JSContext *cx, JS::Handle<JSObject*> obj) |
michael@0 | 81 | { |
michael@0 | 82 | nsXBLPrototypeBinding* protoBinding = |
michael@0 | 83 | static_cast<nsXBLPrototypeBinding*>(::JS_GetReservedSlot(obj, 0).toPrivate()); |
michael@0 | 84 | MOZ_ASSERT(protoBinding); |
michael@0 | 85 | |
michael@0 | 86 | return protoBinding->ResolveAllFields(cx, obj); |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | static const JSClass gPrototypeJSClass = { |
michael@0 | 90 | "XBL prototype JSClass", |
michael@0 | 91 | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | |
michael@0 | 92 | JSCLASS_NEW_RESOLVE | |
michael@0 | 93 | // Our one reserved slot holds the relevant nsXBLPrototypeBinding |
michael@0 | 94 | JSCLASS_HAS_RESERVED_SLOTS(1), |
michael@0 | 95 | JS_PropertyStub, JS_DeletePropertyStub, |
michael@0 | 96 | JS_PropertyStub, JS_StrictPropertyStub, |
michael@0 | 97 | XBLEnumerate, JS_ResolveStub, |
michael@0 | 98 | JS_ConvertStub, XBLFinalize, |
michael@0 | 99 | nullptr, nullptr, nullptr, nullptr |
michael@0 | 100 | }; |
michael@0 | 101 | |
michael@0 | 102 | // Implementation ///////////////////////////////////////////////////////////////// |
michael@0 | 103 | |
michael@0 | 104 | // Constructors/Destructors |
michael@0 | 105 | nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding) |
michael@0 | 106 | : mMarkedForDeath(false) |
michael@0 | 107 | , mUsingXBLScope(false) |
michael@0 | 108 | , mPrototypeBinding(aBinding) |
michael@0 | 109 | { |
michael@0 | 110 | NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!"); |
michael@0 | 111 | // Grab a ref to the document info so the prototype binding won't die |
michael@0 | 112 | NS_ADDREF(mPrototypeBinding->XBLDocumentInfo()); |
michael@0 | 113 | } |
michael@0 | 114 | |
michael@0 | 115 | // Constructor used by web components. |
michael@0 | 116 | nsXBLBinding::nsXBLBinding(ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aBinding) |
michael@0 | 117 | : mMarkedForDeath(false), |
michael@0 | 118 | mUsingXBLScope(false), |
michael@0 | 119 | mPrototypeBinding(aBinding), |
michael@0 | 120 | mContent(aShadowRoot) |
michael@0 | 121 | { |
michael@0 | 122 | NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!"); |
michael@0 | 123 | // Grab a ref to the document info so the prototype binding won't die |
michael@0 | 124 | NS_ADDREF(mPrototypeBinding->XBLDocumentInfo()); |
michael@0 | 125 | } |
michael@0 | 126 | |
michael@0 | 127 | nsXBLBinding::~nsXBLBinding(void) |
michael@0 | 128 | { |
michael@0 | 129 | if (mContent) { |
michael@0 | 130 | nsXBLBinding::UninstallAnonymousContent(mContent->OwnerDoc(), mContent); |
michael@0 | 131 | } |
michael@0 | 132 | nsXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo(); |
michael@0 | 133 | NS_RELEASE(info); |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLBinding) |
michael@0 | 137 | |
michael@0 | 138 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLBinding) |
michael@0 | 139 | // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because |
michael@0 | 140 | // mPrototypeBinding is weak. |
michael@0 | 141 | if (tmp->mContent) { |
michael@0 | 142 | nsXBLBinding::UninstallAnonymousContent(tmp->mContent->OwnerDoc(), |
michael@0 | 143 | tmp->mContent); |
michael@0 | 144 | } |
michael@0 | 145 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent) |
michael@0 | 146 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextBinding) |
michael@0 | 147 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDefaultInsertionPoint) |
michael@0 | 148 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mInsertionPoints) |
michael@0 | 149 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContentList) |
michael@0 | 150 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
michael@0 | 151 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLBinding) |
michael@0 | 152 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, |
michael@0 | 153 | "mPrototypeBinding->XBLDocumentInfo()"); |
michael@0 | 154 | cb.NoteXPCOMChild(tmp->mPrototypeBinding->XBLDocumentInfo()); |
michael@0 | 155 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent) |
michael@0 | 156 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextBinding) |
michael@0 | 157 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultInsertionPoint) |
michael@0 | 158 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInsertionPoints) |
michael@0 | 159 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContentList) |
michael@0 | 160 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
michael@0 | 161 | NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding, AddRef) |
michael@0 | 162 | NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding, Release) |
michael@0 | 163 | |
michael@0 | 164 | void |
michael@0 | 165 | nsXBLBinding::SetBaseBinding(nsXBLBinding* aBinding) |
michael@0 | 166 | { |
michael@0 | 167 | if (mNextBinding) { |
michael@0 | 168 | NS_ERROR("Base XBL binding is already defined!"); |
michael@0 | 169 | return; |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | mNextBinding = aBinding; // Comptr handles rel/add |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | nsXBLBinding* |
michael@0 | 176 | nsXBLBinding::GetBindingWithContent() |
michael@0 | 177 | { |
michael@0 | 178 | if (mContent) { |
michael@0 | 179 | return this; |
michael@0 | 180 | } |
michael@0 | 181 | |
michael@0 | 182 | return mNextBinding ? mNextBinding->GetBindingWithContent() : nullptr; |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | void |
michael@0 | 186 | nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement, |
michael@0 | 187 | bool aChromeOnlyContent) |
michael@0 | 188 | { |
michael@0 | 189 | // We need to ensure two things. |
michael@0 | 190 | // (1) The anonymous content should be fooled into thinking it's in the bound |
michael@0 | 191 | // element's document, assuming that the bound element is in a document |
michael@0 | 192 | // Note that we don't change the current doc of aAnonParent here, since that |
michael@0 | 193 | // quite simply does not matter. aAnonParent is just a way of keeping refs |
michael@0 | 194 | // to all its kids, which are anonymous content from the point of view of |
michael@0 | 195 | // aElement. |
michael@0 | 196 | // (2) The children's parent back pointer should not be to this synthetic root |
michael@0 | 197 | // but should instead point to the enclosing parent element. |
michael@0 | 198 | nsIDocument* doc = aElement->GetCurrentDoc(); |
michael@0 | 199 | bool allowScripts = AllowScripts(); |
michael@0 | 200 | |
michael@0 | 201 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 202 | for (nsIContent* child = aAnonParent->GetFirstChild(); |
michael@0 | 203 | child; |
michael@0 | 204 | child = child->GetNextSibling()) { |
michael@0 | 205 | child->UnbindFromTree(); |
michael@0 | 206 | if (aChromeOnlyContent) { |
michael@0 | 207 | child->SetFlags(NODE_CHROME_ONLY_ACCESS | |
michael@0 | 208 | NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS); |
michael@0 | 209 | } |
michael@0 | 210 | nsresult rv = |
michael@0 | 211 | child->BindToTree(doc, aElement, mBoundElement, allowScripts); |
michael@0 | 212 | if (NS_FAILED(rv)) { |
michael@0 | 213 | // Oh, well... Just give up. |
michael@0 | 214 | // XXXbz This really shouldn't be a void method! |
michael@0 | 215 | child->UnbindFromTree(); |
michael@0 | 216 | return; |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | child->SetFlags(NODE_IS_ANONYMOUS_ROOT); |
michael@0 | 220 | |
michael@0 | 221 | #ifdef MOZ_XUL |
michael@0 | 222 | // To make XUL templates work (and other goodies that happen when |
michael@0 | 223 | // an element is added to a XUL document), we need to notify the |
michael@0 | 224 | // XUL document using its special API. |
michael@0 | 225 | nsCOMPtr<nsIXULDocument> xuldoc(do_QueryInterface(doc)); |
michael@0 | 226 | if (xuldoc) |
michael@0 | 227 | xuldoc->AddSubtreeToDocument(child); |
michael@0 | 228 | #endif |
michael@0 | 229 | } |
michael@0 | 230 | } |
michael@0 | 231 | |
michael@0 | 232 | void |
michael@0 | 233 | nsXBLBinding::UninstallAnonymousContent(nsIDocument* aDocument, |
michael@0 | 234 | nsIContent* aAnonParent) |
michael@0 | 235 | { |
michael@0 | 236 | if (aAnonParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) { |
michael@0 | 237 | // It is unnecessary to uninstall anonymous content in a shadow tree |
michael@0 | 238 | // because the ShadowRoot itself is a DocumentFragment and does not |
michael@0 | 239 | // need any additional cleanup. |
michael@0 | 240 | return; |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 244 | // Hold a strong ref while doing this, just in case. |
michael@0 | 245 | nsCOMPtr<nsIContent> anonParent = aAnonParent; |
michael@0 | 246 | #ifdef MOZ_XUL |
michael@0 | 247 | nsCOMPtr<nsIXULDocument> xuldoc = |
michael@0 | 248 | do_QueryInterface(aDocument); |
michael@0 | 249 | #endif |
michael@0 | 250 | for (nsIContent* child = aAnonParent->GetFirstChild(); |
michael@0 | 251 | child; |
michael@0 | 252 | child = child->GetNextSibling()) { |
michael@0 | 253 | child->UnbindFromTree(); |
michael@0 | 254 | #ifdef MOZ_XUL |
michael@0 | 255 | if (xuldoc) { |
michael@0 | 256 | xuldoc->RemoveSubtreeFromDocument(child); |
michael@0 | 257 | } |
michael@0 | 258 | #endif |
michael@0 | 259 | } |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | void |
michael@0 | 263 | nsXBLBinding::SetBoundElement(nsIContent* aElement) |
michael@0 | 264 | { |
michael@0 | 265 | mBoundElement = aElement; |
michael@0 | 266 | if (mNextBinding) |
michael@0 | 267 | mNextBinding->SetBoundElement(aElement); |
michael@0 | 268 | |
michael@0 | 269 | if (!mBoundElement) { |
michael@0 | 270 | return; |
michael@0 | 271 | } |
michael@0 | 272 | |
michael@0 | 273 | // Compute whether we're using an XBL scope. |
michael@0 | 274 | // |
michael@0 | 275 | // We disable XBL scopes for remote XUL, where we care about compat more |
michael@0 | 276 | // than security. So we need to know whether we're using an XBL scope so that |
michael@0 | 277 | // we can decide what to do about untrusted events when "allowuntrusted" |
michael@0 | 278 | // is not given in the handler declaration. |
michael@0 | 279 | nsCOMPtr<nsIGlobalObject> go = mBoundElement->OwnerDoc()->GetScopeObject(); |
michael@0 | 280 | NS_ENSURE_TRUE_VOID(go && go->GetGlobalJSObject()); |
michael@0 | 281 | mUsingXBLScope = xpc::UseXBLScope(js::GetObjectCompartment(go->GetGlobalJSObject())); |
michael@0 | 282 | } |
michael@0 | 283 | |
michael@0 | 284 | bool |
michael@0 | 285 | nsXBLBinding::HasStyleSheets() const |
michael@0 | 286 | { |
michael@0 | 287 | // Find out if we need to re-resolve style. We'll need to do this |
michael@0 | 288 | // if we have additional stylesheets in our binding document. |
michael@0 | 289 | if (mPrototypeBinding->HasStyleSheets()) |
michael@0 | 290 | return true; |
michael@0 | 291 | |
michael@0 | 292 | return mNextBinding ? mNextBinding->HasStyleSheets() : false; |
michael@0 | 293 | } |
michael@0 | 294 | |
michael@0 | 295 | void |
michael@0 | 296 | nsXBLBinding::GenerateAnonymousContent() |
michael@0 | 297 | { |
michael@0 | 298 | NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), |
michael@0 | 299 | "Someone forgot a script blocker"); |
michael@0 | 300 | |
michael@0 | 301 | // Fetch the content element for this binding. |
michael@0 | 302 | nsIContent* content = |
michael@0 | 303 | mPrototypeBinding->GetImmediateChild(nsGkAtoms::content); |
michael@0 | 304 | |
michael@0 | 305 | if (!content) { |
michael@0 | 306 | // We have no anonymous content. |
michael@0 | 307 | if (mNextBinding) |
michael@0 | 308 | mNextBinding->GenerateAnonymousContent(); |
michael@0 | 309 | |
michael@0 | 310 | return; |
michael@0 | 311 | } |
michael@0 | 312 | |
michael@0 | 313 | // Find out if we're really building kids or if we're just |
michael@0 | 314 | // using the attribute-setting shorthand hack. |
michael@0 | 315 | uint32_t contentCount = content->GetChildCount(); |
michael@0 | 316 | |
michael@0 | 317 | // Plan to build the content by default. |
michael@0 | 318 | bool hasContent = (contentCount > 0); |
michael@0 | 319 | if (hasContent) { |
michael@0 | 320 | nsIDocument* doc = mBoundElement->OwnerDoc(); |
michael@0 | 321 | |
michael@0 | 322 | nsCOMPtr<nsINode> clonedNode; |
michael@0 | 323 | nsCOMArray<nsINode> nodesWithProperties; |
michael@0 | 324 | nsNodeUtils::Clone(content, true, doc->NodeInfoManager(), |
michael@0 | 325 | nodesWithProperties, getter_AddRefs(clonedNode)); |
michael@0 | 326 | mContent = clonedNode->AsElement(); |
michael@0 | 327 | |
michael@0 | 328 | // Search for <xbl:children> elements in the XBL content. In the presence |
michael@0 | 329 | // of multiple default insertion points, we use the last one in document |
michael@0 | 330 | // order. |
michael@0 | 331 | for (nsIContent* child = mContent; child; child = child->GetNextNode(mContent)) { |
michael@0 | 332 | if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { |
michael@0 | 333 | XBLChildrenElement* point = static_cast<XBLChildrenElement*>(child); |
michael@0 | 334 | if (point->IsDefaultInsertion()) { |
michael@0 | 335 | mDefaultInsertionPoint = point; |
michael@0 | 336 | } else { |
michael@0 | 337 | mInsertionPoints.AppendElement(point); |
michael@0 | 338 | } |
michael@0 | 339 | } |
michael@0 | 340 | } |
michael@0 | 341 | |
michael@0 | 342 | // Do this after looking for <children> as this messes up the parent |
michael@0 | 343 | // pointer which would make the GetNextNode call above fail |
michael@0 | 344 | InstallAnonymousContent(mContent, mBoundElement, |
michael@0 | 345 | mPrototypeBinding->ChromeOnlyContent()); |
michael@0 | 346 | |
michael@0 | 347 | // Insert explicit children into insertion points |
michael@0 | 348 | if (mDefaultInsertionPoint && mInsertionPoints.IsEmpty()) { |
michael@0 | 349 | ExplicitChildIterator iter(mBoundElement); |
michael@0 | 350 | for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { |
michael@0 | 351 | mDefaultInsertionPoint->AppendInsertedChild(child); |
michael@0 | 352 | } |
michael@0 | 353 | } else { |
michael@0 | 354 | // It is odd to come into this code if mInsertionPoints is not empty, but |
michael@0 | 355 | // we need to make sure to do the compatibility hack below if the bound |
michael@0 | 356 | // node has any non <xul:template> or <xul:observes> children. |
michael@0 | 357 | ExplicitChildIterator iter(mBoundElement); |
michael@0 | 358 | for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { |
michael@0 | 359 | XBLChildrenElement* point = FindInsertionPointForInternal(child); |
michael@0 | 360 | if (point) { |
michael@0 | 361 | point->AppendInsertedChild(child); |
michael@0 | 362 | } else { |
michael@0 | 363 | nsINodeInfo *ni = child->NodeInfo(); |
michael@0 | 364 | if (ni->NamespaceID() != kNameSpaceID_XUL || |
michael@0 | 365 | (!ni->Equals(nsGkAtoms::_template) && |
michael@0 | 366 | !ni->Equals(nsGkAtoms::observes))) { |
michael@0 | 367 | // Compatibility hack. For some reason the original XBL |
michael@0 | 368 | // implementation dropped the content of a binding if any child of |
michael@0 | 369 | // the bound element didn't match any of the <children> in the |
michael@0 | 370 | // binding. This became a pseudo-API that we have to maintain. |
michael@0 | 371 | |
michael@0 | 372 | // Undo InstallAnonymousContent |
michael@0 | 373 | UninstallAnonymousContent(doc, mContent); |
michael@0 | 374 | |
michael@0 | 375 | // Clear out our children elements to avoid dangling references. |
michael@0 | 376 | ClearInsertionPoints(); |
michael@0 | 377 | |
michael@0 | 378 | // Pretend as though there was no content in the binding. |
michael@0 | 379 | mContent = nullptr; |
michael@0 | 380 | return; |
michael@0 | 381 | } |
michael@0 | 382 | } |
michael@0 | 383 | } |
michael@0 | 384 | } |
michael@0 | 385 | |
michael@0 | 386 | // Set binding parent on default content if need |
michael@0 | 387 | if (mDefaultInsertionPoint) { |
michael@0 | 388 | mDefaultInsertionPoint->MaybeSetupDefaultContent(); |
michael@0 | 389 | } |
michael@0 | 390 | for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) { |
michael@0 | 391 | mInsertionPoints[i]->MaybeSetupDefaultContent(); |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | mPrototypeBinding->SetInitialAttributes(mBoundElement, mContent); |
michael@0 | 395 | } |
michael@0 | 396 | |
michael@0 | 397 | // Always check the content element for potential attributes. |
michael@0 | 398 | // This shorthand hack always happens, even when we didn't |
michael@0 | 399 | // build anonymous content. |
michael@0 | 400 | const nsAttrName* attrName; |
michael@0 | 401 | for (uint32_t i = 0; (attrName = content->GetAttrNameAt(i)); ++i) { |
michael@0 | 402 | int32_t namespaceID = attrName->NamespaceID(); |
michael@0 | 403 | // Hold a strong reference here so that the atom doesn't go away during |
michael@0 | 404 | // UnsetAttr. |
michael@0 | 405 | nsCOMPtr<nsIAtom> name = attrName->LocalName(); |
michael@0 | 406 | |
michael@0 | 407 | if (name != nsGkAtoms::includes) { |
michael@0 | 408 | if (!nsContentUtils::HasNonEmptyAttr(mBoundElement, namespaceID, name)) { |
michael@0 | 409 | nsAutoString value2; |
michael@0 | 410 | content->GetAttr(namespaceID, name, value2); |
michael@0 | 411 | mBoundElement->SetAttr(namespaceID, name, attrName->GetPrefix(), |
michael@0 | 412 | value2, false); |
michael@0 | 413 | } |
michael@0 | 414 | } |
michael@0 | 415 | |
michael@0 | 416 | // Conserve space by wiping the attributes off the clone. |
michael@0 | 417 | if (mContent) |
michael@0 | 418 | mContent->UnsetAttr(namespaceID, name, false); |
michael@0 | 419 | } |
michael@0 | 420 | } |
michael@0 | 421 | |
michael@0 | 422 | XBLChildrenElement* |
michael@0 | 423 | nsXBLBinding::FindInsertionPointFor(nsIContent* aChild) |
michael@0 | 424 | { |
michael@0 | 425 | // XXX We should get rid of this function as it causes us to traverse the |
michael@0 | 426 | // binding chain multiple times |
michael@0 | 427 | if (mContent) { |
michael@0 | 428 | return FindInsertionPointForInternal(aChild); |
michael@0 | 429 | } |
michael@0 | 430 | |
michael@0 | 431 | return mNextBinding ? mNextBinding->FindInsertionPointFor(aChild) |
michael@0 | 432 | : nullptr; |
michael@0 | 433 | } |
michael@0 | 434 | |
michael@0 | 435 | XBLChildrenElement* |
michael@0 | 436 | nsXBLBinding::FindInsertionPointForInternal(nsIContent* aChild) |
michael@0 | 437 | { |
michael@0 | 438 | for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) { |
michael@0 | 439 | XBLChildrenElement* point = mInsertionPoints[i]; |
michael@0 | 440 | if (point->Includes(aChild)) { |
michael@0 | 441 | return point; |
michael@0 | 442 | } |
michael@0 | 443 | } |
michael@0 | 444 | |
michael@0 | 445 | return mDefaultInsertionPoint; |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | void |
michael@0 | 449 | nsXBLBinding::ClearInsertionPoints() |
michael@0 | 450 | { |
michael@0 | 451 | if (mDefaultInsertionPoint) { |
michael@0 | 452 | mDefaultInsertionPoint->ClearInsertedChildren(); |
michael@0 | 453 | } |
michael@0 | 454 | |
michael@0 | 455 | for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) { |
michael@0 | 456 | mInsertionPoints[i]->ClearInsertedChildren(); |
michael@0 | 457 | } |
michael@0 | 458 | } |
michael@0 | 459 | |
michael@0 | 460 | nsAnonymousContentList* |
michael@0 | 461 | nsXBLBinding::GetAnonymousNodeList() |
michael@0 | 462 | { |
michael@0 | 463 | if (!mContent) { |
michael@0 | 464 | return mNextBinding ? mNextBinding->GetAnonymousNodeList() : nullptr; |
michael@0 | 465 | } |
michael@0 | 466 | |
michael@0 | 467 | if (!mAnonymousContentList) { |
michael@0 | 468 | mAnonymousContentList = new nsAnonymousContentList(mContent); |
michael@0 | 469 | } |
michael@0 | 470 | |
michael@0 | 471 | return mAnonymousContentList; |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | void |
michael@0 | 475 | nsXBLBinding::InstallEventHandlers() |
michael@0 | 476 | { |
michael@0 | 477 | // Don't install handlers if scripts aren't allowed. |
michael@0 | 478 | if (AllowScripts()) { |
michael@0 | 479 | // Fetch the handlers prototypes for this binding. |
michael@0 | 480 | nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers(); |
michael@0 | 481 | |
michael@0 | 482 | if (handlerChain) { |
michael@0 | 483 | EventListenerManager* manager = mBoundElement->GetOrCreateListenerManager(); |
michael@0 | 484 | if (!manager) |
michael@0 | 485 | return; |
michael@0 | 486 | |
michael@0 | 487 | bool isChromeDoc = |
michael@0 | 488 | nsContentUtils::IsChromeDoc(mBoundElement->OwnerDoc()); |
michael@0 | 489 | bool isChromeBinding = mPrototypeBinding->IsChrome(); |
michael@0 | 490 | nsXBLPrototypeHandler* curr; |
michael@0 | 491 | for (curr = handlerChain; curr; curr = curr->GetNextHandler()) { |
michael@0 | 492 | // Fetch the event type. |
michael@0 | 493 | nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName(); |
michael@0 | 494 | if (!eventAtom || |
michael@0 | 495 | eventAtom == nsGkAtoms::keyup || |
michael@0 | 496 | eventAtom == nsGkAtoms::keydown || |
michael@0 | 497 | eventAtom == nsGkAtoms::keypress) |
michael@0 | 498 | continue; |
michael@0 | 499 | |
michael@0 | 500 | nsXBLEventHandler* handler = curr->GetEventHandler(); |
michael@0 | 501 | if (handler) { |
michael@0 | 502 | // Figure out if we're using capturing or not. |
michael@0 | 503 | EventListenerFlags flags; |
michael@0 | 504 | flags.mCapture = (curr->GetPhase() == NS_PHASE_CAPTURING); |
michael@0 | 505 | |
michael@0 | 506 | // If this is a command, add it in the system event group |
michael@0 | 507 | if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | |
michael@0 | 508 | NS_HANDLER_TYPE_SYSTEM)) && |
michael@0 | 509 | (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) { |
michael@0 | 510 | flags.mInSystemGroup = true; |
michael@0 | 511 | } |
michael@0 | 512 | |
michael@0 | 513 | bool hasAllowUntrustedAttr = curr->HasAllowUntrustedAttr(); |
michael@0 | 514 | if ((hasAllowUntrustedAttr && curr->AllowUntrustedEvents()) || |
michael@0 | 515 | (!hasAllowUntrustedAttr && !isChromeDoc && !mUsingXBLScope)) { |
michael@0 | 516 | flags.mAllowUntrustedEvents = true; |
michael@0 | 517 | } |
michael@0 | 518 | |
michael@0 | 519 | manager->AddEventListenerByType(handler, |
michael@0 | 520 | nsDependentAtomString(eventAtom), |
michael@0 | 521 | flags); |
michael@0 | 522 | } |
michael@0 | 523 | } |
michael@0 | 524 | |
michael@0 | 525 | const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers = |
michael@0 | 526 | mPrototypeBinding->GetKeyEventHandlers(); |
michael@0 | 527 | int32_t i; |
michael@0 | 528 | for (i = 0; i < keyHandlers->Count(); ++i) { |
michael@0 | 529 | nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i); |
michael@0 | 530 | handler->SetIsBoundToChrome(isChromeDoc); |
michael@0 | 531 | handler->SetUsingXBLScope(mUsingXBLScope); |
michael@0 | 532 | |
michael@0 | 533 | nsAutoString type; |
michael@0 | 534 | handler->GetEventName(type); |
michael@0 | 535 | |
michael@0 | 536 | // If this is a command, add it in the system event group, otherwise |
michael@0 | 537 | // add it to the standard event group. |
michael@0 | 538 | |
michael@0 | 539 | // Figure out if we're using capturing or not. |
michael@0 | 540 | EventListenerFlags flags; |
michael@0 | 541 | flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING); |
michael@0 | 542 | |
michael@0 | 543 | if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | |
michael@0 | 544 | NS_HANDLER_TYPE_SYSTEM)) && |
michael@0 | 545 | (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) { |
michael@0 | 546 | flags.mInSystemGroup = true; |
michael@0 | 547 | } |
michael@0 | 548 | |
michael@0 | 549 | // For key handlers we have to set mAllowUntrustedEvents flag. |
michael@0 | 550 | // Whether the handling of the event is allowed or not is handled in |
michael@0 | 551 | // nsXBLKeyEventHandler::HandleEvent |
michael@0 | 552 | flags.mAllowUntrustedEvents = true; |
michael@0 | 553 | |
michael@0 | 554 | manager->AddEventListenerByType(handler, type, flags); |
michael@0 | 555 | } |
michael@0 | 556 | } |
michael@0 | 557 | } |
michael@0 | 558 | |
michael@0 | 559 | if (mNextBinding) |
michael@0 | 560 | mNextBinding->InstallEventHandlers(); |
michael@0 | 561 | } |
michael@0 | 562 | |
michael@0 | 563 | nsresult |
michael@0 | 564 | nsXBLBinding::InstallImplementation() |
michael@0 | 565 | { |
michael@0 | 566 | // Always install the base class properties first, so that |
michael@0 | 567 | // derived classes can reference the base class properties. |
michael@0 | 568 | |
michael@0 | 569 | if (mNextBinding) { |
michael@0 | 570 | nsresult rv = mNextBinding->InstallImplementation(); |
michael@0 | 571 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 572 | } |
michael@0 | 573 | |
michael@0 | 574 | // iterate through each property in the prototype's list and install the property. |
michael@0 | 575 | if (AllowScripts()) |
michael@0 | 576 | return mPrototypeBinding->InstallImplementation(this); |
michael@0 | 577 | |
michael@0 | 578 | return NS_OK; |
michael@0 | 579 | } |
michael@0 | 580 | |
michael@0 | 581 | nsIAtom* |
michael@0 | 582 | nsXBLBinding::GetBaseTag(int32_t* aNameSpaceID) |
michael@0 | 583 | { |
michael@0 | 584 | nsIAtom *tag = mPrototypeBinding->GetBaseTag(aNameSpaceID); |
michael@0 | 585 | if (!tag && mNextBinding) |
michael@0 | 586 | return mNextBinding->GetBaseTag(aNameSpaceID); |
michael@0 | 587 | |
michael@0 | 588 | return tag; |
michael@0 | 589 | } |
michael@0 | 590 | |
michael@0 | 591 | void |
michael@0 | 592 | nsXBLBinding::AttributeChanged(nsIAtom* aAttribute, int32_t aNameSpaceID, |
michael@0 | 593 | bool aRemoveFlag, bool aNotify) |
michael@0 | 594 | { |
michael@0 | 595 | // XXX Change if we ever allow multiple bindings in a chain to contribute anonymous content |
michael@0 | 596 | if (!mContent) { |
michael@0 | 597 | if (mNextBinding) |
michael@0 | 598 | mNextBinding->AttributeChanged(aAttribute, aNameSpaceID, |
michael@0 | 599 | aRemoveFlag, aNotify); |
michael@0 | 600 | } else { |
michael@0 | 601 | mPrototypeBinding->AttributeChanged(aAttribute, aNameSpaceID, aRemoveFlag, |
michael@0 | 602 | mBoundElement, mContent, aNotify); |
michael@0 | 603 | } |
michael@0 | 604 | } |
michael@0 | 605 | |
michael@0 | 606 | void |
michael@0 | 607 | nsXBLBinding::ExecuteAttachedHandler() |
michael@0 | 608 | { |
michael@0 | 609 | if (mNextBinding) |
michael@0 | 610 | mNextBinding->ExecuteAttachedHandler(); |
michael@0 | 611 | |
michael@0 | 612 | if (AllowScripts()) |
michael@0 | 613 | mPrototypeBinding->BindingAttached(mBoundElement); |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | void |
michael@0 | 617 | nsXBLBinding::ExecuteDetachedHandler() |
michael@0 | 618 | { |
michael@0 | 619 | if (AllowScripts()) |
michael@0 | 620 | mPrototypeBinding->BindingDetached(mBoundElement); |
michael@0 | 621 | |
michael@0 | 622 | if (mNextBinding) |
michael@0 | 623 | mNextBinding->ExecuteDetachedHandler(); |
michael@0 | 624 | } |
michael@0 | 625 | |
michael@0 | 626 | void |
michael@0 | 627 | nsXBLBinding::UnhookEventHandlers() |
michael@0 | 628 | { |
michael@0 | 629 | nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers(); |
michael@0 | 630 | |
michael@0 | 631 | if (handlerChain) { |
michael@0 | 632 | EventListenerManager* manager = mBoundElement->GetExistingListenerManager(); |
michael@0 | 633 | if (!manager) { |
michael@0 | 634 | return; |
michael@0 | 635 | } |
michael@0 | 636 | |
michael@0 | 637 | bool isChromeBinding = mPrototypeBinding->IsChrome(); |
michael@0 | 638 | nsXBLPrototypeHandler* curr; |
michael@0 | 639 | for (curr = handlerChain; curr; curr = curr->GetNextHandler()) { |
michael@0 | 640 | nsXBLEventHandler* handler = curr->GetCachedEventHandler(); |
michael@0 | 641 | if (!handler) { |
michael@0 | 642 | continue; |
michael@0 | 643 | } |
michael@0 | 644 | |
michael@0 | 645 | nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName(); |
michael@0 | 646 | if (!eventAtom || |
michael@0 | 647 | eventAtom == nsGkAtoms::keyup || |
michael@0 | 648 | eventAtom == nsGkAtoms::keydown || |
michael@0 | 649 | eventAtom == nsGkAtoms::keypress) |
michael@0 | 650 | continue; |
michael@0 | 651 | |
michael@0 | 652 | // Figure out if we're using capturing or not. |
michael@0 | 653 | EventListenerFlags flags; |
michael@0 | 654 | flags.mCapture = (curr->GetPhase() == NS_PHASE_CAPTURING); |
michael@0 | 655 | |
michael@0 | 656 | // If this is a command, remove it from the system event group, |
michael@0 | 657 | // otherwise remove it from the standard event group. |
michael@0 | 658 | |
michael@0 | 659 | if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | |
michael@0 | 660 | NS_HANDLER_TYPE_SYSTEM)) && |
michael@0 | 661 | (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) { |
michael@0 | 662 | flags.mInSystemGroup = true; |
michael@0 | 663 | } |
michael@0 | 664 | |
michael@0 | 665 | manager->RemoveEventListenerByType(handler, |
michael@0 | 666 | nsDependentAtomString(eventAtom), |
michael@0 | 667 | flags); |
michael@0 | 668 | } |
michael@0 | 669 | |
michael@0 | 670 | const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers = |
michael@0 | 671 | mPrototypeBinding->GetKeyEventHandlers(); |
michael@0 | 672 | int32_t i; |
michael@0 | 673 | for (i = 0; i < keyHandlers->Count(); ++i) { |
michael@0 | 674 | nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i); |
michael@0 | 675 | |
michael@0 | 676 | nsAutoString type; |
michael@0 | 677 | handler->GetEventName(type); |
michael@0 | 678 | |
michael@0 | 679 | // Figure out if we're using capturing or not. |
michael@0 | 680 | EventListenerFlags flags; |
michael@0 | 681 | flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING); |
michael@0 | 682 | |
michael@0 | 683 | // If this is a command, remove it from the system event group, otherwise |
michael@0 | 684 | // remove it from the standard event group. |
michael@0 | 685 | |
michael@0 | 686 | if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) && |
michael@0 | 687 | (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) { |
michael@0 | 688 | flags.mInSystemGroup = true; |
michael@0 | 689 | } |
michael@0 | 690 | |
michael@0 | 691 | manager->RemoveEventListenerByType(handler, type, flags); |
michael@0 | 692 | } |
michael@0 | 693 | } |
michael@0 | 694 | } |
michael@0 | 695 | |
michael@0 | 696 | static void |
michael@0 | 697 | UpdateInsertionParent(XBLChildrenElement* aPoint, |
michael@0 | 698 | nsIContent* aOldBoundElement) |
michael@0 | 699 | { |
michael@0 | 700 | if (aPoint->IsDefaultInsertion()) { |
michael@0 | 701 | return; |
michael@0 | 702 | } |
michael@0 | 703 | |
michael@0 | 704 | for (size_t i = 0; i < aPoint->InsertedChildrenLength(); ++i) { |
michael@0 | 705 | nsIContent* child = aPoint->mInsertedChildren[i]; |
michael@0 | 706 | |
michael@0 | 707 | MOZ_ASSERT(child->GetParentNode()); |
michael@0 | 708 | |
michael@0 | 709 | // Here, we're iterating children that we inserted. There are two cases: |
michael@0 | 710 | // either |child| is an explicit child of |aOldBoundElement| and is no |
michael@0 | 711 | // longer inserted anywhere or it's a child of a <children> element |
michael@0 | 712 | // parented to |aOldBoundElement|. In the former case, the child is no |
michael@0 | 713 | // longer inserted anywhere, so we set its insertion parent to null. In the |
michael@0 | 714 | // latter case, the child is now inserted into |aOldBoundElement| from some |
michael@0 | 715 | // binding above us, so we set its insertion parent to aOldBoundElement. |
michael@0 | 716 | if (child->GetParentNode() == aOldBoundElement) { |
michael@0 | 717 | child->SetXBLInsertionParent(nullptr); |
michael@0 | 718 | } else { |
michael@0 | 719 | child->SetXBLInsertionParent(aOldBoundElement); |
michael@0 | 720 | } |
michael@0 | 721 | } |
michael@0 | 722 | } |
michael@0 | 723 | |
michael@0 | 724 | void |
michael@0 | 725 | nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocument) |
michael@0 | 726 | { |
michael@0 | 727 | if (aOldDocument == aNewDocument) |
michael@0 | 728 | return; |
michael@0 | 729 | |
michael@0 | 730 | // Now the binding dies. Unhook our prototypes. |
michael@0 | 731 | if (mPrototypeBinding->HasImplementation()) { |
michael@0 | 732 | nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface( |
michael@0 | 733 | aOldDocument->GetScopeObject()); |
michael@0 | 734 | if (global) { |
michael@0 | 735 | nsCOMPtr<nsIScriptContext> context = global->GetContext(); |
michael@0 | 736 | if (context) { |
michael@0 | 737 | JSContext *cx = context->GetNativeContext(); |
michael@0 | 738 | |
michael@0 | 739 | nsCxPusher pusher; |
michael@0 | 740 | pusher.Push(cx); |
michael@0 | 741 | |
michael@0 | 742 | // scope might be null if we've cycle-collected the global |
michael@0 | 743 | // object, since the Unlink phase of cycle collection happens |
michael@0 | 744 | // after JS GC finalization. But in that case, we don't care |
michael@0 | 745 | // about fixing the prototype chain, since everything's going |
michael@0 | 746 | // away immediately. |
michael@0 | 747 | JS::Rooted<JSObject*> scope(cx, global->GetGlobalJSObject()); |
michael@0 | 748 | JS::Rooted<JSObject*> scriptObject(cx, mBoundElement->GetWrapper()); |
michael@0 | 749 | if (scope && scriptObject) { |
michael@0 | 750 | // XXX Stay in sync! What if a layered binding has an |
michael@0 | 751 | // <interface>?! |
michael@0 | 752 | // XXXbz what does that comment mean, really? It seems to date |
michael@0 | 753 | // back to when there was such a thing as an <interface>, whever |
michael@0 | 754 | // that was... |
michael@0 | 755 | |
michael@0 | 756 | // Find the right prototype. |
michael@0 | 757 | JSAutoCompartment ac(cx, scriptObject); |
michael@0 | 758 | |
michael@0 | 759 | JS::Rooted<JSObject*> base(cx, scriptObject); |
michael@0 | 760 | JS::Rooted<JSObject*> proto(cx); |
michael@0 | 761 | for ( ; true; base = proto) { // Will break out on null proto |
michael@0 | 762 | if (!JS_GetPrototype(cx, base, &proto)) { |
michael@0 | 763 | return; |
michael@0 | 764 | } |
michael@0 | 765 | if (!proto) { |
michael@0 | 766 | break; |
michael@0 | 767 | } |
michael@0 | 768 | |
michael@0 | 769 | if (JS_GetClass(proto) != &gPrototypeJSClass) { |
michael@0 | 770 | // Clearly not the right class |
michael@0 | 771 | continue; |
michael@0 | 772 | } |
michael@0 | 773 | |
michael@0 | 774 | nsRefPtr<nsXBLDocumentInfo> docInfo = |
michael@0 | 775 | static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(proto)); |
michael@0 | 776 | if (!docInfo) { |
michael@0 | 777 | // Not the proto we seek |
michael@0 | 778 | continue; |
michael@0 | 779 | } |
michael@0 | 780 | |
michael@0 | 781 | JS::Value protoBinding = ::JS_GetReservedSlot(proto, 0); |
michael@0 | 782 | |
michael@0 | 783 | if (JSVAL_TO_PRIVATE(protoBinding) != mPrototypeBinding) { |
michael@0 | 784 | // Not the right binding |
michael@0 | 785 | continue; |
michael@0 | 786 | } |
michael@0 | 787 | |
michael@0 | 788 | // Alright! This is the right prototype. Pull it out of the |
michael@0 | 789 | // proto chain. |
michael@0 | 790 | JS::Rooted<JSObject*> grandProto(cx); |
michael@0 | 791 | if (!JS_GetPrototype(cx, proto, &grandProto)) { |
michael@0 | 792 | return; |
michael@0 | 793 | } |
michael@0 | 794 | ::JS_SetPrototype(cx, base, grandProto); |
michael@0 | 795 | break; |
michael@0 | 796 | } |
michael@0 | 797 | |
michael@0 | 798 | mPrototypeBinding->UndefineFields(cx, scriptObject); |
michael@0 | 799 | |
michael@0 | 800 | // Don't remove the reference from the document to the |
michael@0 | 801 | // wrapper here since it'll be removed by the element |
michael@0 | 802 | // itself when that's taken out of the document. |
michael@0 | 803 | } |
michael@0 | 804 | } |
michael@0 | 805 | } |
michael@0 | 806 | } |
michael@0 | 807 | |
michael@0 | 808 | // Remove our event handlers |
michael@0 | 809 | UnhookEventHandlers(); |
michael@0 | 810 | |
michael@0 | 811 | { |
michael@0 | 812 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 813 | |
michael@0 | 814 | // Then do our ancestors. This reverses the construction order, so that at |
michael@0 | 815 | // all times things are consistent as far as everyone is concerned. |
michael@0 | 816 | if (mNextBinding) { |
michael@0 | 817 | mNextBinding->ChangeDocument(aOldDocument, aNewDocument); |
michael@0 | 818 | } |
michael@0 | 819 | |
michael@0 | 820 | // Update the anonymous content. |
michael@0 | 821 | // XXXbz why not only for style bindings? |
michael@0 | 822 | if (mContent) { |
michael@0 | 823 | nsXBLBinding::UninstallAnonymousContent(aOldDocument, mContent); |
michael@0 | 824 | } |
michael@0 | 825 | |
michael@0 | 826 | // Now that we've unbound our anonymous content from the tree and updated |
michael@0 | 827 | // its binding parent, update the insertion parent for content inserted |
michael@0 | 828 | // into our <children> elements. |
michael@0 | 829 | if (mDefaultInsertionPoint) { |
michael@0 | 830 | UpdateInsertionParent(mDefaultInsertionPoint, mBoundElement); |
michael@0 | 831 | } |
michael@0 | 832 | |
michael@0 | 833 | for (size_t i = 0; i < mInsertionPoints.Length(); ++i) { |
michael@0 | 834 | UpdateInsertionParent(mInsertionPoints[i], mBoundElement); |
michael@0 | 835 | } |
michael@0 | 836 | |
michael@0 | 837 | // Now that our inserted children no longer think they're inserted |
michael@0 | 838 | // anywhere, make sure our internal state reflects that as well. |
michael@0 | 839 | ClearInsertionPoints(); |
michael@0 | 840 | } |
michael@0 | 841 | } |
michael@0 | 842 | |
michael@0 | 843 | bool |
michael@0 | 844 | nsXBLBinding::InheritsStyle() const |
michael@0 | 845 | { |
michael@0 | 846 | // XXX Will have to change if we ever allow multiple bindings to contribute anonymous content. |
michael@0 | 847 | // Most derived binding with anonymous content determines style inheritance for now. |
michael@0 | 848 | |
michael@0 | 849 | // XXX What about bindings with <content> but no kids, e.g., my treecell-text binding? |
michael@0 | 850 | if (mContent) |
michael@0 | 851 | return mPrototypeBinding->InheritsStyle(); |
michael@0 | 852 | |
michael@0 | 853 | if (mNextBinding) |
michael@0 | 854 | return mNextBinding->InheritsStyle(); |
michael@0 | 855 | |
michael@0 | 856 | return true; |
michael@0 | 857 | } |
michael@0 | 858 | |
michael@0 | 859 | void |
michael@0 | 860 | nsXBLBinding::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc, void* aData) |
michael@0 | 861 | { |
michael@0 | 862 | if (mNextBinding) |
michael@0 | 863 | mNextBinding->WalkRules(aFunc, aData); |
michael@0 | 864 | |
michael@0 | 865 | nsIStyleRuleProcessor *rules = mPrototypeBinding->GetRuleProcessor(); |
michael@0 | 866 | if (rules) |
michael@0 | 867 | (*aFunc)(rules, aData); |
michael@0 | 868 | } |
michael@0 | 869 | |
michael@0 | 870 | // Internal helper methods //////////////////////////////////////////////////////////////// |
michael@0 | 871 | |
michael@0 | 872 | // Get or create a WeakMap object on a given XBL-hosting global. |
michael@0 | 873 | // |
michael@0 | 874 | // The scheme is as follows. XBL-hosting globals (either privileged content |
michael@0 | 875 | // Windows or XBL scopes) get two lazily-defined WeakMap properties. Each |
michael@0 | 876 | // WeakMap is keyed by the grand-proto - i.e. the original prototype of the |
michael@0 | 877 | // content before it was bound, and the prototype of the class object that we |
michael@0 | 878 | // splice in. The values in the WeakMap are simple dictionary-style objects, |
michael@0 | 879 | // mapping from XBL class names to class objects. |
michael@0 | 880 | static JSObject* |
michael@0 | 881 | GetOrCreateClassObjectMap(JSContext *cx, JS::Handle<JSObject*> scope, const char *mapName) |
michael@0 | 882 | { |
michael@0 | 883 | AssertSameCompartment(cx, scope); |
michael@0 | 884 | MOZ_ASSERT(JS_IsGlobalObject(scope)); |
michael@0 | 885 | MOZ_ASSERT(scope == xpc::GetXBLScopeOrGlobal(cx, scope)); |
michael@0 | 886 | |
michael@0 | 887 | // First, see if the map is already defined. |
michael@0 | 888 | JS::Rooted<JSPropertyDescriptor> desc(cx); |
michael@0 | 889 | if (!JS_GetOwnPropertyDescriptor(cx, scope, mapName, &desc)) { |
michael@0 | 890 | return nullptr; |
michael@0 | 891 | } |
michael@0 | 892 | if (desc.object() && desc.value().isObject() && |
michael@0 | 893 | JS::IsWeakMapObject(&desc.value().toObject())) { |
michael@0 | 894 | return &desc.value().toObject(); |
michael@0 | 895 | } |
michael@0 | 896 | |
michael@0 | 897 | // It's not there. Create and define it. |
michael@0 | 898 | JS::Rooted<JSObject*> map(cx, JS::NewWeakMapObject(cx)); |
michael@0 | 899 | if (!map || !JS_DefineProperty(cx, scope, mapName, map, |
michael@0 | 900 | JSPROP_PERMANENT | JSPROP_READONLY, |
michael@0 | 901 | JS_PropertyStub, JS_StrictPropertyStub)) |
michael@0 | 902 | { |
michael@0 | 903 | return nullptr; |
michael@0 | 904 | } |
michael@0 | 905 | return map; |
michael@0 | 906 | } |
michael@0 | 907 | |
michael@0 | 908 | static JSObject* |
michael@0 | 909 | GetOrCreateMapEntryForPrototype(JSContext *cx, JS::Handle<JSObject*> proto) |
michael@0 | 910 | { |
michael@0 | 911 | AssertSameCompartment(cx, proto); |
michael@0 | 912 | // We want to hang our class objects off the XBL scope. But since we also |
michael@0 | 913 | // hoist anonymous content into the XBL scope, this creates the potential for |
michael@0 | 914 | // tricky collisions, since we can simultaneously have a bound in-content |
michael@0 | 915 | // node with grand-proto HTMLDivElement and a bound anonymous node whose |
michael@0 | 916 | // grand-proto is the XBL scope's cross-compartment wrapper to HTMLDivElement. |
michael@0 | 917 | // Since we have to wrap the WeakMap keys into its scope, this distinction |
michael@0 | 918 | // would be lost if we don't do something about it. |
michael@0 | 919 | // |
michael@0 | 920 | // So we define two maps - one class objects that live in content (prototyped |
michael@0 | 921 | // to content prototypes), and the other for class objects that live in the |
michael@0 | 922 | // XBL scope (prototyped to cross-compartment-wrapped content prototypes). |
michael@0 | 923 | const char* name = xpc::IsInXBLScope(proto) ? "__ContentClassObjectMap__" |
michael@0 | 924 | : "__XBLClassObjectMap__"; |
michael@0 | 925 | |
michael@0 | 926 | // Now, enter the XBL scope, since that's where we need to operate, and wrap |
michael@0 | 927 | // the proto accordingly. |
michael@0 | 928 | JS::Rooted<JSObject*> scope(cx, xpc::GetXBLScopeOrGlobal(cx, proto)); |
michael@0 | 929 | NS_ENSURE_TRUE(scope, nullptr); |
michael@0 | 930 | JS::Rooted<JSObject*> wrappedProto(cx, proto); |
michael@0 | 931 | JSAutoCompartment ac(cx, scope); |
michael@0 | 932 | if (!JS_WrapObject(cx, &wrappedProto)) { |
michael@0 | 933 | return nullptr; |
michael@0 | 934 | } |
michael@0 | 935 | |
michael@0 | 936 | // Grab the appropriate WeakMap. |
michael@0 | 937 | JS::Rooted<JSObject*> map(cx, GetOrCreateClassObjectMap(cx, scope, name)); |
michael@0 | 938 | if (!map) { |
michael@0 | 939 | return nullptr; |
michael@0 | 940 | } |
michael@0 | 941 | |
michael@0 | 942 | // See if we already have a map entry for that prototype. |
michael@0 | 943 | JS::Rooted<JS::Value> val(cx); |
michael@0 | 944 | if (!JS::GetWeakMapEntry(cx, map, wrappedProto, &val)) { |
michael@0 | 945 | return nullptr; |
michael@0 | 946 | } |
michael@0 | 947 | if (val.isObject()) { |
michael@0 | 948 | return &val.toObject(); |
michael@0 | 949 | } |
michael@0 | 950 | |
michael@0 | 951 | // We don't have an entry. Create one and stick it in the map. |
michael@0 | 952 | JS::Rooted<JSObject*> entry(cx); |
michael@0 | 953 | entry = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), scope); |
michael@0 | 954 | if (!entry) { |
michael@0 | 955 | return nullptr; |
michael@0 | 956 | } |
michael@0 | 957 | JS::Rooted<JS::Value> entryVal(cx, JS::ObjectValue(*entry)); |
michael@0 | 958 | if (!JS::SetWeakMapEntry(cx, map, wrappedProto, entryVal)) { |
michael@0 | 959 | NS_WARNING("SetWeakMapEntry failed, probably due to non-preservable WeakMap " |
michael@0 | 960 | "key. XBL binding will fail for this element."); |
michael@0 | 961 | return nullptr; |
michael@0 | 962 | } |
michael@0 | 963 | return entry; |
michael@0 | 964 | } |
michael@0 | 965 | |
michael@0 | 966 | // static |
michael@0 | 967 | nsresult |
michael@0 | 968 | nsXBLBinding::DoInitJSClass(JSContext *cx, |
michael@0 | 969 | JS::Handle<JSObject*> obj, |
michael@0 | 970 | const nsAFlatCString& aClassName, |
michael@0 | 971 | nsXBLPrototypeBinding* aProtoBinding, |
michael@0 | 972 | JS::MutableHandle<JSObject*> aClassObject, |
michael@0 | 973 | bool* aNew) |
michael@0 | 974 | { |
michael@0 | 975 | MOZ_ASSERT(obj); |
michael@0 | 976 | |
michael@0 | 977 | // Note that, now that NAC reflectors are created in the XBL scope, the |
michael@0 | 978 | // reflector is not necessarily same-compartment with the document. So we'll |
michael@0 | 979 | // end up creating a separate instance of the oddly-named XBL class object |
michael@0 | 980 | // and defining it as a property on the XBL scope's global. This works fine, |
michael@0 | 981 | // but we need to make sure never to assume that the the reflector and |
michael@0 | 982 | // prototype are same-compartment with the bound document. |
michael@0 | 983 | JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj)); |
michael@0 | 984 | JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, global)); |
michael@0 | 985 | NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED); |
michael@0 | 986 | |
michael@0 | 987 | JS::Rooted<JSObject*> parent_proto(cx); |
michael@0 | 988 | if (!JS_GetPrototype(cx, obj, &parent_proto)) { |
michael@0 | 989 | return NS_ERROR_FAILURE; |
michael@0 | 990 | } |
michael@0 | 991 | |
michael@0 | 992 | // Get the map entry for the parent prototype. In the one-off case that the |
michael@0 | 993 | // parent prototype is null, we somewhat hackily just use the WeakMap itself |
michael@0 | 994 | // as a property holder. |
michael@0 | 995 | JS::Rooted<JSObject*> holder(cx); |
michael@0 | 996 | if (parent_proto) { |
michael@0 | 997 | holder = GetOrCreateMapEntryForPrototype(cx, parent_proto); |
michael@0 | 998 | } else { |
michael@0 | 999 | JSAutoCompartment innerAC(cx, xblScope); |
michael@0 | 1000 | holder = GetOrCreateClassObjectMap(cx, xblScope, "__ContentClassObjectMap__"); |
michael@0 | 1001 | } |
michael@0 | 1002 | if (NS_WARN_IF(!holder)) { |
michael@0 | 1003 | return NS_ERROR_FAILURE; |
michael@0 | 1004 | } |
michael@0 | 1005 | js::AssertSameCompartment(holder, xblScope); |
michael@0 | 1006 | JSAutoCompartment ac(cx, holder); |
michael@0 | 1007 | |
michael@0 | 1008 | // Look up the class on the property holder. The only properties on the |
michael@0 | 1009 | // holder should be class objects. If we don't find the class object, we need |
michael@0 | 1010 | // to create and define it. |
michael@0 | 1011 | JS::Rooted<JSObject*> proto(cx); |
michael@0 | 1012 | JS::Rooted<JSPropertyDescriptor> desc(cx); |
michael@0 | 1013 | if (!JS_GetOwnPropertyDescriptor(cx, holder, aClassName.get(), &desc)) { |
michael@0 | 1014 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1015 | } |
michael@0 | 1016 | *aNew = !desc.object(); |
michael@0 | 1017 | if (desc.object()) { |
michael@0 | 1018 | proto = &desc.value().toObject(); |
michael@0 | 1019 | MOZ_ASSERT(JS_GetClass(js::UncheckedUnwrap(proto)) == &gPrototypeJSClass); |
michael@0 | 1020 | } else { |
michael@0 | 1021 | |
michael@0 | 1022 | // We need to create the prototype. First, enter the compartment where it's |
michael@0 | 1023 | // going to live, and create it. |
michael@0 | 1024 | JSAutoCompartment ac2(cx, global); |
michael@0 | 1025 | proto = JS_NewObjectWithGivenProto(cx, &gPrototypeJSClass, parent_proto, global); |
michael@0 | 1026 | if (!proto) { |
michael@0 | 1027 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1028 | } |
michael@0 | 1029 | |
michael@0 | 1030 | // Keep this proto binding alive while we're alive. Do this first so that |
michael@0 | 1031 | // we can guarantee that in XBLFinalize this will be non-null. |
michael@0 | 1032 | // Note that we can't just store aProtoBinding in the private and |
michael@0 | 1033 | // addref/release the nsXBLDocumentInfo through it, because cycle |
michael@0 | 1034 | // collection doesn't seem to work right if the private is not an |
michael@0 | 1035 | // nsISupports. |
michael@0 | 1036 | nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo(); |
michael@0 | 1037 | ::JS_SetPrivate(proto, docInfo); |
michael@0 | 1038 | NS_ADDREF(docInfo); |
michael@0 | 1039 | JS_SetReservedSlot(proto, 0, PRIVATE_TO_JSVAL(aProtoBinding)); |
michael@0 | 1040 | |
michael@0 | 1041 | // Next, enter the compartment of the property holder, wrap the proto, and |
michael@0 | 1042 | // stick it on. |
michael@0 | 1043 | JSAutoCompartment ac3(cx, holder); |
michael@0 | 1044 | if (!JS_WrapObject(cx, &proto) || |
michael@0 | 1045 | !JS_DefineProperty(cx, holder, aClassName.get(), proto, |
michael@0 | 1046 | JSPROP_READONLY | JSPROP_PERMANENT, |
michael@0 | 1047 | JS_PropertyStub, JS_StrictPropertyStub)) |
michael@0 | 1048 | { |
michael@0 | 1049 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1050 | } |
michael@0 | 1051 | } |
michael@0 | 1052 | |
michael@0 | 1053 | // Whew. We have the proto. Wrap it back into the compartment of |obj|, |
michael@0 | 1054 | // splice it in, and return it. |
michael@0 | 1055 | JSAutoCompartment ac4(cx, obj); |
michael@0 | 1056 | if (!JS_WrapObject(cx, &proto) || !JS_SetPrototype(cx, obj, proto)) { |
michael@0 | 1057 | return NS_ERROR_FAILURE; |
michael@0 | 1058 | } |
michael@0 | 1059 | aClassObject.set(proto); |
michael@0 | 1060 | return NS_OK; |
michael@0 | 1061 | } |
michael@0 | 1062 | |
michael@0 | 1063 | bool |
michael@0 | 1064 | nsXBLBinding::AllowScripts() |
michael@0 | 1065 | { |
michael@0 | 1066 | return mBoundElement && mPrototypeBinding->GetAllowScripts(); |
michael@0 | 1067 | } |
michael@0 | 1068 | |
michael@0 | 1069 | nsXBLBinding* |
michael@0 | 1070 | nsXBLBinding::RootBinding() |
michael@0 | 1071 | { |
michael@0 | 1072 | if (mNextBinding) |
michael@0 | 1073 | return mNextBinding->RootBinding(); |
michael@0 | 1074 | |
michael@0 | 1075 | return this; |
michael@0 | 1076 | } |
michael@0 | 1077 | |
michael@0 | 1078 | bool |
michael@0 | 1079 | nsXBLBinding::ResolveAllFields(JSContext *cx, JS::Handle<JSObject*> obj) const |
michael@0 | 1080 | { |
michael@0 | 1081 | if (!mPrototypeBinding->ResolveAllFields(cx, obj)) { |
michael@0 | 1082 | return false; |
michael@0 | 1083 | } |
michael@0 | 1084 | |
michael@0 | 1085 | if (mNextBinding) { |
michael@0 | 1086 | return mNextBinding->ResolveAllFields(cx, obj); |
michael@0 | 1087 | } |
michael@0 | 1088 | |
michael@0 | 1089 | return true; |
michael@0 | 1090 | } |
michael@0 | 1091 | |
michael@0 | 1092 | bool |
michael@0 | 1093 | nsXBLBinding::LookupMember(JSContext* aCx, JS::Handle<jsid> aId, |
michael@0 | 1094 | JS::MutableHandle<JSPropertyDescriptor> aDesc) |
michael@0 | 1095 | { |
michael@0 | 1096 | // We should never enter this function with a pre-filled property descriptor. |
michael@0 | 1097 | MOZ_ASSERT(!aDesc.object()); |
michael@0 | 1098 | |
michael@0 | 1099 | // Get the string as an nsString before doing anything, so we can make |
michael@0 | 1100 | // convenient comparisons during our search. |
michael@0 | 1101 | if (!JSID_IS_STRING(aId)) { |
michael@0 | 1102 | return true; |
michael@0 | 1103 | } |
michael@0 | 1104 | nsDependentJSString name(aId); |
michael@0 | 1105 | |
michael@0 | 1106 | // We have a weak reference to our bound element, so make sure it's alive. |
michael@0 | 1107 | if (!mBoundElement || !mBoundElement->GetWrapper()) { |
michael@0 | 1108 | return false; |
michael@0 | 1109 | } |
michael@0 | 1110 | |
michael@0 | 1111 | // Get the scope of mBoundElement and the associated XBL scope. We should only |
michael@0 | 1112 | // be calling into this machinery if we're running in a separate XBL scope. |
michael@0 | 1113 | // |
michael@0 | 1114 | // Note that we only end up in LookupMember for XrayWrappers from XBL scopes |
michael@0 | 1115 | // into content. So for NAC reflectors that live in the XBL scope, we should |
michael@0 | 1116 | // never get here. But on the off-chance that someone adds new callsites to |
michael@0 | 1117 | // LookupMember, we do a release-mode assertion as belt-and-braces. |
michael@0 | 1118 | // We do a release-mode assertion here to be extra safe. |
michael@0 | 1119 | JS::Rooted<JSObject*> boundScope(aCx, |
michael@0 | 1120 | js::GetGlobalForObjectCrossCompartment(mBoundElement->GetWrapper())); |
michael@0 | 1121 | MOZ_RELEASE_ASSERT(!xpc::IsInXBLScope(boundScope)); |
michael@0 | 1122 | JS::Rooted<JSObject*> xblScope(aCx, xpc::GetXBLScope(aCx, boundScope)); |
michael@0 | 1123 | NS_ENSURE_TRUE(xblScope, false); |
michael@0 | 1124 | MOZ_ASSERT(boundScope != xblScope); |
michael@0 | 1125 | |
michael@0 | 1126 | // Enter the xbl scope and invoke the internal version. |
michael@0 | 1127 | { |
michael@0 | 1128 | JSAutoCompartment ac(aCx, xblScope); |
michael@0 | 1129 | JS::Rooted<jsid> id(aCx, aId); |
michael@0 | 1130 | if (!JS_WrapId(aCx, &id) || |
michael@0 | 1131 | !LookupMemberInternal(aCx, name, id, aDesc, xblScope)) |
michael@0 | 1132 | { |
michael@0 | 1133 | return false; |
michael@0 | 1134 | } |
michael@0 | 1135 | } |
michael@0 | 1136 | |
michael@0 | 1137 | // Wrap into the caller's scope. |
michael@0 | 1138 | return JS_WrapPropertyDescriptor(aCx, aDesc); |
michael@0 | 1139 | } |
michael@0 | 1140 | |
michael@0 | 1141 | bool |
michael@0 | 1142 | nsXBLBinding::LookupMemberInternal(JSContext* aCx, nsString& aName, |
michael@0 | 1143 | JS::Handle<jsid> aNameAsId, |
michael@0 | 1144 | JS::MutableHandle<JSPropertyDescriptor> aDesc, |
michael@0 | 1145 | JS::Handle<JSObject*> aXBLScope) |
michael@0 | 1146 | { |
michael@0 | 1147 | // First, see if we have an implementation. If we don't, it means that this |
michael@0 | 1148 | // binding doesn't have a class object, and thus doesn't have any members. |
michael@0 | 1149 | // Skip it. |
michael@0 | 1150 | if (!PrototypeBinding()->HasImplementation()) { |
michael@0 | 1151 | if (!mNextBinding) { |
michael@0 | 1152 | return true; |
michael@0 | 1153 | } |
michael@0 | 1154 | return mNextBinding->LookupMemberInternal(aCx, aName, aNameAsId, |
michael@0 | 1155 | aDesc, aXBLScope); |
michael@0 | 1156 | } |
michael@0 | 1157 | |
michael@0 | 1158 | // Find our class object. It's in a protected scope and permanent just in case, |
michael@0 | 1159 | // so should be there no matter what. |
michael@0 | 1160 | JS::Rooted<JS::Value> classObject(aCx); |
michael@0 | 1161 | if (!JS_GetProperty(aCx, aXBLScope, PrototypeBinding()->ClassName().get(), |
michael@0 | 1162 | &classObject)) { |
michael@0 | 1163 | return false; |
michael@0 | 1164 | } |
michael@0 | 1165 | |
michael@0 | 1166 | // The bound element may have been adoped by a document and have a different |
michael@0 | 1167 | // wrapper (and different xbl scope) than when the binding was applied, in |
michael@0 | 1168 | // this case getting the class object will fail. Behave as if the class |
michael@0 | 1169 | // object did not exist. |
michael@0 | 1170 | if (classObject.isUndefined()) { |
michael@0 | 1171 | return true; |
michael@0 | 1172 | } |
michael@0 | 1173 | |
michael@0 | 1174 | MOZ_ASSERT(classObject.isObject()); |
michael@0 | 1175 | |
michael@0 | 1176 | // Look for the property on this binding. If it's not there, try the next |
michael@0 | 1177 | // binding on the chain. |
michael@0 | 1178 | nsXBLProtoImpl* impl = mPrototypeBinding->GetImplementation(); |
michael@0 | 1179 | JS::Rooted<JSObject*> object(aCx, &classObject.toObject()); |
michael@0 | 1180 | if (impl && !impl->LookupMember(aCx, aName, aNameAsId, aDesc, object)) { |
michael@0 | 1181 | return false; |
michael@0 | 1182 | } |
michael@0 | 1183 | if (aDesc.object() || !mNextBinding) { |
michael@0 | 1184 | return true; |
michael@0 | 1185 | } |
michael@0 | 1186 | |
michael@0 | 1187 | return mNextBinding->LookupMemberInternal(aCx, aName, aNameAsId, aDesc, |
michael@0 | 1188 | aXBLScope); |
michael@0 | 1189 | } |
michael@0 | 1190 | |
michael@0 | 1191 | bool |
michael@0 | 1192 | nsXBLBinding::HasField(nsString& aName) |
michael@0 | 1193 | { |
michael@0 | 1194 | // See if this binding has such a field. |
michael@0 | 1195 | return mPrototypeBinding->FindField(aName) || |
michael@0 | 1196 | (mNextBinding && mNextBinding->HasField(aName)); |
michael@0 | 1197 | } |
michael@0 | 1198 | |
michael@0 | 1199 | void |
michael@0 | 1200 | nsXBLBinding::MarkForDeath() |
michael@0 | 1201 | { |
michael@0 | 1202 | mMarkedForDeath = true; |
michael@0 | 1203 | ExecuteDetachedHandler(); |
michael@0 | 1204 | } |
michael@0 | 1205 | |
michael@0 | 1206 | bool |
michael@0 | 1207 | nsXBLBinding::ImplementsInterface(REFNSIID aIID) const |
michael@0 | 1208 | { |
michael@0 | 1209 | return mPrototypeBinding->ImplementsInterface(aIID) || |
michael@0 | 1210 | (mNextBinding && mNextBinding->ImplementsInterface(aIID)); |
michael@0 | 1211 | } |