Coverage Report

Created: 2018-09-25 14:53

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