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