nsresult
nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
                                           const EventListenerHolder& aListener,
                                           nsIDOMEvent* aDOMEvent,
                                           EventTarget* aCurrentTarget,
                                           nsCxPusher* aPusher)
{
  nsresult result = NS_OK;

  // If this is a script handler and we haven't yet
  // compiled the event handler itself
  if ((aListenerStruct->mListenerType == eJSEventListener) &&
      aListenerStruct->mHandlerIsString) {
    nsIJSEventListener *jslistener = aListenerStruct->GetJSListener();
    result = CompileEventHandlerInternal(aListenerStruct,
                                         jslistener->GetEventContext() !=
                                           aPusher->GetCurrentScriptContext(),
                                         nullptr);
  }

  if (NS_SUCCEEDED(result)) {
    nsAutoMicroTask mt;
    // nsIDOMEvent::currentTarget is set in nsEventDispatcher.
    if (aListener.HasWebIDLCallback()) {
      ErrorResult rv;
      aListener.GetWebIDLCallback()->
        HandleEvent(aCurrentTarget, *(aDOMEvent->InternalDOMEvent()), rv);
      result = rv.ErrorCode();
    } else {
      result = aListener.GetXPCOMCallback()->HandleEvent(aDOMEvent);
    }
  }

  return result;
}
void
nsEventListenerManager::AddEventListenerInternal(
                          const EventListenerHolder& aListener,
                          uint32_t aType,
                          nsIAtom* aTypeAtom,
                          const nsAString& aTypeString,
                          const EventListenerFlags& aFlags,
                          bool aHandler,
                          bool aAllEvents)
{
  MOZ_ASSERT((NS_IsMainThread() && aType && aTypeAtom) || // Main thread
             (!NS_IsMainThread() && aType && !aTypeString.IsEmpty()) || // non-main-thread
             aAllEvents, "Missing type"); // all-events listener

  if (!aListener || mClearingListeners) {
    return;
  }

  // Since there is no public API to call us with an EventListenerHolder, we
  // know that there's an EventListenerHolder on the stack holding a strong ref
  // to the listener.

  nsListenerStruct* ls;
  uint32_t count = mListeners.Length();
  for (uint32_t i = 0; i < count; i++) {
    ls = &mListeners.ElementAt(i);
    // mListener == aListener is the last one, since it can be a bit slow.
    if (ls->mListenerIsHandler == aHandler &&
        ls->mFlags == aFlags &&
        EVENT_TYPE_EQUALS(ls, aType, aTypeAtom, aTypeString, aAllEvents) &&
        ls->mListener == aListener) {
      return;
    }
  }

  mNoListenerForEvent = NS_EVENT_NULL;
  mNoListenerForEventAtom = nullptr;

  ls = aAllEvents ? mListeners.InsertElementAt(0) : mListeners.AppendElement();
  ls->mListener = aListener;
  MOZ_ASSERT(aType < PR_UINT16_MAX);
  ls->mEventType = aType;
  ls->mTypeString = aTypeString;
  ls->mTypeAtom = aTypeAtom;
  ls->mFlags = aFlags;
  ls->mListenerIsHandler = aHandler;
  ls->mHandlerIsString = false;
  ls->mAllEvents = aAllEvents;

  // Detect the type of event listener.
  nsCOMPtr<nsIXPConnectWrappedJS> wjs;
  if (aFlags.mListenerIsJSListener) {
    MOZ_ASSERT(!aListener.HasWebIDLCallback());
    ls->mListenerType = eJSEventListener;
  } else if (aListener.HasWebIDLCallback()) {
    ls->mListenerType = eWebIDLListener;
  } else if ((wjs = do_QueryInterface(aListener.GetXPCOMCallback()))) {
    ls->mListenerType = eWrappedJSListener;
  } else {
    ls->mListenerType = eNativeListener;
  }


  if (aFlags.mInSystemGroup) {
    mMayHaveSystemGroupListeners = true;
  }
  if (aFlags.mCapture) {
    mMayHaveCapturingListeners = true;
  }

  if (aType == NS_AFTERPAINT) {
    mMayHavePaintEventListener = true;
    nsPIDOMWindow* window = GetInnerWindowForTarget();
    if (window) {
      window->SetHasPaintEventListeners();
    }
  } else if (aType == NS_MOZAUDIOAVAILABLE) {
    mMayHaveAudioAvailableEventListener = true;
    nsPIDOMWindow* window = GetInnerWindowForTarget();
    if (window) {
      window->SetHasAudioAvailableEventListeners();
    }
  } else if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) {
    // For mutation listeners, we need to update the global bit on the DOM window.
    // Otherwise we won't actually fire the mutation event.
    mMayHaveMutationListeners = true;
    // Go from our target to the nearest enclosing DOM window.
    nsPIDOMWindow* window = GetInnerWindowForTarget();
    if (window) {
      nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
      if (doc) {
        doc->WarnOnceAbout(nsIDocument::eMutationEvent);
      }
      // If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all
      // mutations. nsContentUtils::HasMutationListeners relies on this.
      window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ?
                                   kAllMutationBits :
                                   MutationBitForEventType(aType));
    }
  } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
    EnableDevice(NS_DEVICE_ORIENTATION);
  } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) {
    EnableDevice(NS_DEVICE_PROXIMITY);
  } else if (aTypeAtom == nsGkAtoms::ondevicelight) {
    EnableDevice(NS_DEVICE_LIGHT);
  } else if (aTypeAtom == nsGkAtoms::ondevicemotion) {
    EnableDevice(NS_DEVICE_MOTION);
#ifdef MOZ_B2G
  } else if (aTypeAtom == nsGkAtoms::onmoztimechange) {
    nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
    if (window) {
      window->EnableTimeChangeNotifications();
    }
  } else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) {
    nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
    if (window) {
      window->EnableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
    }
  } else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) {
    nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
    if (window) {
      window->EnableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
    }
#endif // MOZ_B2G
  } else if (aTypeAtom == nsGkAtoms::ontouchstart ||
             aTypeAtom == nsGkAtoms::ontouchend ||
             aTypeAtom == nsGkAtoms::ontouchmove ||
             aTypeAtom == nsGkAtoms::ontouchenter ||
             aTypeAtom == nsGkAtoms::ontouchleave ||
             aTypeAtom == nsGkAtoms::ontouchcancel) {
    mMayHaveTouchEventListener = true;
    nsPIDOMWindow* window = GetInnerWindowForTarget();
    // we don't want touchevent listeners added by scrollbars to flip this flag
    // so we ignore listeners created with system event flag
    if (window && !aFlags.mInSystemGroup) {
      window->SetHasTouchEventListeners();
    }
  } else if (aTypeAtom == nsGkAtoms::onmouseenter ||
             aTypeAtom == nsGkAtoms::onmouseleave) {
    mMayHaveMouseEnterLeaveEventListener = true;
    nsPIDOMWindow* window = GetInnerWindowForTarget();
    if (window) {
#ifdef DEBUG
      nsCOMPtr<nsIDocument> d = window->GetExtantDoc();
      NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d),
                       "Please do not use mouseenter/leave events in chrome. "
                       "They are slower than mouseover/out!");
#endif
      window->SetHasMouseEnterLeaveEventListeners();
    }
#ifdef MOZ_GAMEPAD
  } else if (aType >= NS_GAMEPAD_START &&
             aType <= NS_GAMEPAD_END) {
    nsPIDOMWindow* window = GetInnerWindowForTarget();
    if (window) {
      window->SetHasGamepadEventListener();
    }
#endif
  }
  if (aTypeAtom && mTarget) {
    mTarget->EventListenerAdded(aTypeAtom);
  }
}