Bug 995603 - Ensure mouse-enter/exit events are sent to plugins as appropriate. r=mstange,masayuki a=sledru
authorSteven Michaud <smichaud@pobox.com>
Fri, 16 May 2014 15:51:39 -0500
changeset 192303 70f056b3a700
parent 192302 dfcd5c18e0a8
child 192304 d3ccc8465e4b
push id3564
push usersmichaud@pobox.com
push date2014-05-16 20:52 +0000
Treeherderresults
reviewersmstange, masayuki, sledru
bugs995603
milestone30.0
Bug 995603 - Ensure mouse-enter/exit events are sent to plugins as appropriate. r=mstange,masayuki a=sledru
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
widget/cocoa/nsChildView.mm
widget/cocoa/nsCocoaWindow.h
widget/cocoa/nsCocoaWindow.mm
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -45,36 +45,45 @@ ContentEventHandler::ContentEventHandler
   , mPresShell(aPresContext->GetPresShell())
   , mSelection(nullptr)
   , mFirstSelectedRange(nullptr)
   , mRootContent(nullptr)
 {
 }
 
 nsresult
-ContentEventHandler::InitCommon()
+ContentEventHandler::InitBasic()
 {
-  if (mSelection) {
-    return NS_OK;
-  }
-
   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
 
   // If text frame which has overflowing selection underline is dirty,
   // we need to flush the pending reflow here.
   mPresShell->FlushPendingNotifications(Flush_Layout);
 
   // Flushing notifications can cause mPresShell to be destroyed (bug 577963).
   NS_ENSURE_TRUE(!mPresShell->IsDestroying(), NS_ERROR_FAILURE);
 
+  return NS_OK;
+}
+
+nsresult
+ContentEventHandler::InitCommon()
+{
+  if (mSelection) {
+    return NS_OK;
+  }
+
+  nsresult rv = InitBasic();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsCopySupport::GetSelectionForCopy(mPresShell->GetDocument(),
                                      getter_AddRefs(mSelection));
 
   nsCOMPtr<nsIDOMRange> firstRange;
-  nsresult rv = mSelection->GetRangeAt(0, getter_AddRefs(firstRange));
+  rv = mSelection->GetRangeAt(0, getter_AddRefs(firstRange));
   // This shell doesn't support selection.
   if (NS_FAILED(rv)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   mFirstSelectedRange = static_cast<nsRange*>(firstRange.get());
 
   nsINode* startNode = mFirstSelectedRange->GetStartParent();
   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
@@ -946,21 +955,24 @@ ContentEventHandler::OnQueryCharacterAtP
   aEvent->mReply.mRect = textRect.mReply.mRect;
   aEvent->mSucceeded = true;
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent)
 {
-  nsresult rv = Init(aEvent);
+  NS_ASSERTION(aEvent, "aEvent must not be null");
+
+  nsresult rv = InitBasic();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  aEvent->mSucceeded = false;
   aEvent->mReply.mWidgetIsHit = false;
 
   NS_ENSURE_TRUE(aEvent->widget, NS_ERROR_FAILURE);
 
   nsIDocument* doc = mPresShell->GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
   nsIFrame* docFrame = mPresShell->GetRootFrame();
   NS_ENSURE_TRUE(docFrame, NS_ERROR_FAILURE);
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -57,17 +57,17 @@ protected:
   nsCOMPtr<nsIPresShell> mPresShell;
   nsCOMPtr<nsISelection> mSelection;
   nsRefPtr<nsRange> mFirstSelectedRange;
   nsCOMPtr<nsIContent> mRootContent;
 
   nsresult Init(WidgetQueryContentEvent* aEvent);
   nsresult Init(WidgetSelectionEvent* aEvent);
 
-  // InitCommon() is called from each Init().
+  nsresult InitBasic();
   nsresult InitCommon();
 
 public:
   // FlatText means the text that is generated from DOM tree. The BR elements
   // are replaced to native linefeeds. Other elements are ignored.
 
   // Get the offset in FlatText of the range. (also used by IMEContentObserver)
   static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent,
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -3281,16 +3281,41 @@ NSEvent* gLastDragMouseDownEvent = nil;
   mIsPluginView = aIsPlugin;
 }
 
 -(BOOL)isPluginView
 {
   return mIsPluginView;
 }
 
+- (NSView *)hitTest:(NSPoint)aPoint
+{
+  NSView* target = [super hitTest:aPoint];
+  if ((target == self) && [self isPluginView] && mGeckoChild) {
+    nsAutoRetainCocoaObject kungFuDeathGrip(self);
+
+    NSPoint cocoaLoc = [[self superview] convertPoint:aPoint toView:self];
+    LayoutDeviceIntPoint widgetLoc = LayoutDeviceIntPoint::FromUntyped(
+      mGeckoChild->CocoaPointsToDevPixels(cocoaLoc));
+
+    WidgetQueryContentEvent hitTest(true, NS_QUERY_DOM_WIDGET_HITTEST,
+                                    mGeckoChild);
+    hitTest.InitForQueryDOMWidgetHittest(widgetLoc);
+    // This might destroy our widget.
+    mGeckoChild->DispatchWindowEvent(hitTest);
+    if (!mGeckoChild) {
+      return nil;
+    }
+    if (hitTest.mSucceeded && !hitTest.mReply.mWidgetIsHit) {
+      return nil;
+    }
+  }
+  return target;
+}
+
 // Are we processing an NSLeftMouseDown event that will fail to click through?
 // If so, we shouldn't focus or unfocus a plugin.
 - (BOOL)isInFailingLeftClickThrough
 {
   if (!mGeckoChild)
     return NO;
 
   if (!mClickThroughMouseDownEvent ||
@@ -5526,77 +5551,22 @@ static int32_t RoundUp(double aDouble)
     return YES;
 
   WidgetMouseEvent geckoEvent(true, NS_MOUSE_ACTIVATE, mGeckoChild,
                               WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:aEvent toGeckoEvent:&geckoEvent];
   return !mGeckoChild->DispatchWindowEvent(geckoEvent);
 }
 
-// Don't focus a plugin if the user has clicked on a DOM element above it.
-// In this case the user has actually clicked on the plugin's ChildView
-// (underneath the non-plugin DOM element).  But we shouldn't allow the
-// ChildView to be focused.  See bug 627649.
-- (BOOL)currentEventShouldFocusPlugin
-{
-  if (!mGeckoChild)
-    return NO;
-
-  NSEvent* currentEvent = [NSApp currentEvent];
-  if ([currentEvent type] != NSLeftMouseDown)
-    return YES;
-
-  nsAutoRetainCocoaObject kungFuDeathGrip(self);
-
-  // hitTest needs coordinates in device pixels
-  NSPoint eventLoc = nsCocoaUtils::ScreenLocationForEvent(currentEvent);
-  eventLoc.y = nsCocoaUtils::FlippedScreenY(eventLoc.y);
-  LayoutDeviceIntPoint widgetLoc = LayoutDeviceIntPoint::FromUntyped(
-    mGeckoChild->CocoaPointsToDevPixels(eventLoc) -
-    mGeckoChild->WidgetToScreenOffset());
-
-  WidgetQueryContentEvent hitTest(true, NS_QUERY_DOM_WIDGET_HITTEST,
-                                  mGeckoChild);
-  hitTest.InitForQueryDOMWidgetHittest(widgetLoc);
-  // This might destroy our widget (and null out mGeckoChild).
-  mGeckoChild->DispatchWindowEvent(hitTest);
-  if (!mGeckoChild)
-    return NO;
-  if (hitTest.mSucceeded && !hitTest.mReply.mWidgetIsHit)
-    return NO;
-
-  return YES;
-}
-
-// Don't focus a plugin if we're in a left click-through that will fail (see
-// [ChildView isInFailingLeftClickThrough] above).
-- (BOOL)shouldFocusPlugin:(BOOL)getFocus
-{
-  if (!mGeckoChild)
-    return NO;
-
-  nsCocoaWindow* windowWidget = mGeckoChild->GetXULWindowWidget();
-  if (windowWidget && !windowWidget->ShouldFocusPlugin())
-    return NO;
-
-  if (getFocus && ![self currentEventShouldFocusPlugin])
-    return NO;
-
-  return YES;
-}
-
 // Returns NO if the plugin shouldn't be focused/unfocused.
 - (BOOL)updatePluginFocusStatus:(BOOL)getFocus
 {
   if (!mGeckoChild)
     return NO;
 
-  if (![self shouldFocusPlugin:getFocus])
-    return NO;
-
   if (mPluginEventModel == NPEventModelCocoa) {
     WidgetPluginEvent pluginEvent(true, NS_PLUGIN_FOCUS_EVENT, mGeckoChild);
     NPCocoaEvent cocoaEvent;
     nsCocoaUtils::InitNPCocoaEvent(&cocoaEvent);
     cocoaEvent.type = NPCocoaEventFocusChanged;
     cocoaEvent.data.focus.hasFocus = getFocus;
     nsCocoaUtils::InitPluginEvent(pluginEvent, cocoaEvent);
     mGeckoChild->DispatchWindowEvent(pluginEvent);
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -347,19 +347,16 @@ public:
     NS_IMETHOD_(bool) ExecuteNativeKeyBinding(
                         NativeKeyBindingsType aType,
                         const mozilla::WidgetKeyboardEvent& aEvent,
                         DoCommandCallback aCallback,
                         void* aCallbackData) MOZ_OVERRIDE;
 
     void SetPopupWindowLevel();
 
-    bool IsChildInFailingLeftClickThrough(NSView *aChild);
-    bool ShouldFocusPlugin();
-
     NS_IMETHOD         ReparentNativeWidget(nsIWidget* aNewParent);
 protected:
 
   nsresult             CreateNativeWindow(const NSRect &aRect,
                                           nsBorderStyle aBorderStyle,
                                           bool aRectIsFrameRect);
   nsresult             CreatePopupContentView(const nsIntRect &aRect,
                                               nsDeviceContext *aContext);
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -2089,46 +2089,16 @@ void nsCocoaWindow::SetPopupWindowLevel(
   else {
     // Otherwise, this is a top-level or parent popup. Parent popups always
     // appear just above their parent and essentially ignore the level.
     [mWindow setLevel:NSPopUpMenuWindowLevel];
     [mWindow setHidesOnDeactivate:NO];
   }
 }
 
-bool nsCocoaWindow::IsChildInFailingLeftClickThrough(NSView *aChild)
-{
-  if ([aChild isKindOfClass:[ChildView class]]) {
-    ChildView* childView = (ChildView*) aChild;
-    if ([childView isInFailingLeftClickThrough])
-      return true;
-  }
-  NSArray* subviews = [aChild subviews];
-  if (subviews) {
-    NSUInteger count = [subviews count];
-    for (NSUInteger i = 0; i < count; ++i) {
-      NSView* aView = (NSView*) [subviews objectAtIndex:i];
-      if (IsChildInFailingLeftClickThrough(aView))
-        return true;
-    }
-  }
-  return false;
-}
-
-// Don't focus a plugin if we're in a left click-through that will
-// fail (see [ChildView isInFailingLeftClickThrough]).  Called from
-// [ChildView shouldFocusPlugin].
-bool nsCocoaWindow::ShouldFocusPlugin()
-{
-  if (!mWindow || IsChildInFailingLeftClickThrough([mWindow contentView]))
-    return false;
-
-  return true;
-}
-
 NS_IMETHODIMP
 nsCocoaWindow::NotifyIME(const IMENotification& aIMENotification)
 {
   switch (aIMENotification.mMessage) {
     case NOTIFY_IME_OF_FOCUS:
       if (mInputContext.IsPasswordEditor()) {
         TextInputHandler::EnableSecureEventInput();
       }