Logo Search packages:      
Sourcecode: xulrunner version File versions

nsresult nsGenericElement::HandleDOMEvent ( nsPresContext *  aPresContext,
nsEvent aEvent,
nsIDOMEvent **  aDOMEvent,
PRUint32  aFlags,
nsEventStatus *  aEventStatus 
) [virtual, inherited]

Handle a DOM event for this piece of content. This method is responsible for handling and controlling all three stages of events, capture, local and bubble. It also does strange things to anonymous content which whiz right by this author's head.

Here are some beginning explanations:

  • if in INIT or CAPTURE mode, it must pass the event to its parent in CAPTURE mode (this happens before the event is fired, therefore the firing of events will occur from the root up to the target).
  • The event is fired to listeners.
  • If in INIT or BUBBLE mode, it passes the event to its parent in BUBBLE mode. This means that the events will be fired up the chain starting from the target to the ancestor.

NOTE: if you are extending nsGenericElement and have to do a default action, call super::HandleDOMEvent() first and check for aEventStatus != nsEventStatus_eIgnore and make sure you are not in CAPTURE mode before proceeding.

XXX Go comment that method, Will Robinson.

Parameters:
aPresContext the current presentation context
aEvent the event that is being propagated
aDOMEvent a special event that may contain a modified target. Pass in null here or aDOMEvent if you are in HandleDOMEvent already; don't worry your pretty little head about it.
aFlags flags that describe what mode we are in. Generally NS_EVENT_FLAG_CAPTURE, NS_EVENT_FLAG_BUBBLE and NS_EVENT_FLAG_INIT are the ones that matter.
aEventStatus the status returned from the function. Generally nsEventStatus_eIgnore

Implements nsIContent.

Reimplemented in nsHTMLOptGroupElement, and nsHTMLSelectElement.

Definition at line 2036 of file nsGenericElement.cpp.

References nsEvent::flags, nsGenericElement::GetBindingParent(), nsIContent::GetCurrentDoc(), nsGenericElement::GetListenerManager(), nsGenericElement::GetOwnerDoc(), nsIContent::GetParent(), nsGenericElement::IsInDoc(), nsGenericElement::IsNativeAnonymous(), and nsEvent::message.

Referenced by nsHTMLSelectElement::HandleDOMEvent(), nsGenericHTMLElement::HandleDOMEventForAnchors(), nsGenericElement::SetAttr(), nsGenericHTMLElement::SetAttrAndNotify(), and nsGenericElement::UnsetAttr().

