Bug 1340488 - Add a chrome-only previewValue attribute to <select> for showing preview text. r=baku,heycam
☠☠ backed out by 3f90e402ef50 ☠ ☠
authorRay Lin <ralin@mozilla.com>
Wed, 03 May 2017 17:08:44 +0800
changeset 357238 656b4466e33b889ae1263f110dd0eb97b89c3912
parent 357237 38346bf36faaff4fa4d99ba88d66475f8a18ac57
child 357239 aeb03a50c12ee0cf614f2e433036e48a63e70f20
push id31788
push userkwierso@gmail.com
push dateTue, 09 May 2017 20:48:49 +0000
treeherdermozilla-central@2b6f6881a24a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, heycam
bugs1340488
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1340488 - Add a chrome-only previewValue attribute to <select> for showing preview text. r=baku,heycam MozReview-Commit-ID: En8e0hO35Lj
dom/html/HTMLSelectElement.cpp
dom/html/HTMLSelectElement.h
dom/webidl/HTMLSelectElement.webidl
layout/forms/nsComboboxControlFrame.cpp
layout/forms/nsComboboxControlFrame.h
layout/forms/nsIComboboxControlFrame.h
--- a/dom/html/HTMLSelectElement.cpp
+++ b/dom/html/HTMLSelectElement.cpp
@@ -1927,16 +1927,36 @@ HTMLSelectElement::SetOpenInParentProces
 {
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
   nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
   if (comboFrame) {
     comboFrame->SetOpenInParentProcess(aVal);
   }
 }
 
+void
+HTMLSelectElement::GetPreviewValue(nsAString& aValue)
+{
+  nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
+  nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
+  if (comboFrame) {
+    comboFrame->GetPreviewText(aValue);
+  }
+}
+
+void
+HTMLSelectElement::SetPreviewValue(const nsAString& aValue)
+{
+  nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
+  nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
+  if (comboFrame) {
+    comboFrame->SetPreviewText(aValue);
+  }
+}
+
 JSObject*
 HTMLSelectElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLSelectElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLSelectElement.h
+++ b/dom/html/HTMLSelectElement.h
@@ -438,16 +438,19 @@ public:
   bool IsCombobox() const
   {
     return !Multiple() && Size() <= 1;
   }
 
   bool OpenInParentProcess();
   void SetOpenInParentProcess(bool aVal);
 
