Bumping manifests a=b2g-bump
[gecko.git] / layout / forms / nsFileControlFrame.cpp
blobff4a2345834761a6ecda2a9a250f7b33cd2e02e9
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsFileControlFrame.h"
8 #include "nsGkAtoms.h"
9 #include "nsCOMPtr.h"
10 #include "nsIDocument.h"
11 #include "mozilla/dom/NodeInfo.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/dom/DataTransfer.h"
14 #include "mozilla/dom/HTMLButtonElement.h"
15 #include "mozilla/dom/HTMLInputElement.h"
16 #include "nsNodeInfoManager.h"
17 #include "nsContentCreatorFunctions.h"
18 #include "nsContentUtils.h"
19 #include "mozilla/EventStates.h"
20 #include "mozilla/dom/DOMStringList.h"
21 #include "nsIDOMDragEvent.h"
22 #include "nsIDOMFileList.h"
23 #include "nsContentList.h"
24 #include "nsIDOMMutationEvent.h"
25 #include "nsTextNode.h"
27 using namespace mozilla;
28 using namespace mozilla::dom;
30 nsIFrame*
31 NS_NewFileControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
33 return new (aPresShell) nsFileControlFrame(aContext);
36 NS_IMPL_FRAMEARENA_HELPERS(nsFileControlFrame)
38 nsFileControlFrame::nsFileControlFrame(nsStyleContext* aContext)
39 : nsBlockFrame(aContext)
41 AddStateBits(NS_BLOCK_FLOAT_MGR);
45 void
46 nsFileControlFrame::Init(nsIContent* aContent,
47 nsContainerFrame* aParent,
48 nsIFrame* aPrevInFlow)
50 nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
52 mMouseListener = new DnDListener(this);
55 void
56 nsFileControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
58 ENSURE_TRUE(mContent);
60 // Remove the events.
61 if (mContent) {
62 mContent->RemoveSystemEventListener(NS_LITERAL_STRING("drop"),
63 mMouseListener, false);
64 mContent->RemoveSystemEventListener(NS_LITERAL_STRING("dragover"),
65 mMouseListener, false);
68 nsContentUtils::DestroyAnonymousContent(&mTextContent);
69 nsContentUtils::DestroyAnonymousContent(&mBrowse);
71 mMouseListener->ForgetFrame();
72 nsBlockFrame::DestroyFrom(aDestructRoot);
75 nsresult
76 nsFileControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
78 nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
80 // Create and setup the file picking button.
81 mBrowse = doc->CreateHTMLElement(nsGkAtoms::button);
82 // NOTE: SetIsNativeAnonymousRoot() has to be called before setting any
83 // attribute.
84 mBrowse->SetIsNativeAnonymousRoot();
85 mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
86 NS_LITERAL_STRING("button"), false);
88 // Set the file picking button text depending on the current locale.
89 nsXPIDLString buttonTxt;
90 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
91 "Browse", buttonTxt);
93 // Set the browse button text. It's a bit of a pain to do because we want to
94 // make sure we are not notifying.
95 nsRefPtr<nsTextNode> textContent =
96 new nsTextNode(mBrowse->NodeInfo()->NodeInfoManager());
98 textContent->SetText(buttonTxt, false);
100 nsresult rv = mBrowse->AppendChildTo(textContent, false);
101 NS_ENSURE_SUCCESS(rv, rv);
103 // Make sure access key and tab order for the element actually redirect to the
104 // file picking button.
105 nsRefPtr<HTMLInputElement> fileContent = HTMLInputElement::FromContentOrNull(mContent);
106 nsRefPtr<HTMLButtonElement> browseControl = HTMLButtonElement::FromContentOrNull(mBrowse);
108 nsAutoString accessKey;
109 fileContent->GetAccessKey(accessKey);
110 browseControl->SetAccessKey(accessKey);
112 int32_t tabIndex;
113 fileContent->GetTabIndex(&tabIndex);
114 browseControl->SetTabIndex(tabIndex);
116 if (!aElements.AppendElement(mBrowse)) {
117 return NS_ERROR_OUT_OF_MEMORY;
120 // Create and setup the text showing the selected files.
121 nsRefPtr<NodeInfo> nodeInfo;
122 nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::label, nullptr,
123 kNameSpaceID_XUL,
124 nsIDOMNode::ELEMENT_NODE);
125 NS_TrustedNewXULElement(getter_AddRefs(mTextContent), nodeInfo.forget());
126 // NOTE: SetIsNativeAnonymousRoot() has to be called before setting any
127 // attribute.
128 mTextContent->SetIsNativeAnonymousRoot();
129 mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::crop,
130 NS_LITERAL_STRING("center"), false);
132 // Update the displayed text to reflect the current element's value.
133 nsAutoString value;
134 HTMLInputElement::FromContent(mContent)->GetDisplayFileName(value);
135 UpdateDisplayedValue(value, false);
137 if (!aElements.AppendElement(mTextContent)) {
138 return NS_ERROR_OUT_OF_MEMORY;
141 // We should be able to interact with the element by doing drag and drop.
142 mContent->AddSystemEventListener(NS_LITERAL_STRING("drop"),
143 mMouseListener, false);
144 mContent->AddSystemEventListener(NS_LITERAL_STRING("dragover"),
145 mMouseListener, false);
147 SyncDisabledState();
149 return NS_OK;
152 void
153 nsFileControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
154 uint32_t aFilter)
156 if (mBrowse) {
157 aElements.AppendElement(mBrowse);
160 if (mTextContent) {
161 aElements.AppendElement(mTextContent);
165 NS_QUERYFRAME_HEAD(nsFileControlFrame)
166 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
167 NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
168 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
170 void
171 nsFileControlFrame::SetFocus(bool aOn, bool aRepaint)
176 * This is called when we receive a drop or a dragover.
178 NS_IMETHODIMP
179 nsFileControlFrame::DnDListener::HandleEvent(nsIDOMEvent* aEvent)
181 NS_ASSERTION(mFrame, "We should have been unregistered");
183 bool defaultPrevented = false;
184 aEvent->GetDefaultPrevented(&defaultPrevented);
185 if (defaultPrevented) {
186 return NS_OK;
189 nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent);
190 if (!dragEvent) {
191 return NS_OK;
194 nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
195 dragEvent->GetDataTransfer(getter_AddRefs(dataTransfer));
196 if (!IsValidDropData(dataTransfer)) {
197 return NS_OK;
201 nsIContent* content = mFrame->GetContent();
202 bool supportsMultiple = content && content->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple);
203 if (!CanDropTheseFiles(dataTransfer, supportsMultiple)) {
204 dataTransfer->SetDropEffect(NS_LITERAL_STRING("none"));
205 aEvent->StopPropagation();
206 return NS_OK;
209 nsAutoString eventType;
210 aEvent->GetType(eventType);
211 if (eventType.EqualsLiteral("dragover")) {
212 // Prevent default if we can accept this drag data
213 aEvent->PreventDefault();
214 return NS_OK;
217 if (eventType.EqualsLiteral("drop")) {
218 aEvent->StopPropagation();
219 aEvent->PreventDefault();
221 NS_ASSERTION(content, "The frame has no content???");
223 HTMLInputElement* inputElement = HTMLInputElement::FromContent(content);
224 NS_ASSERTION(inputElement, "No input element for this file upload control frame!");
226 nsCOMPtr<nsIDOMFileList> fileList;
227 dataTransfer->GetFiles(getter_AddRefs(fileList));
229 inputElement->SetFiles(fileList, true);
230 nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content,
231 NS_LITERAL_STRING("change"), true,
232 false);
235 return NS_OK;
238 /* static */ bool
239 nsFileControlFrame::DnDListener::IsValidDropData(nsIDOMDataTransfer* aDOMDataTransfer)
241 nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(aDOMDataTransfer);
242 NS_ENSURE_TRUE(dataTransfer, false);
244 // We only support dropping files onto a file upload control
245 nsRefPtr<DOMStringList> types = dataTransfer->Types();
246 return types->Contains(NS_LITERAL_STRING("Files"));
249 /* static */ bool
250 nsFileControlFrame::DnDListener::CanDropTheseFiles(nsIDOMDataTransfer* aDOMDataTransfer,
251 bool aSupportsMultiple)
253 nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(aDOMDataTransfer);
254 NS_ENSURE_TRUE(dataTransfer, false);
256 nsCOMPtr<nsIDOMFileList> fileList;
257 dataTransfer->GetFiles(getter_AddRefs(fileList));
259 uint32_t listLength = 0;
260 if (fileList) {
261 fileList->GetLength(&listLength);
263 return listLength <= 1 || aSupportsMultiple;
266 nscoord
267 nsFileControlFrame::GetMinISize(nsRenderingContext *aRenderingContext)
269 nscoord result;
270 DISPLAY_MIN_WIDTH(this, result);
272 // Our min width is our pref width
273 result = GetPrefISize(aRenderingContext);
274 return result;
277 void
278 nsFileControlFrame::SyncDisabledState()
280 EventStates eventStates = mContent->AsElement()->State();
281 if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
282 mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
283 true);
284 } else {
285 mBrowse->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
289 nsresult
290 nsFileControlFrame::AttributeChanged(int32_t aNameSpaceID,
291 nsIAtom* aAttribute,
292 int32_t aModType)
294 if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::tabindex) {
295 if (aModType == nsIDOMMutationEvent::REMOVAL) {
296 mBrowse->UnsetAttr(aNameSpaceID, aAttribute, true);
297 } else {
298 nsAutoString value;
299 mContent->GetAttr(aNameSpaceID, aAttribute, value);
300 mBrowse->SetAttr(aNameSpaceID, aAttribute, value, true);
304 return nsBlockFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
307 void
308 nsFileControlFrame::ContentStatesChanged(EventStates aStates)
310 if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
311 nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
315 #ifdef DEBUG_FRAME_DUMP
316 nsresult
317 nsFileControlFrame::GetFrameName(nsAString& aResult) const
319 return MakeFrameName(NS_LITERAL_STRING("FileControl"), aResult);
321 #endif
323 void
324 nsFileControlFrame::UpdateDisplayedValue(const nsAString& aValue, bool aNotify)
326 mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue, aNotify);
329 nsresult
330 nsFileControlFrame::SetFormProperty(nsIAtom* aName,
331 const nsAString& aValue)
333 if (nsGkAtoms::value == aName) {
334 UpdateDisplayedValue(aValue, true);
336 return NS_OK;
339 void
340 nsFileControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
341 const nsRect& aDirtyRect,
342 const nsDisplayListSet& aLists)
344 BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
347 #ifdef ACCESSIBILITY
348 a11y::AccType
349 nsFileControlFrame::AccessibleType()
351 return a11y::eHTMLFileInputType;
353 #endif
355 ////////////////////////////////////////////////////////////
356 // Mouse listener implementation
358 NS_IMPL_ISUPPORTS(nsFileControlFrame::MouseListener,
359 nsIDOMEventListener)