Bug 1340488 - Add a chrome-only previewValue attribute to <select> for showing preview text. r=heycam, baku draft
authorRay Lin <ralin@mozilla.com>
Wed, 03 May 2017 17:08:44 +0800
changeset 574912 e3ef5b6c5b7d428d8507b49f1e0a101d23e9c87d
parent 574519 b21b974d60d3075ae24f6fb1bae75d0f122f28fc
child 627735 78813f3f5b24e1e63e0e01bc42613eb530c78194
push id57854
push userbmo:ralin@mozilla.com
push dateTue, 09 May 2017 15:18:46 +0000
reviewersheycam, baku
bugs1340488
milestone55.0a1
Bug 1340488 - Add a chrome-only previewValue attribute to <select> for showing preview text. r=heycam, baku 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
@@ -172,16 +172,22 @@ public:
     return mIsOpenInParentProcess;
   }
 
   void SetOpenInParentProcess(bool aVal) override
   {
     mIsOpenInParentProcess = aVal;
   }
 
+  void GetPreviewText(nsAString& aValue) override
+  {
+    aValue = mPreviewText;
+  }
+  void SetPreviewText(const nsAString& aValue) override;
+
   // nsISelectControlFrame
   NS_IMETHOD AddOption(int32_t index) override;
   NS_IMETHOD RemoveOption(int32_t index) override;
   NS_IMETHOD DoneAddingChildren(bool aIsDone) override;
   NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) override;
   NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) override;
 
   //nsIRollupListener
@@ -266,17 +272,17 @@ 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);
 
 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();
 
@@ -292,17 +298,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
    */