diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index 0de0fe21f267c9d90f895b85a77126582dea713f..b2a2ecc3b950bc62c03e2ffeeefe9f07b38d0b34 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -313,6 +313,14 @@ QPointer<QWidget> qt_last_mouse_receiver = 0;
 
 void QWidgetWindow::handleEnterLeaveEvent(QEvent *event)
 {
+#if !defined(Q_OS_OSX) && !defined(Q_OS_IOS) // Cocoa tracks popups
+    // Ignore all enter/leave events from QPA if we are not on the first-level context menu.
+    // This prevents duplicated events on most platforms. Fake events will be delivered in
+    // QWidgetWindow::handleMouseEvent(QMouseEvent *). Make an exception whether the widget
+    // is already under mouse - let the mouse leave.
+    if (QApplicationPrivate::inPopupMode() && m_widget != QApplication::activePopupWidget() && !m_widget->underMouse())
+        return;
+#endif
     if (event->type() == QEvent::Leave) {
         QWidget *enter = 0;
         // Check from window system event queue if the next queued enter targets a window
@@ -407,14 +415,13 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
         QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
     if (qApp->d_func()->inPopupMode()) {
         QWidget *activePopupWidget = qApp->activePopupWidget();
-        QWidget *popup = activePopupWidget;
         QPoint mapped = event->pos();
-        if (popup != m_widget)
-            mapped = popup->mapFromGlobal(event->globalPos());
+        if (activePopupWidget != m_widget)
+            mapped = activePopupWidget->mapFromGlobal(event->globalPos());
         bool releaseAfter = false;
-        QWidget *popupChild  = popup->childAt(mapped);
+        QWidget *popupChild  = activePopupWidget->childAt(mapped);
 
-        if (popup != qt_popup_down) {
+        if (activePopupWidget != qt_popup_down) {
             qt_button_down = 0;
             qt_popup_down = 0;
         }
@@ -423,7 +430,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
         case QEvent::MouseButtonPress:
         case QEvent::MouseButtonDblClick:
             qt_button_down = popupChild;
-            qt_popup_down = popup;
+            qt_popup_down = activePopupWidget;
             break;
         case QEvent::MouseButtonRelease:
             releaseAfter = true;
@@ -434,18 +441,41 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
 
         int oldOpenPopupCount = openPopupCount;
 
-        if (popup->isEnabled()) {
+        if (activePopupWidget->isEnabled()) {
             // deliver event
             qt_replay_popup_mouse_event = false;
-            QWidget *receiver = popup;
+            QWidget *receiver = activePopupWidget;
             QPoint widgetPos = mapped;
             if (qt_button_down)
                 receiver = qt_button_down;
             else if (popupChild)
                 receiver = popupChild;
-            if (receiver != popup)
+            if (receiver != activePopupWidget)
                 widgetPos = receiver->mapFromGlobal(event->globalPos());
-            QWidget *alien = m_widget->childAt(m_widget->mapFromGlobal(event->globalPos()));
+            QWidget *alien = receiver;
+
+#if !defined(Q_OS_OSX) && !defined(Q_OS_IOS) // Cocoa tracks popups
+            const bool reallyUnderMouse = activePopupWidget->rect().contains(mapped);
+            const bool underMouse = activePopupWidget->underMouse();
+            if (activePopupWidget != m_widget || (!underMouse && qt_button_down)) {
+                // If active popup menu is not the first-level popup menu then we must emulate enter/leave events,
+                // because first-level popup menu grabs the mouse and enter/leave events are delivered only to it
+                // by QPA. Make an exception for first-level popup menu when the mouse button is pressed on widget.
+                if (underMouse != reallyUnderMouse) {
+                    if (reallyUnderMouse) {
+                        QApplicationPrivate::dispatchEnterLeave(receiver, Q_NULLPTR, event->screenPos());
+                        qt_last_mouse_receiver = receiver;
+                    } else {
+                        QApplicationPrivate::dispatchEnterLeave(Q_NULLPTR, qt_last_mouse_receiver, event->screenPos());
+                        qt_last_mouse_receiver = receiver;
+                        receiver = activePopupWidget;
+                    }
+                }
+            } else if (!reallyUnderMouse) {
+                alien = Q_NULLPTR;
+            }
+#endif
+
             QMouseEvent e(event->type(), widgetPos, event->windowPos(), event->screenPos(),
                           event->button(), event->buttons(), event->modifiers(), event->source());
             e.setTimestamp(event->timestamp());
@@ -457,7 +487,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
             case QEvent::MouseButtonPress:
             case QEvent::MouseButtonDblClick:
             case QEvent::MouseButtonRelease:
-                popup->close();
+                activePopupWidget->close();
                 break;
             default:
                 break;
@@ -503,7 +533,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
         } else if (event->type() == contextMenuTrigger
                    && event->button() == Qt::RightButton
                    && (openPopupCount == oldOpenPopupCount)) {
-            QWidget *popupEvent = popup;
+            QWidget *popupEvent = activePopupWidget;
             if (qt_button_down)
                 popupEvent = qt_button_down;
             else if(popupChild)
diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
index b3f9c54f24f8e0640d7dab44c92a94ad0f9e021c..20f17f6e9e403887a37a61c68df49c411198decc 100644
--- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
+++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
@@ -110,6 +110,9 @@ private slots:
     void QTBUG7411_submenus_activate();
     void QTBUG30595_rtl_submenu();
     void QTBUG20403_nested_popup_on_shortcut_trigger();
+#ifndef QT_NO_CURSOR
+    void QTBUG47515_widgetActionEnterLeave();
+#endif
     void QTBUG_10735_crashWithDialog();
 #ifdef Q_OS_MAC
     void QTBUG_37933_ampersands_data();
@@ -1070,6 +1073,112 @@ void tst_QMenu::QTBUG20403_nested_popup_on_shortcut_trigger()
     QVERIFY(!subsub1.isVisible());
 }
 
+class MyWidget : public QWidget
+{
+public:
+    MyWidget(QWidget *parent) :
+        QWidget(parent),
+        move(0), enter(0), leave(0)
+    {
+        setMinimumSize(100, 100);
+        setMouseTracking(true);
+    }
+
+    bool event(QEvent *e) Q_DECL_OVERRIDE
+    {
+        switch (e->type()) {
+        case QEvent::MouseMove:
+            ++move;
+            break;
+        case QEvent::Enter:
+            ++enter;
+            break;
+        case QEvent::Leave:
+            ++leave;
+            break;
+        default:
+            break;
+        }
+        return QWidget::event(e);
+    }
+
+    int move, enter, leave;
+};
+
+#ifndef QT_NO_CURSOR
+void tst_QMenu::QTBUG47515_widgetActionEnterLeave()
+{
+    if (QGuiApplication::platformName() == QLatin1String("cocoa"))
+        QSKIP("This test fails on OS X on CI");
+
+    const QPoint center = QGuiApplication::primaryScreen()->availableGeometry().center();
+    const QPoint cursorPos = center - QPoint(100, 100);
+
+    QScopedPointer<QMenu> menu1(new QMenu("Menu1"));
+    QScopedPointer<QMenu> menu2(new QMenu("Menu2"));
+
+    QWidgetAction *wA1 = new QWidgetAction(menu1.data());
+    MyWidget *w1 = new MyWidget(menu1.data());
+    wA1->setDefaultWidget(w1);
+
+    QWidgetAction *wA2 = new QWidgetAction(menu2.data());
+    MyWidget *w2 = new MyWidget(menu2.data());
+    wA2->setDefaultWidget(w2);
+
+    QAction *nextMenuAct = menu1->addMenu(menu2.data());
+
+    menu1->addAction(wA1);
+    menu2->addAction(wA2);
+
+    // Root menu
+    {
+        QCursor::setPos(cursorPos);
+        QCoreApplication::processEvents();
+
+        menu1->popup(center);
+        QVERIFY(QTest::qWaitForWindowExposed(menu1.data()));
+
+        QCursor::setPos(w1->mapToGlobal(w1->rect().center()));
+        QVERIFY(w1->isVisible());
+        QTRY_COMPARE(w1->leave, 0);
+        QTRY_COMPARE(w1->enter, 1);
+
+        // Check whether leave event is not delivered on mouse move
+        w1->move = 0;
+        QCursor::setPos(w1->mapToGlobal(w1->rect().center()) + QPoint(1, 1));
+        QTRY_COMPARE(w1->move, 1);
+        QTRY_COMPARE(w1->leave, 0);
+        QTRY_COMPARE(w1->enter, 1);
+
+        QCursor::setPos(cursorPos);
+        QTRY_COMPARE(w1->leave, 1);
+        QTRY_COMPARE(w1->enter, 1);
+    }
+
+    // Submenu
+    {
+        menu1->setActiveAction(nextMenuAct);
+        QVERIFY(QTest::qWaitForWindowExposed(menu2.data()));
+
+        QCursor::setPos(w2->mapToGlobal(w2->rect().center()));
+        QVERIFY(w2->isVisible());
+        QTRY_COMPARE(w2->leave, 0);
+        QTRY_COMPARE(w2->enter, 1);
+
+        // Check whether leave event is not delivered on mouse move
+        w2->move = 0;
+        QCursor::setPos(w2->mapToGlobal(w2->rect().center()) + QPoint(1, 1));
+        QTRY_COMPARE(w2->move, 1);
+        QTRY_COMPARE(w2->leave, 0);
+        QTRY_COMPARE(w2->enter, 1);
+
+        QCursor::setPos(cursorPos);
+        QTRY_COMPARE(w2->leave, 1);
+        QTRY_COMPARE(w2->enter, 1);
+    }
+}
+#endif // !QT_NO_CURSOR
+
 class MyMenu : public QMenu
 {
     Q_OBJECT