tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

XULBroadcastManager.cpp (20507B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=4 sw=2 et 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 "XULBroadcastManager.h"
      8 
      9 #include "mozilla/EventDispatcher.h"
     10 #include "mozilla/Logging.h"
     11 #include "mozilla/dom/DocumentInlines.h"
     12 #include "nsCOMPtr.h"
     13 #include "nsContentUtils.h"
     14 #include "nsXULElement.h"
     15 
     16 struct BroadcastListener {
     17  nsWeakPtr mListener;
     18  RefPtr<nsAtom> mAttribute;
     19 };
     20 
     21 struct BroadcasterMapEntry : public PLDHashEntryHdr {
     22  mozilla::dom::Element* mBroadcaster;  // [WEAK]
     23  nsTArray<BroadcastListener*>
     24      mListeners;  // [OWNING] of BroadcastListener objects
     25 };
     26 
     27 struct nsAttrNameInfo {
     28  nsAttrNameInfo(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix)
     29      : mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {}
     30  nsAttrNameInfo(const nsAttrNameInfo& aOther) = delete;
     31  nsAttrNameInfo(nsAttrNameInfo&& aOther) = default;
     32 
     33  int32_t mNamespaceID;
     34  RefPtr<nsAtom> mName;
     35  RefPtr<nsAtom> mPrefix;
     36 };
     37 
     38 static void ClearBroadcasterMapEntry(PLDHashTable* aTable,
     39                                     PLDHashEntryHdr* aEntry) {
     40  BroadcasterMapEntry* entry = static_cast<BroadcasterMapEntry*>(aEntry);
     41  for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
     42    delete entry->mListeners[i];
     43  }
     44  entry->mListeners.Clear();
     45 
     46  // N.B. that we need to manually run the dtor because we
     47  // constructed the nsTArray object in-place.
     48  entry->mListeners.~nsTArray<BroadcastListener*>();
     49 }
     50 
     51 static bool CanBroadcast(int32_t aNameSpaceID, nsAtom* aAttribute) {
     52  // Don't push changes to the |id|, |persist|, |command| or
     53  // |observes| attribute.
     54  if (aNameSpaceID == kNameSpaceID_None) {
     55    if ((aAttribute == nsGkAtoms::id) || (aAttribute == nsGkAtoms::persist) ||
     56        (aAttribute == nsGkAtoms::command) ||
     57        (aAttribute == nsGkAtoms::observes)) {
     58      return false;
     59    }
     60  }
     61  return true;
     62 }
     63 
     64 namespace mozilla::dom {
     65 static LazyLogModule sXULBroadCastManager("XULBroadcastManager");
     66 
     67 class XULBroadcastManager::nsDelayedBroadcastUpdate {
     68 public:
     69  nsDelayedBroadcastUpdate(Element* aBroadcaster, Element* aListener,
     70                           const nsAString& aAttr)
     71      : mBroadcaster(aBroadcaster),
     72        mListener(aListener),
     73        mAttr(aAttr),
     74        mSetAttr(false),
     75        mNeedsAttrChange(false) {}
     76 
     77  nsDelayedBroadcastUpdate(Element* aBroadcaster, Element* aListener,
     78                           nsAtom* aAttrName, const nsAString& aAttr,
     79                           bool aSetAttr, bool aNeedsAttrChange)
     80      : mBroadcaster(aBroadcaster),
     81        mListener(aListener),
     82        mAttr(aAttr),
     83        mAttrName(aAttrName),
     84        mSetAttr(aSetAttr),
     85        mNeedsAttrChange(aNeedsAttrChange) {}
     86 
     87  nsDelayedBroadcastUpdate(const nsDelayedBroadcastUpdate& aOther) = delete;
     88  nsDelayedBroadcastUpdate(nsDelayedBroadcastUpdate&& aOther) = default;
     89 
     90  RefPtr<Element> mBroadcaster;
     91  RefPtr<Element> mListener;
     92  // Note if mAttrName isn't used, this is the name of the attr, otherwise
     93  // this is the value of the attribute.
     94  nsString mAttr;
     95  RefPtr<nsAtom> mAttrName;
     96  bool mSetAttr;
     97  bool mNeedsAttrChange;
     98 
     99  class Comparator {
    100   public:
    101    static bool Equals(const nsDelayedBroadcastUpdate& a,
    102                       const nsDelayedBroadcastUpdate& b) {
    103      return a.mBroadcaster == b.mBroadcaster && a.mListener == b.mListener &&
    104             a.mAttrName == b.mAttrName;
    105    }
    106  };
    107 };
    108 
    109 /* static */
    110 bool XULBroadcastManager::MayNeedListener(const Element& aElement) {
    111  if (aElement.NodeInfo()->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) {
    112    return true;
    113  }
    114  if (aElement.HasAttr(nsGkAtoms::observes)) {
    115    return true;
    116  }
    117  if (aElement.HasAttr(nsGkAtoms::command) &&
    118      !(aElement.NodeInfo()->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) ||
    119        aElement.NodeInfo()->Equals(nsGkAtoms::key, kNameSpaceID_XUL))) {
    120    return true;
    121  }
    122  return false;
    123 }
    124 
    125 XULBroadcastManager::XULBroadcastManager(Document* aDocument)
    126    : mDocument(aDocument),
    127      mBroadcasterMap(nullptr),
    128      mHandlingDelayedAttrChange(false),
    129      mHandlingDelayedBroadcasters(false) {}
    130 
    131 XULBroadcastManager::~XULBroadcastManager() { delete mBroadcasterMap; }
    132 
    133 void XULBroadcastManager::DropDocumentReference(void) { mDocument = nullptr; }
    134 
    135 void XULBroadcastManager::SynchronizeBroadcastListener(Element* aBroadcaster,
    136                                                       Element* aListener,
    137                                                       const nsAString& aAttr) {
    138  if (!nsContentUtils::IsSafeToRunScript()) {
    139    mDelayedBroadcasters.EmplaceBack(aBroadcaster, aListener, aAttr);
    140    MaybeBroadcast();
    141    return;
    142  }
    143  bool notify = mHandlingDelayedBroadcasters;
    144 
    145  if (aAttr.EqualsLiteral("*")) {
    146    uint32_t count = aBroadcaster->GetAttrCount();
    147    nsTArray<nsAttrNameInfo> attributes(count);
    148    for (uint32_t i = 0; i < count; ++i) {
    149      const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i);
    150      int32_t nameSpaceID = attrName->NamespaceID();
    151      nsAtom* name = attrName->LocalName();
    152 
    153      // _Don't_ push the |id|, |ref|, or |persist| attribute's value!
    154      if (!CanBroadcast(nameSpaceID, name)) continue;
    155 
    156      attributes.AppendElement(
    157          nsAttrNameInfo(nameSpaceID, name, attrName->GetPrefix()));
    158    }
    159 
    160    count = attributes.Length();
    161    while (count-- > 0) {
    162      int32_t nameSpaceID = attributes[count].mNamespaceID;
    163      nsAtom* name = attributes[count].mName;
    164      nsAutoString value;
    165      if (aBroadcaster->GetAttr(nameSpaceID, name, value)) {
    166        aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix, value,
    167                           notify);
    168      }
    169 
    170 #if 0
    171            // XXX we don't fire the |onbroadcast| handler during
    172            // initial hookup: doing so would potentially run the
    173            // |onbroadcast| handler before the |onload| handler,
    174            // which could define JS properties that mask XBL
    175            // properties, etc.
    176            ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
    177 #endif
    178    }
    179  } else {
    180    // Find out if the attribute is even present at all.
    181    RefPtr<nsAtom> name = NS_Atomize(aAttr);
    182 
    183    nsAutoString value;
    184    if (aBroadcaster->GetAttr(name, value)) {
    185      aListener->SetAttr(kNameSpaceID_None, name, value, notify);
    186    } else {
    187      aListener->UnsetAttr(kNameSpaceID_None, name, notify);
    188    }
    189 
    190 #if 0
    191        // XXX we don't fire the |onbroadcast| handler during initial
    192        // hookup: doing so would potentially run the |onbroadcast|
    193        // handler before the |onload| handler, which could define JS
    194        // properties that mask XBL properties, etc.
    195        ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
    196 #endif
    197  }
    198 }
    199 
    200 void XULBroadcastManager::AddListenerFor(Element& aBroadcaster,
    201                                         Element& aListener,
    202                                         const nsAString& aAttr,
    203                                         ErrorResult& aRv) {
    204  if (!mDocument) {
    205    aRv.Throw(NS_ERROR_FAILURE);
    206    return;
    207  }
    208 
    209  nsresult rv = nsContentUtils::CheckSameOrigin(mDocument, &aBroadcaster);
    210 
    211  if (NS_FAILED(rv)) {
    212    aRv.Throw(rv);
    213    return;
    214  }
    215 
    216  rv = nsContentUtils::CheckSameOrigin(mDocument, &aListener);
    217 
    218  if (NS_FAILED(rv)) {
    219    aRv.Throw(rv);
    220    return;
    221  }
    222 
    223  static const PLDHashTableOps gOps = {
    224      PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
    225      PLDHashTable::MoveEntryStub, ClearBroadcasterMapEntry, nullptr};
    226 
    227  if (!mBroadcasterMap) {
    228    mBroadcasterMap = new PLDHashTable(&gOps, sizeof(BroadcasterMapEntry));
    229  }
    230 
    231  auto entry =
    232      static_cast<BroadcasterMapEntry*>(mBroadcasterMap->Search(&aBroadcaster));
    233  if (!entry) {
    234    entry = static_cast<BroadcasterMapEntry*>(
    235        mBroadcasterMap->Add(&aBroadcaster, fallible));
    236 
    237    if (!entry) {
    238      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    239      return;
    240    }
    241 
    242    entry->mBroadcaster = &aBroadcaster;
    243 
    244    // N.B. placement new to construct the nsTArray object in-place
    245    new (&entry->mListeners) nsTArray<BroadcastListener*>();
    246  }
    247 
    248  // Only add the listener if it's not there already!
    249  RefPtr<nsAtom> attr = NS_Atomize(aAttr);
    250 
    251  for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
    252    BroadcastListener* bl = entry->mListeners[i];
    253    nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
    254 
    255    if (blListener == &aListener && bl->mAttribute == attr) return;
    256  }
    257 
    258  BroadcastListener* bl = new BroadcastListener;
    259  bl->mListener = do_GetWeakReference(&aListener);
    260  bl->mAttribute = attr;
    261 
    262  entry->mListeners.AppendElement(bl);
    263 
    264  SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr);
    265 }
    266 
    267 void XULBroadcastManager::RemoveListenerFor(Element& aBroadcaster,
    268                                            Element& aListener,
    269                                            const nsAString& aAttr) {
    270  // If we haven't added any broadcast listeners, then there sure
    271  // aren't any to remove.
    272  if (!mBroadcasterMap) return;
    273 
    274  auto entry =
    275      static_cast<BroadcasterMapEntry*>(mBroadcasterMap->Search(&aBroadcaster));
    276  if (entry) {
    277    RefPtr<nsAtom> attr = NS_Atomize(aAttr);
    278    for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
    279      BroadcastListener* bl = entry->mListeners[i];
    280      nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
    281 
    282      if (blListener == &aListener && bl->mAttribute == attr) {
    283        entry->mListeners.RemoveElementAt(i);
    284        delete bl;
    285 
    286        if (entry->mListeners.IsEmpty()) mBroadcasterMap->RemoveEntry(entry);
    287 
    288        break;
    289      }
    290    }
    291  }
    292 }
    293 
    294 nsresult XULBroadcastManager::ExecuteOnBroadcastHandlerFor(
    295    Element* aBroadcaster, Element* aListener, nsAtom* aAttr) {
    296  if (!mDocument) {
    297    return NS_OK;
    298  }
    299  // Now we execute the onchange handler in the context of the
    300  // observer. We need to find the observer in order to
    301  // execute the handler.
    302 
    303  for (nsCOMPtr<nsIContent> child = aListener->GetFirstChild(); child;
    304       child = child->GetNextSibling()) {
    305    // Look for an <observes> element beneath the listener. This
    306    // ought to have an |element| attribute that refers to
    307    // aBroadcaster, and an |attribute| element that tells us what
    308    // attriubtes we're listening for.
    309    if (!child->IsXULElement(nsGkAtoms::observes)) continue;
    310 
    311    // Is this the element that was listening to us?
    312    nsAutoString listeningToID;
    313    child->AsElement()->GetAttr(nsGkAtoms::element, listeningToID);
    314 
    315    nsAutoString broadcasterID;
    316    aBroadcaster->GetAttr(nsGkAtoms::id, broadcasterID);
    317 
    318    if (listeningToID != broadcasterID) continue;
    319 
    320    // We are observing the broadcaster, but is this the right
    321    // attribute?
    322    nsAutoString listeningToAttribute;
    323    child->AsElement()->GetAttr(nsGkAtoms::attribute, listeningToAttribute);
    324 
    325    if (!aAttr->Equals(listeningToAttribute) &&
    326        !listeningToAttribute.EqualsLiteral("*")) {
    327      continue;
    328    }
    329 
    330    // This is the right <observes> element. Execute the
    331    // |onbroadcast| event handler
    332    WidgetEvent event(true, eXULBroadcast);
    333 
    334    if (RefPtr<nsPresContext> presContext = mDocument->GetPresContext()) {
    335      // Handle the DOM event
    336      nsEventStatus status = nsEventStatus_eIgnore;
    337      EventDispatcher::Dispatch(child, presContext, &event, nullptr, &status);
    338    }
    339  }
    340 
    341  return NS_OK;
    342 }
    343 
    344 void XULBroadcastManager::AttributeChanged(Element* aElement,
    345                                           int32_t aNameSpaceID,
    346                                           nsAtom* aAttribute) {
    347  if (!mDocument) {
    348    return;
    349  }
    350  NS_ASSERTION(aElement->OwnerDoc() == mDocument, "unexpected doc");
    351 
    352  // Synchronize broadcast listeners
    353  if (mBroadcasterMap && CanBroadcast(aNameSpaceID, aAttribute)) {
    354    auto entry =
    355        static_cast<BroadcasterMapEntry*>(mBroadcasterMap->Search(aElement));
    356 
    357    if (entry) {
    358      // We've got listeners: push the value.
    359      nsAutoString value;
    360      bool attrSet = aElement->GetAttr(aAttribute, value);
    361 
    362      for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
    363        BroadcastListener* bl = entry->mListeners[i];
    364        if ((bl->mAttribute == aAttribute) ||
    365            (bl->mAttribute == nsGkAtoms::_asterisk)) {
    366          nsCOMPtr<Element> listenerEl = do_QueryReferent(bl->mListener);
    367          if (listenerEl) {
    368            nsAutoString currentValue;
    369            bool hasAttr = listenerEl->GetAttr(aAttribute, currentValue);
    370            // We need to update listener only if we're
    371            // (1) removing an existing attribute,
    372            // (2) adding a new attribute or
    373            // (3) changing the value of an attribute.
    374            bool needsAttrChange =
    375                attrSet != hasAttr || !value.Equals(currentValue);
    376            nsDelayedBroadcastUpdate delayedUpdate(aElement, listenerEl,
    377                                                   aAttribute, value, attrSet,
    378                                                   needsAttrChange);
    379 
    380            size_t index = mDelayedAttrChangeBroadcasts.IndexOf(
    381                delayedUpdate, 0, nsDelayedBroadcastUpdate::Comparator());
    382            if (index != mDelayedAttrChangeBroadcasts.NoIndex) {
    383              if (mHandlingDelayedAttrChange) {
    384                NS_WARNING("Broadcasting loop!");
    385                continue;
    386              }
    387              mDelayedAttrChangeBroadcasts.RemoveElementAt(index);
    388            }
    389 
    390            mDelayedAttrChangeBroadcasts.AppendElement(
    391                std::move(delayedUpdate));
    392          }
    393        }
    394      }
    395    }
    396  }
    397 }
    398 
    399 void XULBroadcastManager::MaybeBroadcast() {
    400  // Only broadcast when not in an update and when safe to run scripts.
    401  if (mDocument && mDocument->UpdateNestingLevel() == 0 &&
    402      (mDelayedAttrChangeBroadcasts.Length() ||
    403       mDelayedBroadcasters.Length())) {
    404    if (!nsContentUtils::IsSafeToRunScript()) {
    405      if (mDocument) {
    406        nsContentUtils::AddScriptRunner(
    407            NewRunnableMethod("dom::XULBroadcastManager::MaybeBroadcast", this,
    408                              &XULBroadcastManager::MaybeBroadcast));
    409      }
    410      return;
    411    }
    412    if (!mHandlingDelayedAttrChange) {
    413      mHandlingDelayedAttrChange = true;
    414      for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) {
    415        RefPtr<nsAtom> attrName = mDelayedAttrChangeBroadcasts[i].mAttrName;
    416        RefPtr<Element> listener = mDelayedAttrChangeBroadcasts[i].mListener;
    417        if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) {
    418          const nsString& value = mDelayedAttrChangeBroadcasts[i].mAttr;
    419          if (mDelayedAttrChangeBroadcasts[i].mSetAttr) {
    420            listener->SetAttr(kNameSpaceID_None, attrName, value, true);
    421          } else {
    422            listener->UnsetAttr(kNameSpaceID_None, attrName, true);
    423          }
    424        }
    425        RefPtr<Element> broadcaster =
    426            mDelayedAttrChangeBroadcasts[i].mBroadcaster;
    427        ExecuteOnBroadcastHandlerFor(broadcaster, listener, attrName);
    428      }
    429      mDelayedAttrChangeBroadcasts.Clear();
    430      mHandlingDelayedAttrChange = false;
    431    }
    432 
    433    uint32_t length = mDelayedBroadcasters.Length();
    434    if (length) {
    435      bool oldValue = mHandlingDelayedBroadcasters;
    436      mHandlingDelayedBroadcasters = true;
    437      nsTArray<nsDelayedBroadcastUpdate> delayedBroadcasters =
    438          std::move(mDelayedBroadcasters);
    439      for (uint32_t i = 0; i < length; ++i) {
    440        SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster,
    441                                     delayedBroadcasters[i].mListener,
    442                                     delayedBroadcasters[i].mAttr);
    443      }
    444      mHandlingDelayedBroadcasters = oldValue;
    445    }
    446  }
    447 }
    448 
    449 nsresult XULBroadcastManager::FindBroadcaster(Element* aElement,
    450                                              Element** aListener,
    451                                              nsString& aBroadcasterID,
    452                                              nsString& aAttribute,
    453                                              Element** aBroadcaster) {
    454  NodeInfo* ni = aElement->NodeInfo();
    455  *aListener = nullptr;
    456  *aBroadcaster = nullptr;
    457 
    458  if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) {
    459    // It's an <observes> element, which means that the actual
    460    // listener is the _parent_ node. This element should have an
    461    // 'element' attribute that specifies the ID of the
    462    // broadcaster element, and an 'attribute' element, which
    463    // specifies the name of the attribute to observe.
    464    nsIContent* parent = aElement->GetParent();
    465    if (!parent) {
    466      // <observes> is the root element
    467      return NS_FINDBROADCASTER_NOT_FOUND;
    468    }
    469 
    470    *aListener = Element::FromNode(parent);
    471    NS_IF_ADDREF(*aListener);
    472 
    473    aElement->GetAttr(nsGkAtoms::element, aBroadcasterID);
    474    if (aBroadcasterID.IsEmpty()) {
    475      return NS_FINDBROADCASTER_NOT_FOUND;
    476    }
    477    aElement->GetAttr(nsGkAtoms::attribute, aAttribute);
    478  } else {
    479    // It's a generic element, which means that we'll use the
    480    // value of the 'observes' attribute to determine the ID of
    481    // the broadcaster element, and we'll watch _all_ of its
    482    // values.
    483    aElement->GetAttr(nsGkAtoms::observes, aBroadcasterID);
    484 
    485    // Bail if there's no aBroadcasterID
    486    if (aBroadcasterID.IsEmpty()) {
    487      // Try the command attribute next.
    488      aElement->GetAttr(nsGkAtoms::command, aBroadcasterID);
    489      if (!aBroadcasterID.IsEmpty()) {
    490        // We've got something in the command attribute.  We
    491        // only treat this as a normal broadcaster if we are
    492        // not a menuitem or a key.
    493 
    494        if (ni->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) ||
    495            ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
    496          return NS_FINDBROADCASTER_NOT_FOUND;
    497        }
    498      } else {
    499        return NS_FINDBROADCASTER_NOT_FOUND;
    500      }
    501    }
    502 
    503    *aListener = aElement;
    504    NS_ADDREF(*aListener);
    505 
    506    aAttribute.Assign('*');
    507  }
    508 
    509  // Make sure we got a valid listener.
    510  NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED);
    511 
    512  // Try to find the broadcaster element in the document.
    513  Document* doc = aElement->GetComposedDoc();
    514  if (doc) {
    515    *aBroadcaster = doc->GetElementById(aBroadcasterID);
    516  }
    517 
    518  // The broadcaster element is missing.
    519  if (!*aBroadcaster) {
    520    return NS_FINDBROADCASTER_NOT_FOUND;
    521  }
    522 
    523  NS_ADDREF(*aBroadcaster);
    524 
    525  return NS_FINDBROADCASTER_FOUND;
    526 }
    527 
    528 nsresult XULBroadcastManager::UpdateListenerHookup(Element* aElement,
    529                                                   HookupAction aAction) {
    530  // Resolve a broadcaster hookup. Look at the element that we're
    531  // trying to resolve: it could be an '<observes>' element, or just
    532  // a vanilla element with an 'observes' attribute on it.
    533  nsresult rv;
    534 
    535  nsCOMPtr<Element> listener;
    536  nsAutoString broadcasterID;
    537  nsAutoString attribute;
    538  nsCOMPtr<Element> broadcaster;
    539 
    540  rv = FindBroadcaster(aElement, getter_AddRefs(listener), broadcasterID,
    541                       attribute, getter_AddRefs(broadcaster));
    542  switch (rv) {
    543    case NS_FINDBROADCASTER_NOT_FOUND:
    544      return NS_OK;
    545    case NS_FINDBROADCASTER_FOUND:
    546      break;
    547    default:
    548      return rv;
    549  }
    550 
    551  NS_ENSURE_ARG(broadcaster && listener);
    552  if (aAction == eHookupAdd) {
    553    ErrorResult domRv;
    554    AddListenerFor(*broadcaster, *listener, attribute, domRv);
    555    if (domRv.Failed()) {
    556      return domRv.StealNSResult();
    557    }
    558  } else {
    559    RemoveListenerFor(*broadcaster, *listener, attribute);
    560  }
    561 
    562  // Tell the world we succeeded
    563  if (MOZ_LOG_TEST(sXULBroadCastManager, LogLevel::Debug)) {
    564    nsCOMPtr<nsIContent> content = listener;
    565    NS_ASSERTION(content != nullptr, "not an nsIContent");
    566    if (!content) {
    567      return rv;
    568    }
    569 
    570    nsAutoCString attributeC, broadcasteridC;
    571    LossyCopyUTF16toASCII(attribute, attributeC);
    572    LossyCopyUTF16toASCII(broadcasterID, broadcasteridC);
    573    MOZ_LOG(sXULBroadCastManager, LogLevel::Debug,
    574            ("xul: broadcaster hookup <%s attribute='%s'> to %s",
    575             nsAtomCString(content->NodeInfo()->NameAtom()).get(),
    576             attributeC.get(), broadcasteridC.get()));
    577  }
    578 
    579  return NS_OK;
    580 }
    581 
    582 nsresult XULBroadcastManager::AddListener(Element* aElement) {
    583  return UpdateListenerHookup(aElement, eHookupAdd);
    584 }
    585 
    586 nsresult XULBroadcastManager::RemoveListener(Element* aElement) {
    587  return UpdateListenerHookup(aElement, eHookupRemove);
    588 }
    589 
    590 }  // namespace mozilla::dom