fune/layout/forms/nsFileControlFrame.cpp
Robert O'Callahan 613ea8adbd Bug 841192. Part 14: Convert all usage of nsDisplayClip(RoundedRect) to use DisplayListClipState/DisplayItemClip. r=mattwoodrow
This patch does several things. Sorry.

In BuildDisplayList implementations, instead of wrapping display items in nsDisplayClip, we
push clip state onto the nsDisplayListBuilder and give the display items an
explicit clip when they're created.

In FrameLayerBuilder, we use the explicit clips we find on display items instead of
computing our own.

We remove nsDisplayClip and everything that depends on it.

We remove ExplodeAnonymousChildLists. With nsDisplayClip gone, and
nsDisplayOptionEventGrabber removed in a previous patch, there are no
anonymous child lists.

nsDisplayItem::TryMerge implementations need to make sure they have the same
clip before being merged.

I ripped out the part of PruneDisplayListForExtraPage that adjusts clip rects.
As far as I can tell, it isn't actually necessary.

--HG--
extra : rebase_source : 6f3988b385d0ac54ab26fad10b12173884441f48
2013-03-04 22:56:02 +13:00

399 lines
12 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 "nsFileControlFrame.h"
#include "nsIContent.h"
#include "nsIAtom.h"
#include "nsPresContext.h"
#include "nsGkAtoms.h"
#include "nsWidgetsCID.h"
#include "nsIComponentManager.h"
#include "nsHTMLParts.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMHTMLButtonElement.h"
#include "nsIFormControl.h"
#include "nsINameSpaceManager.h"
#include "nsCOMPtr.h"
#include "nsIDOMElement.h"
#include "nsIDocument.h"
#include "nsIPresShell.h"
#include "nsXPCOM.h"
#include "nsISupportsPrimitives.h"
#include "nsPIDOMWindow.h"
#include "nsIFilePicker.h"
#include "nsIDOMMouseEvent.h"
#include "nsINodeInfo.h"
#include "nsIDOMEventTarget.h"
#include "nsIFile.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "nsNodeInfoManager.h"
#include "nsContentCreatorFunctions.h"
#include "nsContentUtils.h"
#include "nsDisplayList.h"
#include "nsEventListenerManager.h"
#include "nsInterfaceHashtable.h"
#include "nsURIHashKey.h"
#include "nsNetCID.h"
#include "nsWeakReference.h"
#include "nsIVariant.h"
#include "mozilla/Services.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDOMFile.h"
#include "nsEventStates.h"
#include "nsTextControlFrame.h"
#include "nsIDOMDOMStringList.h"
#include "nsIDOMDragEvent.h"
#include "nsContentList.h"
#include "nsIDOMMutationEvent.h"
using namespace mozilla;
using namespace mozilla::dom;
nsIFrame*
NS_NewFileControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsFileControlFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsFileControlFrame)
nsFileControlFrame::nsFileControlFrame(nsStyleContext* aContext)
: nsBlockFrame(aContext)
{
AddStateBits(NS_BLOCK_FLOAT_MGR);
}
void
nsFileControlFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow)
{
nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
mMouseListener = new DnDListener(this);
}
void
nsFileControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
ENSURE_TRUE(mContent);
// Remove the events.
if (mContent) {
mContent->RemoveSystemEventListener(NS_LITERAL_STRING("drop"),
mMouseListener, false);
mContent->RemoveSystemEventListener(NS_LITERAL_STRING("dragover"),
mMouseListener, false);
}
nsContentUtils::DestroyAnonymousContent(&mTextContent);
nsContentUtils::DestroyAnonymousContent(&mBrowse);
mMouseListener->ForgetFrame();
nsBlockFrame::DestroyFrom(aDestructRoot);
}
nsresult
nsFileControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
{
nsCOMPtr<nsIDocument> doc = mContent->GetDocument();
nsCOMPtr<nsINodeInfo> nodeInfo;
// Create and setup the file picking button.
nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::button, nullptr,
kNameSpaceID_XHTML,
nsIDOMNode::ELEMENT_NODE);
NS_NewHTMLElement(getter_AddRefs(mBrowse), nodeInfo.forget(),
dom::NOT_FROM_PARSER);
// NOTE: SetNativeAnonymous() has to be called before setting any attribute.
mBrowse->SetNativeAnonymous();
mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
NS_LITERAL_STRING("button"), false);
// Set the file picking button text depending on the current locale.
nsXPIDLString buttonTxt;
nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
"Browse", buttonTxt);
// Set the browse button text. It's a bit of a pain to do because we want to
// make sure we are not notifying.
nsCOMPtr<nsIContent> textContent;
nsresult rv = NS_NewTextNode(getter_AddRefs(textContent),
mBrowse->NodeInfo()->NodeInfoManager());
NS_ENSURE_SUCCESS(rv, rv);
textContent->SetText(buttonTxt, false);
rv = mBrowse->AppendChildTo(textContent, false);
NS_ENSURE_SUCCESS(rv, rv);
// Make sure access key and tab order for the element actually redirect to the
// file picking button.
nsCOMPtr<nsIDOMHTMLInputElement> fileContent = do_QueryInterface(mContent);
nsCOMPtr<nsIDOMHTMLButtonElement> browseControl = do_QueryInterface(mBrowse);
nsAutoString accessKey;
fileContent->GetAccessKey(accessKey);
browseControl->SetAccessKey(accessKey);
int32_t tabIndex;
fileContent->GetTabIndex(&tabIndex);
browseControl->SetTabIndex(tabIndex);
if (!aElements.AppendElement(mBrowse)) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Create and setup the text showing the selected files.
nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::label, nullptr,
kNameSpaceID_XUL,
nsIDOMNode::ELEMENT_NODE);
NS_TrustedNewXULElement(getter_AddRefs(mTextContent), nodeInfo.forget());
// NOTE: SetNativeAnonymous() has to be called before setting any attribute.
mTextContent->SetNativeAnonymous();
mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::crop,
NS_LITERAL_STRING("center"), false);
// Update the displayed text to reflect the current element's value.
nsAutoString value;
HTMLInputElement::FromContent(mContent)->GetDisplayFileName(value);
UpdateDisplayedValue(value, false);
if (!aElements.AppendElement(mTextContent)) {
return NS_ERROR_OUT_OF_MEMORY;
}
// We should be able to interact with the element by doing drag and drop.
mContent->AddSystemEventListener(NS_LITERAL_STRING("drop"),
mMouseListener, false);
mContent->AddSystemEventListener(NS_LITERAL_STRING("dragover"),
mMouseListener, false);
SyncDisabledState();
return NS_OK;
}
void
nsFileControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
uint32_t aFilter)
{
aElements.MaybeAppendElement(mBrowse);
aElements.MaybeAppendElement(mTextContent);
}
NS_QUERYFRAME_HEAD(nsFileControlFrame)
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
void
nsFileControlFrame::SetFocus(bool aOn, bool aRepaint)
{
}
/**
* This is called when we receive a drop or a dragover.
*/
NS_IMETHODIMP
nsFileControlFrame::DnDListener::HandleEvent(nsIDOMEvent* aEvent)
{
NS_ASSERTION(mFrame, "We should have been unregistered");
bool defaultPrevented = false;
aEvent->GetPreventDefault(&defaultPrevented);
if (defaultPrevented) {
return NS_OK;
}
nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent);
if (!dragEvent || !IsValidDropData(dragEvent)) {
return NS_OK;
}
nsAutoString eventType;
aEvent->GetType(eventType);
if (eventType.EqualsLiteral("dragover")) {
// Prevent default if we can accept this drag data
aEvent->PreventDefault();
return NS_OK;
}
if (eventType.EqualsLiteral("drop")) {
aEvent->StopPropagation();
aEvent->PreventDefault();
nsIContent* content = mFrame->GetContent();
NS_ASSERTION(content, "The frame has no content???");
HTMLInputElement* inputElement = HTMLInputElement::FromContent(content);
NS_ASSERTION(inputElement, "No input element for this file upload control frame!");
nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
dragEvent->GetDataTransfer(getter_AddRefs(dataTransfer));
nsCOMPtr<nsIDOMFileList> fileList;
dataTransfer->GetFiles(getter_AddRefs(fileList));
inputElement->SetFiles(fileList, true);
nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content,
NS_LITERAL_STRING("change"), true,
false);
}
return NS_OK;
}
/* static */ bool
nsFileControlFrame::DnDListener::IsValidDropData(nsIDOMDragEvent* aEvent)
{
nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
aEvent->GetDataTransfer(getter_AddRefs(dataTransfer));
NS_ENSURE_TRUE(dataTransfer, false);
nsCOMPtr<nsIDOMDOMStringList> types;
dataTransfer->GetTypes(getter_AddRefs(types));
NS_ENSURE_TRUE(types, false);
// We only support dropping files onto a file upload control
bool typeSupported;
types->Contains(NS_LITERAL_STRING("Files"), &typeSupported);
return typeSupported;
}
nscoord
nsFileControlFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
{
nscoord result;
DISPLAY_MIN_WIDTH(this, result);
// Our min width is our pref width
result = GetPrefWidth(aRenderingContext);
return result;
}
void
nsFileControlFrame::SyncDisabledState()
{
nsEventStates eventStates = mContent->AsElement()->State();
if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
true);
} else {
mBrowse->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
}
}
NS_IMETHODIMP
nsFileControlFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::tabindex) {
if (aModType == nsIDOMMutationEvent::REMOVAL) {
mBrowse->UnsetAttr(aNameSpaceID, aAttribute, true);
} else {
nsAutoString value;
mContent->GetAttr(aNameSpaceID, aAttribute, value);
mBrowse->SetAttr(aNameSpaceID, aAttribute, value, true);
}
}
return nsBlockFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
}
void
nsFileControlFrame::ContentStatesChanged(nsEventStates aStates)
{
if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
}
}
#ifdef DEBUG
NS_IMETHODIMP
nsFileControlFrame::GetFrameName(nsAString& aResult) const
{
return MakeFrameName(NS_LITERAL_STRING("FileControl"), aResult);
}
#endif
void
nsFileControlFrame::UpdateDisplayedValue(const nsAString& aValue, bool aNotify)
{
mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue, aNotify);
}
nsresult
nsFileControlFrame::SetFormProperty(nsIAtom* aName,
const nsAString& aValue)
{
if (nsGkAtoms::value == aName) {
UpdateDisplayedValue(aValue, true);
}
return NS_OK;
}
void
nsFileControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
// box-shadow
if (StyleBorder()->mBoxShadow) {
aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
nsDisplayBoxShadowOuter(aBuilder, this));
}
// Clip height only
nsRect clipRect(aBuilder->ToReferenceFrame(this), GetSize());
clipRect.width = GetVisualOverflowRect().XMost();
nsDisplayListCollection tempList;
{
DisplayListClipState::AutoSaveRestore saveClipState(aBuilder->ClipState());
DisplayItemClip clipOnStack;
aBuilder->ClipState().ClipContainingBlockDescendants(clipRect, nullptr, clipOnStack);
// Our background is inherited to the text input, and we don't really want to
// paint it or out padding and borders (which we never have anyway, per
// styles in forms.css) -- doing it just makes us look ugly in some cases and
// has no effect in others.
nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, tempList);
}
tempList.BorderBackground()->DeleteAll();
tempList.MoveTo(aLists);
// Disabled file controls don't pass mouse events to their children, so we
// put an invisible item in the display list above the children
// just to catch events
nsEventStates eventStates = mContent->AsElement()->State();
if (eventStates.HasState(NS_EVENT_STATE_DISABLED) && IsVisibleForPainting(aBuilder)) {
aLists.Content()->AppendNewToTop(
new (aBuilder) nsDisplayEventReceiver(aBuilder, this));
}
DisplaySelectionOverlay(aBuilder, aLists.Content());
}
#ifdef ACCESSIBILITY
a11y::AccType
nsFileControlFrame::AccessibleType()
{
return a11y::eHTMLFileInputType;
}
#endif
////////////////////////////////////////////////////////////
// Mouse listener implementation
NS_IMPL_ISUPPORTS1(nsFileControlFrame::MouseListener,
nsIDOMEventListener)