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