Bug 1343977 - Extract nsAutoRollup into a more self-contained class and clean it up some. No functional changes intended. r?enndeakin draft
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 13 Mar 2017 10:44:56 -0400
changeset 497655 ddad2e19fc2e1248955aabf46919386246ee121b
parent 497654 7b19a63862252ffb89bfe1ba79724e76e20fb6f4
child 497656 f4437ed5bd6e9dd9586b46ce5361e322a376ce24
push id48942
push userkgupta@mozilla.com
push dateMon, 13 Mar 2017 14:45:51 +0000
reviewersenndeakin
bugs1343977
milestone55.0a1
Bug 1343977 - Extract nsAutoRollup into a more self-contained class and clean it up some. No functional changes intended. r?enndeakin This just decouples nsAutoRollup from the widget class, which it isn't really bound to anyway because the internal data is static. We'll need to be able to use nsAutoRollup independently in the next patch. MozReview-Commit-ID: 1dxSLTr4g1K
layout/forms/nsComboboxControlFrame.cpp
layout/xul/nsXULPopupManager.cpp
widget/moz.build
widget/nsAutoRollup.cpp
widget/nsAutoRollup.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
widget/windows/nsWindow.cpp
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -43,16 +43,17 @@
 #include <algorithm>
 #include "nsTextNode.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Unused.h"
 #include "gfx2DGlue.h"
+#include "mozilla/widget/nsAutoRollup.h"
 
 #ifdef XP_WIN
 #define COMBOBOX_ROLLUP_CONSUME_EVENT 0
 #else
 #define COMBOBOX_ROLLUP_CONSUME_EVENT 1
 #endif
 
 using namespace mozilla;