{
  // Make sure to tell the event that dispatch has started.
  NS_MARK_EVENT_DISPATCH_STARTED(aEvent);

  nsresult ret = NS_OK;
  PRBool retarget = PR_FALSE;
  PRBool externalDOMEvent = PR_FALSE;
  nsCOMPtr<nsIDOMEventTarget> oldTarget;

  nsIDOMEvent* domEvent = nsnull;
  if (NS_EVENT_FLAG_INIT & aFlags) {
    if (aDOMEvent) {
      if (*aDOMEvent) {
        externalDOMEvent = PR_TRUE;   
      }
    } else {
      aDOMEvent = &domEvent;
    }
    aEvent->flags |= aFlags;
    aFlags &= ~(NS_EVENT_FLAG_CANT_BUBBLE | NS_EVENT_FLAG_CANT_CANCEL);
    aFlags |= NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_CAPTURE;
  }

  // Find out whether we're anonymous.
  if (IsNativeAnonymous()) {
    retarget = PR_TRUE;
  } else {
    nsIContent* parent = GetParent();
    if (parent) {
      if (*aDOMEvent) {
        (*aDOMEvent)->GetTarget(getter_AddRefs(oldTarget));
        nsCOMPtr<nsIContent> content(do_QueryInterface(oldTarget));
        if (content && content->GetBindingParent() == parent)
          retarget = PR_TRUE;
      } else if (GetBindingParent() == parent) {
        retarget = PR_TRUE;
      }
    }
  }

  // check for an anonymous parent
  nsCOMPtr<nsIContent> parent;
  nsIDocument* ownerDoc = GetOwnerDoc();
  if (ownerDoc) {
    ownerDoc->BindingManager()->GetInsertionParent(this,
                                                   getter_AddRefs(parent));
  }
  if (!parent) {
    // if we didn't find an anonymous parent, use the explicit one,
    // whether it's null or not...
    parent = GetParent();
  }

  if (retarget || (parent.get() != GetParent())) {
    if (!*aDOMEvent) {
      // We haven't made a DOMEvent yet.  Force making one now.
      nsCOMPtr<nsIEventListenerManager> listenerManager;
      if (NS_FAILED(ret = GetListenerManager(getter_AddRefs(listenerManager)))) {
        return ret;
      }
      nsAutoString empty;
      if (NS_FAILED(ret = listenerManager->CreateEvent(aPresContext, aEvent,
                                                       empty, aDOMEvent)))
        return ret;
    }

    if (!*aDOMEvent) {
      return NS_ERROR_FAILURE;
    }
    nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(*aDOMEvent);
    if (!privateEvent) {
      return NS_ERROR_FAILURE;
    }

    (*aDOMEvent)->GetTarget(getter_AddRefs(oldTarget));

    PRBool hasOriginal;
    privateEvent->HasOriginalTarget(&hasOriginal);

    if (!hasOriginal)
      privateEvent->SetOriginalTarget(oldTarget);

    if (retarget) {
      nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetParent());
      privateEvent->SetTarget(target);
    }
  }

  //Capturing stage evaluation
  if (NS_EVENT_FLAG_CAPTURE & aFlags &&
      aEvent->message != NS_PAGE_LOAD &&
      aEvent->message != NS_SCRIPT_LOAD &&
      aEvent->message != NS_IMAGE_LOAD &&
      aEvent->message != NS_IMAGE_ERROR &&
      aEvent->message != NS_SCROLL_EVENT) {
    //Initiate capturing phase.  Special case first call to document
    if (parent) {
      parent->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
                             aFlags & NS_EVENT_CAPTURE_MASK,
                             aEventStatus);
    } else {
      nsIDocument* document = GetCurrentDoc();
      if (document) {
        ret = document->HandleDOMEvent(aPresContext, aEvent,
                                       aDOMEvent,
                                       aFlags & NS_EVENT_CAPTURE_MASK,
                                       aEventStatus);
      }
    }
  }

  if (retarget) {
    // The event originated beneath us, and we performed a retargeting.
    // We need to restore the original target of the event.
    nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(*aDOMEvent);
    if (privateEvent)
      privateEvent->SetTarget(oldTarget);
  }

  // Weak pointer, which is fine since the hash table owns the
  // listener manager
  nsIEventListenerManager *lm = nsnull;

  if (HasEventListenerManager()) {
    EventListenerManagerMapEntry *entry =
      NS_STATIC_CAST(EventListenerManagerMapEntry *,
                     PL_DHashTableOperate(&sEventListenerManagersHash, this,
                                          PL_DHASH_LOOKUP));

    if (PL_DHASH_ENTRY_IS_FREE(entry)) {
      NS_ERROR("Huh, our bit says we have an event listener manager, but "
               "there's nothing in the hash!?!!");

      return NS_ERROR_UNEXPECTED;
    }

    lm = entry->mListenerManager;
  }

  //Local handling stage
  if (lm &&
      !(NS_EVENT_FLAG_CANT_BUBBLE & aEvent->flags &&
        NS_EVENT_FLAG_BUBBLE & aFlags && !(NS_EVENT_FLAG_INIT & aFlags)) &&
      !(aEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH)) {
    aEvent->flags |= aFlags;

    nsCOMPtr<nsIDOMEventTarget> curTarg =
      do_QueryInterface(NS_STATIC_CAST(nsIXMLContent *, this));

    lm->HandleEvent(aPresContext, aEvent, aDOMEvent, curTarg, aFlags,
                    aEventStatus);

    aEvent->flags &= ~aFlags;

    // We don't want scroll events to bubble further after it has been
    // handled at the local stage.
    if (aEvent->message == NS_SCROLL_EVENT && aFlags & NS_EVENT_FLAG_BUBBLE)
      aEvent->flags |= NS_EVENT_FLAG_CANT_BUBBLE;
  }

  if (retarget) {
    // The event originated beneath us, and we need to perform a
    // retargeting.
    nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(*aDOMEvent);
    if (privateEvent) {
      nsCOMPtr<nsIDOMEventTarget> parentTarget(do_QueryInterface(GetParent()));
      privateEvent->SetTarget(parentTarget);
    }
  }

  //Bubbling stage
  if (NS_EVENT_FLAG_BUBBLE & aFlags && IsInDoc() &&
      aEvent->message != NS_PAGE_LOAD && aEvent->message != NS_SCRIPT_LOAD &&
      aEvent->message != NS_IMAGE_ERROR && aEvent->message != NS_IMAGE_LOAD &&
      !(aEvent->message == NS_SCROLL_EVENT &&
        aEvent->flags & NS_EVENT_FLAG_CANT_BUBBLE)) {
    if (parent) {
      // If there's a parent we pass the event to the parent...

      ret = parent->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
                                   aFlags & NS_EVENT_BUBBLE_MASK,
                                   aEventStatus);
    } else {
      // If there's no parent but there is a document (i.e. this is
      // the root node) we pass the event to the document...
      nsIDocument* document = GetCurrentDoc();
      if (document) {
        ret = document->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
                                       aFlags & NS_EVENT_BUBBLE_MASK, 
                                       aEventStatus);
      }
    }
  }

  if (retarget) {
    // The event originated beneath us, and we performed a
    // retargeting.  We need to restore the original target of the
    // event.

    nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(*aDOMEvent);
    if (privateEvent)
      privateEvent->SetTarget(oldTarget);
  }

  if (NS_EVENT_FLAG_INIT & aFlags) {
    // We're leaving the DOM event loop so if we created a DOM event,
    // release here.  If externalDOMEvent is set the event was passed
    // in and we don't own it

    if (*aDOMEvent && !externalDOMEvent) {
      nsrefcnt rc;
      NS_RELEASE2(*aDOMEvent, rc);
      if (0 != rc) {
        // Okay, so someone in the DOM loop (a listener, JS object)
        // still has a ref to the DOM Event but the internal data
        // hasn't been malloc'd.  Force a copy of the data here so the
        // DOM Event is still valid.

        nsCOMPtr<nsIPrivateDOMEvent> privateEvent =
          do_QueryInterface(*aDOMEvent);

        if (privateEvent) {
          privateEvent->DuplicatePrivateData();
        }
      }

      aDOMEvent = nsnull;
    }

    // Now that we're done with this event, remove the flag that says
    // we're in the process of dispatching this event.
    NS_MARK_EVENT_DISPATCH_DONE(aEvent);
  }

  return ret;
}


Generated by  Doxygen 1.6.0   Back to index