DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "DebuggerNotificationObserver.h"

#include "DebuggerNotification.h"
#include "nsIGlobalObject.h"
#include "WrapperFactory.h"

namespace mozilla {
namespace dom {

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DebuggerNotificationObserver,
                                      mOwnerGlobal, mEventListenerCallbacks)

NS_IMPL_CYCLE_COLLECTING_ADDREF(DebuggerNotificationObserver)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DebuggerNotificationObserver)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DebuggerNotificationObserver)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END

/* static */ already_AddRefed<DebuggerNotificationObserver>
DebuggerNotificationObserver::Constructor(GlobalObject& aGlobal,
                                          ErrorResult& aRv) {
  nsCOMPtr<nsIGlobalObject> globalInterface(
      do_QueryInterface(aGlobal.GetAsSupports()));
  if (NS_WARN_IF(!globalInterface)) {
    aRv.Throw(NS_ERROR_FAILURE);
    return nullptr;
  }

  RefPtr<DebuggerNotificationObserver> observer(
      new DebuggerNotificationObserver(globalInterface));
  return observer.forget();
}

DebuggerNotificationObserver::DebuggerNotificationObserver(
    nsIGlobalObject* aOwnerGlobal)
    : mEventListenerCallbacks(), mOwnerGlobal(aOwnerGlobal) {}

JSObject* DebuggerNotificationObserver::WrapObject(
    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
  return DebuggerNotificationObserver_Binding::Wrap(aCx, this, aGivenProto);
}

static already_AddRefed<DebuggerNotificationManager> GetManager(
    JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal) {
  // The debuggee global here is likely a debugger-compartment cross-compartment
  // wrapper for the debuggee global object, so we need to unwrap it to get
  // the real debuggee-compartment global object.
  JS::Rooted<JSObject*> debuggeeGlobalRooted(
      aCx, js::UncheckedUnwrap(aDebuggeeGlobal, false));

  if (!debuggeeGlobalRooted) {
    return nullptr;
  }

  nsCOMPtr<nsIGlobalObject> debuggeeGlobalObject(
      xpc::NativeGlobal(debuggeeGlobalRooted));
  if (!debuggeeGlobalObject) {
    return nullptr;
  }

  RefPtr<DebuggerNotificationManager> manager(
      debuggeeGlobalObject->GetOrCreateDebuggerNotificationManager());
  return manager.forget();
}

bool DebuggerNotificationObserver::Connect(
    JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal, ErrorResult& aRv) {
  RefPtr<DebuggerNotificationManager> manager(GetManager(aCx, aDebuggeeGlobal));

  if (!manager) {
    aRv.Throw(NS_ERROR_FAILURE);
    return false;
  }

  return manager->Attach(this);
}

bool DebuggerNotificationObserver::Disconnect(
    JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal, ErrorResult& aRv) {
  RefPtr<DebuggerNotificationManager> manager(GetManager(aCx, aDebuggeeGlobal));

  if (!manager) {
    aRv.Throw(NS_ERROR_FAILURE);
    return false;
  }

  return manager->Detach(this);
}

bool DebuggerNotificationObserver::AddListener(
    DebuggerNotificationCallback& aHandlerFn) {
  nsTObserverArray<RefPtr<DebuggerNotificationCallback>>::ForwardIterator iter(
      mEventListenerCallbacks);
  while (iter.HasMore()) {
    if (*iter.GetNext().get() == aHandlerFn) {
      return false;
    }
  }

  RefPtr<DebuggerNotificationCallback> handlerFn(&aHandlerFn);
  mEventListenerCallbacks.AppendElement(handlerFn);
  return true;
}

bool DebuggerNotificationObserver::RemoveListener(
    DebuggerNotificationCallback& aHandlerFn) {
  nsTObserverArray<RefPtr<DebuggerNotificationCallback>>::ForwardIterator iter(
      mEventListenerCallbacks);
  for (uint32_t i = 0; iter.HasMore(); i++) {
    if (*iter.GetNext().get() == aHandlerFn) {
      mEventListenerCallbacks.RemoveElementAt(i);
      return true;
    }
  }

  return false;
}

bool DebuggerNotificationObserver::HasListeners() {
  return !mEventListenerCallbacks.IsEmpty();
}

void DebuggerNotificationObserver::NotifyListeners(
    DebuggerNotification* aNotification) {
  if (!HasListeners()) {
    return;
  }

  // Since we want the notification objects to live in the same compartment
  // as the observer, we create a new instance of the notification before
  // an observer dispatches the event listeners.
  RefPtr<DebuggerNotification> debuggerNotification(
      aNotification->CloneInto(mOwnerGlobal));

  nsTObserverArray<RefPtr<DebuggerNotificationCallback>>::ForwardIterator iter(
      mEventListenerCallbacks);

  while (iter.HasMore()) {
    RefPtr<DebuggerNotificationCallback> cb(iter.GetNext());
    cb->Call(*debuggerNotification);
  }
}

}  // namespace dom
}  // namespace mozilla