@@ -1144,18 +1145,17 @@ nsComboboxControlFrame::HandleEvent(nsPr
 
   EventStates eventStates = mContent->AsElement()->State();
   if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
     return NS_OK;
   }
 
 #if COMBOBOX_ROLLUP_CONSUME_EVENT == 0
   if (aEvent->mMessage == eMouseDown) {
-    nsIWidget* widget = GetNearestWidget();
-    if (widget && GetContent() == widget->GetLastRollup()) {
+    if (GetContent() == mozilla::widget::nsAutoRollup::GetLastRollup()) {
       // This event did a Rollup on this control - prevent it from opening
       // the dropdown again!
       *aEventStatus = nsEventStatus_eConsumeNoDefault;
       return NS_OK;
     }
   }
 #endif
 
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -36,16 +36,17 @@
 #include "nsIObserverService.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Services.h"
+#include "mozilla/widget/nsAutoRollup.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static_assert(nsIDOMKeyEvent::DOM_VK_HOME  == nsIDOMKeyEvent::DOM_VK_END + 1 &&
               nsIDOMKeyEvent::DOM_VK_LEFT  == nsIDOMKeyEvent::DOM_VK_END + 2 &&
               nsIDOMKeyEvent::DOM_VK_UP    == nsIDOMKeyEvent::DOM_VK_END + 3 &&
               nsIDOMKeyEvent::DOM_VK_RIGHT == nsIDOMKeyEvent::DOM_VK_END + 4 &&
@@ -1805,18 +1806,17 @@ nsXULPopupManager::MayShowPopup(nsMenuPo
 
   // Don't show popups that we already have in our popup chain
   if (IsPopupOpen(aPopup->GetContent())) {
     NS_WARNING("Refusing to show duplicate popup");
     return false;
   }
 
   // if the popup was just rolled up, don't reopen it
-  nsCOMPtr<nsIWidget> widget = aPopup->GetWidget();
-  if (widget && widget->GetLastRollup() == aPopup->GetContent())
+  if (mozilla::widget::nsAutoRollup::GetLastRollup() == aPopup->GetContent())
       return false;
 
   nsCOMPtr<nsIDocShellTreeItem> dsti = aPopup->PresContext()->GetDocShell();
   nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(dsti);
   if (!baseWin)
     return false;
 
   nsCOMPtr<nsIDocShellTreeItem> root;
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -124,30 +124,32 @@ EXPORTS.mozilla += [
     'VsyncDispatcher.h',
     'WidgetUtils.h',
 ]
 
 EXPORTS.mozilla.widget += [
     'CompositorWidget.h',
     'IMEData.h',
     'InProcessCompositorWidget.h',
+    'nsAutoRollup.h',
     'PuppetBidiKeyboard.h',
     'WidgetMessageUtils.h',
     'WindowSurface.h'
 ]
 
 UNIFIED_SOURCES += [
     'CompositorWidget.cpp',
     'ContentCache.cpp',
     'GfxDriverInfo.cpp',
     'GfxInfoBase.cpp',
     'GfxInfoCollector.cpp',
     'GfxInfoWebGL.cpp',
     'InProcessCompositorWidget.cpp',
     'InputData.cpp',
+    'nsAutoRollup.cpp',
     'nsBaseAppShell.cpp',
     'nsBaseScreen.cpp',
     'nsClipboardHelper.cpp',
     'nsClipboardProxy.cpp',
     'nsColorPickerProxy.cpp',
     'nsContentProcessWidgetFactory.cpp',
     'nsDatePickerProxy.cpp',
     'nsDragServiceProxy.cpp',
new file mode 100644
--- /dev/null
+++ b/widget/nsAutoRollup.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/widget/nsAutoRollup.h"
+
+namespace mozilla {
+namespace widget {
+
+/*static*/ uint32_t nsAutoRollup::sCount = 0;
+/*static*/ StaticRefPtr<nsIContent> nsAutoRollup::sLastRollup;
+
+nsAutoRollup::nsAutoRollup()
+{
+  // remember if sLastRollup was null, and only clear it upon destruction
+  // if so. This prevents recursive usage of nsAutoRollup from clearing
+  // sLastRollup when it shouldn't.
+  mWasClear = !sLastRollup;
+  sCount++;
+}
+
+nsAutoRollup::~nsAutoRollup()
+{
+  if (sLastRollup && mWasClear) {
+    sLastRollup = nullptr;
+  }
+  sCount--;
+}
+
+/*static*/ void
+nsAutoRollup::SetLastRollup(nsIContent* aLastRollup)
+{
+  // There must be at least one nsAutoRollup on the stack.
+  MOZ_ASSERT(sCount);
+
+  sLastRollup = aLastRollup;
+}
+
+/*static*/ nsIContent*
+nsAutoRollup::GetLastRollup()
+{
+  return sLastRollup.get();
+}
+
+} // namespace widget
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/nsAutoRollup.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsAutoRollup_h__
+#define nsAutoRollup_h__
+
+#include "mozilla/Attributes.h"     // for MOZ_RAII
+#include "mozilla/StaticPtr.h"      // for StaticRefPtr
+
+class nsIContent;
+
+namespace mozilla {
+namespace widget {
+
+// A situation can occur when a mouse event occurs over a menu label while the
+// menu popup is already open. The expected behaviour is to close the popup.
+// This happens by calling nsIRollupListener::Rollup before the mouse event is
+// processed. However, in cases where the mouse event is not consumed, this
+// event will then get targeted at the menu label causing the menu to open
+// again. To prevent this, we store in sLastRollup a reference to the popup
+// that was closed during the Rollup call, and prevent this popup from
+// reopening while processing the mouse event.
+// sLastRollup can only be set while an nsAutoRollup is in scope;
+// when it goes out of scope sLastRollup is cleared automatically.
+// As sLastRollup is static, it can be retrieved by calling
+// nsAutoRollup::GetLastRollup.
+class MOZ_RAII nsAutoRollup
+{
+public:
+  nsAutoRollup();
+  ~nsAutoRollup();
+
+  static void SetLastRollup(nsIContent* aLastRollup);
+  // Return the popup that was last rolled up, or null if there isn't one.
+  static nsIContent* GetLastRollup();
+
+private:
+  // Whether sLastRollup was clear when this nsAutoRollup
+  // was created.
+  bool mWasClear;
+
+  // The number of nsAutoRollup instances active.
+  static uint32_t sCount;
+  // The last rolled up popup.
+  static StaticRefPtr<nsIContent> sLastRollup;
+};
+
+} // namespace widget
+} // namespace mozilla
+
+#endif // nsAutoRollup_h__
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -103,17 +103,16 @@ nsIRollupListener* nsBaseWidget::gRollup
 
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::ipc;
 using namespace mozilla::widget;
 using namespace mozilla;
 using base::Thread;
 
-nsIContent* nsBaseWidget::mLastRollup = nullptr;
 // Global user preference for disabling native theme. Used
 // in NativeWindowTheme.
 bool            gDisableNativeTheme               = false;
 
 // Async pump timer during injected long touch taps
 #define TOUCH_INJECT_PUMP_TIMER_MSEC 50
 #define TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC 1500
 int32_t nsIWidget::sPointerIdCounter = 0;
@@ -136,31 +135,16 @@ WritingMode
 IMENotification::SelectionChangeDataBase::GetWritingMode() const
 {
   return WritingMode(mWritingMode);
 }
 
 } // namespace widget
 } // namespace mozilla
 
-nsAutoRollup::nsAutoRollup()
-{
-  // remember if mLastRollup was null, and only clear it upon destruction
-  // if so. This prevents recursive usage of nsAutoRollup from clearing
-  // mLastRollup when it shouldn't.
-  wasClear = !nsBaseWidget::mLastRollup;
-}
-
-nsAutoRollup::~nsAutoRollup()
-{
-  if (nsBaseWidget::mLastRollup && wasClear) {
-    NS_RELEASE(nsBaseWidget::mLastRollup);
-  }
-}
-
 NS_IMPL_ISUPPORTS(nsBaseWidget, nsIWidget, nsISupportsWeakReference)
 
 //-------------------------------------------------------------------------
 //
 // nsBaseWidget constructor
 //
 //-------------------------------------------------------------------------
 
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -27,17 +27,16 @@
 #if defined(XP_WIN)
 // Scroll capture constants
 const uint32_t kScrollCaptureFillColor = 0xFFa0a0a0; // gray
 const mozilla::gfx::SurfaceFormat kScrollCaptureFormat =
   mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32;
 #endif
 
 class nsIContent;
-class nsAutoRollup;
 class gfxContext;
 
 namespace mozilla {
 class CompositorVsyncDispatcher;
 class LiveResizeListener;
 
 #ifdef ACCESSIBILITY
 namespace a11y {
@@ -107,17 +106,16 @@ public:
  * All cross-platform behavior that all widgets need to implement
  * should be placed in this class.
  * (Note: widget implementations are not required to use this
  * class, but it gives them a head start.)
  */
 
 class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference
 {
-  friend class nsAutoRollup;
   friend class DispatchWheelEventOnMainThread;
   friend class mozilla::widget::InProcessCompositorWidget;
   friend class mozilla::layers::RemoteCompositorSession;
 
 protected:
   typedef base::Thread Thread;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::SourceSurface SourceSurface;
@@ -461,21 +459,16 @@ protected:
                                              const ScrollableLayerGuid& aGuid,
                                              uint64_t aInputBlockId,
                                              nsEventStatus aApzResponse);
 
   const LayoutDeviceIntRegion RegionFromArray(const nsTArray<LayoutDeviceIntRect>& aRects);
   void ArrayFromRegion(const LayoutDeviceIntRegion& aRegion,
                        nsTArray<LayoutDeviceIntRect>& aRects);
 
-  virtual nsIContent* GetLastRollup() override
-  {
-    return mLastRollup;
-  }
-
   virtual nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                             int32_t aNativeKeyCode,
                                             uint32_t aModifierFlags,
                                             const nsAString& aCharacters,
                                             const nsAString& aUnmodifiedCharacters,
                                             nsIObserver* aObserver) override
   {
     mozilla::widget::AutoObserverNotifier notifier(aObserver, "keyevent");
@@ -691,20 +684,16 @@ protected:
   bool              mUpdateCursor;
   bool              mUseAttachedEvents;
   bool              mIMEHasFocus;
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
   bool              mAccessibilityInUseFlag;
 #endif
   static nsIRollupListener* gRollupListener;
 
-  // the last rolled up popup. Only set this when an nsAutoRollup is in scope,
-  // so it can be cleared automatically.
-  static nsIContent* mLastRollup;
-
   struct InitialZoomConstraints {
     InitialZoomConstraints(const uint32_t& aPresShellID,
                            const FrameMetrics::ViewID& aViewID,
                            const ZoomConstraints& aConstraints)
       : mPresShellID(aPresShellID), mViewID(aViewID), mConstraints(aConstraints)
     {
     }
 
@@ -742,31 +731,9 @@ protected:
                                    const nsIntRegion &   aPaintEvent,
                                    const char *          aWidgetName,
                                    int32_t               aWindowID);
 
   static bool debug_GetCachedBoolPref(const char* aPrefName);
 #endif
 };
 
-// A situation can occur when a mouse event occurs over a menu label while the
-// menu popup is already open. The expected behaviour is to close the popup.
-// This happens by calling nsIRollupListener::Rollup before the mouse event is
-// processed. However, in cases where the mouse event is not consumed, this
-// event will then get targeted at the menu label causing the menu to open
-// again. To prevent this, we store in mLastRollup a reference to the popup
-// that was closed during the Rollup call, and prevent this popup from
-// reopening while processing the mouse event.
-// mLastRollup should only be set while an nsAutoRollup is in scope;
-// when it goes out of scope mLastRollup is cleared automatically.
-// As mLastRollup is static, it can be retrieved by calling
-// nsIWidget::GetLastRollup on any widget.
-class nsAutoRollup
-{
-  bool wasClear;
-
-  public:
-
-  nsAutoRollup();
-  ~nsAutoRollup();
-};
-
 #endif // nsBaseWidget_h__
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1451,21 +1451,16 @@ class nsIWidget : public nsISupports
      * Returns false on any platform that does not support it.
      *
      * @param aResizerRect The resizer's rect in device pixels.
      * @return Whether a resize widget is shown.
      */
     virtual bool ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) = 0;
 
     /**
-     * Return the popup that was last rolled up, or null if there isn't one.
-     */
-    virtual nsIContent* GetLastRollup() = 0;
-
-    /**
      * Begin a window resizing drag, based on the event passed in.
      */
     virtual MOZ_MUST_USE nsresult
     BeginResizeDrag(mozilla::WidgetGUIEvent* aEvent,
                     int32_t aHorizontal,
                     int32_t aVertical) = 0;
 
     /**
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -130,16 +130,17 @@
 #include "nsIWidgetListener.h"
 #include "mozilla/dom/Touch.h"
 #include "mozilla/gfx/2D.h"
 #include "nsToolkitCompsCID.h"
 #include "nsIAppStartup.h"
 #include "mozilla/WindowsVersion.h"
 #include "mozilla/TextEvents.h" // For WidgetKeyboardEvent
 #include "mozilla/TextEventDispatcherListener.h"
+#include "mozilla/widget/nsAutoRollup.h"
 #include "mozilla/widget/WinNativeEventData.h"
 #include "mozilla/widget/PlatformWidgetTypes.h"
 #include "nsThemeConstants.h"
 #include "nsBidiKeyboard.h"
 #include "nsThemeConstants.h"
 #include "gfxConfig.h"
 #include "InProcessWinCompositorWidget.h"
 
@@ -7911,35 +7912,36 @@ nsWindow::DealWithPopups(HWND aWnd, UINT
     case WM_MENUSELECT:
       break;
 
     default:
       return false;
   }
 
   // Only need to deal with the last rollup for left mouse down events.
-  NS_ASSERTION(!mLastRollup, "mLastRollup is null");
+  NS_ASSERTION(!nsAutoRollup::GetLastRollup(), "last rollup is null");
 
   if (nativeMessage == WM_TOUCH || nativeMessage == WM_LBUTTONDOWN || nativeMessage == WM_POINTERDOWN) {
     nsIntPoint pos;
     if (nativeMessage == WM_TOUCH) {
       if (nsWindow* win = WinUtils::GetNSWindowPtr(aWnd)) {
         pos = win->GetTouchCoordinates(aWParam, aLParam);
       }
     } else {
       POINT pt;
       pt.x = GET_X_LPARAM(aLParam);
       pt.y = GET_Y_LPARAM(aLParam);
       ::ClientToScreen(aWnd, &pt);
       pos = nsIntPoint(pt.x, pt.y);
     }
 
+    nsIContent* lastRollup;
     consumeRollupEvent =
-      rollupListener->Rollup(popupsToRollup, true, &pos, &mLastRollup);
-    NS_IF_ADDREF(mLastRollup);
+      rollupListener->Rollup(popupsToRollup, true, &pos, &lastRollup);
+    nsAutoRollup::SetLastRollup(lastRollup);
   } else {
     consumeRollupEvent =
       rollupListener->Rollup(popupsToRollup, true, nullptr, nullptr);
   }
 
   // Tell hook to stop processing messages
   sProcessHook = false;
   sRollupMsgId = 0;