Bug 962251 - Add relatedTarget in FocusEvent, r=smaug
authorKershaw Chang <kechang@mozilla.com>
Wed, 30 Mar 2016 00:53:00 +0200
changeset 291199 60a67e654ae703d0f2584a76778e8571b9598770
parent 291198 2579ddeadeb1574d16c4f3efc489f45b4c8ac439
child 291200 31397f0f3154c2d609155d99339a7625a50eafa4
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs962251
milestone48.0a1
Bug 962251 - Add relatedTarget in FocusEvent, r=smaug
dom/base/nsFocusManager.cpp
dom/base/nsFocusManager.h
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -93,54 +93,59 @@ LazyLogModule gFocusNavigationLog("Focus
 #define LOGCONTENT(format, content) LOGTAG(gFocusLog, format, content)
 #define LOGCONTENTNAVIGATION(format, content) LOGTAG(gFocusNavigationLog, format, content)
 
 struct nsDelayedBlurOrFocusEvent
 {
   nsDelayedBlurOrFocusEvent(EventMessage aEventMessage,
                             nsIPresShell* aPresShell,
                             nsIDocument* aDocument,
-                            EventTarget* aTarget)
+                            EventTarget* aTarget,
+                            EventTarget* aRelatedTarget)
     : mPresShell(aPresShell)
     , mDocument(aDocument)
     , mTarget(aTarget)
     , mEventMessage(aEventMessage)
+    , mRelatedTarget(aRelatedTarget)
  {
  }
 
   nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent& aOther)
     : mPresShell(aOther.mPresShell)
     , mDocument(aOther.mDocument)
     , mTarget(aOther.mTarget)
     , mEventMessage(aOther.mEventMessage)
   {
   }
 
   nsCOMPtr<nsIPresShell> mPresShell;
   nsCOMPtr<nsIDocument> mDocument;
   nsCOMPtr<EventTarget> mTarget;
   EventMessage mEventMessage;
+  nsCOMPtr<EventTarget> mRelatedTarget;
 };
 
 inline void ImplCycleCollectionUnlink(nsDelayedBlurOrFocusEvent& aField)
 {
   aField.mPresShell = nullptr;
   aField.mDocument = nullptr;
   aField.mTarget = nullptr;
+  aField.mRelatedTarget = nullptr;
 }
 
 inline void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
                             nsDelayedBlurOrFocusEvent& aField,
                             const char* aName,
                             uint32_t aFlags = 0)
 {
   CycleCollectionNoteChild(aCallback, aField.mPresShell.get(), aName, aFlags);
   CycleCollectionNoteChild(aCallback, aField.mDocument.get(), aName, aFlags);
   CycleCollectionNoteChild(aCallback, aField.mTarget.get(), aName, aFlags);
+  CycleCollectionNoteChild(aCallback, aField.mRelatedTarget.get(), aName, aFlags);
 }
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager)
   NS_INTERFACE_MAP_ENTRY(nsIFocusManager)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFocusManager)
 NS_INTERFACE_MAP_END
@@ -1026,18 +1031,20 @@ nsFocusManager::FireDelayedEvents(nsIDoc
         // firing events on it. Note the symmetry between this condition and
         // the similar one in nsDocument.cpp:FireOrClearDelayedEvents.
         mDelayedBlurFocusEvents.RemoveElementAt(i);
         --i;
       } else if (!aDocument->EventHandlingSuppressed()) {
         EventMessage message = mDelayedBlurFocusEvents[i].mEventMessage;
         nsCOMPtr<EventTarget> target = mDelayedBlurFocusEvents[i].mTarget;
         nsCOMPtr<nsIPresShell> presShell = mDelayedBlurFocusEvents[i].mPresShell;
+        nsCOMPtr<EventTarget> relatedTarget = mDelayedBlurFocusEvents[i].mRelatedTarget;
         mDelayedBlurFocusEvents.RemoveElementAt(i);
-        SendFocusOrBlurEvent(message, presShell, aDocument, target, 0, false);
+        SendFocusOrBlurEvent(message, presShell, aDocument, target, 0,
+                             false, false, relatedTarget);
         --i;
       }
     }
   }
 
   return NS_OK;
 }
 
