diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index e11f054032be..7c75aa86100b 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -56,6 +56,8 @@ #include "nsMappedAttributes.h" #include "nsIFormControl.h" #include "mozilla/dom/Document.h" +#include "mozilla/dom/HTMLDataListElement.h" +#include "mozilla/dom/HTMLOptionElement.h" #include "nsIFormControlFrame.h" #include "nsITextControlFrame.h" #include "nsIFrame.h" @@ -687,6 +689,33 @@ static bool IsPopupBlocked(Document* aDoc) { return true; } +nsTArray HTMLInputElement::GetColorsFromList() { + RefPtr dataList = GetList(); + if (!dataList) { + return {}; + } + + nsTArray colors; + + RefPtr options = dataList->Options(); + uint32_t length = options->Length(true); + for (uint32_t i = 0; i < length; ++i) { + auto* option = HTMLOptionElement::FromNodeOrNull(options->Item(i, false)); + if (!option) { + continue; + } + + nsString value; + option->GetValue(value); + if (IsValidSimpleColor(value)) { + ToLowerCase(value); + colors.AppendElement(value); + } + } + + return colors; +} + nsresult HTMLInputElement::InitColorPicker() { MOZ_ASSERT(IsMutable()); @@ -719,7 +748,8 @@ nsresult HTMLInputElement::InitColorPicker() { nsAutoString initialValue; GetNonFileValueInternal(initialValue); - nsresult rv = colorPicker->Init(win, title, initialValue); + nsTArray colors = GetColorsFromList(); + nsresult rv = colorPicker->Init(win, title, initialValue, colors); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr callback = @@ -4764,6 +4794,22 @@ void HTMLInputElement::SanitizeValue(nsAString& aValue, } } +Maybe HTMLInputElement::ParseSimpleColor(const nsAString& aColor) { + // Input color string should be 7 length (i.e. a string representing a valid + // simple color) + if (aColor.Length() != 7 || aColor.First() != '#') { + return {}; + } + + const nsAString& withoutHash = StringTail(aColor, 6); + nscolor color; + if (!NS_HexToRGBA(withoutHash, nsHexColorType::NoAlpha, &color)) { + return {}; + } + + return Some(color); +} + bool HTMLInputElement::IsValidSimpleColor(const nsAString& aValue) const { if (aValue.Length() != 7 || aValue.First() != '#') { return false; diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 9817bd964f77..ba028f1abc4c 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -9,6 +9,7 @@ #include "mozilla/Attributes.h" #include "mozilla/Decimal.h" +#include "mozilla/Maybe.h" #include "mozilla/TextControlElement.h" #include "mozilla/TextControlState.h" #include "mozilla/UniquePtr.h" @@ -858,6 +859,9 @@ class HTMLInputElement final : public TextControlElement, */ bool IsValueEmpty() const; + // Parse a simple (hex) color. + static mozilla::Maybe ParseSimpleColor(const nsAString& aColor); + protected: MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual ~HTMLInputElement(); @@ -1384,6 +1388,11 @@ class HTMLInputElement final : public TextControlElement, */ nsresult MaybeInitPickers(EventChainPostVisitor& aVisitor); + /** + * Returns all valid colors in the for the input with type=color. + */ + nsTArray GetColorsFromList(); + enum FilePickerType { FILE_PICKER_FILE, FILE_PICKER_DIRECTORY }; nsresult InitFilePicker(FilePickerType aType); nsresult InitColorPicker(); diff --git a/dom/html/test/forms/mochitest.ini b/dom/html/test/forms/mochitest.ini index 2097d43a04b8..ab67dc60911d 100644 --- a/dom/html/test/forms/mochitest.ini +++ b/dom/html/test/forms/mochitest.ini @@ -30,6 +30,7 @@ support-files = file_double_submit.html [test_formnovalidate_attribute.html] [test_input_attributes_reflection.html] [test_input_color_input_change_events.html] +[test_input_color_picker_datalist.html] [test_input_color_picker_initial.html] [test_input_color_picker_popup.html] [test_input_color_picker_update.html] diff --git a/dom/html/test/forms/test_input_color_picker_datalist.html b/dom/html/test/forms/test_input_color_picker_datalist.html new file mode 100644 index 000000000000..1a268c070153 --- /dev/null +++ b/dom/html/test/forms/test_input_color_picker_datalist.html @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp index 652ce29b6f35..5758d922cc4f 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -2272,8 +2272,8 @@ bool BrowserChild::DeallocPDocAccessibleChild( } #endif -PColorPickerChild* BrowserChild::AllocPColorPickerChild(const nsAString&, - const nsAString&) { +PColorPickerChild* BrowserChild::AllocPColorPickerChild( + const nsAString&, const nsAString&, const nsTArray&) { MOZ_CRASH("unused"); return nullptr; } diff --git a/dom/ipc/BrowserChild.h b/dom/ipc/BrowserChild.h index a52f3baf4183..1d9e5e4b9425 100644 --- a/dom/ipc/BrowserChild.h +++ b/dom/ipc/BrowserChild.h @@ -436,8 +436,9 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, bool DeallocPDocAccessibleChild(PDocAccessibleChild*); #endif - PColorPickerChild* AllocPColorPickerChild(const nsAString& aTitle, - const nsAString& aInitialColor); + PColorPickerChild* AllocPColorPickerChild( + const nsAString& aTitle, const nsAString& aInitialColor, + const nsTArray& aDefaultColors); bool DeallocPColorPickerChild(PColorPickerChild* aActor); diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp index d16a45f42a98..dc2bb3c123dd 100644 --- a/dom/ipc/BrowserParent.cpp +++ b/dom/ipc/BrowserParent.cpp @@ -3402,8 +3402,9 @@ BrowserParent::GetAuthPrompt(uint32_t aPromptReason, const nsIID& iid, } PColorPickerParent* BrowserParent::AllocPColorPickerParent( - const nsString& aTitle, const nsString& aInitialColor) { - return new ColorPickerParent(aTitle, aInitialColor); + const nsString& aTitle, const nsString& aInitialColor, + const nsTArray& aDefaultColors) { + return new ColorPickerParent(aTitle, aInitialColor, aDefaultColors); } bool BrowserParent::DeallocPColorPickerParent(PColorPickerParent* actor) { diff --git a/dom/ipc/BrowserParent.h b/dom/ipc/BrowserParent.h index 32f569c318f4..728fb0d9391d 100644 --- a/dom/ipc/BrowserParent.h +++ b/dom/ipc/BrowserParent.h @@ -423,8 +423,9 @@ class BrowserParent final : public PBrowserParent, const ScrollAxis& aHorizontal, const ScrollFlags& aScrollFlags, const int32_t& aAppUnitsPerDevPixel); - PColorPickerParent* AllocPColorPickerParent(const nsString& aTitle, - const nsString& aInitialColor); + PColorPickerParent* AllocPColorPickerParent( + const nsString& aTitle, const nsString& aInitialColor, + const nsTArray& aDefaultColors); bool DeallocPColorPickerParent(PColorPickerParent* aColorPicker); diff --git a/dom/ipc/ColorPickerParent.cpp b/dom/ipc/ColorPickerParent.cpp index 9bbd52508afd..1328e3a428e1 100644 --- a/dom/ipc/ColorPickerParent.cpp +++ b/dom/ipc/ColorPickerParent.cpp @@ -53,7 +53,8 @@ bool ColorPickerParent::CreateColorPicker() { return false; } - return NS_SUCCEEDED(mPicker->Init(window, mTitle, mInitialColor)); + return NS_SUCCEEDED( + mPicker->Init(window, mTitle, mInitialColor, mDefaultColors)); } mozilla::ipc::IPCResult ColorPickerParent::RecvOpen() { diff --git a/dom/ipc/ColorPickerParent.h b/dom/ipc/ColorPickerParent.h index f53afc763446..c02225faf314 100644 --- a/dom/ipc/ColorPickerParent.h +++ b/dom/ipc/ColorPickerParent.h @@ -14,8 +14,11 @@ namespace mozilla::dom { class ColorPickerParent : public PColorPickerParent { public: - ColorPickerParent(const nsString& aTitle, const nsString& aInitialColor) - : mTitle(aTitle), mInitialColor(aInitialColor) {} + ColorPickerParent(const nsString& aTitle, const nsString& aInitialColor, + const nsTArray& aDefaultColors) + : mTitle(aTitle), + mInitialColor(aInitialColor), + mDefaultColors(aDefaultColors.Clone()) {} virtual mozilla::ipc::IPCResult RecvOpen() override; virtual void ActorDestroy(ActorDestroyReason aWhy) override; @@ -45,6 +48,7 @@ class ColorPickerParent : public PColorPickerParent { nsString mTitle; nsString mInitialColor; + nsTArray mDefaultColors; }; } // namespace mozilla::dom diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 894400eeacdd..2ed05e10f73c 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -436,7 +436,7 @@ parent: * Create an asynchronous color picker on the parent side, * but don't open it yet. */ - async PColorPicker(nsString title, nsString initialColor); + async PColorPicker(nsString title, nsString initialColor, nsString[] defaultColors); async PFilePicker(nsString aTitle, int16_t aMode); diff --git a/mobile/android/components/geckoview/ColorPickerDelegate.jsm b/mobile/android/components/geckoview/ColorPickerDelegate.jsm index dbb45ba350a9..78b7297da968 100644 --- a/mobile/android/components/geckoview/ColorPickerDelegate.jsm +++ b/mobile/android/components/geckoview/ColorPickerDelegate.jsm @@ -22,7 +22,8 @@ XPCOMUtils.defineLazyModuleGetters(lazy, { const { debug, warn } = GeckoViewUtils.initLogging("ColorPickerDelegate"); class ColorPickerDelegate { - init(aParent, aTitle, aInitialColor) { + // TODO(bug 1805397): Implement default colors + init(aParent, aTitle, aInitialColor, aDefaultColors) { this._prompt = new lazy.GeckoViewPrompter(aParent); this._msg = { type: "color", diff --git a/testing/specialpowers/content/MockColorPicker.sys.mjs b/testing/specialpowers/content/MockColorPicker.sys.mjs index c466d74f5662..9ea4281606c7 100644 --- a/testing/specialpowers/content/MockColorPicker.sys.mjs +++ b/testing/specialpowers/content/MockColorPicker.sys.mjs @@ -70,9 +70,10 @@ function MockColorPickerInstance(window) { } MockColorPickerInstance.prototype = { QueryInterface: ChromeUtils.generateQI(["nsIColorPicker"]), - init(aParent, aTitle, aInitialColor) { + init(aParent, aTitle, aInitialColor, aDefaultColors) { this.parent = aParent; this.initialColor = aInitialColor; + this.defaultColors = aDefaultColors; }, initialColor: "", parent: null, diff --git a/widget/cocoa/nsColorPicker.h b/widget/cocoa/nsColorPicker.h index b8ee6328bad0..325f159fde9c 100644 --- a/widget/cocoa/nsColorPicker.h +++ b/widget/cocoa/nsColorPicker.h @@ -19,10 +19,7 @@ class mozIDOMWindowProxy; class nsColorPicker final : public nsIColorPicker { public: NS_DECL_ISUPPORTS - - NS_IMETHOD Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle, - const nsAString& aInitialColor) override; - NS_IMETHOD Open(nsIColorPickerShownCallback* aCallback) override; + NS_DECL_NSICOLORPICKER // For NSColorPanelWrapper. void Update(NSColor* aColor); diff --git a/widget/cocoa/nsColorPicker.mm b/widget/cocoa/nsColorPicker.mm index 4d833e90a8a8..255610b27c07 100644 --- a/widget/cocoa/nsColorPicker.mm +++ b/widget/cocoa/nsColorPicker.mm @@ -93,9 +93,10 @@ nsColorPicker::~nsColorPicker() { } } +// TODO(bug 1805397): Implement default colors NS_IMETHODIMP nsColorPicker::Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle, - const nsAString& aInitialColor) { + const nsAString& aInitialColor, const nsTArray& aDefaultColors) { MOZ_ASSERT(NS_IsMainThread(), "Color pickers can only be opened from main thread currently"); mTitle = aTitle; mColor = aInitialColor; diff --git a/widget/gtk/nsColorPicker.cpp b/widget/gtk/nsColorPicker.cpp index a2e461d8cf9b..4719710d9ebb 100644 --- a/widget/gtk/nsColorPicker.cpp +++ b/widget/gtk/nsColorPicker.cpp @@ -5,6 +5,8 @@ #include +#include "mozilla/Maybe.h" +#include "mozilla/dom/HTMLInputElement.h" #include "nsColor.h" #include "nsColorPicker.h" #include "nsGtkUtils.h" @@ -12,6 +14,8 @@ #include "WidgetUtils.h" #include "nsPIDOMWindow.h" +using mozilla::dom::HTMLInputElement; + NS_IMPL_ISUPPORTS(nsColorPicker, nsIColorPicker) #if defined(ACTIVATE_GTK3_COLOR_PICKER) @@ -59,28 +63,24 @@ GtkColorSelection* nsColorPicker::WidgetGetColorSelection(GtkWidget* widget) { NS_IMETHODIMP nsColorPicker::Init(mozIDOMWindowProxy* aParent, const nsAString& title, - const nsAString& initialColor) { + const nsAString& initialColor, + const nsTArray& aDefaultColors) { auto* parent = nsPIDOMWindowOuter::From(aParent); mParentWidget = mozilla::widget::WidgetUtils::DOMWindowToWidget(parent); mTitle = title; mInitialColor = initialColor; + mDefaultColors.Assign(aDefaultColors); return NS_OK; } NS_IMETHODIMP nsColorPicker::Open( nsIColorPickerShownCallback* aColorPickerShownCallback) { - // Input color string should be 7 length (i.e. a string representing a valid - // simple color) - if (mInitialColor.Length() != 7) { - return NS_ERROR_FAILURE; - } - - const nsAString& withoutHash = StringTail(mInitialColor, 6); - nscolor color; - if (!NS_HexToRGBA(withoutHash, nsHexColorType::NoAlpha, &color)) { + auto maybeColor = HTMLInputElement::ParseSimpleColor(mInitialColor); + if (maybeColor.isNothing()) { return NS_ERROR_FAILURE; } + nscolor color = maybeColor.value(); if (mCallback) { // It means Open has already been called: this is not allowed @@ -106,6 +106,16 @@ NS_IMETHODIMP nsColorPicker::Open( } gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_chooser), FALSE); + + // Setting the default colors will put them into "Custom" colors list. + for (const nsString& defaultColor : mDefaultColors) { + if (auto color = HTMLInputElement::ParseSimpleColor(defaultColor)) { + GdkRGBA color_rgba = convertToRgbaColor(*color); + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(color_chooser), &color_rgba); + } + } + + // The initial color needs to be set last. GdkRGBA color_rgba = convertToRgbaColor(color); gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(color_chooser), &color_rgba); diff --git a/widget/gtk/nsColorPicker.h b/widget/gtk/nsColorPicker.h index 745f72a1f54d..d6931757175c 100644 --- a/widget/gtk/nsColorPicker.h +++ b/widget/gtk/nsColorPicker.h @@ -65,6 +65,7 @@ class nsColorPicker final : public nsIColorPicker { nsString mTitle; nsString mColor; nsString mInitialColor; + nsTArray mDefaultColors; }; #endif // nsColorPicker_h__ diff --git a/widget/nsColorPickerProxy.cpp b/widget/nsColorPickerProxy.cpp index 95ca2bf192b5..771b4e1ce55f 100644 --- a/widget/nsColorPickerProxy.cpp +++ b/widget/nsColorPickerProxy.cpp @@ -14,14 +14,15 @@ NS_IMPL_ISUPPORTS(nsColorPickerProxy, nsIColorPicker) NS_IMETHODIMP nsColorPickerProxy::Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle, - const nsAString& aInitialColor) { + const nsAString& aInitialColor, + const nsTArray& aDefaultColors) { BrowserChild* browserChild = BrowserChild::GetFrom(aParent); if (!browserChild) { return NS_ERROR_FAILURE; } - browserChild->SendPColorPickerConstructor(this, nsString(aTitle), - nsString(aInitialColor)); + browserChild->SendPColorPickerConstructor(this, aTitle, aInitialColor, + aDefaultColors); NS_ADDREF_THIS(); return NS_OK; } diff --git a/widget/nsIColorPicker.idl b/widget/nsIColorPicker.idl index 24b128e1b161..aa24bf73cc40 100644 --- a/widget/nsIColorPicker.idl +++ b/widget/nsIColorPicker.idl @@ -62,7 +62,7 @@ interface nsIColorPicker : nsISupports * parameter has to follow the format specified on top * of this file. */ - void init(in mozIDOMWindowProxy parent, in AString title, in AString initialColor); + void init(in mozIDOMWindowProxy parent, in AString title, in AString initialColor, in Array defaultColors); /** * Opens the color dialog asynchrounously. diff --git a/widget/windows/nsColorPicker.cpp b/widget/windows/nsColorPicker.cpp index 835794b91a3c..892d152329fc 100644 --- a/widget/windows/nsColorPicker.cpp +++ b/widget/windows/nsColorPicker.cpp @@ -8,6 +8,7 @@ #include +#include "mozilla/ArrayUtils.h" #include "mozilla/AutoRestore.h" #include "nsIWidget.h" #include "nsString.h" @@ -86,18 +87,18 @@ static void BGRIntToRGBString(DWORD color, nsAString& aResult) { static AsyncColorChooser* gColorChooser; AsyncColorChooser::AsyncColorChooser(COLORREF aInitialColor, + const nsTArray& aDefaultColors, nsIWidget* aParentWidget, nsIColorPickerShownCallback* aCallback) : mozilla::Runnable("AsyncColorChooser"), mInitialColor(aInitialColor), + mDefaultColors(aDefaultColors.Clone()), mColor(aInitialColor), mParentWidget(aParentWidget), mCallback(aCallback) {} NS_IMETHODIMP AsyncColorChooser::Run() { - static COLORREF sCustomColors[16] = {0}; - MOZ_ASSERT(NS_IsMainThread(), "Color pickers can only be opened from main thread currently"); @@ -112,12 +113,21 @@ AsyncColorChooser::Run() { ? mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr)); + COLORREF customColors[16]; + for (size_t i = 0; i < mozilla::ArrayLength(customColors); i++) { + if (i < mDefaultColors.Length()) { + customColors[i] = ColorStringToRGB(mDefaultColors[i]); + } else { + customColors[i] = 0x00FFFFFF; + } + } + CHOOSECOLOR options; options.lStructSize = sizeof(options); options.hwndOwner = adtw.get(); options.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ENABLEHOOK; options.rgbResult = mInitialColor; - options.lpCustColors = sCustomColors; + options.lpCustColors = customColors; options.lpfnHook = HookProc; mColor = ChooseColor(&options) ? options.rgbResult : mInitialColor; @@ -191,19 +201,21 @@ NS_IMPL_ISUPPORTS(nsColorPicker, nsIColorPicker) NS_IMETHODIMP nsColorPicker::Init(mozIDOMWindowProxy* parent, const nsAString& title, - const nsAString& aInitialColor) { + const nsAString& aInitialColor, + const nsTArray& aDefaultColors) { MOZ_ASSERT(parent, "Null parent passed to colorpicker, no color picker for you!"); mParentWidget = WidgetUtils::DOMWindowToWidget(nsPIDOMWindowOuter::From(parent)); mInitialColor = ColorStringToRGB(aInitialColor); + mDefaultColors.Assign(aDefaultColors); return NS_OK; } NS_IMETHODIMP nsColorPicker::Open(nsIColorPickerShownCallback* aCallback) { NS_ENSURE_ARG(aCallback); - nsCOMPtr event = - new AsyncColorChooser(mInitialColor, mParentWidget, aCallback); + nsCOMPtr event = new AsyncColorChooser( + mInitialColor, mDefaultColors, mParentWidget, aCallback); return NS_DispatchToMainThread(event); } diff --git a/widget/windows/nsColorPicker.h b/widget/windows/nsColorPicker.h index 6d2136351fe6..ff34b9bd4830 100644 --- a/widget/windows/nsColorPicker.h +++ b/widget/windows/nsColorPicker.h @@ -18,7 +18,9 @@ class nsIWidget; class AsyncColorChooser : public mozilla::Runnable { public: - AsyncColorChooser(COLORREF aInitialColor, nsIWidget* aParentWidget, + AsyncColorChooser(COLORREF aInitialColor, + const nsTArray& aDefaultColors, + nsIWidget* aParentWidget, nsIColorPickerShownCallback* aCallback); NS_IMETHOD Run() override; @@ -29,6 +31,7 @@ class AsyncColorChooser : public mozilla::Runnable { LPARAM aLParam); COLORREF mInitialColor; + nsTArray mDefaultColors; COLORREF mColor; nsCOMPtr mParentWidget; nsCOMPtr mCallback; @@ -41,13 +44,11 @@ class nsColorPicker : public nsIColorPicker { nsColorPicker(); NS_DECL_ISUPPORTS - - NS_IMETHOD Init(mozIDOMWindowProxy* parent, const nsAString& title, - const nsAString& aInitialColor) override; - NS_IMETHOD Open(nsIColorPickerShownCallback* aCallback) override; + NS_DECL_NSICOLORPICKER private: COLORREF mInitialColor; + nsTArray mDefaultColors; nsCOMPtr mParentWidget; };