forked from mirrors/gecko-dev
		
	 613ea8adbd
			
		
	
	
		613ea8adbd
		
	
	
	
	
		
			
			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
		
			
				
	
	
		
			399 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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)
 |