@@ -1277,16 +1284,17 @@ nsFocusManager::SetFocusInner(nsIContent
 
   LOGCONTENT("Shift Focus: %s", contentToFocus.get());
   LOGFOCUS((" Flags: %x Current Window: %p New Window: %p Current Element: %p",
            aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedContent.get()));
   LOGFOCUS((" In Active Window: %d In Focused Window: %d SendFocus: %d",
            isElementInActiveWindow, isElementInFocusedWindow, sendFocusEvent));
 
   if (sendFocusEvent) {
+    nsCOMPtr<nsIContent> oldFocusedContent = mFocusedContent;
     // return if blurring fails or the focus changes during the blur
     if (mFocusedWindow) {
       // if the focus is being moved to another element in the same document,
       // or to a descendant, pass the existing window to Blur so that the
       // current node in the existing window is cleared. If moving to a
       // window elsewhere, we want to maintain the current node in the
       // window but still blur it.
       bool currentIsSameOrAncestor = IsSameOrAncestor(mFocusedWindow, newWindow);
@@ -1302,22 +1310,23 @@ nsFocusManager::SetFocusInner(nsIContent
       // D is focused and we want to focus C. Once D has been blurred, we need
       // to clear out the focus in A, otherwise A would still maintain that B
       // was focused, and B that D was focused.
       nsCOMPtr<nsPIDOMWindowOuter> commonAncestor;
       if (!isElementInFocusedWindow)
         commonAncestor = GetCommonAncestor(newWindow, mFocusedWindow);
 
       if (!Blur(currentIsSameOrAncestor ? mFocusedWindow.get() : nullptr,
-                commonAncestor, !isElementInFocusedWindow, aAdjustWidget))
+                commonAncestor, !isElementInFocusedWindow, aAdjustWidget,
+                contentToFocus))
         return;
     }
 
     Focus(newWindow, contentToFocus, aFlags, !isElementInFocusedWindow,
-          aFocusChanged, false, aAdjustWidget);
+          aFocusChanged, false, aAdjustWidget, oldFocusedContent);
   }
   else {
     // otherwise, for inactive windows and when the caller cannot steal the
     // focus, update the node in the window, and  raise the window if desired.
     if (allowFrameSwitch)
       AdjustWindowFocus(newWindow, true);
 
     // set the focus node and method as needed
@@ -1555,17 +1564,18 @@ nsFocusManager::CheckIfFocusable(nsICont
   
   return frame->IsFocusable(nullptr, aFlags & FLAG_BYMOUSE) ? aContent : nullptr;
 }
 
 bool
 nsFocusManager::Blur(nsPIDOMWindowOuter* aWindowToClear,
                      nsPIDOMWindowOuter* aAncestorWindowToFocus,
                      bool aIsLeavingDocument,
-                     bool aAdjustWidgets)
+                     bool aAdjustWidgets,
+                     nsIContent* aContentToFocus)
 {
   LOGFOCUS(("<<Blur begin>>"));
 
   // hold a reference to the focused content, which may be null
   nsCOMPtr<nsIContent> content = mFocusedContent;
   if (content) {
     if (!content->IsInComposedDoc()) {
       mFocusedContent = nullptr;
@@ -1665,17 +1675,18 @@ nsFocusManager::Blur(nsPIDOMWindowOuter*
   if (sendBlurEvent) {
     // if there is an active window, update commands. If there isn't an active
     // window, then this was a blur caused by the active window being lowered,
     // so there is no need to update the commands
     if (mActiveWindow)
       window->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
 
     SendFocusOrBlurEvent(eBlur, presShell,
-                         content->GetComposedDoc(), content, 1, false);
+                         content->GetComposedDoc(), content, 1,
+                         false, false, aContentToFocus);
   }
 
   // if we are leaving the document or the window was lowered, make the caret
   // invisible.
   if (aIsLeavingDocument || !mActiveWindow) {
     SetCaretVisible(presShell, false, nullptr);
   }
 
@@ -1736,17 +1747,18 @@ nsFocusManager::Blur(nsPIDOMWindowOuter*
 
 void
 nsFocusManager::Focus(nsPIDOMWindowOuter* aWindow,
                       nsIContent* aContent,
                       uint32_t aFlags,
                       bool aIsNewDocument,
                       bool aFocusChanged,
                       bool aWindowRaised,
-                      bool aAdjustWidgets)
+                      bool aAdjustWidgets,
+                      nsIContent* aContentLostFocus)
 {
   LOGFOCUS(("<<Focus begin>>"));
 
   if (!aWindow)
     return;
 
   if (aContent && (aContent == mFirstFocusEvent || aContent == mFirstBlurEvent))
     return;
@@ -1890,17 +1902,17 @@ nsFocusManager::Focus(nsPIDOMWindowOuter
       // commands
       // XXXndeakin P2 someone could adjust the focus during the update
       if (!aWindowRaised)
         aWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
 
       SendFocusOrBlurEvent(eFocus, presShell,
                            aContent->GetComposedDoc(),
                            aContent, aFlags & FOCUSMETHOD_MASK,
-                           aWindowRaised, isRefocus);
+                           aWindowRaised, isRefocus, aContentLostFocus);
     } else {
       IMEStateManager::OnChangeFocus(presContext, nullptr,
                                      GetFocusMoveActionCause(aFlags));
       if (!aWindowRaised) {
         aWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
       }
     }
   }
@@ -1946,85 +1958,105 @@ nsFocusManager::Focus(nsPIDOMWindowOuter
     mFirstFocusEvent = nullptr;
 }
 
 class FocusBlurEvent : public nsRunnable
 {
 public:
   FocusBlurEvent(nsISupports* aTarget, EventMessage aEventMessage,
                  nsPresContext* aContext, bool aWindowRaised,
-                 bool aIsRefocus)
+                 bool aIsRefocus, EventTarget* aRelatedTarget)
     : mTarget(aTarget)
     , mContext(aContext)
     , mEventMessage(aEventMessage)
     , mWindowRaised(aWindowRaised)
     , mIsRefocus(aIsRefocus)
+    , mRelatedTarget(aRelatedTarget)
   {
   }
 
   NS_IMETHOD Run()
   {
     InternalFocusEvent event(true, mEventMessage);
     event.mFlags.mBubbles = false;
     event.mFlags.mCancelable = false;
     event.mFromRaise = mWindowRaised;
     event.mIsRefocus = mIsRefocus;
+    event.mRelatedTarget = mRelatedTarget;
     return EventDispatcher::Dispatch(mTarget, mContext, &event);
   }
 
   nsCOMPtr<nsISupports>   mTarget;
   RefPtr<nsPresContext> mContext;
   EventMessage            mEventMessage;
   bool                    mWindowRaised;
   bool                    mIsRefocus;
+  nsCOMPtr<EventTarget>   mRelatedTarget;
 };
 
+static nsIDocument*
+GetDocumentHelper(EventTarget* aTarget)
+{
+  nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
+  if (!node) {
+    nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aTarget);
+    return win ? win->GetExtantDoc() : nullptr;
+  }
+
+  return node->OwnerDoc();
+}
+
 void
 nsFocusManager::SendFocusOrBlurEvent(EventMessage aEventMessage,
                                      nsIPresShell* aPresShell,
                                      nsIDocument* aDocument,
                                      nsISupports* aTarget,
                                      uint32_t aFocusMethod,
                                      bool aWindowRaised,
-                                     bool aIsRefocus)
+                                     bool aIsRefocus,
+                                     EventTarget* aRelatedTarget)
 {
   NS_ASSERTION(aEventMessage == eFocus || aEventMessage == eBlur,
                "Wrong event type for SendFocusOrBlurEvent");
 
   nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(aTarget);
-
-  nsCOMPtr<nsINode> n = do_QueryInterface(aTarget);
-  if (!n) {
-    nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aTarget);
-    n = win ? win->GetExtantDoc() : nullptr;
+  nsCOMPtr<nsIDocument> eventTargetDoc = GetDocumentHelper(eventTarget);
+  nsCOMPtr<nsIDocument> relatedTargetDoc = GetDocumentHelper(aRelatedTarget);
+
+  // set aRelatedTarget to null if it's not in the same document as eventTarget
+  if (eventTargetDoc != relatedTargetDoc) {
+    aRelatedTarget = nullptr;
   }
-  bool dontDispatchEvent = n && nsContentUtils::IsUserFocusIgnored(n);
+
+  bool dontDispatchEvent =
+    eventTargetDoc && nsContentUtils::IsUserFocusIgnored(eventTargetDoc);
 
   // for focus events, if this event was from a mouse or key and event
   // handling on the document is suppressed, queue the event and fire it
   // later. For blur events, a non-zero value would be set for aFocusMethod.
   if (aFocusMethod && !dontDispatchEvent &&
       aDocument && aDocument->EventHandlingSuppressed()) {
     // aFlags is always 0 when aWindowRaised is true so this won't be called
     // on a window raise.
     NS_ASSERTION(!aWindowRaised, "aWindowRaised should not be set");
 
     for (uint32_t i = mDelayedBlurFocusEvents.Length(); i > 0; --i) {
       // if this event was already queued, remove it and append it to the end
       if (mDelayedBlurFocusEvents[i - 1].mEventMessage == aEventMessage &&
           mDelayedBlurFocusEvents[i - 1].mPresShell == aPresShell &&
           mDelayedBlurFocusEvents[i - 1].mDocument == aDocument &&
-          mDelayedBlurFocusEvents[i - 1].mTarget == eventTarget) {
+          mDelayedBlurFocusEvents[i - 1].mTarget == eventTarget &&
+          mDelayedBlurFocusEvents[i - 1].mRelatedTarget == aRelatedTarget) {
         mDelayedBlurFocusEvents.RemoveElementAt(i - 1);
       }
     }
 
     mDelayedBlurFocusEvents.AppendElement(
       nsDelayedBlurOrFocusEvent(aEventMessage, aPresShell,
-                                aDocument, eventTarget));
+                                aDocument, eventTarget, aRelatedTarget));
     return;
   }
 
 #ifdef ACCESSIBILITY
   nsAccessibilityService* accService = GetAccService();
   if (accService) {
     if (aEventMessage == eFocus) {
       accService->NotifyOfDOMFocus(aTarget);
@@ -2032,17 +2064,17 @@ nsFocusManager::SendFocusOrBlurEvent(Eve
       accService->NotifyOfDOMBlur(aTarget);
     }
   }
 #endif
 
   if (!dontDispatchEvent) {
     nsContentUtils::AddScriptRunner(
       new FocusBlurEvent(aTarget, aEventMessage, aPresShell->GetPresContext(),
-                         aWindowRaised, aIsRefocus));
+                         aWindowRaised, aIsRefocus, aRelatedTarget));
   }
 }
 
 void
 nsFocusManager::ScrollIntoView(nsIPresShell* aPresShell,
                                nsIContent* aContent,
                                uint32_t aFlags)
 {
--- a/dom/base/nsFocusManager.h
+++ b/dom/base/nsFocusManager.h
@@ -236,17 +236,18 @@ protected:
    * blurred as well. Document/window blur events will be fired. It should be
    * false if an element is the same document is about to be focused.
    *
    * If aAdjustWidget is false, don't change the widget focus state.
    */
   bool Blur(nsPIDOMWindowOuter* aWindowToClear,
             nsPIDOMWindowOuter* aAncestorWindowToFocus,
             bool aIsLeavingDocument,
-            bool aAdjustWidget);
+            bool aAdjustWidget,
+            nsIContent* aContentToFocus = nullptr);
 
   /**
    * Focus an element in the active window and child frame.
    *
    * aWindow is the window containing the element aContent to focus.
    *
    * aFlags is the flags passed to the various focus methods in
    * nsIFocusManager.
@@ -270,33 +271,35 @@ protected:
    * If aAdjustWidget is false, don't change the widget focus state.
    */
   void Focus(nsPIDOMWindowOuter* aWindow,
              nsIContent* aContent,
              uint32_t aFlags,
              bool aIsNewDocument,
              bool aFocusChanged,
              bool aWindowRaised,
-             bool aAdjustWidget);
+             bool aAdjustWidget,
+             nsIContent* aContentLostFocus = nullptr);
 
   /**
    * Fires a focus or blur event at aTarget.
    *
    * aEventMessage should be either eFocus or eBlur.
    * For blur events, aFocusMethod should normally be non-zero.
    *
    * aWindowRaised should only be true if called from WindowRaised.
    */
   void SendFocusOrBlurEvent(mozilla::EventMessage aEventMessage,
                             nsIPresShell* aPresShell,
                             nsIDocument* aDocument,
                             nsISupports* aTarget,
                             uint32_t aFocusMethod,
                             bool aWindowRaised,
-                            bool aIsRefocus = false);
+                            bool aIsRefocus = false,
+                            mozilla::dom::EventTarget* aRelatedTarget = nullptr);
 
   /**
    * Scrolls aContent into view unless the FLAG_NOSCROLL flag is set.
    */
   void ScrollIntoView(nsIPresShell* aPresShell,
                       nsIContent* aContent,
                       uint32_t aFlags);