forked from mirrors/gecko-dev
nsColorControlFrame::UpdateColor() looks up the color value from the corresponding <input> element -- and it expects to receive a valid color string, regardless of what the user/author has done (or whether they've done anything), thanks to the HTMLInputElement sanitization code that gets run when the value is set. As a basic sanity-check, UpdateColor() has an assertion to verify that the value it receives is non-empty. However, if it happens to be called while the element is still being appended (e.g. due to greedy frame construction), then it *can* legitimately get an empty value. So, the assertion isn't entirely valid! Hence, this patch relaxes the assertion to only take effect after the frame has been reflowed, and it also makes UpdateColor() a no-op in that case. This is fine because we can expect that UpdateColor() will be called again (and will see a non-empty color value at that point) before the frame gets reflowed/painted. MozReview-Commit-ID: LOymuwy6gIM --HG-- extra : rebase_source : 57a2b58cc371ec82f1d2db0334b2236a33056e40
153 lines
5.1 KiB
C++
153 lines
5.1 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsColorControlFrame.h"
|
|
|
|
#include "nsContentCreatorFunctions.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsCSSPseudoElements.h"
|
|
#include "nsCheckboxRadioFrame.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsIDOMNode.h"
|
|
#include "nsIFormControl.h"
|
|
#include "mozilla/StyleSetHandle.h"
|
|
#include "mozilla/StyleSetHandleInlines.h"
|
|
#include "mozilla/dom/HTMLInputElement.h"
|
|
#include "nsIDocument.h"
|
|
|
|
using mozilla::dom::Element;
|
|
using mozilla::dom::HTMLInputElement;
|
|
using mozilla::dom::CallerType;
|
|
|
|
nsColorControlFrame::nsColorControlFrame(nsStyleContext* aContext)
|
|
: nsHTMLButtonControlFrame(aContext, kClassID)
|
|
{
|
|
}
|
|
|
|
nsIFrame*
|
|
NS_NewColorControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
|
{
|
|
return new (aPresShell) nsColorControlFrame(aContext);
|
|
}
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsColorControlFrame)
|
|
|
|
NS_QUERYFRAME_HEAD(nsColorControlFrame)
|
|
NS_QUERYFRAME_ENTRY(nsColorControlFrame)
|
|
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
|
|
NS_QUERYFRAME_TAIL_INHERITING(nsHTMLButtonControlFrame)
|
|
|
|
|
|
void nsColorControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
|
{
|
|
nsCheckboxRadioFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
|
|
DestroyAnonymousContent(mColorContent.forget());
|
|
nsHTMLButtonControlFrame::DestroyFrom(aDestructRoot);
|
|
}
|
|
|
|
#ifdef DEBUG_FRAME_DUMP
|
|
nsresult
|
|
nsColorControlFrame::GetFrameName(nsAString& aResult) const
|
|
{
|
|
return MakeFrameName(NS_LITERAL_STRING("ColorControl"), aResult);
|
|
}
|
|
#endif
|
|
|
|
// Create the color area for the button.
|
|
// The frame will be generated by the frame constructor.
|
|
nsresult
|
|
nsColorControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
|
|
{
|
|
nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
|
|
mColorContent = doc->CreateHTMLElement(nsGkAtoms::div);
|
|
mColorContent->SetPseudoElementType(CSSPseudoElementType::mozColorSwatch);
|
|
|
|
// Mark the element to be native anonymous before setting any attributes.
|
|
mColorContent->SetIsNativeAnonymousRoot();
|
|
|
|
nsresult rv = UpdateColor();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (!aElements.AppendElement(mColorContent)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsColorControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
|
|
uint32_t aFilter)
|
|
{
|
|
if (mColorContent) {
|
|
aElements.AppendElement(mColorContent);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsColorControlFrame::UpdateColor()
|
|
{
|
|
// Get the color from the "value" property of our content; it will return the
|
|
// default color (through the sanitization algorithm) if the value is empty.
|
|
nsAutoString color;
|
|
HTMLInputElement* elt = HTMLInputElement::FromContent(mContent);
|
|
elt->GetValue(color, CallerType::System);
|
|
|
|
if (color.IsEmpty()) {
|
|
// OK, there is one case the color string might be empty -- if our content
|
|
// is still being created, i.e. if it has mDoneCreating==false. In that
|
|
// case, we simply do nothing, because we'll be called again with a complete
|
|
// content node before we ever reflow or paint. Specifically: we can expect
|
|
// that HTMLInputElement::DoneCreatingElement() will set mDoneCreating to
|
|
// true (which enables sanitization) and then it'll call SetValueInternal(),
|
|
// which produces a nonempty color (via sanitization), and then it'll call
|
|
// this function here, and we'll get the nonempty default color.
|
|
MOZ_ASSERT(HasAnyStateBits(NS_FRAME_FIRST_REFLOW),
|
|
"Content node's GetValue() should return a valid color string "
|
|
"by the time we've been reflowed (the default color, in case "
|
|
"no valid color is set)");
|
|
return NS_OK;
|
|
}
|
|
|
|
// Set the background-color CSS property of the swatch element to this color.
|
|
return mColorContent->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
|
|
NS_LITERAL_STRING("background-color:") + color,
|
|
/* aNotify */ true);
|
|
}
|
|
|
|
nsresult
|
|
nsColorControlFrame::AttributeChanged(int32_t aNameSpaceID,
|
|
nsIAtom* aAttribute,
|
|
int32_t aModType)
|
|
{
|
|
NS_ASSERTION(mColorContent, "The color div must exist");
|
|
|
|
// If the value attribute is set, update the color box, but only if we're
|
|
// still a color control, which might not be the case if the type attribute
|
|
// was removed/changed.
|
|
nsCOMPtr<nsIFormControl> fctrl = do_QueryInterface(GetContent());
|
|
if (fctrl->ControlType() == NS_FORM_INPUT_COLOR &&
|
|
aNameSpaceID == kNameSpaceID_None && nsGkAtoms::value == aAttribute) {
|
|
UpdateColor();
|
|
}
|
|
return nsHTMLButtonControlFrame::AttributeChanged(aNameSpaceID, aAttribute,
|
|
aModType);
|
|
}
|
|
|
|
nsContainerFrame*
|
|
nsColorControlFrame::GetContentInsertionFrame()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
Element*
|
|
nsColorControlFrame::GetPseudoElement(CSSPseudoElementType aType)
|
|
{
|
|
if (aType == CSSPseudoElementType::mozColorSwatch) {
|
|
return mColorContent;
|
|
}
|
|
|
|
return nsContainerFrame::GetPseudoElement(aType);
|
|
}
|