1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Mats Palmgren <matspal@gmail.com>
24 : * Geoff Lankow <geoff@darktrojan.net>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsFileControlFrame.h"
41 :
42 : #include "nsIContent.h"
43 : #include "prtypes.h"
44 : #include "nsIAtom.h"
45 : #include "nsPresContext.h"
46 : #include "nsGkAtoms.h"
47 : #include "nsWidgetsCID.h"
48 : #include "nsIComponentManager.h"
49 : #include "nsHTMLParts.h"
50 : #include "nsIDOMHTMLInputElement.h"
51 : #include "nsIFormControl.h"
52 : #include "nsINameSpaceManager.h"
53 : #include "nsCOMPtr.h"
54 : #include "nsIDOMElement.h"
55 : #include "nsIDOMDocument.h"
56 : #include "nsIDocument.h"
57 : #include "nsIPresShell.h"
58 : #include "nsXPCOM.h"
59 : #include "nsISupportsPrimitives.h"
60 : #include "nsPIDOMWindow.h"
61 : #include "nsIFilePicker.h"
62 : #include "nsIDOMMouseEvent.h"
63 : #include "nsINodeInfo.h"
64 : #include "nsIDOMEventTarget.h"
65 : #include "nsILocalFile.h"
66 : #include "nsHTMLInputElement.h"
67 : #include "nsNodeInfoManager.h"
68 : #include "nsContentCreatorFunctions.h"
69 : #include "nsContentUtils.h"
70 : #include "nsDisplayList.h"
71 : #include "nsIDOMNSEvent.h"
72 : #include "nsEventListenerManager.h"
73 : #ifdef ACCESSIBILITY
74 : #include "nsAccessibilityService.h"
75 : #endif
76 :
77 : #include "nsInterfaceHashtable.h"
78 : #include "nsURIHashKey.h"
79 : #include "nsNetCID.h"
80 : #include "nsWeakReference.h"
81 : #include "nsIVariant.h"
82 : #include "mozilla/Services.h"
83 : #include "nsDirectoryServiceDefs.h"
84 : #include "nsICapturePicker.h"
85 : #include "nsIFileURL.h"
86 : #include "nsDOMFile.h"
87 : #include "nsEventStates.h"
88 : #include "nsTextControlFrame.h"
89 :
90 : #include "nsIDOMDOMStringList.h"
91 : #include "nsIDOMDragEvent.h"
92 :
93 : namespace dom = mozilla::dom;
94 :
95 : #define SYNC_TEXT 0x1
96 : #define SYNC_BUTTON 0x2
97 :
98 : nsIFrame*
99 0 : NS_NewFileControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
100 : {
101 0 : return new (aPresShell) nsFileControlFrame(aContext);
102 : }
103 :
104 0 : NS_IMPL_FRAMEARENA_HELPERS(nsFileControlFrame)
105 :
106 0 : nsFileControlFrame::nsFileControlFrame(nsStyleContext* aContext):
107 0 : nsBlockFrame(aContext)
108 : {
109 0 : AddStateBits(NS_BLOCK_FLOAT_MGR);
110 0 : }
111 :
112 :
113 : NS_IMETHODIMP
114 0 : nsFileControlFrame::Init(nsIContent* aContent,
115 : nsIFrame* aParent,
116 : nsIFrame* aPrevInFlow)
117 : {
118 0 : nsresult rv = nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
119 0 : NS_ENSURE_SUCCESS(rv, rv);
120 :
121 0 : mMouseListener = new BrowseMouseListener(this);
122 0 : NS_ENSURE_TRUE(mMouseListener, NS_ERROR_OUT_OF_MEMORY);
123 0 : mCaptureMouseListener = new CaptureMouseListener(this);
124 0 : NS_ENSURE_TRUE(mCaptureMouseListener, NS_ERROR_OUT_OF_MEMORY);
125 :
126 0 : return rv;
127 : }
128 :
129 : void
130 0 : nsFileControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
131 : {
132 0 : ENSURE_TRUE(mContent);
133 :
134 : // Remove the drag events
135 0 : if (mContent) {
136 0 : mContent->RemoveSystemEventListener(NS_LITERAL_STRING("drop"),
137 0 : mMouseListener, false);
138 0 : mContent->RemoveSystemEventListener(NS_LITERAL_STRING("dragover"),
139 0 : mMouseListener, false);
140 : }
141 :
142 : // remove mMouseListener as a mouse event listener (bug 40533, bug 355931)
143 0 : nsContentUtils::DestroyAnonymousContent(&mCapture);
144 :
145 0 : if (mBrowse) {
146 0 : mBrowse->RemoveSystemEventListener(NS_LITERAL_STRING("click"),
147 0 : mMouseListener, false);
148 : }
149 0 : nsContentUtils::DestroyAnonymousContent(&mBrowse);
150 :
151 0 : if (mTextContent) {
152 0 : mTextContent->RemoveSystemEventListener(NS_LITERAL_STRING("click"),
153 0 : mMouseListener, false);
154 : }
155 0 : nsContentUtils::DestroyAnonymousContent(&mTextContent);
156 :
157 0 : mCaptureMouseListener->ForgetFrame();
158 0 : mMouseListener->ForgetFrame();
159 0 : nsBlockFrame::DestroyFrom(aDestructRoot);
160 : }
161 :
162 : nsresult
163 0 : nsFileControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
164 : {
165 : // Get the NodeInfoManager and tag necessary to create input elements
166 0 : nsCOMPtr<nsIDocument> doc = mContent->GetDocument();
167 :
168 0 : nsCOMPtr<nsINodeInfo> nodeInfo;
169 : nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::input, nsnull,
170 : kNameSpaceID_XHTML,
171 0 : nsIDOMNode::ELEMENT_NODE);
172 :
173 : // Create the text content
174 0 : NS_NewHTMLElement(getter_AddRefs(mTextContent), nodeInfo.forget(),
175 0 : dom::NOT_FROM_PARSER);
176 0 : if (!mTextContent)
177 0 : return NS_ERROR_OUT_OF_MEMORY;
178 :
179 : // Mark the element to be native anonymous before setting any attributes.
180 0 : mTextContent->SetNativeAnonymous();
181 :
182 : mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
183 0 : NS_LITERAL_STRING("text"), false);
184 :
185 : nsHTMLInputElement* inputElement =
186 0 : nsHTMLInputElement::FromContent(mContent);
187 0 : NS_ASSERTION(inputElement, "Why is our content not a <input>?");
188 :
189 : // Initialize value when we create the content in case the value was set
190 : // before we got here
191 0 : nsAutoString value;
192 0 : inputElement->GetDisplayFileName(value);
193 :
194 0 : nsCOMPtr<nsIDOMHTMLInputElement> textControl = do_QueryInterface(mTextContent);
195 0 : NS_ASSERTION(textControl, "Why is the <input> we created not a <input>?");
196 0 : textControl->SetValue(value);
197 :
198 0 : textControl->SetTabIndex(-1);
199 0 : textControl->SetReadOnly(true);
200 :
201 0 : if (!aElements.AppendElement(mTextContent))
202 0 : return NS_ERROR_OUT_OF_MEMORY;
203 :
204 : // Register the whole frame as an event listener of drag events
205 0 : mContent->AddSystemEventListener(NS_LITERAL_STRING("drop"),
206 0 : mMouseListener, false);
207 0 : mContent->AddSystemEventListener(NS_LITERAL_STRING("dragover"),
208 0 : mMouseListener, false);
209 :
210 : // Register as an event listener of the textbox
211 : // to open file dialog on mouse click
212 0 : mTextContent->AddSystemEventListener(NS_LITERAL_STRING("click"),
213 0 : mMouseListener, false);
214 :
215 : // Create the browse button
216 : nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::input, nsnull,
217 : kNameSpaceID_XHTML,
218 0 : nsIDOMNode::ELEMENT_NODE);
219 0 : NS_NewHTMLElement(getter_AddRefs(mBrowse), nodeInfo.forget(),
220 0 : dom::NOT_FROM_PARSER);
221 0 : if (!mBrowse)
222 0 : return NS_ERROR_OUT_OF_MEMORY;
223 :
224 : // Mark the element to be native anonymous before setting any attributes.
225 0 : mBrowse->SetNativeAnonymous();
226 :
227 : mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
228 0 : NS_LITERAL_STRING("button"), false);
229 :
230 : // Create the capture button
231 0 : nsCOMPtr<nsICapturePicker> capturePicker;
232 0 : capturePicker = do_GetService("@mozilla.org/capturepicker;1");
233 0 : if (capturePicker) {
234 : CaptureCallbackData data;
235 0 : data.picker = capturePicker;
236 0 : data.mode = GetCaptureMode(data);
237 :
238 0 : if (data.mode != 0) {
239 0 : mCaptureMouseListener->mMode = data.mode;
240 : nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::input, nsnull,
241 : kNameSpaceID_XHTML,
242 0 : nsIDOMNode::ELEMENT_NODE);
243 0 : NS_NewHTMLElement(getter_AddRefs(mCapture), nodeInfo.forget(),
244 0 : dom::NOT_FROM_PARSER);
245 0 : if (!mCapture)
246 0 : return NS_ERROR_OUT_OF_MEMORY;
247 :
248 : // Mark the element to be native anonymous before setting any attributes.
249 0 : mCapture->SetNativeAnonymous();
250 :
251 : mCapture->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
252 0 : NS_LITERAL_STRING("button"), false);
253 :
254 : mCapture->SetAttr(kNameSpaceID_None, nsGkAtoms::value,
255 0 : NS_LITERAL_STRING("capture"), false);
256 :
257 0 : mCapture->AddSystemEventListener(NS_LITERAL_STRING("click"),
258 0 : mCaptureMouseListener, false);
259 : }
260 : }
261 0 : nsCOMPtr<nsIDOMHTMLInputElement> fileContent = do_QueryInterface(mContent);
262 0 : nsCOMPtr<nsIDOMHTMLInputElement> browseControl = do_QueryInterface(mBrowse);
263 0 : if (fileContent && browseControl) {
264 : PRInt32 tabIndex;
265 0 : nsAutoString accessKey;
266 :
267 0 : fileContent->GetAccessKey(accessKey);
268 0 : browseControl->SetAccessKey(accessKey);
269 0 : fileContent->GetTabIndex(&tabIndex);
270 0 : browseControl->SetTabIndex(tabIndex);
271 : }
272 :
273 0 : if (!aElements.AppendElement(mBrowse))
274 0 : return NS_ERROR_OUT_OF_MEMORY;
275 :
276 0 : if (mCapture && !aElements.AppendElement(mCapture))
277 0 : return NS_ERROR_OUT_OF_MEMORY;
278 :
279 : // Register as an event listener of the button
280 : // to open file dialog on mouse click
281 0 : mBrowse->AddSystemEventListener(NS_LITERAL_STRING("click"),
282 0 : mMouseListener, false);
283 :
284 0 : SyncAttr(kNameSpaceID_None, nsGkAtoms::size, SYNC_TEXT);
285 0 : SyncDisabledState();
286 :
287 0 : return NS_OK;
288 : }
289 :
290 : void
291 0 : nsFileControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
292 : PRUint32 aFilter)
293 : {
294 0 : aElements.MaybeAppendElement(mTextContent);
295 0 : aElements.MaybeAppendElement(mBrowse);
296 0 : aElements.MaybeAppendElement(mCapture);
297 0 : }
298 :
299 0 : NS_QUERYFRAME_HEAD(nsFileControlFrame)
300 0 : NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
301 0 : NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
302 0 : NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
303 :
304 : void
305 0 : nsFileControlFrame::SetFocus(bool aOn, bool aRepaint)
306 : {
307 0 : }
308 :
309 0 : bool ShouldProcessMouseClick(nsIDOMEvent* aMouseEvent)
310 : {
311 : // only allow the left button
312 0 : nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
313 0 : nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aMouseEvent);
314 0 : NS_ENSURE_TRUE(mouseEvent && domNSEvent, false);
315 0 : bool defaultPrevented = false;
316 0 : domNSEvent->GetPreventDefault(&defaultPrevented);
317 0 : if (defaultPrevented) {
318 0 : return false;
319 : }
320 :
321 : PRUint16 whichButton;
322 0 : if (NS_FAILED(mouseEvent->GetButton(&whichButton)) || whichButton != 0) {
323 0 : return false;
324 : }
325 :
326 : PRInt32 clickCount;
327 0 : if (NS_FAILED(mouseEvent->GetDetail(&clickCount)) || clickCount > 1) {
328 0 : return false;
329 : }
330 :
331 0 : return true;
332 : }
333 :
334 : /**
335 : * This is called when our capture button is clicked
336 : */
337 : NS_IMETHODIMP
338 0 : nsFileControlFrame::CaptureMouseListener::HandleEvent(nsIDOMEvent* aMouseEvent)
339 : {
340 : nsresult rv;
341 :
342 0 : NS_ASSERTION(mFrame, "We should have been unregistered");
343 0 : if (!ShouldProcessMouseClick(aMouseEvent))
344 0 : return NS_OK;
345 :
346 : // Get parent nsPIDOMWindow object.
347 0 : nsIContent* content = mFrame->GetContent();
348 0 : if (!content)
349 0 : return NS_ERROR_FAILURE;
350 :
351 0 : nsHTMLInputElement* inputElement = nsHTMLInputElement::FromContent(content);
352 0 : if (!inputElement)
353 0 : return NS_ERROR_FAILURE;
354 :
355 0 : nsCOMPtr<nsIDocument> doc = content->GetDocument();
356 0 : if (!doc)
357 0 : return NS_ERROR_FAILURE;
358 :
359 : // Get Loc title
360 0 : nsXPIDLString title;
361 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
362 0 : "MediaUpload", title);
363 :
364 0 : nsPIDOMWindow* win = doc->GetWindow();
365 0 : if (!win) {
366 0 : return NS_ERROR_FAILURE;
367 : }
368 :
369 0 : nsCOMPtr<nsICapturePicker> capturePicker;
370 0 : capturePicker = do_CreateInstance("@mozilla.org/capturepicker;1");
371 0 : if (!capturePicker)
372 0 : return NS_ERROR_FAILURE;
373 :
374 0 : rv = capturePicker->Init(win, title, mMode);
375 0 : NS_ENSURE_SUCCESS(rv, rv);
376 :
377 : // Tell our text control frame to remember the currently focused value.
378 0 : nsTextControlFrame* textControlFrame = mFrame->GetTextControlFrame();
379 0 : textControlFrame->InitFocusedValue();
380 :
381 : // Show dialog
382 : PRUint32 result;
383 0 : rv = capturePicker->Show(&result);
384 0 : NS_ENSURE_SUCCESS(rv, rv);
385 0 : if (result == nsICapturePicker::RETURN_CANCEL)
386 0 : return NS_OK;
387 :
388 0 : if (!mFrame) {
389 : // The frame got destroyed while the filepicker was up. Don't do
390 : // anything here.
391 : // (This listener itself can't be destroyed because the event listener
392 : // manager holds a strong reference to us while it fires the event.)
393 0 : return NS_OK;
394 : }
395 :
396 0 : nsCOMPtr<nsIDOMFile> domFile;
397 0 : rv = capturePicker->GetFile(getter_AddRefs(domFile));
398 0 : NS_ENSURE_SUCCESS(rv, rv);
399 :
400 0 : nsCOMArray<nsIDOMFile> newFiles;
401 0 : if (domFile) {
402 0 : newFiles.AppendObject(domFile);
403 : } else {
404 0 : return NS_ERROR_FAILURE;
405 : }
406 :
407 : // XXXkhuey we really should have a better UI story than the tired old
408 : // uneditable text box with the file name inside.
409 : // Set new selected files
410 0 : if (newFiles.Count()) {
411 : // Tell our text control frame that this update of the value is a user
412 : // initiated change. Otherwise it'll think that the value is being set by
413 : // a script and not fire onchange when it should.
414 0 : bool oldState = textControlFrame->GetFireChangeEventState();
415 0 : textControlFrame->SetFireChangeEventState(true);
416 0 : inputElement->SetFiles(newFiles, true);
417 0 : textControlFrame->SetFireChangeEventState(oldState);
418 :
419 : // May need to fire an onchange here
420 0 : textControlFrame->CheckFireOnChange();
421 : }
422 :
423 0 : return NS_OK;
424 : }
425 :
426 : /**
427 : * This is called when we receive any registered events on the control.
428 : * We've only registered for drop, dragover and click events.
429 : */
430 : NS_IMETHODIMP
431 0 : nsFileControlFrame::BrowseMouseListener::HandleEvent(nsIDOMEvent* aEvent)
432 : {
433 0 : NS_ASSERTION(mFrame, "We should have been unregistered");
434 :
435 0 : nsAutoString eventType;
436 0 : aEvent->GetType(eventType);
437 0 : if (eventType.EqualsLiteral("click")) {
438 0 : if (!ShouldProcessMouseClick(aEvent))
439 0 : return NS_OK;
440 :
441 : nsHTMLInputElement* input =
442 0 : nsHTMLInputElement::FromContent(mFrame->GetContent());
443 0 : return input ? input->FireAsyncClickHandler() : NS_OK;
444 : }
445 :
446 0 : nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aEvent);
447 0 : NS_ENSURE_STATE(domNSEvent);
448 0 : bool defaultPrevented = false;
449 0 : domNSEvent->GetPreventDefault(&defaultPrevented);
450 0 : if (defaultPrevented) {
451 0 : return NS_OK;
452 : }
453 :
454 0 : nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent);
455 0 : if (!dragEvent || !IsValidDropData(dragEvent)) {
456 0 : return NS_OK;
457 : }
458 :
459 0 : if (eventType.EqualsLiteral("dragover")) {
460 : // Prevent default if we can accept this drag data
461 0 : aEvent->PreventDefault();
462 0 : return NS_OK;
463 : }
464 :
465 0 : if (eventType.EqualsLiteral("drop")) {
466 0 : aEvent->StopPropagation();
467 0 : aEvent->PreventDefault();
468 :
469 0 : nsIContent* content = mFrame->GetContent();
470 0 : NS_ASSERTION(content, "The frame has no content???");
471 :
472 0 : nsHTMLInputElement* inputElement = nsHTMLInputElement::FromContent(content);
473 0 : NS_ASSERTION(inputElement, "No input element for this file upload control frame!");
474 :
475 0 : nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
476 0 : dragEvent->GetDataTransfer(getter_AddRefs(dataTransfer));
477 :
478 0 : nsCOMPtr<nsIDOMFileList> fileList;
479 0 : dataTransfer->GetFiles(getter_AddRefs(fileList));
480 :
481 0 : nsTextControlFrame* textControlFrame = mFrame->GetTextControlFrame();
482 0 : bool oldState = textControlFrame->GetFireChangeEventState();
483 0 : textControlFrame->SetFireChangeEventState(true);
484 0 : inputElement->SetFiles(fileList, true);
485 0 : textControlFrame->SetFireChangeEventState(oldState);
486 0 : textControlFrame->CheckFireOnChange();
487 : }
488 :
489 0 : return NS_OK;
490 : }
491 :
492 : /* static */ bool
493 0 : nsFileControlFrame::BrowseMouseListener::IsValidDropData(nsIDOMDragEvent* aEvent)
494 : {
495 0 : nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
496 0 : aEvent->GetDataTransfer(getter_AddRefs(dataTransfer));
497 0 : NS_ENSURE_TRUE(dataTransfer, false);
498 :
499 0 : nsCOMPtr<nsIDOMDOMStringList> types;
500 0 : dataTransfer->GetTypes(getter_AddRefs(types));
501 0 : NS_ENSURE_TRUE(types, false);
502 :
503 : // We only support dropping files onto a file upload control
504 : bool typeSupported;
505 0 : types->Contains(NS_LITERAL_STRING("Files"), &typeSupported);
506 0 : return typeSupported;
507 : }
508 :
509 : nscoord
510 0 : nsFileControlFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
511 : {
512 : nscoord result;
513 0 : DISPLAY_MIN_WIDTH(this, result);
514 :
515 : // Our min width is our pref width
516 0 : result = GetPrefWidth(aRenderingContext);
517 0 : return result;
518 : }
519 :
520 : nsTextControlFrame*
521 0 : nsFileControlFrame::GetTextControlFrame()
522 : {
523 0 : nsITextControlFrame* tc = do_QueryFrame(mTextContent->GetPrimaryFrame());
524 0 : return static_cast<nsTextControlFrame*>(tc);
525 : }
526 :
527 : PRIntn
528 0 : nsFileControlFrame::GetSkipSides() const
529 : {
530 0 : return 0;
531 : }
532 :
533 : void
534 0 : nsFileControlFrame::SyncAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
535 : PRInt32 aWhichControls)
536 : {
537 0 : nsAutoString value;
538 0 : if (mContent->GetAttr(aNameSpaceID, aAttribute, value)) {
539 0 : if (aWhichControls & SYNC_TEXT && mTextContent) {
540 0 : mTextContent->SetAttr(aNameSpaceID, aAttribute, value, true);
541 : }
542 0 : if (aWhichControls & SYNC_BUTTON && mBrowse) {
543 0 : mBrowse->SetAttr(aNameSpaceID, aAttribute, value, true);
544 : }
545 : } else {
546 0 : if (aWhichControls & SYNC_TEXT && mTextContent) {
547 0 : mTextContent->UnsetAttr(aNameSpaceID, aAttribute, true);
548 : }
549 0 : if (aWhichControls & SYNC_BUTTON && mBrowse) {
550 0 : mBrowse->UnsetAttr(aNameSpaceID, aAttribute, true);
551 : }
552 : }
553 0 : }
554 :
555 : void
556 0 : nsFileControlFrame::SyncDisabledState()
557 : {
558 0 : nsEventStates eventStates = mContent->AsElement()->State();
559 0 : if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
560 0 : mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
561 0 : true);
562 0 : mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
563 0 : true);
564 : } else {
565 0 : mTextContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
566 0 : mBrowse->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
567 : }
568 0 : }
569 :
570 : NS_IMETHODIMP
571 0 : nsFileControlFrame::AttributeChanged(PRInt32 aNameSpaceID,
572 : nsIAtom* aAttribute,
573 : PRInt32 aModType)
574 : {
575 0 : if (aNameSpaceID == kNameSpaceID_None) {
576 0 : if (aAttribute == nsGkAtoms::size) {
577 0 : SyncAttr(aNameSpaceID, aAttribute, SYNC_TEXT);
578 0 : } else if (aAttribute == nsGkAtoms::tabindex) {
579 0 : SyncAttr(aNameSpaceID, aAttribute, SYNC_BUTTON);
580 : }
581 : }
582 :
583 0 : return nsBlockFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
584 : }
585 :
586 : void
587 0 : nsFileControlFrame::ContentStatesChanged(nsEventStates aStates)
588 : {
589 0 : if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
590 0 : nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
591 : }
592 0 : }
593 :
594 : bool
595 0 : nsFileControlFrame::IsLeaf() const
596 : {
597 0 : return true;
598 : }
599 :
600 : #ifdef NS_DEBUG
601 : NS_IMETHODIMP
602 0 : nsFileControlFrame::GetFrameName(nsAString& aResult) const
603 : {
604 0 : return MakeFrameName(NS_LITERAL_STRING("FileControl"), aResult);
605 : }
606 : #endif
607 :
608 : nsresult
609 0 : nsFileControlFrame::SetFormProperty(nsIAtom* aName,
610 : const nsAString& aValue)
611 : {
612 0 : if (nsGkAtoms::value == aName) {
613 : nsCOMPtr<nsIDOMHTMLInputElement> textControl =
614 0 : do_QueryInterface(mTextContent);
615 0 : NS_ASSERTION(textControl,
616 : "The text control should exist and be an input element");
617 0 : textControl->SetValue(aValue);
618 : }
619 0 : return NS_OK;
620 : }
621 :
622 : nsresult
623 0 : nsFileControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const
624 : {
625 0 : aValue.Truncate(); // initialize out param
626 :
627 0 : if (nsGkAtoms::value == aName) {
628 : nsHTMLInputElement* inputElement =
629 0 : nsHTMLInputElement::FromContent(mContent);
630 :
631 0 : if (inputElement) {
632 0 : inputElement->GetDisplayFileName(aValue);
633 : }
634 : }
635 0 : return NS_OK;
636 : }
637 :
638 : NS_IMETHODIMP
639 0 : nsFileControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
640 : const nsRect& aDirtyRect,
641 : const nsDisplayListSet& aLists)
642 : {
643 : // box-shadow
644 0 : if (GetStyleBorder()->mBoxShadow) {
645 : nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
646 0 : nsDisplayBoxShadowOuter(aBuilder, this));
647 0 : NS_ENSURE_SUCCESS(rv, rv);
648 : }
649 :
650 : // Our background is inherited to the text input, and we don't really want to
651 : // paint it or out padding and borders (which we never have anyway, per
652 : // styles in forms.css) -- doing it just makes us look ugly in some cases and
653 : // has no effect in others.
654 0 : nsDisplayListCollection tempList;
655 0 : nsresult rv = nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, tempList);
656 0 : if (NS_FAILED(rv))
657 0 : return rv;
658 :
659 0 : tempList.BorderBackground()->DeleteAll();
660 :
661 : // Clip height only
662 0 : nsRect clipRect(aBuilder->ToReferenceFrame(this), GetSize());
663 0 : clipRect.width = GetVisualOverflowRect().XMost();
664 0 : nscoord radii[8] = {0, 0, 0, 0, 0, 0, 0, 0};
665 0 : rv = OverflowClip(aBuilder, tempList, aLists, clipRect, radii);
666 0 : NS_ENSURE_SUCCESS(rv, rv);
667 :
668 : // Disabled file controls don't pass mouse events to their children, so we
669 : // put an invisible item in the display list above the children
670 : // just to catch events
671 0 : nsEventStates eventStates = mContent->AsElement()->State();
672 0 : if (eventStates.HasState(NS_EVENT_STATE_DISABLED) && IsVisibleForPainting(aBuilder)) {
673 : rv = aLists.Content()->AppendNewToTop(
674 0 : new (aBuilder) nsDisplayEventReceiver(aBuilder, this));
675 0 : if (NS_FAILED(rv))
676 0 : return rv;
677 : }
678 :
679 0 : return DisplaySelectionOverlay(aBuilder, aLists.Content());
680 : }
681 :
682 : #ifdef ACCESSIBILITY
683 : already_AddRefed<nsAccessible>
684 0 : nsFileControlFrame::CreateAccessible()
685 : {
686 : // Accessible object exists just to hold onto its children, for later shutdown
687 0 : nsAccessibilityService* accService = nsIPresShell::AccService();
688 0 : if (!accService)
689 0 : return nsnull;
690 :
691 : return accService->CreateHTMLFileInputAccessible(mContent,
692 0 : PresContext()->PresShell());
693 : }
694 : #endif
695 :
696 : PRUint32
697 0 : nsFileControlFrame::GetCaptureMode(const CaptureCallbackData& aData)
698 : {
699 0 : PRInt32 filters = nsHTMLInputElement::FromContent(mContent)->GetFilterFromAccept();
700 : nsresult rv;
701 : bool captureEnabled;
702 :
703 0 : if (filters == nsIFilePicker::filterImages) {
704 : rv = aData.picker->ModeMayBeAvailable(nsICapturePicker::MODE_STILL,
705 0 : &captureEnabled);
706 0 : NS_ENSURE_SUCCESS(rv, 0);
707 0 : if (captureEnabled) {
708 0 : return nsICapturePicker::MODE_STILL;
709 : }
710 0 : return 0;
711 : }
712 :
713 0 : if (filters == nsIFilePicker::filterAudio) {
714 : rv = aData.picker->ModeMayBeAvailable(nsICapturePicker::MODE_AUDIO_CLIP,
715 0 : &captureEnabled);
716 0 : NS_ENSURE_SUCCESS(rv, 0);
717 0 : if (captureEnabled) {
718 0 : return nsICapturePicker::MODE_AUDIO_CLIP;
719 : }
720 0 : return 0;
721 : }
722 :
723 0 : if (filters == nsIFilePicker::filterVideo) {
724 : rv = aData.picker->ModeMayBeAvailable(nsICapturePicker::MODE_VIDEO_CLIP,
725 0 : &captureEnabled);
726 0 : NS_ENSURE_SUCCESS(rv, 0);
727 0 : if (captureEnabled) {
728 0 : return nsICapturePicker::MODE_VIDEO_CLIP;
729 : }
730 : rv = aData.picker->ModeMayBeAvailable(nsICapturePicker::MODE_VIDEO_NO_SOUND_CLIP,
731 0 : &captureEnabled);
732 0 : NS_ENSURE_SUCCESS(rv, 0);
733 0 : if (captureEnabled) {
734 0 : return nsICapturePicker::MODE_VIDEO_NO_SOUND_CLIP;
735 : }
736 0 : return 0;
737 : }
738 :
739 0 : return 0;
740 : }
741 : ////////////////////////////////////////////////////////////
742 : // Mouse listener implementation
743 :
744 0 : NS_IMPL_ISUPPORTS1(nsFileControlFrame::MouseListener,
745 : nsIDOMEventListener)
|