dom/events/EventListenerManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/events/EventListenerManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1364 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +// Microsoft's API Name hackery sucks
    1.10 +#undef CreateEvent
    1.11 +
    1.12 +#include "mozilla/BasicEvents.h"
    1.13 +#include "mozilla/EventDispatcher.h"
    1.14 +#include "mozilla/EventListenerManager.h"
    1.15 +#ifdef MOZ_B2G
    1.16 +#include "mozilla/Hal.h"
    1.17 +#endif // #ifdef MOZ_B2G
    1.18 +#include "mozilla/HalSensor.h"
    1.19 +#include "mozilla/InternalMutationEvent.h"
    1.20 +#include "mozilla/JSEventHandler.h"
    1.21 +#include "mozilla/MemoryReporting.h"
    1.22 +#include "mozilla/dom/BindingUtils.h"
    1.23 +#include "mozilla/dom/Element.h"
    1.24 +#include "mozilla/dom/Event.h"
    1.25 +
    1.26 +#include "EventListenerService.h"
    1.27 +#include "nsCOMArray.h"
    1.28 +#include "nsCOMPtr.h"
    1.29 +#include "nsContentUtils.h"
    1.30 +#include "nsDOMCID.h"
    1.31 +#include "nsError.h"
    1.32 +#include "nsGkAtoms.h"
    1.33 +#include "nsIContent.h"
    1.34 +#include "nsIContentSecurityPolicy.h"
    1.35 +#include "nsIDocument.h"
    1.36 +#include "nsIDOMEventListener.h"
    1.37 +#include "nsIScriptGlobalObject.h"
    1.38 +#include "nsISupports.h"
    1.39 +#include "nsIXPConnect.h"
    1.40 +#include "nsJSUtils.h"
    1.41 +#include "nsNameSpaceManager.h"
    1.42 +#include "nsPIDOMWindow.h"
    1.43 +#include "nsSandboxFlags.h"
    1.44 +#include "xpcpublic.h"
    1.45 +
    1.46 +namespace mozilla {
    1.47 +
    1.48 +using namespace dom;
    1.49 +using namespace hal;
    1.50 +
    1.51 +#define EVENT_TYPE_EQUALS(ls, type, userType, typeString, allEvents) \
    1.52 +  ((ls->mEventType == type &&                                        \
    1.53 +    (ls->mEventType != NS_USER_DEFINED_EVENT ||                      \
    1.54 +    (mIsMainThreadELM && ls->mTypeAtom == userType) ||               \
    1.55 +    (!mIsMainThreadELM && ls->mTypeString.Equals(typeString)))) ||   \
    1.56 +   (allEvents && ls->mAllEvents))
    1.57 +
    1.58 +static const uint32_t kAllMutationBits =
    1.59 +  NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED |
    1.60 +  NS_EVENT_BITS_MUTATION_NODEINSERTED |
    1.61 +  NS_EVENT_BITS_MUTATION_NODEREMOVED |
    1.62 +  NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT |
    1.63 +  NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT |
    1.64 +  NS_EVENT_BITS_MUTATION_ATTRMODIFIED |
    1.65 +  NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
    1.66 +
    1.67 +static uint32_t
    1.68 +MutationBitForEventType(uint32_t aEventType)
    1.69 +{
    1.70 +  switch (aEventType) {
    1.71 +    case NS_MUTATION_SUBTREEMODIFIED:
    1.72 +      return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED;
    1.73 +    case NS_MUTATION_NODEINSERTED:
    1.74 +      return NS_EVENT_BITS_MUTATION_NODEINSERTED;
    1.75 +    case NS_MUTATION_NODEREMOVED:
    1.76 +      return NS_EVENT_BITS_MUTATION_NODEREMOVED;
    1.77 +    case NS_MUTATION_NODEREMOVEDFROMDOCUMENT:
    1.78 +      return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT;
    1.79 +    case NS_MUTATION_NODEINSERTEDINTODOCUMENT:
    1.80 +      return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT;
    1.81 +    case NS_MUTATION_ATTRMODIFIED:
    1.82 +      return NS_EVENT_BITS_MUTATION_ATTRMODIFIED;
    1.83 +    case NS_MUTATION_CHARACTERDATAMODIFIED:
    1.84 +      return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
    1.85 +    default:
    1.86 +      break;
    1.87 +  }
    1.88 +  return 0;
    1.89 +}
    1.90 +
    1.91 +uint32_t EventListenerManager::sMainThreadCreatedCount = 0;
    1.92 +
    1.93 +EventListenerManager::EventListenerManager(EventTarget* aTarget)
    1.94 +  : mMayHavePaintEventListener(false)
    1.95 +  , mMayHaveMutationListeners(false)
    1.96 +  , mMayHaveCapturingListeners(false)
    1.97 +  , mMayHaveSystemGroupListeners(false)
    1.98 +  , mMayHaveTouchEventListener(false)
    1.99 +  , mMayHaveMouseEnterLeaveEventListener(false)
   1.100 +  , mMayHavePointerEnterLeaveEventListener(false)
   1.101 +  , mClearingListeners(false)
   1.102 +  , mIsMainThreadELM(NS_IsMainThread())
   1.103 +  , mNoListenerForEvent(0)
   1.104 +  , mTarget(aTarget)
   1.105 +{
   1.106 +  NS_ASSERTION(aTarget, "unexpected null pointer");
   1.107 +
   1.108 +  if (mIsMainThreadELM) {
   1.109 +    ++sMainThreadCreatedCount;
   1.110 +  }
   1.111 +}
   1.112 +
   1.113 +EventListenerManager::~EventListenerManager()
   1.114 +{
   1.115 +  // If your code fails this assertion, a possible reason is that
   1.116 +  // a class did not call our Disconnect() manually. Note that
   1.117 +  // this class can have Disconnect called in one of two ways:
   1.118 +  // if it is part of a cycle, then in Unlink() (such a cycle
   1.119 +  // would be with one of the listeners, not mTarget which is weak).
   1.120 +  // If not part of a cycle, then Disconnect must be called manually,
   1.121 +  // typically from the destructor of the owner class (mTarget).
   1.122 +  // XXX azakai: Is there any reason to not just call Disconnect
   1.123 +  //             from right here, if not previously called?
   1.124 +  NS_ASSERTION(!mTarget, "didn't call Disconnect");
   1.125 +  RemoveAllListeners();
   1.126 +}
   1.127 +
   1.128 +void
   1.129 +EventListenerManager::RemoveAllListeners()
   1.130 +{
   1.131 +  if (mClearingListeners) {
   1.132 +    return;
   1.133 +  }
   1.134 +  mClearingListeners = true;
   1.135 +  mListeners.Clear();
   1.136 +  mClearingListeners = false;
   1.137 +}
   1.138 +
   1.139 +void
   1.140 +EventListenerManager::Shutdown()
   1.141 +{
   1.142 +  Event::Shutdown();
   1.143 +}
   1.144 +
   1.145 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(EventListenerManager, AddRef)
   1.146 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(EventListenerManager, Release)
   1.147 +
   1.148 +inline void
   1.149 +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
   1.150 +                            EventListenerManager::Listener& aField,
   1.151 +                            const char* aName,
   1.152 +                            unsigned aFlags)
   1.153 +{
   1.154 +  if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
   1.155 +    nsAutoCString name;
   1.156 +    name.AppendASCII(aName);
   1.157 +    if (aField.mTypeAtom) {
   1.158 +      name.AppendASCII(" event=");
   1.159 +      name.Append(nsAtomCString(aField.mTypeAtom));
   1.160 +      name.AppendASCII(" listenerType=");
   1.161 +      name.AppendInt(aField.mListenerType);
   1.162 +      name.AppendASCII(" ");
   1.163 +    }
   1.164 +    CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), name.get(),
   1.165 +                             aFlags);
   1.166 +  } else {
   1.167 +    CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName,
   1.168 +                             aFlags);
   1.169 +  }
   1.170 +}
   1.171 +
   1.172 +NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager)
   1.173 +
   1.174 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager)
   1.175 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
   1.176 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1.177 +
   1.178 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager)
   1.179 +  tmp->Disconnect();
   1.180 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1.181 +
   1.182 +
   1.183 +nsPIDOMWindow*
   1.184 +EventListenerManager::GetInnerWindowForTarget()
   1.185 +{
   1.186 +  nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
   1.187 +  if (node) {
   1.188 +    // XXX sXBL/XBL2 issue -- do we really want the owner here?  What
   1.189 +    // if that's the XBL document?
   1.190 +    return node->OwnerDoc()->GetInnerWindow();
   1.191 +  }
   1.192 +
   1.193 +  nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   1.194 +  return window;
   1.195 +}
   1.196 +
   1.197 +already_AddRefed<nsPIDOMWindow>
   1.198 +EventListenerManager::GetTargetAsInnerWindow() const
   1.199 +{
   1.200 +  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
   1.201 +  if (!window) {
   1.202 +    return nullptr;
   1.203 +  }
   1.204 +
   1.205 +  NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
   1.206 +  return window.forget();
   1.207 +}
   1.208 +
   1.209 +void
   1.210 +EventListenerManager::AddEventListenerInternal(
   1.211 +                        const EventListenerHolder& aListenerHolder,
   1.212 +                        uint32_t aType,
   1.213 +                        nsIAtom* aTypeAtom,
   1.214 +                        const nsAString& aTypeString,
   1.215 +                        const EventListenerFlags& aFlags,
   1.216 +                        bool aHandler,
   1.217 +                        bool aAllEvents)
   1.218 +{
   1.219 +  MOZ_ASSERT((NS_IsMainThread() && aType && aTypeAtom) || // Main thread
   1.220 +             (!NS_IsMainThread() && aType && !aTypeString.IsEmpty()) || // non-main-thread
   1.221 +             aAllEvents, "Missing type"); // all-events listener
   1.222 +
   1.223 +  if (!aListenerHolder || mClearingListeners) {
   1.224 +    return;
   1.225 +  }
   1.226 +
   1.227 +  // Since there is no public API to call us with an EventListenerHolder, we
   1.228 +  // know that there's an EventListenerHolder on the stack holding a strong ref
   1.229 +  // to the listener.
   1.230 +
   1.231 +  Listener* listener;
   1.232 +  uint32_t count = mListeners.Length();
   1.233 +  for (uint32_t i = 0; i < count; i++) {
   1.234 +    listener = &mListeners.ElementAt(i);
   1.235 +    // mListener == aListenerHolder is the last one, since it can be a bit slow.
   1.236 +    if (listener->mListenerIsHandler == aHandler &&
   1.237 +        listener->mFlags == aFlags &&
   1.238 +        EVENT_TYPE_EQUALS(listener, aType, aTypeAtom, aTypeString,
   1.239 +                          aAllEvents) &&
   1.240 +        listener->mListener == aListenerHolder) {
   1.241 +      return;
   1.242 +    }
   1.243 +  }
   1.244 +
   1.245 +  mNoListenerForEvent = NS_EVENT_NULL;
   1.246 +  mNoListenerForEventAtom = nullptr;
   1.247 +
   1.248 +  listener = aAllEvents ? mListeners.InsertElementAt(0) :
   1.249 +                          mListeners.AppendElement();
   1.250 +  listener->mListener = aListenerHolder;
   1.251 +  MOZ_ASSERT(aType < PR_UINT16_MAX);
   1.252 +  listener->mEventType = aType;
   1.253 +  listener->mTypeString = aTypeString;
   1.254 +  listener->mTypeAtom = aTypeAtom;
   1.255 +  listener->mFlags = aFlags;
   1.256 +  listener->mListenerIsHandler = aHandler;
   1.257 +  listener->mHandlerIsString = false;
   1.258 +  listener->mAllEvents = aAllEvents;
   1.259 +
   1.260 +  // Detect the type of event listener.
   1.261 +  nsCOMPtr<nsIXPConnectWrappedJS> wjs;
   1.262 +  if (aFlags.mListenerIsJSListener) {
   1.263 +    MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback());
   1.264 +    listener->mListenerType = Listener::eJSEventListener;
   1.265 +  } else if (aListenerHolder.HasWebIDLCallback()) {
   1.266 +    listener->mListenerType = Listener::eWebIDLListener;
   1.267 +  } else if ((wjs = do_QueryInterface(aListenerHolder.GetXPCOMCallback()))) {
   1.268 +    listener->mListenerType = Listener::eWrappedJSListener;
   1.269 +  } else {
   1.270 +    listener->mListenerType = Listener::eNativeListener;
   1.271 +  }
   1.272 +
   1.273 +
   1.274 +  if (aFlags.mInSystemGroup) {
   1.275 +    mMayHaveSystemGroupListeners = true;
   1.276 +  }
   1.277 +  if (aFlags.mCapture) {
   1.278 +    mMayHaveCapturingListeners = true;
   1.279 +  }
   1.280 +
   1.281 +  if (aType == NS_AFTERPAINT) {
   1.282 +    mMayHavePaintEventListener = true;
   1.283 +    nsPIDOMWindow* window = GetInnerWindowForTarget();
   1.284 +    if (window) {
   1.285 +      window->SetHasPaintEventListeners();
   1.286 +    }
   1.287 +  } else if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) {
   1.288 +    // For mutation listeners, we need to update the global bit on the DOM window.
   1.289 +    // Otherwise we won't actually fire the mutation event.
   1.290 +    mMayHaveMutationListeners = true;
   1.291 +    // Go from our target to the nearest enclosing DOM window.
   1.292 +    nsPIDOMWindow* window = GetInnerWindowForTarget();
   1.293 +    if (window) {
   1.294 +      nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   1.295 +      if (doc) {
   1.296 +        doc->WarnOnceAbout(nsIDocument::eMutationEvent);
   1.297 +      }
   1.298 +      // If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all
   1.299 +      // mutations. nsContentUtils::HasMutationListeners relies on this.
   1.300 +      window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ?
   1.301 +                                   kAllMutationBits :
   1.302 +                                   MutationBitForEventType(aType));
   1.303 +    }
   1.304 +  } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
   1.305 +    EnableDevice(NS_DEVICE_ORIENTATION);
   1.306 +  } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) {
   1.307 +    EnableDevice(NS_DEVICE_PROXIMITY);
   1.308 +  } else if (aTypeAtom == nsGkAtoms::ondevicelight) {
   1.309 +    EnableDevice(NS_DEVICE_LIGHT);
   1.310 +  } else if (aTypeAtom == nsGkAtoms::ondevicemotion) {
   1.311 +    EnableDevice(NS_DEVICE_MOTION);
   1.312 +#ifdef MOZ_B2G
   1.313 +  } else if (aTypeAtom == nsGkAtoms::onmoztimechange) {
   1.314 +    nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   1.315 +    if (window) {
   1.316 +      window->EnableTimeChangeNotifications();
   1.317 +    }
   1.318 +  } else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) {
   1.319 +    nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   1.320 +    if (window) {
   1.321 +      window->EnableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
   1.322 +    }
   1.323 +  } else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) {
   1.324 +    nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   1.325 +    if (window) {
   1.326 +      window->EnableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
   1.327 +    }
   1.328 +#endif // MOZ_B2G
   1.329 +  } else if (aTypeAtom == nsGkAtoms::ontouchstart ||
   1.330 +             aTypeAtom == nsGkAtoms::ontouchend ||
   1.331 +             aTypeAtom == nsGkAtoms::ontouchmove ||
   1.332 +             aTypeAtom == nsGkAtoms::ontouchenter ||
   1.333 +             aTypeAtom == nsGkAtoms::ontouchleave ||
   1.334 +             aTypeAtom == nsGkAtoms::ontouchcancel) {
   1.335 +    mMayHaveTouchEventListener = true;
   1.336 +    nsPIDOMWindow* window = GetInnerWindowForTarget();
   1.337 +    // we don't want touchevent listeners added by scrollbars to flip this flag
   1.338 +    // so we ignore listeners created with system event flag
   1.339 +    if (window && !aFlags.mInSystemGroup) {
   1.340 +      window->SetHasTouchEventListeners();
   1.341 +    }
   1.342 +  } else if (aType >= NS_POINTER_EVENT_START && aType <= NS_POINTER_LOST_CAPTURE) {
   1.343 +    nsPIDOMWindow* window = GetInnerWindowForTarget();
   1.344 +    if (aTypeAtom == nsGkAtoms::onpointerenter ||
   1.345 +        aTypeAtom == nsGkAtoms::onpointerleave) {
   1.346 +      mMayHavePointerEnterLeaveEventListener = true;
   1.347 +      if (window) {
   1.348 +#ifdef DEBUG
   1.349 +        nsCOMPtr<nsIDocument> d = window->GetExtantDoc();
   1.350 +        NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d),
   1.351 +                         "Please do not use pointerenter/leave events in chrome. "
   1.352 +                         "They are slower than pointerover/out!");
   1.353 +#endif
   1.354 +        window->SetHasPointerEnterLeaveEventListeners();
   1.355 +      }
   1.356 +    }
   1.357 +  } else if (aTypeAtom == nsGkAtoms::onmouseenter ||
   1.358 +             aTypeAtom == nsGkAtoms::onmouseleave) {
   1.359 +    mMayHaveMouseEnterLeaveEventListener = true;
   1.360 +    nsPIDOMWindow* window = GetInnerWindowForTarget();
   1.361 +    if (window) {
   1.362 +#ifdef DEBUG
   1.363 +      nsCOMPtr<nsIDocument> d = window->GetExtantDoc();
   1.364 +      NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d),
   1.365 +                       "Please do not use mouseenter/leave events in chrome. "
   1.366 +                       "They are slower than mouseover/out!");
   1.367 +#endif
   1.368 +      window->SetHasMouseEnterLeaveEventListeners();
   1.369 +    }
   1.370 +#ifdef MOZ_GAMEPAD
   1.371 +  } else if (aType >= NS_GAMEPAD_START &&
   1.372 +             aType <= NS_GAMEPAD_END) {
   1.373 +    nsPIDOMWindow* window = GetInnerWindowForTarget();
   1.374 +    if (window) {
   1.375 +      window->SetHasGamepadEventListener();
   1.376 +    }
   1.377 +#endif
   1.378 +  }
   1.379 +  if (aTypeAtom && mTarget) {
   1.380 +    mTarget->EventListenerAdded(aTypeAtom);
   1.381 +  }
   1.382 +}
   1.383 +
   1.384 +bool
   1.385 +EventListenerManager::IsDeviceType(uint32_t aType)
   1.386 +{
   1.387 +  switch (aType) {
   1.388 +    case NS_DEVICE_ORIENTATION:
   1.389 +    case NS_DEVICE_MOTION:
   1.390 +    case NS_DEVICE_LIGHT:
   1.391 +    case NS_DEVICE_PROXIMITY:
   1.392 +    case NS_USER_PROXIMITY:
   1.393 +      return true;
   1.394 +    default:
   1.395 +      break;
   1.396 +  }
   1.397 +  return false;
   1.398 +}
   1.399 +
   1.400 +void
   1.401 +EventListenerManager::EnableDevice(uint32_t aType)
   1.402 +{
   1.403 +  nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   1.404 +  if (!window) {
   1.405 +    return;
   1.406 +  }
   1.407 +
   1.408 +  switch (aType) {
   1.409 +    case NS_DEVICE_ORIENTATION:
   1.410 +      window->EnableDeviceSensor(SENSOR_ORIENTATION);
   1.411 +      break;
   1.412 +    case NS_DEVICE_PROXIMITY:
   1.413 +    case NS_USER_PROXIMITY:
   1.414 +      window->EnableDeviceSensor(SENSOR_PROXIMITY);
   1.415 +      break;
   1.416 +    case NS_DEVICE_LIGHT:
   1.417 +      window->EnableDeviceSensor(SENSOR_LIGHT);
   1.418 +      break;
   1.419 +    case NS_DEVICE_MOTION:
   1.420 +      window->EnableDeviceSensor(SENSOR_ACCELERATION);
   1.421 +      window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
   1.422 +      window->EnableDeviceSensor(SENSOR_GYROSCOPE);
   1.423 +      break;
   1.424 +    default:
   1.425 +      NS_WARNING("Enabling an unknown device sensor.");
   1.426 +      break;
   1.427 +  }
   1.428 +}
   1.429 +
   1.430 +void
   1.431 +EventListenerManager::DisableDevice(uint32_t aType)
   1.432 +{
   1.433 +  nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   1.434 +  if (!window) {
   1.435 +    return;
   1.436 +  }
   1.437 +
   1.438 +  switch (aType) {
   1.439 +    case NS_DEVICE_ORIENTATION:
   1.440 +      window->DisableDeviceSensor(SENSOR_ORIENTATION);
   1.441 +      break;
   1.442 +    case NS_DEVICE_MOTION:
   1.443 +      window->DisableDeviceSensor(SENSOR_ACCELERATION);
   1.444 +      window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
   1.445 +      window->DisableDeviceSensor(SENSOR_GYROSCOPE);
   1.446 +      break;
   1.447 +    case NS_DEVICE_PROXIMITY:
   1.448 +    case NS_USER_PROXIMITY:
   1.449 +      window->DisableDeviceSensor(SENSOR_PROXIMITY);
   1.450 +      break;
   1.451 +    case NS_DEVICE_LIGHT:
   1.452 +      window->DisableDeviceSensor(SENSOR_LIGHT);
   1.453 +      break;
   1.454 +    default:
   1.455 +      NS_WARNING("Disabling an unknown device sensor.");
   1.456 +      break;
   1.457 +  }
   1.458 +}
   1.459 +
   1.460 +void
   1.461 +EventListenerManager::RemoveEventListenerInternal(
   1.462 +                        const EventListenerHolder& aListenerHolder,
   1.463 +                        uint32_t aType,
   1.464 +                        nsIAtom* aUserType,
   1.465 +                        const nsAString& aTypeString,
   1.466 +                        const EventListenerFlags& aFlags,
   1.467 +                        bool aAllEvents)
   1.468 +{
   1.469 +  if (!aListenerHolder || !aType || mClearingListeners) {
   1.470 +    return;
   1.471 +  }
   1.472 +
   1.473 +  Listener* listener;
   1.474 +
   1.475 +  uint32_t count = mListeners.Length();
   1.476 +  uint32_t typeCount = 0;
   1.477 +  bool deviceType = IsDeviceType(aType);
   1.478 +#ifdef MOZ_B2G
   1.479 +  bool timeChangeEvent = (aType == NS_MOZ_TIME_CHANGE_EVENT);
   1.480 +  bool networkEvent = (aType == NS_NETWORK_UPLOAD_EVENT ||
   1.481 +                       aType == NS_NETWORK_DOWNLOAD_EVENT);
   1.482 +#endif // MOZ_B2G
   1.483 +
   1.484 +  for (uint32_t i = 0; i < count; ++i) {
   1.485 +    listener = &mListeners.ElementAt(i);
   1.486 +    if (EVENT_TYPE_EQUALS(listener, aType, aUserType, aTypeString,
   1.487 +                          aAllEvents)) {
   1.488 +      ++typeCount;
   1.489 +      if (listener->mListener == aListenerHolder &&
   1.490 +          listener->mFlags.EqualsIgnoringTrustness(aFlags)) {
   1.491 +        nsRefPtr<EventListenerManager> kungFuDeathGrip(this);
   1.492 +        mListeners.RemoveElementAt(i);
   1.493 +        --count;
   1.494 +        mNoListenerForEvent = NS_EVENT_NULL;
   1.495 +        mNoListenerForEventAtom = nullptr;
   1.496 +        if (mTarget && aUserType) {
   1.497 +          mTarget->EventListenerRemoved(aUserType);
   1.498 +        }
   1.499 +
   1.500 +        if (!deviceType
   1.501 +#ifdef MOZ_B2G
   1.502 +            && !timeChangeEvent && !networkEvent
   1.503 +#endif // MOZ_B2G
   1.504 +            ) {
   1.505 +          return;
   1.506 +        }
   1.507 +        --typeCount;
   1.508 +      }
   1.509 +    }
   1.510 +  }
   1.511 +
   1.512 +  if (!aAllEvents && deviceType && typeCount == 0) {
   1.513 +    DisableDevice(aType);
   1.514 +#ifdef MOZ_B2G
   1.515 +  } else if (timeChangeEvent && typeCount == 0) {
   1.516 +    nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   1.517 +    if (window) {
   1.518 +      window->DisableTimeChangeNotifications();
   1.519 +    }
   1.520 +  } else if (!aAllEvents && networkEvent && typeCount == 0) {
   1.521 +    nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   1.522 +    if (window) {
   1.523 +      window->DisableNetworkEvent(aType);
   1.524 +    }
   1.525 +#endif // MOZ_B2G
   1.526 +  }
   1.527 +}
   1.528 +
   1.529 +bool
   1.530 +EventListenerManager::ListenerCanHandle(Listener* aListener,
   1.531 +                                        WidgetEvent* aEvent)
   1.532 +{
   1.533 +  // This is slightly different from EVENT_TYPE_EQUALS in that it returns
   1.534 +  // true even when aEvent->message == NS_USER_DEFINED_EVENT and
   1.535 +  // aListener=>mEventType != NS_USER_DEFINED_EVENT as long as the atoms are
   1.536 +  // the same
   1.537 +  if (aListener->mAllEvents) {
   1.538 +    return true;
   1.539 +  }
   1.540 +  if (aEvent->message == NS_USER_DEFINED_EVENT) {
   1.541 +    if (mIsMainThreadELM) {
   1.542 +      return aListener->mTypeAtom == aEvent->userType;
   1.543 +    }
   1.544 +    return aListener->mTypeString.Equals(aEvent->typeString);
   1.545 +  }
   1.546 +  MOZ_ASSERT(mIsMainThreadELM);
   1.547 +  return aListener->mEventType == aEvent->message;
   1.548 +}
   1.549 +
   1.550 +void
   1.551 +EventListenerManager::AddEventListenerByType(
   1.552 +                        const EventListenerHolder& aListenerHolder,
   1.553 +                        const nsAString& aType,
   1.554 +                        const EventListenerFlags& aFlags)
   1.555 +{
   1.556 +  nsCOMPtr<nsIAtom> atom =
   1.557 +    mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr;
   1.558 +  uint32_t type = nsContentUtils::GetEventId(atom);
   1.559 +  AddEventListenerInternal(aListenerHolder, type, atom, aType, aFlags);
   1.560 +}
   1.561 +
   1.562 +void
   1.563 +EventListenerManager::RemoveEventListenerByType(
   1.564 +                        const EventListenerHolder& aListenerHolder,
   1.565 +                        const nsAString& aType,
   1.566 +                        const EventListenerFlags& aFlags)
   1.567 +{
   1.568 +  nsCOMPtr<nsIAtom> atom =
   1.569 +    mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr;
   1.570 +  uint32_t type = nsContentUtils::GetEventId(atom);
   1.571 +  RemoveEventListenerInternal(aListenerHolder, type, atom, aType, aFlags);
   1.572 +}
   1.573 +
   1.574 +EventListenerManager::Listener*
   1.575 +EventListenerManager::FindEventHandler(uint32_t aEventType,
   1.576 +                                       nsIAtom* aTypeAtom,
   1.577 +                                       const nsAString& aTypeString)
   1.578 +{
   1.579 +  // Run through the listeners for this type and see if a script
   1.580 +  // listener is registered
   1.581 +  Listener* listener;
   1.582 +  uint32_t count = mListeners.Length();
   1.583 +  for (uint32_t i = 0; i < count; ++i) {
   1.584 +    listener = &mListeners.ElementAt(i);
   1.585 +    if (listener->mListenerIsHandler &&
   1.586 +        EVENT_TYPE_EQUALS(listener, aEventType, aTypeAtom, aTypeString,
   1.587 +                          false)) {
   1.588 +      return listener;
   1.589 +    }
   1.590 +  }
   1.591 +  return nullptr;
   1.592 +}
   1.593 +
   1.594 +EventListenerManager::Listener*
   1.595 +EventListenerManager::SetEventHandlerInternal(
   1.596 +                        nsIAtom* aName,
   1.597 +                        const nsAString& aTypeString,
   1.598 +                        const TypedEventHandler& aTypedHandler,
   1.599 +                        bool aPermitUntrustedEvents)
   1.600 +{
   1.601 +  MOZ_ASSERT(aName || !aTypeString.IsEmpty());
   1.602 +
   1.603 +  uint32_t eventType = nsContentUtils::GetEventId(aName);
   1.604 +  Listener* listener = FindEventHandler(eventType, aName, aTypeString);
   1.605 +
   1.606 +  if (!listener) {
   1.607 +    // If we didn't find a script listener or no listeners existed
   1.608 +    // create and add a new one.
   1.609 +    EventListenerFlags flags;
   1.610 +    flags.mListenerIsJSListener = true;
   1.611 +
   1.612 +    nsCOMPtr<JSEventHandler> jsEventHandler;
   1.613 +    NS_NewJSEventHandler(mTarget, aName,
   1.614 +                         aTypedHandler, getter_AddRefs(jsEventHandler));
   1.615 +    EventListenerHolder listenerHolder(jsEventHandler);
   1.616 +    AddEventListenerInternal(listenerHolder, eventType, aName, aTypeString,
   1.617 +                             flags, true);
   1.618 +
   1.619 +    listener = FindEventHandler(eventType, aName, aTypeString);
   1.620 +  } else {
   1.621 +    JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
   1.622 +    MOZ_ASSERT(jsEventHandler,
   1.623 +               "How can we have an event handler with no JSEventHandler?");
   1.624 +
   1.625 +    bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler;
   1.626 +    // Possibly the same listener, but update still the context and scope.
   1.627 +    jsEventHandler->SetHandler(aTypedHandler);
   1.628 +    if (mTarget && !same && aName) {
   1.629 +      mTarget->EventListenerRemoved(aName);
   1.630 +      mTarget->EventListenerAdded(aName);
   1.631 +    }
   1.632 +  }
   1.633 +
   1.634 +  // Set flag to indicate possible need for compilation later
   1.635 +  listener->mHandlerIsString = !aTypedHandler.HasEventHandler();
   1.636 +  if (aPermitUntrustedEvents) {
   1.637 +    listener->mFlags.mAllowUntrustedEvents = true;
   1.638 +  }
   1.639 +
   1.640 +  return listener;
   1.641 +}
   1.642 +
   1.643 +nsresult
   1.644 +EventListenerManager::SetEventHandler(nsIAtom* aName,
   1.645 +                                      const nsAString& aBody,
   1.646 +                                      uint32_t aLanguage,
   1.647 +                                      bool aDeferCompilation,
   1.648 +                                      bool aPermitUntrustedEvents,
   1.649 +                                      Element* aElement)
   1.650 +{
   1.651 +  NS_PRECONDITION(aLanguage != nsIProgrammingLanguage::UNKNOWN,
   1.652 +                  "Must know the language for the script event listener");
   1.653 +
   1.654 +  // |aPermitUntrustedEvents| is set to False for chrome - events
   1.655 +  // *generated* from an unknown source are not allowed.
   1.656 +  // However, for script languages with no 'sandbox', we want to reject
   1.657 +  // such scripts based on the source of their code, not just the source
   1.658 +  // of the event.
   1.659 +  if (aPermitUntrustedEvents && 
   1.660 +      aLanguage != nsIProgrammingLanguage::JAVASCRIPT) {
   1.661 +    NS_WARNING("Discarding non-JS event listener from untrusted source");
   1.662 +    return NS_ERROR_FAILURE;
   1.663 +  }
   1.664 +
   1.665 +  nsCOMPtr<nsIDocument> doc;
   1.666 +  nsCOMPtr<nsIScriptGlobalObject> global =
   1.667 +    GetScriptGlobalAndDocument(getter_AddRefs(doc));
   1.668 +
   1.669 +  if (!global) {
   1.670 +    // This can happen; for example this document might have been
   1.671 +    // loaded as data.
   1.672 +    return NS_OK;
   1.673 +  }
   1.674 +
   1.675 +#ifdef DEBUG
   1.676 +  nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(global);
   1.677 +  if (win) {
   1.678 +    MOZ_ASSERT(win->IsInnerWindow(), "We should not have an outer window here!");
   1.679 +  }
   1.680 +#endif
   1.681 +
   1.682 +  nsresult rv = NS_OK;
   1.683 +  // return early preventing the event listener from being added
   1.684 +  // 'doc' is fetched above
   1.685 +  if (doc) {
   1.686 +    // Don't allow adding an event listener if the document is sandboxed
   1.687 +    // without 'allow-scripts'.
   1.688 +    if (doc->GetSandboxFlags() & SANDBOXED_SCRIPTS) {
   1.689 +      return NS_ERROR_DOM_SECURITY_ERR;
   1.690 +    }
   1.691 +
   1.692 +    nsCOMPtr<nsIContentSecurityPolicy> csp;
   1.693 +    rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
   1.694 +    NS_ENSURE_SUCCESS(rv, rv);
   1.695 +
   1.696 +    if (csp) {
   1.697 +      bool inlineOK = true;
   1.698 +      bool reportViolations = false;
   1.699 +      rv = csp->GetAllowsInlineScript(&reportViolations, &inlineOK);
   1.700 +      NS_ENSURE_SUCCESS(rv, rv);
   1.701 +
   1.702 +      if (reportViolations) {
   1.703 +        // gather information to log with violation report
   1.704 +        nsIURI* uri = doc->GetDocumentURI();
   1.705 +        nsAutoCString asciiSpec;
   1.706 +        if (uri)
   1.707 +          uri->GetAsciiSpec(asciiSpec);
   1.708 +        nsAutoString scriptSample, attr, tagName(NS_LITERAL_STRING("UNKNOWN"));
   1.709 +        aName->ToString(attr);
   1.710 +        nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mTarget));
   1.711 +        if (domNode)
   1.712 +          domNode->GetNodeName(tagName);
   1.713 +        // build a "script sample" based on what we know about this element
   1.714 +        scriptSample.Assign(attr);
   1.715 +        scriptSample.AppendLiteral(" attribute on ");
   1.716 +        scriptSample.Append(tagName);
   1.717 +        scriptSample.AppendLiteral(" element");
   1.718 +        csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT,
   1.719 +                                 NS_ConvertUTF8toUTF16(asciiSpec),
   1.720 +                                 scriptSample,
   1.721 +                                 0,
   1.722 +                                 EmptyString(),
   1.723 +                                 EmptyString());
   1.724 +      }
   1.725 +
   1.726 +      // return early if CSP wants us to block inline scripts
   1.727 +      if (!inlineOK) {
   1.728 +        return NS_OK;
   1.729 +      }
   1.730 +    }
   1.731 +  }
   1.732 +
   1.733 +  // This might be the first reference to this language in the global
   1.734 +  // We must init the language before we attempt to fetch its context.
   1.735 +  if (NS_FAILED(global->EnsureScriptEnvironment())) {
   1.736 +    NS_WARNING("Failed to setup script environment for this language");
   1.737 +    // but fall through and let the inevitable failure below handle it.
   1.738 +  }
   1.739 +
   1.740 +  nsIScriptContext* context = global->GetScriptContext();
   1.741 +  NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
   1.742 +  NS_ENSURE_STATE(global->GetGlobalJSObject());
   1.743 +
   1.744 +  Listener* listener = SetEventHandlerInternal(aName,
   1.745 +                                               EmptyString(),
   1.746 +                                               TypedEventHandler(),
   1.747 +                                               aPermitUntrustedEvents);
   1.748 +
   1.749 +  if (!aDeferCompilation) {
   1.750 +    return CompileEventHandlerInternal(listener, &aBody, aElement);
   1.751 +  }
   1.752 +
   1.753 +  return NS_OK;
   1.754 +}
   1.755 +
   1.756 +void
   1.757 +EventListenerManager::RemoveEventHandler(nsIAtom* aName,
   1.758 +                                         const nsAString& aTypeString)
   1.759 +{
   1.760 +  if (mClearingListeners) {
   1.761 +    return;
   1.762 +  }
   1.763 +
   1.764 +  uint32_t eventType = nsContentUtils::GetEventId(aName);
   1.765 +  Listener* listener = FindEventHandler(eventType, aName, aTypeString);
   1.766 +
   1.767 +  if (listener) {
   1.768 +    mListeners.RemoveElementAt(uint32_t(listener - &mListeners.ElementAt(0)));
   1.769 +    mNoListenerForEvent = NS_EVENT_NULL;
   1.770 +    mNoListenerForEventAtom = nullptr;
   1.771 +    if (mTarget && aName) {
   1.772 +      mTarget->EventListenerRemoved(aName);
   1.773 +    }
   1.774 +  }
   1.775 +}
   1.776 +
   1.777 +nsresult
   1.778 +EventListenerManager::CompileEventHandlerInternal(Listener* aListener,
   1.779 +                                                  const nsAString* aBody,
   1.780 +                                                  Element* aElement)
   1.781 +{
   1.782 +  MOZ_ASSERT(aListener->GetJSEventHandler());
   1.783 +  MOZ_ASSERT(aListener->mHandlerIsString, "Why are we compiling a non-string JS listener?");
   1.784 +  JSEventHandler* jsEventHandler = aListener->GetJSEventHandler();
   1.785 +  MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(),
   1.786 +             "What is there to compile?");
   1.787 +
   1.788 +  nsresult result = NS_OK;
   1.789 +  nsCOMPtr<nsIDocument> doc;
   1.790 +  nsCOMPtr<nsIScriptGlobalObject> global =
   1.791 +    GetScriptGlobalAndDocument(getter_AddRefs(doc));
   1.792 +  NS_ENSURE_STATE(global);
   1.793 +
   1.794 +  nsIScriptContext* context = global->GetScriptContext();
   1.795 +  NS_ENSURE_STATE(context);
   1.796 +
   1.797 +  // Activate JSAPI, and make sure that exceptions are reported on the right
   1.798 +  // Window.
   1.799 +  AutoJSAPIWithErrorsReportedToWindow jsapi(context);
   1.800 +  JSContext* cx = jsapi.cx();
   1.801 +
   1.802 +  nsCOMPtr<nsIAtom> typeAtom = aListener->mTypeAtom;
   1.803 +  nsIAtom* attrName = typeAtom;
   1.804 +
   1.805 +  // Flag us as not a string so we don't keep trying to compile strings which
   1.806 +  // can't be compiled.
   1.807 +  aListener->mHandlerIsString = false;
   1.808 +
   1.809 +  // mTarget may not be an Element if it's a window and we're
   1.810 +  // getting an inline event listener forwarded from <html:body> or
   1.811 +  // <html:frameset> or <xul:window> or the like.
   1.812 +  // XXX I don't like that we have to reference content from
   1.813 +  // here. The alternative is to store the event handler string on
   1.814 +  // the JSEventHandler itself, and that still doesn't address
   1.815 +  // the arg names issue.
   1.816 +  nsCOMPtr<Element> element = do_QueryInterface(mTarget);
   1.817 +  MOZ_ASSERT(element || aBody, "Where will we get our body?");
   1.818 +  nsAutoString handlerBody;
   1.819 +  const nsAString* body = aBody;
   1.820 +  if (!aBody) {
   1.821 +    if (aListener->mTypeAtom == nsGkAtoms::onSVGLoad) {
   1.822 +      attrName = nsGkAtoms::onload;
   1.823 +    } else if (aListener->mTypeAtom == nsGkAtoms::onSVGUnload) {
   1.824 +      attrName = nsGkAtoms::onunload;
   1.825 +    } else if (aListener->mTypeAtom == nsGkAtoms::onSVGResize) {
   1.826 +      attrName = nsGkAtoms::onresize;
   1.827 +    } else if (aListener->mTypeAtom == nsGkAtoms::onSVGScroll) {
   1.828 +      attrName = nsGkAtoms::onscroll;
   1.829 +    } else if (aListener->mTypeAtom == nsGkAtoms::onSVGZoom) {
   1.830 +      attrName = nsGkAtoms::onzoom;
   1.831 +    } else if (aListener->mTypeAtom == nsGkAtoms::onbeginEvent) {
   1.832 +      attrName = nsGkAtoms::onbegin;
   1.833 +    } else if (aListener->mTypeAtom == nsGkAtoms::onrepeatEvent) {
   1.834 +      attrName = nsGkAtoms::onrepeat;
   1.835 +    } else if (aListener->mTypeAtom == nsGkAtoms::onendEvent) {
   1.836 +      attrName = nsGkAtoms::onend;
   1.837 +    }
   1.838 +
   1.839 +    element->GetAttr(kNameSpaceID_None, attrName, handlerBody);
   1.840 +    body = &handlerBody;
   1.841 +    aElement = element;
   1.842 +  }
   1.843 +  aListener = nullptr;
   1.844 +
   1.845 +  uint32_t lineNo = 0;
   1.846 +  nsAutoCString url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener"));
   1.847 +  MOZ_ASSERT(body);
   1.848 +  MOZ_ASSERT(aElement);
   1.849 +  nsIURI *uri = aElement->OwnerDoc()->GetDocumentURI();
   1.850 +  if (uri) {
   1.851 +    uri->GetSpec(url);
   1.852 +    lineNo = 1;
   1.853 +  }
   1.854 +
   1.855 +  uint32_t argCount;
   1.856 +  const char **argNames;
   1.857 +  nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(),
   1.858 +                                   typeAtom,
   1.859 +                                   &argCount, &argNames);
   1.860 +
   1.861 +  // Wrap the event target, so that we can use it as the scope for the event
   1.862 +  // handler. Note that mTarget is different from aElement in the <body> case,
   1.863 +  // where mTarget is a Window.
   1.864 +  //
   1.865 +  // The wrapScope doesn't really matter here, because the target will create
   1.866 +  // its reflector in the proper scope, and then we'll enter that compartment.
   1.867 +  JS::Rooted<JSObject*> wrapScope(cx, context->GetWindowProxy());
   1.868 +  JS::Rooted<JS::Value> v(cx);
   1.869 +  {
   1.870 +    JSAutoCompartment ac(cx, wrapScope);
   1.871 +    nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v,
   1.872 +                                             /* aAllowWrapping = */ false);
   1.873 +    if (NS_WARN_IF(NS_FAILED(rv))) {
   1.874 +      return rv;
   1.875 +    }
   1.876 +  }
   1.877 +  JS::Rooted<JSObject*> target(cx, &v.toObject());
   1.878 +  JSAutoCompartment ac(cx, target);
   1.879 +
   1.880 +  nsDependentAtomString str(attrName);
   1.881 +  // Most of our names are short enough that we don't even have to malloc
   1.882 +  // the JS string stuff, so don't worry about playing games with
   1.883 +  // refcounting XPCOM stringbuffers.
   1.884 +  JS::Rooted<JSString*> jsStr(cx, JS_NewUCStringCopyN(cx,
   1.885 +                                                      str.BeginReading(),
   1.886 +                                                      str.Length()));
   1.887 +  NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY);
   1.888 +
   1.889 +  // Get the reflector for |aElement|, so that we can pass to setElement.
   1.890 +  if (NS_WARN_IF(!WrapNewBindingObject(cx, target, aElement, &v))) {
   1.891 +    return NS_ERROR_FAILURE;
   1.892 +  }
   1.893 +  JS::CompileOptions options(cx);
   1.894 +  options.setIntroductionType("eventHandler")
   1.895 +         .setFileAndLine(url.get(), lineNo)
   1.896 +         .setVersion(SCRIPTVERSION_DEFAULT)
   1.897 +         .setElement(&v.toObject())
   1.898 +         .setElementAttributeName(jsStr)
   1.899 +         .setDefineOnScope(false);
   1.900 +
   1.901 +  JS::Rooted<JSObject*> handler(cx);
   1.902 +  result = nsJSUtils::CompileFunction(cx, target, options,
   1.903 +                                      nsAtomCString(typeAtom),
   1.904 +                                      argCount, argNames, *body, handler.address());
   1.905 +  NS_ENSURE_SUCCESS(result, result);
   1.906 +  NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
   1.907 +
   1.908 +  nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mTarget);
   1.909 +  if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) {
   1.910 +    nsRefPtr<OnErrorEventHandlerNonNull> handlerCallback =
   1.911 +      new OnErrorEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr);
   1.912 +    jsEventHandler->SetHandler(handlerCallback);
   1.913 +  } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) {
   1.914 +    nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback =
   1.915 +      new OnBeforeUnloadEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr);
   1.916 +    jsEventHandler->SetHandler(handlerCallback);
   1.917 +  } else {
   1.918 +    nsRefPtr<EventHandlerNonNull> handlerCallback =
   1.919 +      new EventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr);
   1.920 +    jsEventHandler->SetHandler(handlerCallback);
   1.921 +  }
   1.922 +
   1.923 +  return result;
   1.924 +}
   1.925 +
   1.926 +nsresult
   1.927 +EventListenerManager::HandleEventSubType(Listener* aListener,
   1.928 +                                         nsIDOMEvent* aDOMEvent,
   1.929 +                                         EventTarget* aCurrentTarget)
   1.930 +{
   1.931 +  nsresult result = NS_OK;
   1.932 +  EventListenerHolder listenerHolder(aListener->mListener);  // strong ref
   1.933 +
   1.934 +  // If this is a script handler and we haven't yet
   1.935 +  // compiled the event handler itself
   1.936 +  if ((aListener->mListenerType == Listener::eJSEventListener) &&
   1.937 +      aListener->mHandlerIsString) {
   1.938 +    result = CompileEventHandlerInternal(aListener, nullptr, nullptr);
   1.939 +    aListener = nullptr;
   1.940 +  }
   1.941 +
   1.942 +  if (NS_SUCCEEDED(result)) {
   1.943 +    if (mIsMainThreadELM) {
   1.944 +      nsContentUtils::EnterMicroTask();
   1.945 +    }
   1.946 +    // nsIDOMEvent::currentTarget is set in EventDispatcher.
   1.947 +    if (listenerHolder.HasWebIDLCallback()) {
   1.948 +      ErrorResult rv;
   1.949 +      listenerHolder.GetWebIDLCallback()->
   1.950 +        HandleEvent(aCurrentTarget, *(aDOMEvent->InternalDOMEvent()), rv);
   1.951 +      result = rv.ErrorCode();
   1.952 +    } else {
   1.953 +      result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent);
   1.954 +    }
   1.955 +    if (mIsMainThreadELM) {
   1.956 +      nsContentUtils::LeaveMicroTask();
   1.957 +    }
   1.958 +  }
   1.959 +
   1.960 +  return result;
   1.961 +}
   1.962 +
   1.963 +/**
   1.964 +* Causes a check for event listeners and processing by them if they exist.
   1.965 +* @param an event listener
   1.966 +*/
   1.967 +
   1.968 +void
   1.969 +EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
   1.970 +                                          WidgetEvent* aEvent,
   1.971 +                                          nsIDOMEvent** aDOMEvent,
   1.972 +                                          EventTarget* aCurrentTarget,
   1.973 +                                          nsEventStatus* aEventStatus)
   1.974 +{
   1.975 +  //Set the value of the internal PreventDefault flag properly based on aEventStatus
   1.976 +  if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
   1.977 +    aEvent->mFlags.mDefaultPrevented = true;
   1.978 +  }
   1.979 +
   1.980 +  nsAutoTObserverArray<Listener, 2>::EndLimitedIterator iter(mListeners);
   1.981 +  Maybe<nsAutoPopupStatePusher> popupStatePusher;
   1.982 +  if (mIsMainThreadELM) {
   1.983 +    popupStatePusher.construct(Event::GetEventPopupControlState(aEvent));
   1.984 +  }
   1.985 +
   1.986 +  bool hasListener = false;
   1.987 +  while (iter.HasMore()) {
   1.988 +    if (aEvent->mFlags.mImmediatePropagationStopped) {
   1.989 +      break;
   1.990 +    }
   1.991 +    Listener* listener = &iter.GetNext();
   1.992 +    // Check that the phase is same in event and event listener.
   1.993 +    // Handle only trusted events, except when listener permits untrusted events.
   1.994 +    if (ListenerCanHandle(listener, aEvent)) {
   1.995 +      hasListener = true;
   1.996 +      if (listener->IsListening(aEvent) &&
   1.997 +          (aEvent->mFlags.mIsTrusted ||
   1.998 +           listener->mFlags.mAllowUntrustedEvents)) {
   1.999 +        if (!*aDOMEvent) {
  1.1000 +          // This is tiny bit slow, but happens only once per event.
  1.1001 +          nsCOMPtr<EventTarget> et =
  1.1002 +            do_QueryInterface(aEvent->originalTarget);
  1.1003 +          EventDispatcher::CreateEvent(et, aPresContext,
  1.1004 +                                       aEvent, EmptyString(), aDOMEvent);
  1.1005 +        }
  1.1006 +        if (*aDOMEvent) {
  1.1007 +          if (!aEvent->currentTarget) {
  1.1008 +            aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent();
  1.1009 +            if (!aEvent->currentTarget) {
  1.1010 +              break;
  1.1011 +            }
  1.1012 +          }
  1.1013 +
  1.1014 +          if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent,
  1.1015 +                                           aCurrentTarget))) {
  1.1016 +            aEvent->mFlags.mExceptionHasBeenRisen = true;
  1.1017 +          }
  1.1018 +        }
  1.1019 +      }
  1.1020 +    }
  1.1021 +  }
  1.1022 +
  1.1023 +  aEvent->currentTarget = nullptr;
  1.1024 +
  1.1025 +  if (mIsMainThreadELM && !hasListener) {
  1.1026 +    mNoListenerForEvent = aEvent->message;
  1.1027 +    mNoListenerForEventAtom = aEvent->userType;
  1.1028 +  }
  1.1029 +
  1.1030 +  if (aEvent->mFlags.mDefaultPrevented) {
  1.1031 +    *aEventStatus = nsEventStatus_eConsumeNoDefault;
  1.1032 +  }
  1.1033 +}
  1.1034 +
  1.1035 +void
  1.1036 +EventListenerManager::Disconnect()
  1.1037 +{
  1.1038 +  mTarget = nullptr;
  1.1039 +  RemoveAllListeners();
  1.1040 +}
  1.1041 +
  1.1042 +void
  1.1043 +EventListenerManager::AddEventListener(
  1.1044 +                        const nsAString& aType,
  1.1045 +                        const EventListenerHolder& aListenerHolder,
  1.1046 +                        bool aUseCapture,
  1.1047 +                        bool aWantsUntrusted)
  1.1048 +{
  1.1049 +  EventListenerFlags flags;
  1.1050 +  flags.mCapture = aUseCapture;
  1.1051 +  flags.mAllowUntrustedEvents = aWantsUntrusted;
  1.1052 +  return AddEventListenerByType(aListenerHolder, aType, flags);
  1.1053 +}
  1.1054 +
  1.1055 +void
  1.1056 +EventListenerManager::RemoveEventListener(
  1.1057 +                        const nsAString& aType,
  1.1058 +                        const EventListenerHolder& aListenerHolder,
  1.1059 +                        bool aUseCapture)
  1.1060 +{
  1.1061 +  EventListenerFlags flags;
  1.1062 +  flags.mCapture = aUseCapture;
  1.1063 +  RemoveEventListenerByType(aListenerHolder, aType, flags);
  1.1064 +}
  1.1065 +
  1.1066 +void
  1.1067 +EventListenerManager::AddListenerForAllEvents(nsIDOMEventListener* aDOMListener,
  1.1068 +                                              bool aUseCapture,
  1.1069 +                                              bool aWantsUntrusted,
  1.1070 +                                              bool aSystemEventGroup)
  1.1071 +{
  1.1072 +  EventListenerFlags flags;
  1.1073 +  flags.mCapture = aUseCapture;
  1.1074 +  flags.mAllowUntrustedEvents = aWantsUntrusted;
  1.1075 +  flags.mInSystemGroup = aSystemEventGroup;
  1.1076 +  EventListenerHolder listenerHolder(aDOMListener);
  1.1077 +  AddEventListenerInternal(listenerHolder, NS_EVENT_ALL, nullptr, EmptyString(),
  1.1078 +                           flags, false, true);
  1.1079 +}
  1.1080 +
  1.1081 +void
  1.1082 +EventListenerManager::RemoveListenerForAllEvents(
  1.1083 +                        nsIDOMEventListener* aDOMListener,
  1.1084 +                        bool aUseCapture,
  1.1085 +                        bool aSystemEventGroup)
  1.1086 +{
  1.1087 +  EventListenerFlags flags;
  1.1088 +  flags.mCapture = aUseCapture;
  1.1089 +  flags.mInSystemGroup = aSystemEventGroup;
  1.1090 +  EventListenerHolder listenerHolder(aDOMListener);
  1.1091 +  RemoveEventListenerInternal(listenerHolder, NS_EVENT_ALL, nullptr,
  1.1092 +                              EmptyString(), flags, true);
  1.1093 +}
  1.1094 +
  1.1095 +bool
  1.1096 +EventListenerManager::HasMutationListeners()
  1.1097 +{
  1.1098 +  if (mMayHaveMutationListeners) {
  1.1099 +    uint32_t count = mListeners.Length();
  1.1100 +    for (uint32_t i = 0; i < count; ++i) {
  1.1101 +      Listener* listener = &mListeners.ElementAt(i);
  1.1102 +      if (listener->mEventType >= NS_MUTATION_START &&
  1.1103 +          listener->mEventType <= NS_MUTATION_END) {
  1.1104 +        return true;
  1.1105 +      }
  1.1106 +    }
  1.1107 +  }
  1.1108 +
  1.1109 +  return false;
  1.1110 +}
  1.1111 +
  1.1112 +uint32_t
  1.1113 +EventListenerManager::MutationListenerBits()
  1.1114 +{
  1.1115 +  uint32_t bits = 0;
  1.1116 +  if (mMayHaveMutationListeners) {
  1.1117 +    uint32_t count = mListeners.Length();
  1.1118 +    for (uint32_t i = 0; i < count; ++i) {
  1.1119 +      Listener* listener = &mListeners.ElementAt(i);
  1.1120 +      if (listener->mEventType >= NS_MUTATION_START &&
  1.1121 +          listener->mEventType <= NS_MUTATION_END) {
  1.1122 +        if (listener->mEventType == NS_MUTATION_SUBTREEMODIFIED) {
  1.1123 +          return kAllMutationBits;
  1.1124 +        }
  1.1125 +        bits |= MutationBitForEventType(listener->mEventType);
  1.1126 +      }
  1.1127 +    }
  1.1128 +  }
  1.1129 +  return bits;
  1.1130 +}
  1.1131 +
  1.1132 +bool
  1.1133 +EventListenerManager::HasListenersFor(const nsAString& aEventName)
  1.1134 +{
  1.1135 +  nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName);
  1.1136 +  return HasListenersFor(atom);
  1.1137 +}
  1.1138 +
  1.1139 +bool
  1.1140 +EventListenerManager::HasListenersFor(nsIAtom* aEventNameWithOn)
  1.1141 +{
  1.1142 +#ifdef DEBUG
  1.1143 +  nsAutoString name;
  1.1144 +  aEventNameWithOn->ToString(name);
  1.1145 +#endif
  1.1146 +  NS_ASSERTION(StringBeginsWith(name, NS_LITERAL_STRING("on")),
  1.1147 +               "Event name does not start with 'on'");
  1.1148 +  uint32_t count = mListeners.Length();
  1.1149 +  for (uint32_t i = 0; i < count; ++i) {
  1.1150 +    Listener* listener = &mListeners.ElementAt(i);
  1.1151 +    if (listener->mTypeAtom == aEventNameWithOn) {
  1.1152 +      return true;
  1.1153 +    }
  1.1154 +  }
  1.1155 +  return false;
  1.1156 +}
  1.1157 +
  1.1158 +bool
  1.1159 +EventListenerManager::HasListeners()
  1.1160 +{
  1.1161 +  return !mListeners.IsEmpty();
  1.1162 +}
  1.1163 +
  1.1164 +nsresult
  1.1165 +EventListenerManager::GetListenerInfo(nsCOMArray<nsIEventListenerInfo>* aList)
  1.1166 +{
  1.1167 +  nsCOMPtr<EventTarget> target = do_QueryInterface(mTarget);
  1.1168 +  NS_ENSURE_STATE(target);
  1.1169 +  aList->Clear();
  1.1170 +  uint32_t count = mListeners.Length();
  1.1171 +  for (uint32_t i = 0; i < count; ++i) {
  1.1172 +    const Listener& listener = mListeners.ElementAt(i);
  1.1173 +    // If this is a script handler and we haven't yet
  1.1174 +    // compiled the event handler itself go ahead and compile it
  1.1175 +    if (listener.mListenerType == Listener::eJSEventListener &&
  1.1176 +        listener.mHandlerIsString) {
  1.1177 +      CompileEventHandlerInternal(const_cast<Listener*>(&listener), nullptr,
  1.1178 +                                  nullptr);
  1.1179 +    }
  1.1180 +    nsAutoString eventType;
  1.1181 +    if (listener.mAllEvents) {
  1.1182 +      eventType.SetIsVoid(true);
  1.1183 +    } else {
  1.1184 +      eventType.Assign(Substring(nsDependentAtomString(listener.mTypeAtom), 2));
  1.1185 +    }
  1.1186 +    // EventListenerInfo is defined in XPCOM, so we have to go ahead
  1.1187 +    // and convert to an XPCOM callback here...
  1.1188 +    nsRefPtr<EventListenerInfo> info =
  1.1189 +      new EventListenerInfo(eventType, listener.mListener.ToXPCOMCallback(),
  1.1190 +                            listener.mFlags.mCapture,
  1.1191 +                            listener.mFlags.mAllowUntrustedEvents,
  1.1192 +                            listener.mFlags.mInSystemGroup);
  1.1193 +    aList->AppendObject(info);
  1.1194 +  }
  1.1195 +  return NS_OK;
  1.1196 +}
  1.1197 +
  1.1198 +bool
  1.1199 +EventListenerManager::HasUnloadListeners()
  1.1200 +{
  1.1201 +  uint32_t count = mListeners.Length();
  1.1202 +  for (uint32_t i = 0; i < count; ++i) {
  1.1203 +    Listener* listener = &mListeners.ElementAt(i);
  1.1204 +    if (listener->mEventType == NS_PAGE_UNLOAD ||
  1.1205 +        listener->mEventType == NS_BEFORE_PAGE_UNLOAD) {
  1.1206 +      return true;
  1.1207 +    }
  1.1208 +  }
  1.1209 +  return false;
  1.1210 +}
  1.1211 +
  1.1212 +void
  1.1213 +EventListenerManager::SetEventHandler(nsIAtom* aEventName,
  1.1214 +                                      const nsAString& aTypeString,
  1.1215 +                                      EventHandlerNonNull* aHandler)
  1.1216 +{
  1.1217 +  if (!aHandler) {
  1.1218 +    RemoveEventHandler(aEventName, aTypeString);
  1.1219 +    return;
  1.1220 +  }
  1.1221 +
  1.1222 +  // Untrusted events are always permitted for non-chrome script
  1.1223 +  // handlers.
  1.1224 +  SetEventHandlerInternal(aEventName, aTypeString, TypedEventHandler(aHandler),
  1.1225 +                          !mIsMainThreadELM ||
  1.1226 +                          !nsContentUtils::IsCallerChrome());
  1.1227 +}
  1.1228 +
  1.1229 +void
  1.1230 +EventListenerManager::SetEventHandler(OnErrorEventHandlerNonNull* aHandler)
  1.1231 +{
  1.1232 +  if (mIsMainThreadELM) {
  1.1233 +    if (!aHandler) {
  1.1234 +      RemoveEventHandler(nsGkAtoms::onerror, EmptyString());
  1.1235 +      return;
  1.1236 +    }
  1.1237 +
  1.1238 +    // Untrusted events are always permitted for non-chrome script
  1.1239 +    // handlers.
  1.1240 +    SetEventHandlerInternal(nsGkAtoms::onerror, EmptyString(),
  1.1241 +                            TypedEventHandler(aHandler),
  1.1242 +                            !nsContentUtils::IsCallerChrome());
  1.1243 +  } else {
  1.1244 +    if (!aHandler) {
  1.1245 +      RemoveEventHandler(nullptr, NS_LITERAL_STRING("error"));
  1.1246 +      return;
  1.1247 +    }
  1.1248 +
  1.1249 +    // Untrusted events are always permitted.
  1.1250 +    SetEventHandlerInternal(nullptr, NS_LITERAL_STRING("error"),
  1.1251 +                            TypedEventHandler(aHandler), true);
  1.1252 +  }
  1.1253 +}
  1.1254 +
  1.1255 +void
  1.1256 +EventListenerManager::SetEventHandler(
  1.1257 +                        OnBeforeUnloadEventHandlerNonNull* aHandler)
  1.1258 +{
  1.1259 +  if (!aHandler) {
  1.1260 +    RemoveEventHandler(nsGkAtoms::onbeforeunload, EmptyString());
  1.1261 +    return;
  1.1262 +  }
  1.1263 +
  1.1264 +  // Untrusted events are always permitted for non-chrome script
  1.1265 +  // handlers.
  1.1266 +  SetEventHandlerInternal(nsGkAtoms::onbeforeunload, EmptyString(),
  1.1267 +                          TypedEventHandler(aHandler),
  1.1268 +                          !mIsMainThreadELM ||
  1.1269 +                          !nsContentUtils::IsCallerChrome());
  1.1270 +}
  1.1271 +
  1.1272 +const TypedEventHandler*
  1.1273 +EventListenerManager::GetTypedEventHandler(nsIAtom* aEventName,
  1.1274 +                                           const nsAString& aTypeString)
  1.1275 +{
  1.1276 +  uint32_t eventType = nsContentUtils::GetEventId(aEventName);
  1.1277 +  Listener* listener = FindEventHandler(eventType, aEventName, aTypeString);
  1.1278 +
  1.1279 +  if (!listener) {
  1.1280 +    return nullptr;
  1.1281 +  }
  1.1282 +
  1.1283 +  JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
  1.1284 +
  1.1285 +  if (listener->mHandlerIsString) {
  1.1286 +    CompileEventHandlerInternal(listener, nullptr, nullptr);
  1.1287 +  }
  1.1288 +
  1.1289 +  const TypedEventHandler& typedHandler =
  1.1290 +    jsEventHandler->GetTypedEventHandler();
  1.1291 +  return typedHandler.HasEventHandler() ? &typedHandler : nullptr;
  1.1292 +}
  1.1293 +
  1.1294 +size_t
  1.1295 +EventListenerManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
  1.1296 +{
  1.1297 +  size_t n = aMallocSizeOf(this);
  1.1298 +  n += mListeners.SizeOfExcludingThis(aMallocSizeOf);
  1.1299 +  uint32_t count = mListeners.Length();
  1.1300 +  for (uint32_t i = 0; i < count; ++i) {
  1.1301 +    JSEventHandler* jsEventHandler =
  1.1302 +      mListeners.ElementAt(i).GetJSEventHandler();
  1.1303 +    if (jsEventHandler) {
  1.1304 +      n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf);
  1.1305 +    }
  1.1306 +  }
  1.1307 +  return n;
  1.1308 +}
  1.1309 +
  1.1310 +void
  1.1311 +EventListenerManager::MarkForCC()
  1.1312 +{
  1.1313 +  uint32_t count = mListeners.Length();
  1.1314 +  for (uint32_t i = 0; i < count; ++i) {
  1.1315 +    const Listener& listener = mListeners.ElementAt(i);
  1.1316 +    JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
  1.1317 +    if (jsEventHandler) {
  1.1318 +      const TypedEventHandler& typedHandler =
  1.1319 +        jsEventHandler->GetTypedEventHandler();
  1.1320 +      if (typedHandler.HasEventHandler()) {
  1.1321 +        JS::ExposeObjectToActiveJS(typedHandler.Ptr()->Callable());
  1.1322 +      }
  1.1323 +    } else if (listener.mListenerType == Listener::eWrappedJSListener) {
  1.1324 +      xpc_TryUnmarkWrappedGrayObject(listener.mListener.GetXPCOMCallback());
  1.1325 +    } else if (listener.mListenerType == Listener::eWebIDLListener) {
  1.1326 +      // Callback() unmarks gray
  1.1327 +      listener.mListener.GetWebIDLCallback()->Callback();
  1.1328 +    }
  1.1329 +  }
  1.1330 +  if (mRefCnt.IsPurple()) {
  1.1331 +    mRefCnt.RemovePurple();
  1.1332 +  }
  1.1333 +}
  1.1334 +
  1.1335 +already_AddRefed<nsIScriptGlobalObject>
  1.1336 +EventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc)
  1.1337 +{
  1.1338 +  nsCOMPtr<nsINode> node(do_QueryInterface(mTarget));
  1.1339 +  nsCOMPtr<nsIDocument> doc;
  1.1340 +  nsCOMPtr<nsIScriptGlobalObject> global;
  1.1341 +  if (node) {
  1.1342 +    // Try to get context from doc
  1.1343 +    // XXX sXBL/XBL2 issue -- do we really want the owner here?  What
  1.1344 +    // if that's the XBL document?
  1.1345 +    doc = node->OwnerDoc();
  1.1346 +    if (doc->IsLoadedAsData()) {
  1.1347 +      return nullptr;
  1.1348 +    }
  1.1349 +
  1.1350 +    // We want to allow compiling an event handler even in an unloaded
  1.1351 +    // document, so use GetScopeObject here, not GetScriptHandlingObject.
  1.1352 +    global = do_QueryInterface(doc->GetScopeObject());
  1.1353 +  } else {
  1.1354 +    nsCOMPtr<nsPIDOMWindow> win = GetTargetAsInnerWindow();
  1.1355 +    if (win) {
  1.1356 +      doc = win->GetExtantDoc();
  1.1357 +      global = do_QueryInterface(win);
  1.1358 +    } else {
  1.1359 +      global = do_QueryInterface(mTarget);
  1.1360 +    }
  1.1361 +  }
  1.1362 +
  1.1363 +  doc.forget(aDoc);
  1.1364 +  return global.forget();
  1.1365 +}
  1.1366 +
  1.1367 +} // namespace mozilla

mercurial