+  void GetPreviewValue(nsAString& aValue);
+  void SetPreviewValue(const nsAString& aValue);
+
 protected:
   virtual ~HTMLSelectElement();
 
   friend class SafeOptionListMutation;
 
   // Helper Methods
   /**
    * Check whether the option specified by the index is selected
--- a/dom/webidl/HTMLSelectElement.webidl
+++ b/dom/webidl/HTMLSelectElement.webidl
@@ -62,9 +62,11 @@ interface HTMLSelectElement : HTMLElemen
 
 // Chrome only interface
 
 partial interface HTMLSelectElement {
   [ChromeOnly]
   attribute boolean openInParentProcess;
   [ChromeOnly]
   AutocompleteInfo getAutocompleteInfo();
+  [ChromeOnly]
+  attribute DOMString previewValue;
 };
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -827,32 +827,22 @@ nsComboboxControlFrame::Reflow(nsPresCon
   //    mListControlFrame.
 
   if (!mDisplayFrame || !mButtonFrame || !mDropdownFrame) {
     NS_ERROR("Why did the frame constructor allow this to happen?  Fix it!!");
     return;
   }
 
   // Make sure the displayed text is the same as the selected option, bug 297389.
-  int32_t selectedIndex;
-  nsAutoString selectedOptionText;
   if (!mDroppedDown) {
-    selectedIndex = mListControlFrame->GetSelectedIndex();
+    mDisplayedIndex = mListControlFrame->GetSelectedIndex();
   }
-  else {
-    // In dropped down mode the "selected index" is the hovered menu item,
-    // we want the last selected item which is |mDisplayedIndex| in this case.
-    selectedIndex = mDisplayedIndex;
-  }
-  if (selectedIndex != -1) {
-    mListControlFrame->GetOptionText(selectedIndex, selectedOptionText);
-  }
-  if (mDisplayedOptionText != selectedOptionText) {
-    RedisplayText(selectedIndex);
-  }
+  // In dropped down mode the "selected index" is the hovered menu item,
+  // we want the last selected item which is |mDisplayedIndex| in this case.
+  RedisplayText();
 
   // First reflow our dropdown so that we know how tall we should be.
   ReflowDropdown(aPresContext, aReflowInput);
   RefPtr<nsResizeDropdownAtFinalPosition> resize =
     new nsResizeDropdownAtFinalPosition(this);
   if (NS_SUCCEEDED(aPresContext->PresShell()->PostReflowCallback(resize))) {
     // The reflow callback queue doesn't AddRef so we keep it alive until
     // it's released in its ReflowFinished / ReflowCallbackCanceled.
@@ -964,40 +954,55 @@ nsComboboxControlFrame::SetDropDown(nsIF
 nsIFrame*
 nsComboboxControlFrame::GetDropDown()
 {
   return mDropdownFrame;
 }
 
 ///////////////////////////////////////////////////////////////
 
+void
+nsComboboxControlFrame::SetPreviewText(const nsAString& aValue)
+{
+  nsAutoString previewValue(aValue);
+  nsContentUtils::RemoveNewlines(previewValue);
+
+  mPreviewText = previewValue;
+  RedisplayText();
+}
+
 NS_IMETHODIMP
 nsComboboxControlFrame::RedisplaySelectedText()
 {
   nsAutoScriptBlocker scriptBlocker;
-  return RedisplayText(mListControlFrame->GetSelectedIndex());
+  mDisplayedIndex = mListControlFrame->GetSelectedIndex();
+  return RedisplayText();
 }
 
+
 nsresult
-nsComboboxControlFrame::RedisplayText(int32_t aIndex)
+nsComboboxControlFrame::RedisplayText()
 {
+  nsString previousText(mDisplayedOptionTextOrPreview);
   // Get the text to display
-  if (aIndex != -1) {
-    mListControlFrame->GetOptionText(aIndex, mDisplayedOptionText);
+  if (!mPreviewText.IsEmpty()) {
+    mDisplayedOptionTextOrPreview = mPreviewText;
+  } else if (mDisplayedIndex != -1) {
+    mListControlFrame->GetOptionText(mDisplayedIndex, mDisplayedOptionTextOrPreview);
   } else {
-    mDisplayedOptionText.Truncate();
+    mDisplayedOptionTextOrPreview.Truncate();
   }
-  mDisplayedIndex = aIndex;
 
   REFLOW_DEBUG_MSG2("RedisplayText \"%s\"\n",
-                    NS_LossyConvertUTF16toASCII(mDisplayedOptionText).get());
+                    NS_LossyConvertUTF16toASCII(mDisplayedOptionTextOrPreview).get());
 
   // Send reflow command because the new text maybe larger
   nsresult rv = NS_OK;
-  if (mDisplayContent) {
+  if (mDisplayContent &&
+      !previousText.Equals(mDisplayedOptionTextOrPreview)) {
     // Don't call ActuallyDisplayText(true) directly here since that
     // could cause recursive frame construction. See bug 283117 and the comment in
     // HandleRedisplayTextEvent() below.
 
     // Revoke outstanding events to avoid out-of-order events which could mean
     // displaying the wrong text.
     mRedisplayTextEvent.Revoke();
 
@@ -1041,23 +1046,23 @@ nsComboboxControlFrame::HandleRedisplayT
                                                NS_FRAME_IS_DIRTY);
 
   mInRedisplayText = false;
 }
 
 void
 nsComboboxControlFrame::ActuallyDisplayText(bool aNotify)
 {
-  if (mDisplayedOptionText.IsEmpty()) {
+  if (mDisplayedOptionTextOrPreview.IsEmpty()) {
     // Have to use a non-breaking space for line-block-size calculations
     // to be right
     static const char16_t space = 0xA0;
     mDisplayContent->SetText(&space, 1, aNotify);
   } else {
-    mDisplayContent->SetText(mDisplayedOptionText, aNotify);
+    mDisplayContent->SetText(mDisplayedOptionTextOrPreview, aNotify);
   }
 }
 
 int32_t
 nsComboboxControlFrame::GetIndexOfDisplayArea()
 {
   return mDisplayedIndex;
 }
@@ -1091,36 +1096,38 @@ NS_IMETHODIMP
 nsComboboxControlFrame::RemoveOption(int32_t aIndex)
 {
   AutoWeakFrame weakThis(this);
   if (mListControlFrame->GetNumberOfOptions() > 0) {
     if (aIndex < mDisplayedIndex) {
       --mDisplayedIndex;
     } else if (aIndex == mDisplayedIndex) {
       mDisplayedIndex = 0; // IE6 compat
-      RedisplayText(mDisplayedIndex);
+      RedisplayText();
     }
   }
   else {
     // If we removed the last option, we need to blank things out
-    RedisplayText(-1);
+    mDisplayedIndex = -1;
+    RedisplayText();
   }
 
   if (!weakThis.IsAlive())
     return NS_OK;
 
   nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame);
   return lcf->RemoveOption(aIndex);
 }
 
 NS_IMETHODIMP
 nsComboboxControlFrame::OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex)
 {
   nsAutoScriptBlocker scriptBlocker;
-  RedisplayText(aNewIndex);
+  mDisplayedIndex = aNewIndex;
+  RedisplayText();
   NS_ASSERTION(mDropdownFrame, "No dropdown frame!");
 
   nsISelectControlFrame* listFrame = do_QueryFrame(mDropdownFrame);
   NS_ASSERTION(listFrame, "No list frame!");
 
   return listFrame->OnSetSelectedIndex(aOldIndex, aNewIndex);
 }
 
@@ -1216,17 +1223,17 @@ nsComboboxControlFrame::CreateAnonymousC
 
   nsNodeInfoManager *nimgr = mContent->NodeInfo()->NodeInfoManager();
 
   mDisplayContent = new nsTextNode(nimgr);
 
   // set the value of the text node
   mDisplayedIndex = mListControlFrame->GetSelectedIndex();
   if (mDisplayedIndex != -1) {
-    mListControlFrame->GetOptionText(mDisplayedIndex, mDisplayedOptionText);
+    mListControlFrame->GetOptionText(mDisplayedIndex, mDisplayedOptionTextOrPreview);
   }
   ActuallyDisplayText(false);
 
   if (!aElements.AppendElement(mDisplayContent))
     return NS_ERROR_OUT_OF_MEMORY;
 
   mButtonContent = mContent->OwnerDoc()->CreateHTMLElement(nsGkAtoms::button);
   if (!mButtonContent)
@@ -1625,17 +1632,18 @@ nsComboboxControlFrame::OnOptionSelected
   if (mDroppedDown) {
     nsISelectControlFrame *selectFrame = do_QueryFrame(mListControlFrame);
     if (selectFrame) {
       selectFrame->OnOptionSelected(aIndex, aSelected);
     }
   } else {
     if (aSelected) {
       nsAutoScriptBlocker blocker;
-      RedisplayText(aIndex);
+      mDisplayedIndex = aIndex;
+      RedisplayText();
     } else {
       AutoWeakFrame weakFrame(this);
       RedisplaySelectedText();
       if (weakFrame.IsAlive()) {
         FireValueChangeEvent(); // Fire after old option is unselected
       }
     }
   }
--- a/layout/forms/nsComboboxControlFrame.h
+++ b/layout/forms/nsComboboxControlFrame.h
@@ -266,19 +266,24 @@ protected:
    * Show or hide the dropdown list.
    * @param aShowList true to show, false to hide the dropdown.
    * @note This method might destroy |this|.
    * @return false if this frame is destroyed, true if still alive.
    */
   bool ShowList(bool aShowList);
   void CheckFireOnChange();
   void FireValueChangeEvent();
