1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsColorControlFrame.h"
8
9 #include "nsContentCreatorFunctions.h"
10 #include "nsContentUtils.h"
11 #include "nsCSSPseudoElements.h"
12 #include "nsCheckboxRadioFrame.h"
13 #include "nsGkAtoms.h"
14 #include "nsIDOMNode.h"
15 #include "nsIFormControl.h"
16 #include "mozilla/StyleSetHandle.h"
17 #include "mozilla/StyleSetHandleInlines.h"
18 #include "mozilla/dom/HTMLInputElement.h"
19 #include "nsIDocument.h"
20
21 using mozilla::dom::CallerType;
22 using mozilla::dom::Element;
23 using mozilla::dom::HTMLInputElement;
24
nsColorControlFrame(nsStyleContext * aContext)25 nsColorControlFrame::nsColorControlFrame(nsStyleContext* aContext)
26 : nsHTMLButtonControlFrame(aContext, kClassID) {}
27
NS_NewColorControlFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)28 nsIFrame* NS_NewColorControlFrame(nsIPresShell* aPresShell,
29 nsStyleContext* aContext) {
30 return new (aPresShell) nsColorControlFrame(aContext);
31 }
32
33 NS_IMPL_FRAMEARENA_HELPERS(nsColorControlFrame)
34
NS_QUERYFRAME_HEAD(nsColorControlFrame)35 NS_QUERYFRAME_HEAD(nsColorControlFrame)
36 NS_QUERYFRAME_ENTRY(nsColorControlFrame)
37 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
38 NS_QUERYFRAME_TAIL_INHERITING(nsHTMLButtonControlFrame)
39
40 void nsColorControlFrame::DestroyFrom(nsIFrame* aDestructRoot,
41 PostDestroyData& aPostDestroyData) {
42 nsCheckboxRadioFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
43 aPostDestroyData.AddAnonymousContent(mColorContent.forget());
44 nsHTMLButtonControlFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
45 }
46
47 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const48 nsresult nsColorControlFrame::GetFrameName(nsAString& aResult) const {
49 return MakeFrameName(NS_LITERAL_STRING("ColorControl"), aResult);
50 }
51 #endif
52
53 // Create the color area for the button.
54 // The frame will be generated by the frame constructor.
CreateAnonymousContent(nsTArray<ContentInfo> & aElements)55 nsresult nsColorControlFrame::CreateAnonymousContent(
56 nsTArray<ContentInfo>& aElements) {
57 nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
58 mColorContent = doc->CreateHTMLElement(nsGkAtoms::div);
59 mColorContent->SetPseudoElementType(CSSPseudoElementType::mozColorSwatch);
60
61 // Mark the element to be native anonymous before setting any attributes.
62 mColorContent->SetIsNativeAnonymousRoot();
63
64 nsresult rv = UpdateColor();
65 NS_ENSURE_SUCCESS(rv, rv);
66
67 if (!aElements.AppendElement(mColorContent)) {
68 return NS_ERROR_OUT_OF_MEMORY;
69 }
70
71 return NS_OK;
72 }
73
AppendAnonymousContentTo(nsTArray<nsIContent * > & aElements,uint32_t aFilter)74 void nsColorControlFrame::AppendAnonymousContentTo(
75 nsTArray<nsIContent*>& aElements, uint32_t aFilter) {
76 if (mColorContent) {
77 aElements.AppendElement(mColorContent);
78 }
79 }
80
UpdateColor()81 nsresult nsColorControlFrame::UpdateColor() {
82 // Get the color from the "value" property of our content; it will return the
83 // default color (through the sanitization algorithm) if the value is empty.
84 nsAutoString color;
85 HTMLInputElement* elt = HTMLInputElement::FromContent(mContent);
86 elt->GetValue(color, CallerType::System);
87
88 if (color.IsEmpty()) {
89 // OK, there is one case the color string might be empty -- if our content
90 // is still being created, i.e. if it has mDoneCreating==false. In that
91 // case, we simply do nothing, because we'll be called again with a complete
92 // content node before we ever reflow or paint. Specifically: we can expect
93 // that HTMLInputElement::DoneCreatingElement() will set mDoneCreating to
94 // true (which enables sanitization) and then it'll call SetValueInternal(),
95 // which produces a nonempty color (via sanitization), and then it'll call
96 // this function here, and we'll get the nonempty default color.
97 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_FIRST_REFLOW),
98 "Content node's GetValue() should return a valid color string "
99 "by the time we've been reflowed (the default color, in case "
100 "no valid color is set)");
101 return NS_OK;
102 }
103
104 // Set the background-color CSS property of the swatch element to this color.
105 return mColorContent->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
106 NS_LITERAL_STRING("background-color:") + color,
107 /* aNotify */ true);
108 }
109
AttributeChanged(int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)110 nsresult nsColorControlFrame::AttributeChanged(int32_t aNameSpaceID,
111 nsAtom* aAttribute,
112 int32_t aModType) {
113 NS_ASSERTION(mColorContent, "The color div must exist");
114
115 // If the value attribute is set, update the color box, but only if we're
116 // still a color control, which might not be the case if the type attribute
117 // was removed/changed.
118 nsCOMPtr<nsIFormControl> fctrl = do_QueryInterface(GetContent());
119 if (fctrl->ControlType() == NS_FORM_INPUT_COLOR &&
120 aNameSpaceID == kNameSpaceID_None && nsGkAtoms::value == aAttribute) {
121 UpdateColor();
122 }
123 return nsHTMLButtonControlFrame::AttributeChanged(aNameSpaceID, aAttribute,
124 aModType);
125 }
126
GetContentInsertionFrame()127 nsContainerFrame* nsColorControlFrame::GetContentInsertionFrame() {
128 return this;
129 }
130
GetPseudoElement(CSSPseudoElementType aType)131 Element* nsColorControlFrame::GetPseudoElement(CSSPseudoElementType aType) {
132 if (aType == CSSPseudoElementType::mozColorSwatch) {
133 return mColorContent;
134 }
135
136 return nsContainerFrame::GetPseudoElement(aType);
137 }
138