bug 1249664 - Save the combobox's dropped-down state across frame reconstruction r?dbaron draft
authorChris H-C <chutten@mozilla.com>
Thu, 10 Mar 2016 14:21:05 -0500
changeset 339971 bb6e7d0e2f881cdb173cac7583351c451d0a6797
parent 339215 be172faf1080d913a536b65961ae2b515cd1a681
child 516098 ffc953eabc71cf1bb39528139d2b743bc2b0f5cf
push id12853
push userbmo:chutten@mozilla.com
push dateMon, 14 Mar 2016 15:45:22 +0000
reviewersdbaron
bugs1249664
milestone47.0a1
bug 1249664 - Save the combobox's dropped-down state across frame reconstruction r?dbaron We already restore the scroll-position state of the list control frame (via nsHTMLScrollFrame), so the combobox control frame needs to add a suffix to its state key so it doesn't overlap. MozReview-Commit-ID: Eq0X0FCOciZ
layout/forms/nsComboboxControlFrame.cpp
layout/forms/nsComboboxControlFrame.h
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -14,16 +14,17 @@
 #include "nsGkAtoms.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsHTMLParts.h"
 #include "nsIFormControl.h"
 #include "nsNameSpaceManager.h"
 #include "nsIListControlFrame.h"
 #include "nsPIDOMWindow.h"
 #include "nsIPresShell.h"
+#include "nsPresState.h"
 #include "nsContentList.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMNode.h"
 #include "nsISelectControlFrame.h"
 #include "nsContentUtils.h"
 #include "nsIDocument.h"
@@ -311,24 +312,25 @@ nsComboboxControlFrame::ShowPopup(bool a
     viewManager->ResizeView(view, rect);
     viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
   } else {
     viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
     nsRect emptyRect(0, 0, 0, 0);
     viewManager->ResizeView(view, emptyRect);
   }
 
-  // fire a popup dom event
-  nsEventStatus status = nsEventStatus_eIgnore;
-  WidgetMouseEvent event(true, aShowPopup ? eXULPopupShowing : eXULPopupHiding,
-                         nullptr, WidgetMouseEvent::eReal);
+  // fire a popup dom event if it is safe to do so
+  nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
+  if (shell && nsContentUtils::IsSafeToRunScript()) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    WidgetMouseEvent event(true, aShowPopup ? eXULPopupShowing : eXULPopupHiding,
+                           nullptr, WidgetMouseEvent::eReal);
 
-  nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
-  if (shell)
     shell->HandleDOMEventWithTarget(mContent, &event, &status);
+  }
 }
 
 bool
 nsComboboxControlFrame::ShowList(bool aShowList)
 {
   nsView* view = mDropdownFrame->GetView();
   if (aShowList) {
     NS_ASSERTION(!view->HasWidget(),
@@ -442,17 +444,17 @@ nsComboboxControlFrame::ReflowDropdown(n
   // both sets of mComputedBorderPadding.
   nscoord forcedISize = aReflowState.ComputedISize() +
     aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm) -
     kidReflowState.ComputedLogicalBorderPadding().IStartEnd(wm);
   kidReflowState.SetComputedISize(std::max(kidReflowState.ComputedISize(),
                                          forcedISize));
 
   // ensure we start off hidden
-  if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
+  if (!mDroppedDown && GetStateBits() & NS_FRAME_FIRST_REFLOW) {
     nsView* view = mDropdownFrame->GetView();
     nsViewManager* viewManager = view->GetViewManager();
     viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
     nsRect emptyRect(0, 0, 0, 0);
     viewManager->ResizeView(view, emptyRect);
   }
 
   // Allow the child to move/size/change-visibility its view if it's currently
@@ -1647,34 +1649,47 @@ nsComboboxControlFrame::OnContentReset()
 
 
 //--------------------------------------------------------
 // nsIStatefulFrame
 //--------------------------------------------------------
 NS_IMETHODIMP
 nsComboboxControlFrame::SaveState(nsPresState** aState)
 {
-  if (!mListControlFrame)
-    return NS_ERROR_FAILURE;
-
-  nsIStatefulFrame* stateful = do_QueryFrame(mListControlFrame);
-  return stateful->SaveState(aState);
+  MOZ_ASSERT(!(*aState));
+  (*aState) = new nsPresState();
+  (*aState)->SetDroppedDown(mDroppedDown);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsComboboxControlFrame::RestoreState(nsPresState* aState)
 {
-  if (!mListControlFrame)
+  if (!aState) {
     return NS_ERROR_FAILURE;
-
-  nsIStatefulFrame* stateful = do_QueryFrame(mListControlFrame);
-  NS_ASSERTION(stateful, "Must implement nsIStatefulFrame");
-  return stateful->RestoreState(aState);
+  }
+  ShowList(aState->GetDroppedDown()); // might destroy us
+  return NS_OK;
 }
 
+// Append a suffix so that the state key for the combobox is different
+// from the state key the list control uses to sometimes save the scroll
+// position for the same Element
+NS_IMETHODIMP
+nsComboboxControlFrame::GenerateStateKey(nsIContent* aContent,
+                                        nsIDocument* aDocument,
+                                        nsACString& aKey)
+{
+  nsresult rv = nsContentUtils::GenerateStateKey(aContent, aDocument, aKey);
+  if (NS_FAILED(rv) || aKey.IsEmpty()) {
+    return rv;
+  }
+  aKey.Append("CCF");
+  return NS_OK;
+}
 
 // Fennec uses a custom combobox built-in widget.
 //
 
 /* static */
 bool
 nsComboboxControlFrame::ToolkitHasNativePopup()
 {
--- a/layout/forms/nsComboboxControlFrame.h
+++ b/layout/forms/nsComboboxControlFrame.h
@@ -199,16 +199,19 @@ public:
   virtual uint32_t GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain) override
     { return 0; }
 
   virtual nsIWidget* GetRollupWidget() override;
 
   //nsIStatefulFrame
   NS_IMETHOD SaveState(nsPresState** aState) override;
   NS_IMETHOD RestoreState(nsPresState* aState) override;
+  NS_IMETHOD GenerateStateKey(nsIContent* aContent,
+                              nsIDocument* aDocument,
+                              nsACString& aKey) override;
 
   static bool ToolkitHasNativePopup();
 
 protected:
   friend class RedisplayTextEvent;
   friend class nsAsyncResize;
   friend class nsResizeDropdownAtFinalPosition;