-  nsresult RedisplayText(int32_t aIndex);
+  nsresult RedisplayText();
   void HandleRedisplayTextEvent();
   void ActuallyDisplayText(bool aNotify);
+  void GetPreviewText(nsAString& aValue)
+  {
+    aValue = mPreviewText;
+  }
+  void SetPreviewText(const nsAString& aValue);
 
 private:
   // If our total transform to the root frame of the root document is only a 2d
   // translation then return that translation, otherwise returns (0,0).
   nsPoint GetCSSTransformTranslation();
 
 protected:
   nsFrameList              mPopupFrames;             // additional named child list
@@ -292,17 +297,18 @@ protected:
   // The inline size of our display area.  Used by that frame's reflow
   // to size to the full inline size except the drop-marker.
   nscoord mDisplayISize;
   
   nsRevocableEventPtr<RedisplayTextEvent> mRedisplayTextEvent;
 
   int32_t               mRecentSelectedIndex;
   int32_t               mDisplayedIndex;
-  nsString              mDisplayedOptionText;
+  nsString              mDisplayedOptionTextOrPreview;
+  nsString              mPreviewText;
 
   // make someone to listen to the button. If its programmatically pressed by someone like Accessibility
   // then open or close the combo box.
   nsCOMPtr<nsIDOMEventListener> mButtonListener;
 
   // The last y-positions used for estimating available space before and
   // after for the dropdown list in GetAvailableDropdownSpace.  These are
   // reset to nscoord_MIN in AbsolutelyPositionDropDown when placing the
--- a/layout/forms/nsIComboboxControlFrame.h
+++ b/layout/forms/nsIComboboxControlFrame.h
@@ -48,16 +48,26 @@ public:
 
   /**
    * Redisplay the selected text (will do nothing if text has not changed).
    * This method might destroy this frame or any others that happen to be
    * around.  It might even run script.
    */
   NS_IMETHOD RedisplaySelectedText() = 0;
 
+  /*
+   * Update preview text for the select control.
+   */
+  virtual void SetPreviewText(const nsAString& aValue) = 0;
+
+  /**
+   * Get the current preview text for select control.
+   */
+  virtual void GetPreviewText(nsAString& aValue) = 0;
+
   /**
    * Method for the listbox to set and get the recent index
    */
   virtual int32_t UpdateRecentIndex(int32_t aIndex) = 0;
 
   /**
    * Notification that the content has been reset
    */