From 1d08bd439be6c67528b34e9717e9dbffa5a8a790 Mon Sep 17 00:00:00 2001 From: Timothy Guan-tin Chien Date: Wed, 10 May 2017 10:48:50 -0400 Subject: [PATCH] Bug 1360500 - Allow custom colors on find selection type selections. r=jaws,masayuki,smaug This patch implements chrome-only Selection#setColors and Selection#resetColors methods, and use it to set the background color of the preferences search highlight. MozReview-Commit-ID: 2U92aBCAyeh --HG-- extra : rebase_source : b07af1f37309d8184584b298a720cd5c1382929a --- .../preferences/in-content/findInPage.js | 1 + dom/base/nsISelectionPrivate.idl | 30 ++++++ dom/webidl/Selection.webidl | 7 ++ layout/generic/Selection.h | 13 ++- layout/generic/nsFrameSelection.h | 18 ++++ layout/generic/nsSelection.cpp | 98 +++++++++++++++++++ layout/generic/nsTextFrame.cpp | 87 ++++++++++++++-- layout/generic/nsTextFrame.h | 1 + 8 files changed, 247 insertions(+), 8 deletions(-) diff --git a/browser/components/preferences/in-content/findInPage.js b/browser/components/preferences/in-content/findInPage.js index 2c2a6210a227..a5fb2b43f59b 100644 --- a/browser/components/preferences/in-content/findInPage.js +++ b/browser/components/preferences/in-content/findInPage.js @@ -12,6 +12,7 @@ var gSearchResultsPane = { init() { let controller = this.getSelectionController(); this.findSelection = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND); + this.findSelection.setColors("currentColor", "#ffe900", "currentColor", "#540ead"); this.searchResultsCategory = document.getElementById("category-search-results"); this.searchInput = document.getElementById("searchInput"); diff --git a/dom/base/nsISelectionPrivate.idl b/dom/base/nsISelectionPrivate.idl index 68412885e309..489169d2f0a4 100644 --- a/dom/base/nsISelectionPrivate.idl +++ b/dom/base/nsISelectionPrivate.idl @@ -161,5 +161,35 @@ interface nsISelectionPrivate : nsISelection * PR_FALSE if the new language is left-to-right. */ [noscript] void selectionLanguageChange(in boolean langRTL); + + /** + * setColors() sets custom colors for the selection. + * Currently, this is supported only when the selection type is SELECTION_FIND. + * Otherwise, throws an exception. + * + * @param aForegroundColor The foreground color of the selection. + * If this is "currentColor", foreground color + * isn't changed by this selection. + * @param aBackgroundColor The background color of the selection. + * If this is "transparent", background color is + * never painted. + * @param aAltForegroundColor The alternative foreground color of the + * selection. + * If aBackgroundColor doesn't have sufficient + * contrast with its around or foreground color + * if "currentColor" is specified, alternative + * colors are used if it have higher contrast. + * @param aAltBackgroundColor The alternative background color of the + * selection. + */ + void setColors(in DOMString aForegroundColor, + in DOMString aBackgroundColor, + in DOMString aAltForegroundColor, + in DOMString aAltBackgroundColor); + + /** + * resetColors() forget the customized colors which were set by setColors(). + */ + void resetColors(); }; diff --git a/dom/webidl/Selection.webidl b/dom/webidl/Selection.webidl index 4acd78555323..9253e7e198b0 100644 --- a/dom/webidl/Selection.webidl +++ b/dom/webidl/Selection.webidl @@ -90,4 +90,11 @@ partial interface Selection { [ChromeOnly,Throws] void scrollIntoView(short aRegion, boolean aIsSynchronous, short aVPercent, short aHPercent); + + [ChromeOnly,Throws] + void setColors(DOMString aForegroundColor, DOMString aBackgroundColor, + DOMString aAltForegroundColor, DOMString aAltBackgroundColor); + + [ChromeOnly,Throws] + void resetColors(); }; diff --git a/layout/generic/Selection.h b/layout/generic/Selection.h index 7a61a6267ef2..cdcacbeef1bd 100644 --- a/layout/generic/Selection.h +++ b/layout/generic/Selection.h @@ -30,6 +30,7 @@ class nsIHTMLEditor; class nsFrameSelection; class nsPIDOMWindowOuter; struct SelectionDetails; +struct SelectionCustomColors; class nsCopySupport; class nsHTMLCopyEncoder; @@ -251,6 +252,12 @@ public: int16_t aVPercent, int16_t aHPercent, mozilla::ErrorResult& aRv); + void SetColors(const nsAString& aForeColor, const nsAString& aBackColor, + const nsAString& aAltForeColor, const nsAString& aAltBackColor, + mozilla::ErrorResult& aRv); + + void ResetColors(mozilla::ErrorResult& aRv); + // Non-JS callers should use the following methods. void Collapse(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv); void CollapseToStart(mozilla::ErrorResult& aRv); @@ -284,6 +291,8 @@ public: mSelectionType = aSelectionType; } + SelectionCustomColors* GetCustomColors() const { return mCustomColors.get(); } + nsresult NotifySelectionListeners(bool aCalledByJS); nsresult NotifySelectionListeners(); @@ -418,9 +427,11 @@ private: RefPtr mAutoScrollTimer; nsCOMArray mSelectionListeners; nsRevocableEventPtr mScrollEvent; - CachedOffsetForFrame *mCachedOffsetForFrame; + CachedOffsetForFrame* mCachedOffsetForFrame; nsDirection mDirection; SelectionType mSelectionType; + UniquePtr mCustomColors; + /** * True if the current selection operation was initiated by user action. * It determines whether we exclude -moz-user-select:none nodes or not, diff --git a/layout/generic/nsFrameSelection.h b/layout/generic/nsFrameSelection.h index c9cfabdd2125..0e7f8a6d38a2 100644 --- a/layout/generic/nsFrameSelection.h +++ b/layout/generic/nsFrameSelection.h @@ -45,6 +45,24 @@ struct SelectionDetails mozilla::UniquePtr mNext; }; +struct SelectionCustomColors +{ +#ifdef NS_BUILD_REFCNT_LOGGING + SelectionCustomColors() + { + MOZ_COUNT_CTOR(SelectionCustomColors); + } + ~SelectionCustomColors() + { + MOZ_COUNT_DTOR(SelectionCustomColors); + } +#endif + mozilla::Maybe mForegroundColor; + mozilla::Maybe mBackgroundColor; + mozilla::Maybe mAltForegroundColor; + mozilla::Maybe mAltBackgroundColor; +}; + class nsIPresShell; /** PeekOffsetStruct is used to group various arguments (both input and output) diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index bce36173b592..28de090a2185 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -3490,6 +3490,7 @@ Selection::Selection() : mCachedOffsetForFrame(nullptr) , mDirection(eDirNext) , mSelectionType(SelectionType::eNormal) + , mCustomColors(nullptr) , mUserInitiated(false) , mCalledByJS(false) , mSelectionChangeBlockerCount(0) @@ -3501,6 +3502,7 @@ Selection::Selection(nsFrameSelection* aList) , mCachedOffsetForFrame(nullptr) , mDirection(eDirNext) , mSelectionType(SelectionType::eNormal) + , mCustomColors(nullptr) , mUserInitiated(false) , mCalledByJS(false) , mSelectionChangeBlockerCount(0) @@ -6821,6 +6823,102 @@ Selection::SelectionLanguageChange(bool aLangRTL) return NS_OK; } +NS_IMETHODIMP +Selection::SetColors(const nsAString& aForegroundColor, + const nsAString& aBackgroundColor, + const nsAString& aAltForegroundColor, + const nsAString& aAltBackgroundColor) +{ + ErrorResult result; + SetColors(aForegroundColor, aBackgroundColor, + aAltForegroundColor, aAltBackgroundColor, result); + return result.StealNSResult(); +} + +void +Selection::SetColors(const nsAString& aForegroundColor, + const nsAString& aBackgroundColor, + const nsAString& aAltForegroundColor, + const nsAString& aAltBackgroundColor, + ErrorResult& aRv) +{ + if (mSelectionType != SelectionType::eFind) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + mCustomColors.reset(new SelectionCustomColors); + + NS_NAMED_LITERAL_STRING(currentColorStr, "currentColor"); + NS_NAMED_LITERAL_STRING(transparentStr, "transparent"); + + if (!aForegroundColor.Equals(currentColorStr)) { + nscolor foregroundColor; + nsAttrValue aForegroundColorValue; + aForegroundColorValue.ParseColor(aForegroundColor); + if (!aForegroundColorValue.GetColorValue(foregroundColor)) { + aRv.Throw(NS_ERROR_INVALID_ARG); + return; + } + mCustomColors->mForegroundColor = Some(foregroundColor); + } else { + mCustomColors->mForegroundColor = Nothing(); + } + + if (!aBackgroundColor.Equals(transparentStr)) { + nscolor backgroundColor; + nsAttrValue aBackgroundColorValue; + aBackgroundColorValue.ParseColor(aBackgroundColor); + if (!aBackgroundColorValue.GetColorValue(backgroundColor)) { + aRv.Throw(NS_ERROR_INVALID_ARG); + return; + } + mCustomColors->mBackgroundColor = Some(backgroundColor); + } else { + mCustomColors->mBackgroundColor = Nothing(); + } + + if (!aAltForegroundColor.Equals(currentColorStr)) { + nscolor altForegroundColor; + nsAttrValue aAltForegroundColorValue; + aAltForegroundColorValue.ParseColor(aAltForegroundColor); + if (!aAltForegroundColorValue.GetColorValue(altForegroundColor)) { + aRv.Throw(NS_ERROR_INVALID_ARG); + return; + } + mCustomColors->mAltForegroundColor = Some(altForegroundColor); + } else { + mCustomColors->mAltForegroundColor = Nothing(); + } + + if (!aAltBackgroundColor.Equals(transparentStr)) { + nscolor altBackgroundColor; + nsAttrValue aAltBackgroundColorValue; + aAltBackgroundColorValue.ParseColor(aAltBackgroundColor); + if (!aAltBackgroundColorValue.GetColorValue(altBackgroundColor)) { + aRv.Throw(NS_ERROR_INVALID_ARG); + return; + } + mCustomColors->mAltBackgroundColor = Some(altBackgroundColor); + } else { + mCustomColors->mAltBackgroundColor = Nothing(); + } +} + +NS_IMETHODIMP +Selection::ResetColors() +{ + ErrorResult result; + ResetColors(result); + return result.StealNSResult(); +} + +void +Selection::ResetColors(ErrorResult& aRv) +{ + mCustomColors = nullptr; +} + NS_IMETHODIMP_(nsDirection) Selection::GetSelectionDirection() { return mDirection; diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index f1ed4213d83a..13c986c7971c 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -3828,13 +3828,86 @@ nsTextPaintStyle::GetHighlightColors(nscolor* aForeColor, NS_ASSERTION(aForeColor, "aForeColor is null"); NS_ASSERTION(aBackColor, "aBackColor is null"); - nscolor backColor = - LookAndFeel::GetColor(LookAndFeel::eColorID_TextHighlightBackground); - nscolor foreColor = - LookAndFeel::GetColor(LookAndFeel::eColorID_TextHighlightForeground); - EnsureSufficientContrast(&foreColor, &backColor); - *aForeColor = foreColor; - *aBackColor = backColor; + const nsFrameSelection* frameSelection = mFrame->GetConstFrameSelection(); + const Selection* selection = + frameSelection->GetSelection(SelectionType::eFind); + const SelectionCustomColors* customColors = nullptr; + if (selection) { + customColors = selection->GetCustomColors(); + } + + if (!customColors) { + nscolor backColor = + LookAndFeel::GetColor(LookAndFeel::eColorID_TextHighlightBackground); + nscolor foreColor = + LookAndFeel::GetColor(LookAndFeel::eColorID_TextHighlightForeground); + EnsureSufficientContrast(&foreColor, &backColor); + *aForeColor = foreColor; + *aBackColor = backColor; + + return; + } + + if (customColors->mForegroundColor && customColors->mBackgroundColor) { + nscolor foreColor = *customColors->mForegroundColor; + nscolor backColor = *customColors->mBackgroundColor; + + if (EnsureSufficientContrast(&foreColor, &backColor) && + customColors->mAltForegroundColor && + customColors->mAltBackgroundColor) { + foreColor = *customColors->mAltForegroundColor; + backColor = *customColors->mAltBackgroundColor; + } + + *aForeColor = foreColor; + *aBackColor = backColor; + return; + } + + if (customColors->mBackgroundColor) { + // !mForegroundColor means "currentColor"; the current color of the text. + nscolor foreColor = GetTextColor(); + nscolor backColor = *customColors->mBackgroundColor; + + if (customColors->mAltBackgroundColor) { + int32_t foreLuminosityDifference = + NS_LUMINOSITY_DIFFERENCE(foreColor, backColor); + + // The sufficient luminosity difference is based on the link color of + // about:preferences, so we don't invert the background color on these text. + // XXX: Make this more generic. + int32_t sufficientLuminosityDifference = + NS_LUMINOSITY_DIFFERENCE(NS_RGBA(23, 140, 229, 255), backColor); + + if (foreLuminosityDifference < sufficientLuminosityDifference) { + backColor = *customColors->mAltBackgroundColor; + } + } + + *aForeColor = foreColor; + *aBackColor = backColor; + return; + } + + if (customColors->mForegroundColor) { + nscolor foreColor = *customColors->mForegroundColor; + // !mBackgroundColor means "transparent"; the current color of the background. + nscolor backColor = mFrameBackgroundColor; + + if (customColors->mAltForegroundColor && + EnsureSufficientContrast(&foreColor, &backColor)) { + foreColor = *customColors->mAltForegroundColor; + backColor = mFrameBackgroundColor; + } + + *aForeColor = foreColor; + *aBackColor = backColor; + return; + } + + // There are neither mForegroundColor nor mBackgroundColor. + *aForeColor = GetTextColor(); + *aBackColor = NS_TRANSPARENT; } void diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h index 0c57324cb2cc..e216f682180a 100644 --- a/layout/generic/nsTextFrame.h +++ b/layout/generic/nsTextFrame.h @@ -11,6 +11,7 @@ #include "mozilla/gfx/2D.h" #include "mozilla/UniquePtr.h" #include "nsFrame.h" +#include "nsFrameSelection.h" #include "nsSplittableFrame.h" #include "nsLineBox.h" #include "gfxSkipChars.h"