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 Communicator client 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 : * Pierre Phaneuf <pp@ludusdesign.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 "mozilla/Util.h"
41 :
42 : #include "nsHTMLInputElement.h"
43 :
44 : #include "nsIDOMHTMLInputElement.h"
45 : #include "nsITextControlElement.h"
46 : #include "nsIDOMNSEditableElement.h"
47 : #include "nsIRadioVisitor.h"
48 : #include "nsIPhonetic.h"
49 :
50 : #include "nsIControllers.h"
51 : #include "nsFocusManager.h"
52 : #include "nsPIDOMWindow.h"
53 : #include "nsContentCID.h"
54 : #include "nsIComponentManager.h"
55 : #include "nsIDOMHTMLFormElement.h"
56 : #include "nsGkAtoms.h"
57 : #include "nsStyleConsts.h"
58 : #include "nsPresContext.h"
59 : #include "nsMappedAttributes.h"
60 : #include "nsIFormControl.h"
61 : #include "nsIForm.h"
62 : #include "nsFormSubmission.h"
63 : #include "nsFormSubmissionConstants.h"
64 : #include "nsIDocument.h"
65 : #include "nsIPresShell.h"
66 : #include "nsIFormControlFrame.h"
67 : #include "nsITextControlFrame.h"
68 : #include "nsIFrame.h"
69 : #include "nsEventStates.h"
70 : #include "nsIServiceManager.h"
71 : #include "nsIScriptSecurityManager.h"
72 : #include "nsDOMError.h"
73 : #include "nsIPrivateDOMEvent.h"
74 : #include "nsIEditor.h"
75 : #include "nsGUIEvent.h"
76 : #include "nsIIOService.h"
77 : #include "nsDocument.h"
78 : #include "nsAttrValueOrString.h"
79 :
80 : #include "nsPresState.h"
81 : #include "nsLayoutErrors.h"
82 : #include "nsIDOMEvent.h"
83 : #include "nsIDOMNSEvent.h"
84 : #include "nsIDOMNodeList.h"
85 : #include "nsIDOMHTMLCollection.h"
86 : #include "nsLinebreakConverter.h" //to strip out carriage returns
87 : #include "nsReadableUtils.h"
88 : #include "nsUnicharUtils.h"
89 : #include "nsEventDispatcher.h"
90 : #include "nsLayoutUtils.h"
91 :
92 : #include "nsIDOMMutationEvent.h"
93 : #include "nsIDOMEventTarget.h"
94 : #include "nsMutationEvent.h"
95 : #include "nsEventListenerManager.h"
96 :
97 : #include "nsRuleData.h"
98 :
99 : // input type=radio
100 : #include "nsIRadioGroupContainer.h"
101 :
102 : // input type=file
103 : #include "nsILocalFile.h"
104 : #include "nsNetUtil.h"
105 : #include "nsDOMFile.h"
106 : #include "nsIFilePicker.h"
107 : #include "nsDirectoryServiceDefs.h"
108 : #include "nsIContentPrefService.h"
109 : #include "nsIObserverService.h"
110 : #include "nsIPopupWindowManager.h"
111 : #include "nsGlobalWindow.h"
112 :
113 : // input type=image
114 : #include "nsImageLoadingContent.h"
115 :
116 : #include "mozAutoDocUpdate.h"
117 : #include "nsContentCreatorFunctions.h"
118 : #include "nsContentUtils.h"
119 : #include "nsRadioVisitor.h"
120 :
121 : #include "mozilla/LookAndFeel.h"
122 : #include "mozilla/Util.h" // DebugOnly
123 :
124 : #include "nsIIDNService.h"
125 :
126 : using namespace mozilla;
127 : using namespace mozilla::dom;
128 :
129 : // XXX align=left, hspace, vspace, border? other nav4 attrs
130 :
131 : static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
132 :
133 : // First bits are needed for the control type.
134 : #define NS_OUTER_ACTIVATE_EVENT (1 << 9)
135 : #define NS_ORIGINAL_CHECKED_VALUE (1 << 10)
136 : #define NS_NO_CONTENT_DISPATCH (1 << 11)
137 : #define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12)
138 : #define NS_CONTROL_TYPE(bits) ((bits) & ~( \
139 : NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE | NS_NO_CONTENT_DISPATCH | \
140 : NS_ORIGINAL_INDETERMINATE_VALUE))
141 :
142 : // whether textfields should be selected once focused:
143 : // -1: no, 1: yes, 0: uninitialized
144 : static PRInt32 gSelectTextFieldOnFocus;
145 : UploadLastDir* nsHTMLInputElement::gUploadLastDir;
146 :
147 : static const nsAttrValue::EnumTable kInputTypeTable[] = {
148 : { "button", NS_FORM_INPUT_BUTTON },
149 : { "checkbox", NS_FORM_INPUT_CHECKBOX },
150 : { "email", NS_FORM_INPUT_EMAIL },
151 : { "file", NS_FORM_INPUT_FILE },
152 : { "hidden", NS_FORM_INPUT_HIDDEN },
153 : { "reset", NS_FORM_INPUT_RESET },
154 : { "image", NS_FORM_INPUT_IMAGE },
155 : { "password", NS_FORM_INPUT_PASSWORD },
156 : { "radio", NS_FORM_INPUT_RADIO },
157 : { "search", NS_FORM_INPUT_SEARCH },
158 : { "submit", NS_FORM_INPUT_SUBMIT },
159 : { "tel", NS_FORM_INPUT_TEL },
160 : { "text", NS_FORM_INPUT_TEXT },
161 : { "url", NS_FORM_INPUT_URL },
162 : { 0 }
163 : };
164 :
165 : // Default type is 'text'.
166 : static const nsAttrValue::EnumTable* kInputDefaultType = &kInputTypeTable[12];
167 :
168 : static const PRUint8 NS_INPUT_AUTOCOMPLETE_OFF = 0;
169 : static const PRUint8 NS_INPUT_AUTOCOMPLETE_ON = 1;
170 : static const PRUint8 NS_INPUT_AUTOCOMPLETE_DEFAULT = 2;
171 :
172 : static const nsAttrValue::EnumTable kInputAutocompleteTable[] = {
173 : { "", NS_INPUT_AUTOCOMPLETE_DEFAULT },
174 : { "on", NS_INPUT_AUTOCOMPLETE_ON },
175 : { "off", NS_INPUT_AUTOCOMPLETE_OFF },
176 : { 0 }
177 : };
178 :
179 : // Default autocomplete value is "".
180 : static const nsAttrValue::EnumTable* kInputDefaultAutocomplete = &kInputAutocompleteTable[0];
181 :
182 : #define NS_INPUT_ELEMENT_STATE_IID \
183 : { /* dc3b3d14-23e2-4479-b513-7b369343e3a0 */ \
184 : 0xdc3b3d14, \
185 : 0x23e2, \
186 : 0x4479, \
187 : {0xb5, 0x13, 0x7b, 0x36, 0x93, 0x43, 0xe3, 0xa0} \
188 : }
189 :
190 : class nsHTMLInputElementState MOZ_FINAL : public nsISupports
191 0 : {
192 : public:
193 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_INPUT_ELEMENT_STATE_IID)
194 : NS_DECL_ISUPPORTS
195 :
196 0 : bool IsCheckedSet() {
197 0 : return mCheckedSet;
198 : }
199 :
200 0 : bool GetChecked() {
201 0 : return mChecked;
202 : }
203 :
204 0 : void SetChecked(bool aChecked) {
205 0 : mChecked = aChecked;
206 0 : mCheckedSet = true;
207 0 : }
208 :
209 0 : const nsString& GetValue() {
210 0 : return mValue;
211 : }
212 :
213 0 : void SetValue(const nsAString &aValue) {
214 0 : mValue = aValue;
215 0 : }
216 :
217 0 : const nsCOMArray<nsIDOMFile>& GetFiles() {
218 0 : return mFiles;
219 : }
220 :
221 0 : void SetFiles(const nsCOMArray<nsIDOMFile> &aFiles) {
222 0 : mFiles.Clear();
223 0 : mFiles.AppendObjects(aFiles);
224 0 : }
225 :
226 0 : nsHTMLInputElementState()
227 : : mValue()
228 : , mChecked(false)
229 0 : , mCheckedSet(false)
230 0 : {};
231 :
232 : protected:
233 : nsString mValue;
234 : nsCOMArray<nsIDOMFile> mFiles;
235 : bool mChecked;
236 : bool mCheckedSet;
237 : };
238 :
239 0 : NS_IMPL_ISUPPORTS1(nsHTMLInputElementState, nsHTMLInputElementState)
240 : NS_DEFINE_STATIC_IID_ACCESSOR(nsHTMLInputElementState, NS_INPUT_ELEMENT_STATE_IID)
241 :
242 0 : class AsyncClickHandler : public nsRunnable {
243 : public:
244 0 : AsyncClickHandler(nsHTMLInputElement* aInput)
245 0 : : mInput(aInput) {
246 :
247 0 : nsPIDOMWindow* win = aInput->OwnerDoc()->GetWindow();
248 0 : if (win) {
249 0 : mPopupControlState = win->GetPopupControlState();
250 : }
251 0 : };
252 :
253 : NS_IMETHOD Run();
254 :
255 : protected:
256 : nsRefPtr<nsHTMLInputElement> mInput;
257 : PopupControlState mPopupControlState;
258 : };
259 :
260 : NS_IMETHODIMP
261 0 : AsyncClickHandler::Run()
262 : {
263 : // Get parent nsPIDOMWindow object.
264 0 : nsCOMPtr<nsIDocument> doc = mInput->OwnerDoc();
265 :
266 0 : nsPIDOMWindow* win = doc->GetWindow();
267 0 : if (!win) {
268 0 : return NS_ERROR_FAILURE;
269 : }
270 :
271 : // Check if page is allowed to open the popup
272 0 : if (mPopupControlState > openControlled) {
273 : nsCOMPtr<nsIPopupWindowManager> pm =
274 0 : do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
275 :
276 0 : if (!pm) {
277 0 : return NS_OK;
278 : }
279 :
280 : PRUint32 permission;
281 0 : pm->TestPermission(doc->GetDocumentURI(), &permission);
282 0 : if (permission == nsIPopupWindowManager::DENY_POPUP) {
283 0 : nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
284 0 : nsGlobalWindow::FirePopupBlockedEvent(domDoc, win, nsnull, EmptyString(), EmptyString());
285 0 : return NS_OK;
286 : }
287 : }
288 :
289 : // Get Loc title
290 0 : nsXPIDLString title;
291 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
292 0 : "FileUpload", title);
293 :
294 0 : nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1");
295 0 : if (!filePicker)
296 0 : return NS_ERROR_FAILURE;
297 :
298 0 : bool multi = mInput->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple);
299 :
300 0 : nsresult rv = filePicker->Init(win, title,
301 : multi
302 : ? static_cast<PRInt16>(nsIFilePicker::modeOpenMultiple)
303 0 : : static_cast<PRInt16>(nsIFilePicker::modeOpen));
304 0 : NS_ENSURE_SUCCESS(rv, rv);
305 :
306 0 : if (mInput->HasAttr(kNameSpaceID_None, nsGkAtoms::accept)) {
307 0 : PRInt32 filters = mInput->GetFilterFromAccept();
308 :
309 0 : if (filters) {
310 : // We add |filterAll| to be sure the user always has a sane fallback.
311 0 : filePicker->AppendFilters(filters | nsIFilePicker::filterAll);
312 :
313 : // If the accept attribute asked for a filter, we need to make it default.
314 : // |filterAll| will always use index=0 so we need to set index=1 as the
315 : // current filter.
316 0 : filePicker->SetFilterIndex(1);
317 : } else {
318 0 : filePicker->AppendFilters(nsIFilePicker::filterAll);
319 : }
320 : } else {
321 0 : filePicker->AppendFilters(nsIFilePicker::filterAll);
322 : }
323 :
324 : // Set default directry and filename
325 0 : nsAutoString defaultName;
326 :
327 0 : const nsCOMArray<nsIDOMFile>& oldFiles = mInput->GetFiles();
328 :
329 0 : if (oldFiles.Count()) {
330 0 : nsString path;
331 :
332 0 : oldFiles[0]->GetMozFullPathInternal(path);
333 :
334 0 : nsCOMPtr<nsILocalFile> localFile;
335 0 : rv = NS_NewLocalFile(path, false, getter_AddRefs(localFile));
336 :
337 0 : if (NS_SUCCEEDED(rv)) {
338 0 : nsCOMPtr<nsIFile> parentFile;
339 0 : rv = localFile->GetParent(getter_AddRefs(parentFile));
340 0 : if (NS_SUCCEEDED(rv)) {
341 0 : nsCOMPtr<nsILocalFile> parentLocalFile = do_QueryInterface(parentFile, &rv);
342 0 : if (parentLocalFile) {
343 0 : filePicker->SetDisplayDirectory(parentLocalFile);
344 : }
345 : }
346 : }
347 :
348 : // Unfortunately nsIFilePicker doesn't allow multiple files to be
349 : // default-selected, so only select something by default if exactly
350 : // one file was selected before.
351 0 : if (oldFiles.Count() == 1) {
352 0 : nsAutoString leafName;
353 0 : oldFiles[0]->GetName(leafName);
354 0 : if (!leafName.IsEmpty()) {
355 0 : filePicker->SetDefaultString(leafName);
356 : }
357 : }
358 : } else {
359 : // Attempt to retrieve the last used directory from the content pref service
360 0 : nsCOMPtr<nsILocalFile> localFile;
361 : nsHTMLInputElement::gUploadLastDir->FetchLastUsedDirectory(doc->GetDocumentURI(),
362 0 : getter_AddRefs(localFile));
363 0 : if (!localFile) {
364 : // Default to "desktop" directory for each platform
365 0 : nsCOMPtr<nsIFile> homeDir;
366 0 : NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(homeDir));
367 0 : localFile = do_QueryInterface(homeDir);
368 : }
369 0 : filePicker->SetDisplayDirectory(localFile);
370 : }
371 :
372 : // Open dialog
373 : PRInt16 mode;
374 0 : rv = filePicker->Show(&mode);
375 0 : NS_ENSURE_SUCCESS(rv, rv);
376 0 : if (mode == nsIFilePicker::returnCancel) {
377 0 : return NS_OK;
378 : }
379 :
380 : // Collect new selected filenames
381 0 : nsCOMArray<nsIDOMFile> newFiles;
382 0 : if (multi) {
383 0 : nsCOMPtr<nsISimpleEnumerator> iter;
384 0 : rv = filePicker->GetFiles(getter_AddRefs(iter));
385 0 : NS_ENSURE_SUCCESS(rv, rv);
386 :
387 0 : nsCOMPtr<nsISupports> tmp;
388 0 : bool prefSaved = false;
389 0 : bool loop = true;
390 0 : while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) {
391 0 : iter->GetNext(getter_AddRefs(tmp));
392 0 : nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(tmp);
393 0 : if (localFile) {
394 0 : nsString unicodePath;
395 0 : rv = localFile->GetPath(unicodePath);
396 0 : if (!unicodePath.IsEmpty()) {
397 : nsCOMPtr<nsIDOMFile> domFile =
398 0 : do_QueryObject(new nsDOMFileFile(localFile));
399 0 : newFiles.AppendObject(domFile);
400 : }
401 0 : if (!prefSaved) {
402 : // Store the last used directory using the content pref service
403 : nsHTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(doc->GetDocumentURI(),
404 0 : localFile);
405 0 : prefSaved = true;
406 : }
407 : }
408 : }
409 : }
410 : else {
411 0 : nsCOMPtr<nsILocalFile> localFile;
412 0 : rv = filePicker->GetFile(getter_AddRefs(localFile));
413 0 : if (localFile) {
414 0 : nsString unicodePath;
415 0 : rv = localFile->GetPath(unicodePath);
416 0 : if (!unicodePath.IsEmpty()) {
417 : nsCOMPtr<nsIDOMFile> domFile=
418 0 : do_QueryObject(new nsDOMFileFile(localFile));
419 0 : newFiles.AppendObject(domFile);
420 : }
421 : // Store the last used directory using the content pref service
422 : nsHTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(doc->GetDocumentURI(),
423 0 : localFile);
424 : }
425 : }
426 :
427 : // Set new selected files
428 0 : if (newFiles.Count()) {
429 : // The text control frame (if there is one) isn't going to send a change
430 : // event because it will think this is done by a script.
431 : // So, we can safely send one by ourself.
432 0 : mInput->SetFiles(newFiles, true);
433 0 : nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
434 0 : static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
435 0 : NS_LITERAL_STRING("change"), true,
436 0 : false);
437 : }
438 :
439 0 : return NS_OK;
440 : }
441 :
442 : #define CPS_PREF_NAME NS_LITERAL_STRING("browser.upload.lastDir")
443 :
444 6 : NS_IMPL_ISUPPORTS2(UploadLastDir, nsIObserver, nsISupportsWeakReference)
445 :
446 : void
447 1 : nsHTMLInputElement::InitUploadLastDir() {
448 1 : gUploadLastDir = new UploadLastDir();
449 1 : NS_ADDREF(gUploadLastDir);
450 :
451 : nsCOMPtr<nsIObserverService> observerService =
452 2 : mozilla::services::GetObserverService();
453 1 : if (observerService && gUploadLastDir) {
454 1 : observerService->AddObserver(gUploadLastDir, "browser:purge-session-history", true);
455 : }
456 1 : }
457 :
458 : void
459 1403 : nsHTMLInputElement::DestroyUploadLastDir() {
460 1403 : NS_IF_RELEASE(gUploadLastDir);
461 1403 : }
462 :
463 : nsresult
464 0 : UploadLastDir::FetchLastUsedDirectory(nsIURI* aURI, nsILocalFile** aFile)
465 : {
466 0 : NS_PRECONDITION(aURI, "aURI is null");
467 0 : NS_PRECONDITION(aFile, "aFile is null");
468 : // Attempt to get the CPS, if it's not present we'll just return
469 : nsCOMPtr<nsIContentPrefService> contentPrefService =
470 0 : do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
471 0 : if (!contentPrefService)
472 0 : return NS_ERROR_NOT_AVAILABLE;
473 0 : nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
474 0 : if (!uri)
475 0 : return NS_ERROR_OUT_OF_MEMORY;
476 0 : uri->SetAsISupports(aURI);
477 :
478 : // Get the last used directory, if it is stored
479 : bool hasPref;
480 0 : if (NS_SUCCEEDED(contentPrefService->HasPref(uri, CPS_PREF_NAME, &hasPref)) && hasPref) {
481 0 : nsCOMPtr<nsIVariant> pref;
482 0 : contentPrefService->GetPref(uri, CPS_PREF_NAME, nsnull, getter_AddRefs(pref));
483 0 : nsString prefStr;
484 0 : pref->GetAsAString(prefStr);
485 :
486 0 : nsCOMPtr<nsILocalFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
487 0 : if (!localFile)
488 0 : return NS_ERROR_OUT_OF_MEMORY;
489 0 : localFile->InitWithPath(prefStr);
490 0 : localFile.forget(aFile);
491 : }
492 0 : return NS_OK;
493 : }
494 :
495 : nsresult
496 0 : UploadLastDir::StoreLastUsedDirectory(nsIURI* aURI, nsILocalFile* aFile)
497 : {
498 0 : NS_PRECONDITION(aURI, "aURI is null");
499 0 : NS_PRECONDITION(aFile, "aFile is null");
500 0 : nsCOMPtr<nsIFile> parentFile;
501 0 : aFile->GetParent(getter_AddRefs(parentFile));
502 0 : if (!parentFile) {
503 0 : return NS_OK;
504 : }
505 0 : nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(parentFile);
506 :
507 : // Attempt to get the CPS, if it's not present we'll just return
508 : nsCOMPtr<nsIContentPrefService> contentPrefService =
509 0 : do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
510 0 : if (!contentPrefService)
511 0 : return NS_ERROR_NOT_AVAILABLE;
512 0 : nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
513 0 : if (!uri)
514 0 : return NS_ERROR_OUT_OF_MEMORY;
515 0 : uri->SetAsISupports(aURI);
516 :
517 : // Find the parent of aFile, and store it
518 0 : nsString unicodePath;
519 0 : parentFile->GetPath(unicodePath);
520 0 : if (unicodePath.IsEmpty()) // nothing to do
521 0 : return NS_OK;
522 0 : nsCOMPtr<nsIWritableVariant> prefValue = do_CreateInstance(NS_VARIANT_CONTRACTID);
523 0 : if (!prefValue)
524 0 : return NS_ERROR_OUT_OF_MEMORY;
525 0 : prefValue->SetAsAString(unicodePath);
526 0 : return contentPrefService->SetPref(uri, CPS_PREF_NAME, prefValue);
527 : }
528 :
529 : NS_IMETHODIMP
530 0 : UploadLastDir::Observe(nsISupports *aSubject, char const *aTopic, PRUnichar const *aData)
531 : {
532 0 : if (strcmp(aTopic, "browser:purge-session-history") == 0) {
533 : nsCOMPtr<nsIContentPrefService> contentPrefService =
534 0 : do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
535 0 : if (contentPrefService)
536 0 : contentPrefService->RemovePrefsByName(CPS_PREF_NAME);
537 : }
538 0 : return NS_OK;
539 : }
540 :
541 : #ifdef ACCESSIBILITY
542 : //Helper method
543 : static nsresult FireEventForAccessibility(nsIDOMHTMLInputElement* aTarget,
544 : nsPresContext* aPresContext,
545 : const nsAString& aEventType);
546 : #endif
547 :
548 : //
549 : // construction, destruction
550 : //
551 :
552 2 : NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Input)
553 :
554 1 : nsHTMLInputElement::nsHTMLInputElement(already_AddRefed<nsINodeInfo> aNodeInfo,
555 : FromParser aFromParser)
556 : : nsGenericHTMLFormElement(aNodeInfo)
557 : , mType(kInputDefaultType->value)
558 : , mDisabledChanged(false)
559 : , mValueChanged(false)
560 : , mCheckedChanged(false)
561 : , mChecked(false)
562 : , mHandlingSelectEvent(false)
563 : , mShouldInitChecked(false)
564 : , mParserCreating(aFromParser != NOT_FROM_PARSER)
565 : , mInInternalActivate(false)
566 : , mCheckedIsToggled(false)
567 : , mIndeterminate(false)
568 : , mInhibitRestoration(aFromParser & FROM_PARSER_FRAGMENT)
569 : , mCanShowValidUI(true)
570 1 : , mCanShowInvalidUI(true)
571 : {
572 1 : mInputData.mState = new nsTextEditorState(this);
573 1 : NS_ADDREF(mInputData.mState);
574 :
575 1 : if (!gUploadLastDir)
576 1 : nsHTMLInputElement::InitUploadLastDir();
577 :
578 : // Set up our default state. By default we're enabled (since we're
579 : // a control type that can be disabled but not actually disabled
580 : // right now), optional, and valid. We are NOT readwrite by default
581 : // until someone calls UpdateEditableState on us, apparently! Also
582 : // by default we don't have to show validity UI and so forth.
583 : AddStatesSilently(NS_EVENT_STATE_ENABLED |
584 : NS_EVENT_STATE_OPTIONAL |
585 1 : NS_EVENT_STATE_VALID);
586 1 : }
587 :
588 3 : nsHTMLInputElement::~nsHTMLInputElement()
589 : {
590 1 : if (mFileList) {
591 0 : mFileList->Disconnect();
592 : }
593 1 : DestroyImageLoadingContent();
594 1 : FreeData();
595 4 : }
596 :
597 : void
598 1 : nsHTMLInputElement::FreeData()
599 : {
600 1 : if (!IsSingleLineTextControl(false)) {
601 0 : nsMemory::Free(mInputData.mValue);
602 0 : mInputData.mValue = nsnull;
603 : } else {
604 1 : UnbindFromFrame(nsnull);
605 1 : NS_IF_RELEASE(mInputData.mState);
606 : }
607 1 : }
608 :
609 : nsTextEditorState*
610 2 : nsHTMLInputElement::GetEditorState() const
611 : {
612 2 : if (!IsSingleLineTextControl(false)) {
613 0 : return nsnull;
614 : }
615 :
616 2 : NS_ASSERTION(mInputData.mState,
617 : "Single line text controls need to have a state associated with them");
618 :
619 2 : return mInputData.mState;
620 : }
621 :
622 :
623 : // nsISupports
624 :
625 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLInputElement)
626 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLInputElement,
627 : nsGenericHTMLFormElement)
628 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mControllers)
629 1 : if (tmp->IsSingleLineTextControl(false)) {
630 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mInputData.mState, nsTextEditorState)
631 : }
632 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mFiles)
633 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFileList)
634 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
635 :
636 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLInputElement,
637 : nsGenericHTMLFormElement)
638 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mControllers)
639 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mFiles)
640 1 : if (tmp->mFileList) {
641 0 : tmp->mFileList->Disconnect();
642 0 : tmp->mFileList = nsnull;
643 : }
644 : //XXX should unlink more?
645 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
646 :
647 19 : NS_IMPL_ADDREF_INHERITED(nsHTMLInputElement, nsGenericElement)
648 19 : NS_IMPL_RELEASE_INHERITED(nsHTMLInputElement, nsGenericElement)
649 :
650 :
651 1 : DOMCI_NODE_DATA(HTMLInputElement, nsHTMLInputElement)
652 :
653 : // QueryInterface implementation for nsHTMLInputElement
654 39 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLInputElement)
655 24 : NS_HTML_CONTENT_INTERFACE_TABLE8(nsHTMLInputElement,
656 : nsIDOMHTMLInputElement,
657 : nsITextControlElement,
658 : nsIPhonetic,
659 : imgIDecoderObserver,
660 : nsIImageLoadingContent,
661 : imgIContainerObserver,
662 : nsIDOMNSEditableElement,
663 : nsIConstraintValidation)
664 23 : NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLInputElement,
665 : nsGenericHTMLFormElement)
666 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLInputElement)
667 :
668 : // nsIConstraintValidation
669 0 : NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(nsHTMLInputElement)
670 :
671 : // nsIDOMNode
672 :
673 : nsresult
674 0 : nsHTMLInputElement::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
675 : {
676 0 : *aResult = nsnull;
677 :
678 0 : nsCOMPtr<nsINodeInfo> ni = aNodeInfo;
679 : nsRefPtr<nsHTMLInputElement> it =
680 0 : new nsHTMLInputElement(ni.forget(), NOT_FROM_PARSER);
681 :
682 0 : nsresult rv = CopyInnerTo(it);
683 0 : NS_ENSURE_SUCCESS(rv, rv);
684 :
685 0 : switch (mType) {
686 : case NS_FORM_INPUT_EMAIL:
687 : case NS_FORM_INPUT_SEARCH:
688 : case NS_FORM_INPUT_TEXT:
689 : case NS_FORM_INPUT_PASSWORD:
690 : case NS_FORM_INPUT_TEL:
691 : case NS_FORM_INPUT_URL:
692 0 : if (mValueChanged) {
693 : // We don't have our default value anymore. Set our value on
694 : // the clone.
695 0 : nsAutoString value;
696 0 : GetValueInternal(value);
697 : // SetValueInternal handles setting the VALUE_CHANGED bit for us
698 0 : it->SetValueInternal(value, false, true);
699 : }
700 0 : break;
701 : case NS_FORM_INPUT_FILE:
702 0 : if (it->OwnerDoc()->IsStaticDocument()) {
703 : // We're going to be used in print preview. Since the doc is static
704 : // we can just grab the pretty string and use it as wallpaper
705 0 : GetDisplayFileName(it->mStaticDocFileList);
706 : } else {
707 0 : it->mFiles.Clear();
708 0 : it->mFiles.AppendObjects(mFiles);
709 : }
710 0 : break;
711 : case NS_FORM_INPUT_RADIO:
712 : case NS_FORM_INPUT_CHECKBOX:
713 0 : if (mCheckedChanged) {
714 : // We no longer have our original checked state. Set our
715 : // checked state on the clone.
716 0 : it->DoSetChecked(mChecked, false, true);
717 : }
718 0 : break;
719 : case NS_FORM_INPUT_IMAGE:
720 0 : if (it->OwnerDoc()->IsStaticDocument()) {
721 0 : CreateStaticImageClone(it);
722 : }
723 0 : break;
724 : default:
725 0 : break;
726 : }
727 :
728 0 : it.forget(aResult);
729 0 : return NS_OK;
730 : }
731 :
732 : nsresult
733 0 : nsHTMLInputElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
734 : const nsAttrValueOrString* aValue,
735 : bool aNotify)
736 : {
737 0 : if (aNameSpaceID == kNameSpaceID_None) {
738 : //
739 : // When name or type changes, radio should be removed from radio group.
740 : // (type changes are handled in the form itself currently)
741 : // If the parser is not done creating the radio, we also should not do it.
742 : //
743 0 : if ((aName == nsGkAtoms::name ||
744 0 : (aName == nsGkAtoms::type && !mForm)) &&
745 : mType == NS_FORM_INPUT_RADIO &&
746 0 : (mForm || !mParserCreating)) {
747 0 : WillRemoveFromRadioGroup();
748 0 : } else if (aNotify && aName == nsGkAtoms::src &&
749 : mType == NS_FORM_INPUT_IMAGE) {
750 0 : if (aValue) {
751 0 : LoadImage(aValue->String(), true, aNotify);
752 : } else {
753 : // Null value means the attr got unset; drop the image
754 0 : CancelImageRequests(aNotify);
755 : }
756 0 : } else if (aNotify && aName == nsGkAtoms::disabled) {
757 0 : mDisabledChanged = true;
758 : }
759 : }
760 :
761 : return nsGenericHTMLFormElement::BeforeSetAttr(aNameSpaceID, aName,
762 0 : aValue, aNotify);
763 : }
764 :
765 : nsresult
766 0 : nsHTMLInputElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
767 : const nsAttrValue* aValue, bool aNotify)
768 : {
769 0 : if (aNameSpaceID == kNameSpaceID_None) {
770 : //
771 : // When name or type changes, radio should be added to radio group.
772 : // (type changes are handled in the form itself currently)
773 : // If the parser is not done creating the radio, we also should not do it.
774 : //
775 0 : if ((aName == nsGkAtoms::name ||
776 0 : (aName == nsGkAtoms::type && !mForm)) &&
777 : mType == NS_FORM_INPUT_RADIO &&
778 0 : (mForm || !mParserCreating)) {
779 0 : AddedToRadioGroup();
780 0 : UpdateValueMissingValidityStateForRadio(false);
781 : }
782 :
783 : // If @value is changed and BF_VALUE_CHANGED is false, @value is the value
784 : // of the element so, if the value of the element is different than @value,
785 : // we have to re-set it. This is only the case when GetValueMode() returns
786 : // VALUE_MODE_VALUE.
787 0 : if (aName == nsGkAtoms::value &&
788 0 : !mValueChanged && GetValueMode() == VALUE_MODE_VALUE) {
789 0 : SetDefaultValueAsValue();
790 : }
791 :
792 : //
793 : // Checked must be set no matter what type of control it is, since
794 : // mChecked must reflect the new value
795 0 : if (aName == nsGkAtoms::checked && !mCheckedChanged) {
796 : // Delay setting checked if the parser is creating this element (wait
797 : // until everything is set)
798 0 : if (mParserCreating) {
799 0 : mShouldInitChecked = true;
800 : } else {
801 0 : DoSetChecked(DefaultChecked(), true, true);
802 0 : SetCheckedChanged(false);
803 : }
804 : }
805 :
806 0 : if (aName == nsGkAtoms::type) {
807 0 : if (!aValue) {
808 : // We're now a text input. Note that we have to handle this manually,
809 : // since removing an attribute (which is what happened, since aValue is
810 : // null) doesn't call ParseAttribute.
811 0 : HandleTypeChange(kInputDefaultType->value);
812 : }
813 :
814 0 : UpdateBarredFromConstraintValidation();
815 :
816 0 : if (mType != NS_FORM_INPUT_IMAGE) {
817 : // We're no longer an image input. Cancel our image requests, if we have
818 : // any. Note that doing this when we already weren't an image is ok --
819 : // just does nothing.
820 0 : CancelImageRequests(aNotify);
821 0 : } else if (aNotify) {
822 : // We just got switched to be an image input; we should see
823 : // whether we have an image to load;
824 0 : nsAutoString src;
825 0 : if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
826 0 : LoadImage(src, false, aNotify);
827 : }
828 : }
829 : }
830 :
831 0 : if (mType == NS_FORM_INPUT_RADIO && aName == nsGkAtoms::required) {
832 0 : nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
833 :
834 0 : if (container) {
835 0 : nsAutoString name;
836 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
837 0 : container->RadioRequiredChanged(name, this);
838 : }
839 : }
840 :
841 0 : if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
842 : aName == nsGkAtoms::readonly) {
843 0 : UpdateValueMissingValidityState();
844 :
845 : // This *has* to be called *after* validity has changed.
846 0 : if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
847 0 : UpdateBarredFromConstraintValidation();
848 : }
849 0 : } else if (MaxLengthApplies() && aName == nsGkAtoms::maxlength) {
850 0 : UpdateTooLongValidityState();
851 0 : } else if (aName == nsGkAtoms::pattern) {
852 0 : UpdatePatternMismatchValidityState();
853 0 : } else if (aName == nsGkAtoms::multiple) {
854 0 : UpdateTypeMismatchValidityState();
855 : }
856 :
857 0 : UpdateEditableState(aNotify);
858 0 : UpdateState(aNotify);
859 : }
860 :
861 : return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
862 0 : aValue, aNotify);
863 : }
864 :
865 : // nsIDOMHTMLInputElement
866 :
867 : NS_IMETHODIMP
868 0 : nsHTMLInputElement::GetForm(nsIDOMHTMLFormElement** aForm)
869 : {
870 0 : return nsGenericHTMLFormElement::GetForm(aForm);
871 : }
872 :
873 1 : NS_IMPL_STRING_ATTR(nsHTMLInputElement, DefaultValue, value)
874 0 : NS_IMPL_BOOL_ATTR(nsHTMLInputElement, DefaultChecked, checked)
875 0 : NS_IMPL_STRING_ATTR(nsHTMLInputElement, Accept, accept)
876 0 : NS_IMPL_STRING_ATTR(nsHTMLInputElement, Align, align)
877 0 : NS_IMPL_STRING_ATTR(nsHTMLInputElement, Alt, alt)
878 0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLInputElement, Autocomplete, autocomplete,
879 : kInputDefaultAutocomplete->tag)
880 0 : NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Autofocus, autofocus)
881 : //NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Checked, checked)
882 0 : NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Disabled, disabled)
883 0 : NS_IMPL_ACTION_ATTR(nsHTMLInputElement, FormAction, formaction)
884 0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLInputElement, FormEnctype, formenctype,
885 : kFormDefaultEnctype->tag)
886 0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLInputElement, FormMethod, formmethod,
887 : kFormDefaultMethod->tag)
888 0 : NS_IMPL_BOOL_ATTR(nsHTMLInputElement, FormNoValidate, formnovalidate)
889 0 : NS_IMPL_STRING_ATTR(nsHTMLInputElement, FormTarget, formtarget)
890 0 : NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Multiple, multiple)
891 0 : NS_IMPL_NON_NEGATIVE_INT_ATTR(nsHTMLInputElement, MaxLength, maxlength)
892 0 : NS_IMPL_STRING_ATTR(nsHTMLInputElement, Name, name)
893 0 : NS_IMPL_BOOL_ATTR(nsHTMLInputElement, ReadOnly, readonly)
894 0 : NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Required, required)
895 0 : NS_IMPL_URI_ATTR(nsHTMLInputElement, Src, src)
896 0 : NS_IMPL_INT_ATTR(nsHTMLInputElement, TabIndex, tabindex)
897 0 : NS_IMPL_STRING_ATTR(nsHTMLInputElement, UseMap, usemap)
898 : //NS_IMPL_STRING_ATTR(nsHTMLInputElement, Value, value)
899 0 : NS_IMPL_UINT_ATTR_NON_ZERO_DEFAULT_VALUE(nsHTMLInputElement, Size, size, DEFAULT_COLS)
900 0 : NS_IMPL_STRING_ATTR(nsHTMLInputElement, Pattern, pattern)
901 0 : NS_IMPL_STRING_ATTR(nsHTMLInputElement, Placeholder, placeholder)
902 0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLInputElement, Type, type,
903 : kInputDefaultType->tag)
904 :
905 : NS_IMETHODIMP
906 0 : nsHTMLInputElement::GetIndeterminate(bool* aValue)
907 : {
908 0 : *aValue = mIndeterminate;
909 0 : return NS_OK;
910 : }
911 :
912 : nsresult
913 0 : nsHTMLInputElement::SetIndeterminateInternal(bool aValue,
914 : bool aShouldInvalidate)
915 : {
916 0 : mIndeterminate = aValue;
917 :
918 0 : if (aShouldInvalidate) {
919 : // Repaint the frame
920 0 : nsIFrame* frame = GetPrimaryFrame();
921 0 : if (frame)
922 0 : frame->InvalidateFrameSubtree();
923 : }
924 :
925 0 : UpdateState(true);
926 :
927 0 : return NS_OK;
928 : }
929 :
930 : NS_IMETHODIMP
931 0 : nsHTMLInputElement::SetIndeterminate(bool aValue)
932 : {
933 0 : return SetIndeterminateInternal(aValue, true);
934 : }
935 :
936 : NS_IMETHODIMP
937 1 : nsHTMLInputElement::GetValue(nsAString& aValue)
938 : {
939 1 : return GetValueInternal(aValue);
940 : }
941 :
942 : nsresult
943 1 : nsHTMLInputElement::GetValueInternal(nsAString& aValue) const
944 : {
945 1 : switch (GetValueMode()) {
946 : case VALUE_MODE_VALUE:
947 1 : mInputData.mState->GetValue(aValue, true);
948 1 : return NS_OK;
949 :
950 : case VALUE_MODE_FILENAME:
951 0 : if (nsContentUtils::CallerHasUniversalXPConnect()) {
952 0 : if (mFiles.Count()) {
953 0 : return mFiles[0]->GetMozFullPath(aValue);
954 : }
955 : else {
956 0 : aValue.Truncate();
957 : }
958 : } else {
959 : // Just return the leaf name
960 0 : if (mFiles.Count() == 0 || NS_FAILED(mFiles[0]->GetName(aValue))) {
961 0 : aValue.Truncate();
962 : }
963 : }
964 :
965 0 : return NS_OK;
966 :
967 : case VALUE_MODE_DEFAULT:
968 : // Treat defaultValue as value.
969 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue);
970 0 : return NS_OK;
971 :
972 : case VALUE_MODE_DEFAULT_ON:
973 : // Treat default value as value and returns "on" if no value.
974 0 : if (!GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue)) {
975 0 : aValue.AssignLiteral("on");
976 : }
977 0 : return NS_OK;
978 : }
979 :
980 : // This return statement is required for some compilers.
981 : return NS_OK;
982 : }
983 :
984 : bool
985 0 : nsHTMLInputElement::IsValueEmpty() const
986 : {
987 0 : nsAutoString value;
988 0 : GetValueInternal(value);
989 :
990 0 : return value.IsEmpty();
991 : }
992 :
993 : NS_IMETHODIMP
994 0 : nsHTMLInputElement::SetValue(const nsAString& aValue)
995 : {
996 : // check security. Note that setting the value to the empty string is always
997 : // OK and gives pages a way to clear a file input if necessary.
998 0 : if (mType == NS_FORM_INPUT_FILE) {
999 0 : if (!aValue.IsEmpty()) {
1000 0 : if (!nsContentUtils::CallerHasUniversalXPConnect()) {
1001 : // setting the value of a "FILE" input widget requires the
1002 : // UniversalXPConnect privilege
1003 0 : return NS_ERROR_DOM_SECURITY_ERR;
1004 : }
1005 0 : const PRUnichar *name = PromiseFlatString(aValue).get();
1006 0 : return MozSetFileNameArray(&name, 1);
1007 : }
1008 : else {
1009 0 : ClearFiles(true);
1010 : }
1011 : }
1012 : else {
1013 0 : SetValueInternal(aValue, false, true);
1014 : }
1015 :
1016 0 : return NS_OK;
1017 : }
1018 :
1019 : NS_IMETHODIMP
1020 0 : nsHTMLInputElement::GetList(nsIDOMHTMLElement** aValue)
1021 : {
1022 0 : *aValue = nsnull;
1023 :
1024 0 : nsAutoString dataListId;
1025 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::list, dataListId);
1026 0 : if (dataListId.IsEmpty()) {
1027 0 : return NS_OK;
1028 : }
1029 :
1030 0 : nsIDocument* doc = GetCurrentDoc();
1031 0 : if (!doc) {
1032 0 : return NS_OK;
1033 : }
1034 :
1035 0 : Element* element = doc->GetElementById(dataListId);
1036 0 : if (!element || !element->IsHTML(nsGkAtoms::datalist)) {
1037 0 : return NS_OK;
1038 : }
1039 :
1040 0 : CallQueryInterface(element, aValue);
1041 0 : return NS_OK;
1042 : }
1043 :
1044 : NS_IMETHODIMP
1045 0 : nsHTMLInputElement::MozGetFileNameArray(PRUint32 *aLength, PRUnichar ***aFileNames)
1046 : {
1047 0 : if (!nsContentUtils::CallerHasUniversalXPConnect()) {
1048 : // Since this function returns full paths it's important that normal pages
1049 : // can't call it.
1050 0 : return NS_ERROR_DOM_SECURITY_ERR;
1051 : }
1052 :
1053 0 : *aLength = mFiles.Count();
1054 : PRUnichar **ret =
1055 0 : static_cast<PRUnichar **>(NS_Alloc(mFiles.Count() * sizeof(PRUnichar*)));
1056 0 : if (!ret) {
1057 0 : return NS_ERROR_OUT_OF_MEMORY;
1058 : }
1059 :
1060 0 : for (PRInt32 i = 0; i < mFiles.Count(); i++) {
1061 0 : nsString str;
1062 0 : mFiles[i]->GetMozFullPathInternal(str);
1063 0 : ret[i] = NS_strdup(str.get());
1064 : }
1065 :
1066 0 : *aFileNames = ret;
1067 :
1068 0 : return NS_OK;
1069 : }
1070 :
1071 : NS_IMETHODIMP
1072 0 : nsHTMLInputElement::MozSetFileNameArray(const PRUnichar **aFileNames, PRUint32 aLength)
1073 : {
1074 0 : if (!nsContentUtils::CallerHasUniversalXPConnect()) {
1075 : // setting the value of a "FILE" input widget requires the
1076 : // UniversalXPConnect privilege
1077 0 : return NS_ERROR_DOM_SECURITY_ERR;
1078 : }
1079 :
1080 0 : nsCOMArray<nsIDOMFile> files;
1081 0 : for (PRUint32 i = 0; i < aLength; ++i) {
1082 0 : nsCOMPtr<nsIFile> file;
1083 0 : if (StringBeginsWith(nsDependentString(aFileNames[i]),
1084 0 : NS_LITERAL_STRING("file:"),
1085 0 : nsASCIICaseInsensitiveStringComparator())) {
1086 : // Converts the URL string into the corresponding nsIFile if possible
1087 : // A local file will be created if the URL string begins with file://
1088 0 : NS_GetFileFromURLSpec(NS_ConvertUTF16toUTF8(aFileNames[i]),
1089 0 : getter_AddRefs(file));
1090 : }
1091 :
1092 0 : if (!file) {
1093 : // this is no "file://", try as local file
1094 0 : nsCOMPtr<nsILocalFile> localFile;
1095 0 : NS_NewLocalFile(nsDependentString(aFileNames[i]),
1096 0 : false, getter_AddRefs(localFile));
1097 0 : file = do_QueryInterface(localFile);
1098 : }
1099 :
1100 0 : if (file) {
1101 0 : nsCOMPtr<nsIDOMFile> domFile = new nsDOMFileFile(file);
1102 0 : files.AppendObject(domFile);
1103 : } else {
1104 0 : continue; // Not much we can do if the file doesn't exist
1105 : }
1106 :
1107 : }
1108 :
1109 0 : SetFiles(files, true);
1110 :
1111 0 : return NS_OK;
1112 : }
1113 :
1114 : NS_IMETHODIMP
1115 0 : nsHTMLInputElement::MozIsTextField(bool aExcludePassword, bool* aResult)
1116 : {
1117 0 : *aResult = IsSingleLineTextControl(aExcludePassword);
1118 :
1119 0 : return NS_OK;
1120 : }
1121 :
1122 : NS_IMETHODIMP
1123 0 : nsHTMLInputElement::SetUserInput(const nsAString& aValue)
1124 : {
1125 0 : if (!nsContentUtils::IsCallerTrustedForWrite()) {
1126 0 : return NS_ERROR_DOM_SECURITY_ERR;
1127 : }
1128 :
1129 0 : if (mType == NS_FORM_INPUT_FILE)
1130 : {
1131 0 : const PRUnichar* name = PromiseFlatString(aValue).get();
1132 0 : return MozSetFileNameArray(&name, 1);
1133 : } else {
1134 0 : SetValueInternal(aValue, true, true);
1135 : }
1136 :
1137 : return nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
1138 : static_cast<nsIDOMHTMLInputElement*>(this),
1139 0 : NS_LITERAL_STRING("input"), true,
1140 0 : true);
1141 : }
1142 :
1143 : NS_IMETHODIMP_(nsIEditor*)
1144 0 : nsHTMLInputElement::GetTextEditor()
1145 : {
1146 0 : nsTextEditorState *state = GetEditorState();
1147 0 : if (state) {
1148 0 : return state->GetEditor();
1149 : }
1150 0 : return nsnull;
1151 : }
1152 :
1153 : NS_IMETHODIMP_(nsISelectionController*)
1154 0 : nsHTMLInputElement::GetSelectionController()
1155 : {
1156 0 : nsTextEditorState *state = GetEditorState();
1157 0 : if (state) {
1158 0 : return state->GetSelectionController();
1159 : }
1160 0 : return nsnull;
1161 : }
1162 :
1163 : nsFrameSelection*
1164 0 : nsHTMLInputElement::GetConstFrameSelection()
1165 : {
1166 0 : nsTextEditorState *state = GetEditorState();
1167 0 : if (state) {
1168 0 : return state->GetConstFrameSelection();
1169 : }
1170 0 : return nsnull;
1171 : }
1172 :
1173 : NS_IMETHODIMP
1174 0 : nsHTMLInputElement::BindToFrame(nsTextControlFrame* aFrame)
1175 : {
1176 0 : nsTextEditorState *state = GetEditorState();
1177 0 : if (state) {
1178 0 : return state->BindToFrame(aFrame);
1179 : }
1180 0 : return NS_ERROR_FAILURE;
1181 : }
1182 :
1183 : NS_IMETHODIMP_(void)
1184 1 : nsHTMLInputElement::UnbindFromFrame(nsTextControlFrame* aFrame)
1185 : {
1186 1 : nsTextEditorState *state = GetEditorState();
1187 1 : if (state && aFrame) {
1188 0 : state->UnbindFromFrame(aFrame);
1189 : }
1190 1 : }
1191 :
1192 : NS_IMETHODIMP
1193 0 : nsHTMLInputElement::CreateEditor()
1194 : {
1195 0 : nsTextEditorState *state = GetEditorState();
1196 0 : if (state) {
1197 0 : return state->PrepareEditor();
1198 : }
1199 0 : return NS_ERROR_FAILURE;
1200 : }
1201 :
1202 : NS_IMETHODIMP_(nsIContent*)
1203 0 : nsHTMLInputElement::GetRootEditorNode()
1204 : {
1205 0 : nsTextEditorState *state = GetEditorState();
1206 0 : if (state) {
1207 0 : return state->GetRootNode();
1208 : }
1209 0 : return nsnull;
1210 : }
1211 :
1212 : NS_IMETHODIMP_(nsIContent*)
1213 0 : nsHTMLInputElement::CreatePlaceholderNode()
1214 : {
1215 0 : nsTextEditorState *state = GetEditorState();
1216 0 : if (state) {
1217 0 : NS_ENSURE_SUCCESS(state->CreatePlaceholderNode(), nsnull);
1218 0 : return state->GetPlaceholderNode();
1219 : }
1220 0 : return nsnull;
1221 : }
1222 :
1223 : NS_IMETHODIMP_(nsIContent*)
1224 0 : nsHTMLInputElement::GetPlaceholderNode()
1225 : {
1226 0 : nsTextEditorState *state = GetEditorState();
1227 0 : if (state) {
1228 0 : return state->GetPlaceholderNode();
1229 : }
1230 0 : return nsnull;
1231 : }
1232 :
1233 : NS_IMETHODIMP_(void)
1234 0 : nsHTMLInputElement::UpdatePlaceholderText(bool aNotify)
1235 : {
1236 0 : nsTextEditorState *state = GetEditorState();
1237 0 : if (state) {
1238 0 : state->UpdatePlaceholderText(aNotify);
1239 : }
1240 0 : }
1241 :
1242 : NS_IMETHODIMP_(void)
1243 0 : nsHTMLInputElement::SetPlaceholderClass(bool aVisible, bool aNotify)
1244 : {
1245 0 : nsTextEditorState *state = GetEditorState();
1246 0 : if (state) {
1247 0 : state->SetPlaceholderClass(aVisible, aNotify);
1248 : }
1249 0 : }
1250 :
1251 : void
1252 0 : nsHTMLInputElement::GetDisplayFileName(nsAString& aValue) const
1253 : {
1254 0 : if (OwnerDoc()->IsStaticDocument()) {
1255 0 : aValue = mStaticDocFileList;
1256 0 : return;
1257 : }
1258 :
1259 0 : aValue.Truncate();
1260 0 : for (PRInt32 i = 0; i < mFiles.Count(); ++i) {
1261 0 : nsString str;
1262 0 : mFiles[i]->GetMozFullPathInternal(str);
1263 0 : if (i == 0) {
1264 0 : aValue.Append(str);
1265 : }
1266 : else {
1267 0 : aValue.Append(NS_LITERAL_STRING(", ") + str);
1268 : }
1269 : }
1270 : }
1271 :
1272 : void
1273 0 : nsHTMLInputElement::SetFiles(const nsCOMArray<nsIDOMFile>& aFiles,
1274 : bool aSetValueChanged)
1275 : {
1276 0 : mFiles.Clear();
1277 0 : mFiles.AppendObjects(aFiles);
1278 :
1279 0 : AfterSetFiles(aSetValueChanged);
1280 0 : }
1281 :
1282 : void
1283 0 : nsHTMLInputElement::SetFiles(nsIDOMFileList* aFiles,
1284 : bool aSetValueChanged)
1285 : {
1286 0 : mFiles.Clear();
1287 :
1288 0 : if (aFiles) {
1289 : PRUint32 listLength;
1290 0 : aFiles->GetLength(&listLength);
1291 0 : for (PRUint32 i = 0; i < listLength; i++) {
1292 0 : nsCOMPtr<nsIDOMFile> file;
1293 0 : aFiles->Item(i, getter_AddRefs(file));
1294 0 : mFiles.AppendObject(file);
1295 : }
1296 : }
1297 :
1298 0 : AfterSetFiles(aSetValueChanged);
1299 0 : }
1300 :
1301 : void
1302 0 : nsHTMLInputElement::AfterSetFiles(bool aSetValueChanged)
1303 : {
1304 : // No need to flush here, if there's no frame at this point we
1305 : // don't need to force creation of one just to tell it about this
1306 : // new value. We just want the display to update as needed.
1307 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
1308 0 : if (formControlFrame) {
1309 0 : nsAutoString readableValue;
1310 0 : GetDisplayFileName(readableValue);
1311 0 : formControlFrame->SetFormProperty(nsGkAtoms::value, readableValue);
1312 : }
1313 :
1314 0 : UpdateFileList();
1315 :
1316 0 : if (aSetValueChanged) {
1317 0 : SetValueChanged(true);
1318 : }
1319 :
1320 0 : UpdateAllValidityStates(true);
1321 0 : }
1322 :
1323 : const nsCOMArray<nsIDOMFile>&
1324 0 : nsHTMLInputElement::GetFiles() const
1325 : {
1326 0 : return mFiles;
1327 : }
1328 :
1329 : nsresult
1330 0 : nsHTMLInputElement::UpdateFileList()
1331 : {
1332 0 : if (mFileList) {
1333 0 : mFileList->Clear();
1334 :
1335 0 : const nsCOMArray<nsIDOMFile>& files = GetFiles();
1336 0 : for (PRInt32 i = 0; i < files.Count(); ++i) {
1337 0 : if (!mFileList->Append(files[i])) {
1338 0 : return NS_ERROR_FAILURE;
1339 : }
1340 : }
1341 : }
1342 :
1343 0 : return NS_OK;
1344 : }
1345 :
1346 : nsresult
1347 1 : nsHTMLInputElement::SetValueInternal(const nsAString& aValue,
1348 : bool aUserInput,
1349 : bool aSetValueChanged)
1350 : {
1351 1 : NS_PRECONDITION(GetValueMode() != VALUE_MODE_FILENAME,
1352 : "Don't call SetValueInternal for file inputs");
1353 :
1354 1 : switch (GetValueMode()) {
1355 : case VALUE_MODE_VALUE:
1356 : {
1357 : // At the moment, only single line text control have to sanitize their value
1358 : // Because we have to create a new string for that, we should prevent doing
1359 : // it if it's useless.
1360 2 : nsAutoString value(aValue);
1361 :
1362 1 : if (!mParserCreating) {
1363 1 : SanitizeValue(value);
1364 : }
1365 :
1366 1 : if (aSetValueChanged) {
1367 0 : SetValueChanged(true);
1368 : }
1369 :
1370 1 : mInputData.mState->SetValue(value, aUserInput);
1371 :
1372 : // This call might be useless in some situations because if the element is
1373 : // a single line text control, nsTextEditorState::SetValue will call
1374 : // nsHTMLInputElement::OnValueChanged which is going to call UpdateState()
1375 : // if the element is focused. This bug 665547.
1376 2 : if (PlaceholderApplies() &&
1377 1 : HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
1378 0 : UpdateState(true);
1379 : }
1380 :
1381 1 : return NS_OK;
1382 : }
1383 :
1384 : case VALUE_MODE_DEFAULT:
1385 : case VALUE_MODE_DEFAULT_ON:
1386 : // If the value of a hidden input was changed, we mark it changed so that we
1387 : // will know we need to save / restore the value. Yes, we are overloading
1388 : // the meaning of ValueChanged just a teensy bit to save a measly byte of
1389 : // storage space in nsHTMLInputElement. Yes, you are free to make a new flag,
1390 : // NEED_TO_SAVE_VALUE, at such time as mBitField becomes a 16-bit value.
1391 0 : if (mType == NS_FORM_INPUT_HIDDEN) {
1392 0 : SetValueChanged(true);
1393 : }
1394 :
1395 : // Treat value == defaultValue for other input elements.
1396 : return nsGenericHTMLFormElement::SetAttr(kNameSpaceID_None,
1397 : nsGkAtoms::value, aValue,
1398 0 : true);
1399 :
1400 : case VALUE_MODE_FILENAME:
1401 0 : return NS_ERROR_UNEXPECTED;
1402 : }
1403 :
1404 : // This return statement is required for some compilers.
1405 : return NS_OK;
1406 : }
1407 :
1408 : NS_IMETHODIMP
1409 0 : nsHTMLInputElement::SetValueChanged(bool aValueChanged)
1410 : {
1411 0 : bool valueChangedBefore = mValueChanged;
1412 :
1413 0 : mValueChanged = aValueChanged;
1414 :
1415 0 : if (valueChangedBefore != aValueChanged) {
1416 0 : UpdateState(true);
1417 : }
1418 :
1419 0 : return NS_OK;
1420 : }
1421 :
1422 : NS_IMETHODIMP
1423 0 : nsHTMLInputElement::GetChecked(bool* aChecked)
1424 : {
1425 0 : *aChecked = mChecked;
1426 0 : return NS_OK;
1427 : }
1428 :
1429 : void
1430 0 : nsHTMLInputElement::SetCheckedChanged(bool aCheckedChanged)
1431 : {
1432 0 : DoSetCheckedChanged(aCheckedChanged, true);
1433 0 : }
1434 :
1435 : void
1436 0 : nsHTMLInputElement::DoSetCheckedChanged(bool aCheckedChanged,
1437 : bool aNotify)
1438 : {
1439 0 : if (mType == NS_FORM_INPUT_RADIO) {
1440 0 : if (mCheckedChanged != aCheckedChanged) {
1441 : nsCOMPtr<nsIRadioVisitor> visitor =
1442 0 : new nsRadioSetCheckedChangedVisitor(aCheckedChanged);
1443 0 : VisitGroup(visitor, aNotify);
1444 : }
1445 : } else {
1446 0 : SetCheckedChangedInternal(aCheckedChanged);
1447 : }
1448 0 : }
1449 :
1450 : void
1451 0 : nsHTMLInputElement::SetCheckedChangedInternal(bool aCheckedChanged)
1452 : {
1453 0 : bool checkedChangedBefore = mCheckedChanged;
1454 :
1455 0 : mCheckedChanged = aCheckedChanged;
1456 :
1457 : // This method can't be called when we are not authorized to notify
1458 : // so we do not need a aNotify parameter.
1459 0 : if (checkedChangedBefore != aCheckedChanged) {
1460 0 : UpdateState(true);
1461 : }
1462 0 : }
1463 :
1464 : NS_IMETHODIMP
1465 0 : nsHTMLInputElement::SetChecked(bool aChecked)
1466 : {
1467 0 : return DoSetChecked(aChecked, true, true);
1468 : }
1469 :
1470 : nsresult
1471 0 : nsHTMLInputElement::DoSetChecked(bool aChecked, bool aNotify,
1472 : bool aSetValueChanged)
1473 : {
1474 : // If the user or JS attempts to set checked, whether it actually changes the
1475 : // value or not, we say the value was changed so that defaultValue don't
1476 : // affect it no more.
1477 0 : if (aSetValueChanged) {
1478 0 : DoSetCheckedChanged(true, aNotify);
1479 : }
1480 :
1481 : // Don't do anything if we're not changing whether it's checked (it would
1482 : // screw up state actually, especially when you are setting radio button to
1483 : // false)
1484 0 : if (mChecked == aChecked) {
1485 0 : return NS_OK;
1486 : }
1487 :
1488 : // Set checked
1489 0 : if (mType != NS_FORM_INPUT_RADIO) {
1490 0 : SetCheckedInternal(aChecked, aNotify);
1491 0 : return NS_OK;
1492 : }
1493 :
1494 : // For radio button, we need to do some extra fun stuff
1495 0 : if (aChecked) {
1496 0 : return RadioSetChecked(aNotify);
1497 : }
1498 :
1499 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
1500 0 : if (container) {
1501 0 : nsAutoString name;
1502 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
1503 0 : container->SetCurrentRadioButton(name, nsnull);
1504 : }
1505 : // SetCheckedInternal is going to ask all radios to update their
1506 : // validity state. We have to be sure the radio group container knows
1507 : // the currently selected radio.
1508 0 : SetCheckedInternal(false, aNotify);
1509 0 : return NS_OK;
1510 : }
1511 :
1512 : nsresult
1513 0 : nsHTMLInputElement::RadioSetChecked(bool aNotify)
1514 : {
1515 : // Find the selected radio button so we can deselect it
1516 0 : nsCOMPtr<nsIDOMHTMLInputElement> currentlySelected = GetSelectedRadioButton();
1517 :
1518 : // Deselect the currently selected radio button
1519 0 : if (currentlySelected) {
1520 : // Pass true for the aNotify parameter since the currently selected
1521 : // button is already in the document.
1522 0 : static_cast<nsHTMLInputElement*>(currentlySelected.get())
1523 0 : ->SetCheckedInternal(false, true);
1524 : }
1525 :
1526 : // Let the group know that we are now the One True Radio Button
1527 0 : nsresult rv = NS_OK;
1528 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
1529 0 : if (container) {
1530 0 : nsAutoString name;
1531 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
1532 0 : rv = container->SetCurrentRadioButton(name, this);
1533 : }
1534 :
1535 : // SetCheckedInternal is going to ask all radios to update their
1536 : // validity state. We have to be sure the radio group container knows
1537 : // the currently selected radio.
1538 0 : if (NS_SUCCEEDED(rv)) {
1539 0 : SetCheckedInternal(true, aNotify);
1540 : }
1541 :
1542 0 : return rv;
1543 : }
1544 :
1545 : nsIRadioGroupContainer*
1546 0 : nsHTMLInputElement::GetRadioGroupContainer() const
1547 : {
1548 0 : NS_ASSERTION(mType == NS_FORM_INPUT_RADIO,
1549 : "GetRadioGroupContainer should only be called when type='radio'");
1550 :
1551 0 : nsAutoString name;
1552 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
1553 :
1554 0 : if (name.IsEmpty()) {
1555 0 : return nsnull;
1556 : }
1557 :
1558 0 : if (mForm) {
1559 0 : return mForm;
1560 : }
1561 :
1562 0 : return static_cast<nsDocument*>(GetCurrentDoc());
1563 : }
1564 :
1565 : already_AddRefed<nsIDOMHTMLInputElement>
1566 0 : nsHTMLInputElement::GetSelectedRadioButton()
1567 : {
1568 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
1569 0 : if (!container) {
1570 0 : return nsnull;
1571 : }
1572 :
1573 0 : nsAutoString name;
1574 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
1575 :
1576 0 : nsCOMPtr<nsIDOMHTMLInputElement> selected;
1577 0 : container->GetCurrentRadioButton(name, getter_AddRefs(selected));
1578 0 : return selected.forget();
1579 : }
1580 :
1581 : nsresult
1582 0 : nsHTMLInputElement::MaybeSubmitForm(nsPresContext* aPresContext)
1583 : {
1584 0 : if (!mForm) {
1585 : // Nothing to do here.
1586 0 : return NS_OK;
1587 : }
1588 :
1589 0 : nsCOMPtr<nsIPresShell> shell = aPresContext->GetPresShell();
1590 0 : if (!shell) {
1591 0 : return NS_OK;
1592 : }
1593 :
1594 : // Get the default submit element
1595 0 : nsIFormControl* submitControl = mForm->GetDefaultSubmitElement();
1596 0 : if (submitControl) {
1597 0 : nsCOMPtr<nsIContent> submitContent = do_QueryInterface(submitControl);
1598 0 : NS_ASSERTION(submitContent, "Form control not implementing nsIContent?!");
1599 : // Fire the button's onclick handler and let the button handle
1600 : // submitting the form.
1601 0 : nsMouseEvent event(true, NS_MOUSE_CLICK, nsnull, nsMouseEvent::eReal);
1602 0 : nsEventStatus status = nsEventStatus_eIgnore;
1603 0 : shell->HandleDOMEventWithTarget(submitContent, &event, &status);
1604 0 : } else if (mForm->HasSingleTextControl() &&
1605 0 : (mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate) ||
1606 0 : mForm->CheckValidFormSubmission())) {
1607 : // TODO: removing this code and have the submit event sent by the form,
1608 : // bug 592124.
1609 : // If there's only one text control, just submit the form
1610 : // Hold strong ref across the event
1611 0 : nsRefPtr<nsHTMLFormElement> form = mForm;
1612 0 : nsFormEvent event(true, NS_FORM_SUBMIT);
1613 0 : nsEventStatus status = nsEventStatus_eIgnore;
1614 0 : shell->HandleDOMEventWithTarget(mForm, &event, &status);
1615 : }
1616 :
1617 0 : return NS_OK;
1618 : }
1619 :
1620 : void
1621 0 : nsHTMLInputElement::SetCheckedInternal(bool aChecked, bool aNotify)
1622 : {
1623 : // Set the value
1624 0 : mChecked = aChecked;
1625 :
1626 : // Notify the frame
1627 0 : if (mType == NS_FORM_INPUT_CHECKBOX || mType == NS_FORM_INPUT_RADIO) {
1628 0 : nsIFrame* frame = GetPrimaryFrame();
1629 0 : if (frame) {
1630 0 : frame->InvalidateFrameSubtree();
1631 : }
1632 : }
1633 :
1634 0 : UpdateAllValidityStates(aNotify);
1635 :
1636 : // Notify the document that the CSS :checked pseudoclass for this element
1637 : // has changed state.
1638 0 : UpdateState(aNotify);
1639 0 : }
1640 :
1641 : NS_IMETHODIMP
1642 0 : nsHTMLInputElement::Focus()
1643 : {
1644 0 : if (mType != NS_FORM_INPUT_FILE) {
1645 0 : return nsGenericHTMLElement::Focus();
1646 : }
1647 :
1648 : // For file inputs, focus the button instead.
1649 0 : nsIFrame* frame = GetPrimaryFrame();
1650 0 : if (frame) {
1651 0 : for (nsIFrame* childFrame = frame->GetFirstPrincipalChild();
1652 : childFrame;
1653 : childFrame = childFrame->GetNextSibling()) {
1654 : // See if the child is a button control.
1655 : nsCOMPtr<nsIFormControl> formCtrl =
1656 0 : do_QueryInterface(childFrame->GetContent());
1657 0 : if (formCtrl && formCtrl->GetType() == NS_FORM_INPUT_BUTTON) {
1658 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(formCtrl);
1659 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
1660 0 : if (fm && element) {
1661 0 : fm->SetFocus(element, 0);
1662 : }
1663 : break;
1664 : }
1665 : }
1666 : }
1667 :
1668 0 : return NS_OK;
1669 : }
1670 :
1671 : NS_IMETHODIMP
1672 0 : nsHTMLInputElement::Select()
1673 : {
1674 0 : if (!IsSingleLineTextControl(false)) {
1675 0 : return NS_OK;
1676 : }
1677 :
1678 : // XXX Bug? We have to give the input focus before contents can be
1679 : // selected
1680 :
1681 0 : FocusTristate state = FocusState();
1682 0 : if (state == eUnfocusable) {
1683 0 : return NS_OK;
1684 : }
1685 :
1686 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
1687 :
1688 0 : nsRefPtr<nsPresContext> presContext = GetPresContext();
1689 0 : if (state == eInactiveWindow) {
1690 0 : if (fm)
1691 0 : fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
1692 0 : SelectAll(presContext);
1693 0 : return NS_OK;
1694 : }
1695 :
1696 0 : if (DispatchSelectEvent(presContext) && fm) {
1697 0 : fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
1698 :
1699 : // ensure that the element is actually focused
1700 0 : nsCOMPtr<nsIDOMElement> focusedElement;
1701 0 : fm->GetFocusedElement(getter_AddRefs(focusedElement));
1702 0 : if (SameCOMIdentity(static_cast<nsIDOMNode *>(this), focusedElement)) {
1703 : // Now Select all the text!
1704 0 : SelectAll(presContext);
1705 : }
1706 : }
1707 :
1708 0 : return NS_OK;
1709 : }
1710 :
1711 : bool
1712 0 : nsHTMLInputElement::DispatchSelectEvent(nsPresContext* aPresContext)
1713 : {
1714 0 : nsEventStatus status = nsEventStatus_eIgnore;
1715 :
1716 : // If already handling select event, don't dispatch a second.
1717 0 : if (!mHandlingSelectEvent) {
1718 0 : nsEvent event(nsContentUtils::IsCallerChrome(), NS_FORM_SELECTED);
1719 :
1720 0 : mHandlingSelectEvent = true;
1721 : nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this),
1722 0 : aPresContext, &event, nsnull, &status);
1723 0 : mHandlingSelectEvent = false;
1724 : }
1725 :
1726 : // If the DOM event was not canceled (e.g. by a JS event handler
1727 : // returning false)
1728 0 : return (status == nsEventStatus_eIgnore);
1729 : }
1730 :
1731 : void
1732 0 : nsHTMLInputElement::SelectAll(nsPresContext* aPresContext)
1733 : {
1734 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
1735 :
1736 0 : if (formControlFrame) {
1737 0 : formControlFrame->SetFormProperty(nsGkAtoms::select, EmptyString());
1738 : }
1739 0 : }
1740 :
1741 : NS_IMETHODIMP
1742 0 : nsHTMLInputElement::Click()
1743 : {
1744 0 : if (mType == NS_FORM_INPUT_FILE)
1745 0 : FireAsyncClickHandler();
1746 :
1747 0 : return nsGenericHTMLElement::Click();
1748 : }
1749 :
1750 : NS_IMETHODIMP
1751 0 : nsHTMLInputElement::FireAsyncClickHandler()
1752 : {
1753 0 : nsCOMPtr<nsIRunnable> event = new AsyncClickHandler(this);
1754 0 : return NS_DispatchToMainThread(event);
1755 : }
1756 :
1757 : bool
1758 0 : nsHTMLInputElement::NeedToInitializeEditorForEvent(nsEventChainPreVisitor& aVisitor) const
1759 : {
1760 : // We only need to initialize the editor for single line input controls because they
1761 : // are lazily initialized. We don't need to initialize the control for
1762 : // certain types of events, because we know that those events are safe to be
1763 : // handled without the editor being initialized. These events include:
1764 : // mousein/move/out, and DOM mutation events.
1765 0 : if (!IsSingleLineTextControl(false) ||
1766 : aVisitor.mEvent->eventStructType == NS_MUTATION_EVENT) {
1767 0 : return false;
1768 : }
1769 :
1770 0 : switch (aVisitor.mEvent->message) {
1771 : case NS_MOUSE_MOVE:
1772 : case NS_MOUSE_ENTER:
1773 : case NS_MOUSE_EXIT:
1774 : case NS_MOUSE_ENTER_SYNTH:
1775 : case NS_MOUSE_EXIT_SYNTH:
1776 0 : return false;
1777 : default:
1778 0 : return true;
1779 : }
1780 : }
1781 :
1782 : nsresult
1783 0 : nsHTMLInputElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
1784 : {
1785 : // Do not process any DOM events if the element is disabled
1786 0 : aVisitor.mCanHandle = false;
1787 0 : if (IsElementDisabledForEvents(aVisitor.mEvent->message, GetPrimaryFrame())) {
1788 0 : return NS_OK;
1789 : }
1790 :
1791 : // Initialize the editor if needed.
1792 0 : if (NeedToInitializeEditorForEvent(aVisitor)) {
1793 0 : nsITextControlFrame* textControlFrame = do_QueryFrame(GetPrimaryFrame());
1794 0 : if (textControlFrame)
1795 0 : textControlFrame->EnsureEditorInitialized();
1796 : }
1797 :
1798 : //FIXME Allow submission etc. also when there is no prescontext, Bug 329509.
1799 0 : if (!aVisitor.mPresContext) {
1800 0 : return nsGenericHTMLElement::PreHandleEvent(aVisitor);
1801 : }
1802 : //
1803 : // Web pages expect the value of a radio button or checkbox to be set
1804 : // *before* onclick and DOMActivate fire, and they expect that if they set
1805 : // the value explicitly during onclick or DOMActivate it will not be toggled
1806 : // or any such nonsense.
1807 : // In order to support that (bug 57137 and 58460 are examples) we toggle
1808 : // the checked attribute *first*, and then fire onclick. If the user
1809 : // returns false, we reset the control to the old checked value. Otherwise,
1810 : // we dispatch DOMActivate. If DOMActivate is cancelled, we also reset
1811 : // the control to the old checked value. We need to keep track of whether
1812 : // we've already toggled the state from onclick since the user could
1813 : // explicitly dispatch DOMActivate on the element.
1814 : //
1815 : // This is a compatibility hack.
1816 : //
1817 :
1818 : // Track whether we're in the outermost Dispatch invocation that will
1819 : // cause activation of the input. That is, if we're a click event, or a
1820 : // DOMActivate that was dispatched directly, this will be set, but if we're
1821 : // a DOMActivate dispatched from click handling, it will not be set.
1822 : bool outerActivateEvent =
1823 : (NS_IS_MOUSE_LEFT_CLICK(aVisitor.mEvent) ||
1824 0 : (aVisitor.mEvent->message == NS_UI_ACTIVATE && !mInInternalActivate));
1825 :
1826 0 : if (outerActivateEvent) {
1827 0 : aVisitor.mItemFlags |= NS_OUTER_ACTIVATE_EVENT;
1828 : }
1829 :
1830 0 : bool originalCheckedValue = false;
1831 :
1832 0 : if (outerActivateEvent) {
1833 0 : mCheckedIsToggled = false;
1834 :
1835 0 : switch(mType) {
1836 : case NS_FORM_INPUT_CHECKBOX:
1837 : {
1838 0 : if (mIndeterminate) {
1839 : // indeterminate is always set to FALSE when the checkbox is toggled
1840 0 : SetIndeterminateInternal(false, false);
1841 0 : aVisitor.mItemFlags |= NS_ORIGINAL_INDETERMINATE_VALUE;
1842 : }
1843 :
1844 0 : GetChecked(&originalCheckedValue);
1845 0 : DoSetChecked(!originalCheckedValue, true, true);
1846 0 : mCheckedIsToggled = true;
1847 : }
1848 0 : break;
1849 :
1850 : case NS_FORM_INPUT_RADIO:
1851 : {
1852 0 : nsCOMPtr<nsIDOMHTMLInputElement> selectedRadioButton = GetSelectedRadioButton();
1853 0 : aVisitor.mItemData = selectedRadioButton;
1854 :
1855 0 : originalCheckedValue = mChecked;
1856 0 : if (!originalCheckedValue) {
1857 0 : DoSetChecked(true, true, true);
1858 0 : mCheckedIsToggled = true;
1859 : }
1860 : }
1861 0 : break;
1862 :
1863 : case NS_FORM_INPUT_SUBMIT:
1864 : case NS_FORM_INPUT_IMAGE:
1865 0 : if(mForm) {
1866 : // tell the form that we are about to enter a click handler.
1867 : // that means that if there are scripted submissions, the
1868 : // latest one will be deferred until after the exit point of the handler.
1869 0 : mForm->OnSubmitClickBegin(this);
1870 : }
1871 0 : break;
1872 :
1873 : default:
1874 0 : break;
1875 : }
1876 : }
1877 :
1878 0 : if (originalCheckedValue) {
1879 0 : aVisitor.mItemFlags |= NS_ORIGINAL_CHECKED_VALUE;
1880 : }
1881 :
1882 : // If NS_EVENT_FLAG_NO_CONTENT_DISPATCH is set we will not allow content to handle
1883 : // this event. But to allow middle mouse button paste to work we must allow
1884 : // middle clicks to go to text fields anyway.
1885 0 : if (aVisitor.mEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH) {
1886 0 : aVisitor.mItemFlags |= NS_NO_CONTENT_DISPATCH;
1887 : }
1888 0 : if (IsSingleLineTextControl(false) &&
1889 : aVisitor.mEvent->message == NS_MOUSE_CLICK &&
1890 : aVisitor.mEvent->eventStructType == NS_MOUSE_EVENT &&
1891 : static_cast<nsMouseEvent*>(aVisitor.mEvent)->button ==
1892 : nsMouseEvent::eMiddleButton) {
1893 0 : aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_NO_CONTENT_DISPATCH;
1894 : }
1895 :
1896 : // We must cache type because mType may change during JS event (bug 2369)
1897 0 : aVisitor.mItemFlags |= mType;
1898 :
1899 : // Fire onchange (if necessary), before we do the blur, bug 357684.
1900 0 : if (aVisitor.mEvent->message == NS_BLUR_CONTENT) {
1901 0 : nsIFrame* primaryFrame = GetPrimaryFrame();
1902 0 : if (primaryFrame) {
1903 0 : nsITextControlFrame* textFrame = do_QueryFrame(primaryFrame);
1904 0 : if (textFrame) {
1905 0 : textFrame->CheckFireOnChange();
1906 : }
1907 : }
1908 : }
1909 :
1910 0 : return nsGenericHTMLFormElement::PreHandleEvent(aVisitor);
1911 : }
1912 :
1913 : static bool
1914 0 : SelectTextFieldOnFocus()
1915 : {
1916 0 : if (!gSelectTextFieldOnFocus) {
1917 0 : PRInt32 selectTextfieldsOnKeyFocus = -1;
1918 : nsresult rv =
1919 : LookAndFeel::GetInt(LookAndFeel::eIntID_SelectTextfieldsOnKeyFocus,
1920 0 : &selectTextfieldsOnKeyFocus);
1921 0 : if (NS_FAILED(rv)) {
1922 0 : gSelectTextFieldOnFocus = -1;
1923 : } else {
1924 0 : gSelectTextFieldOnFocus = selectTextfieldsOnKeyFocus != 0 ? 1 : -1;
1925 : }
1926 : }
1927 :
1928 0 : return gSelectTextFieldOnFocus == 1;
1929 : }
1930 :
1931 : nsresult
1932 0 : nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
1933 : {
1934 0 : if (!aVisitor.mPresContext) {
1935 0 : return NS_OK;
1936 : }
1937 :
1938 0 : if (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
1939 : aVisitor.mEvent->message == NS_BLUR_CONTENT) {
1940 :
1941 0 : UpdateValidityUIBits(aVisitor.mEvent->message == NS_FOCUS_CONTENT);
1942 :
1943 0 : UpdateState(true);
1944 : }
1945 :
1946 : // ignore the activate event fired by the "Browse..." button
1947 : // (file input controls fire their own) (bug 500885)
1948 0 : if (mType == NS_FORM_INPUT_FILE) {
1949 : nsCOMPtr<nsIContent> maybeButton =
1950 0 : do_QueryInterface(aVisitor.mEvent->originalTarget);
1951 0 : if (maybeButton &&
1952 0 : maybeButton->IsRootOfNativeAnonymousSubtree() &&
1953 0 : maybeButton->AttrValueIs(kNameSpaceID_None,
1954 : nsGkAtoms::type,
1955 : nsGkAtoms::button,
1956 0 : eCaseMatters)) {
1957 0 : return NS_OK;
1958 : }
1959 : }
1960 :
1961 0 : nsresult rv = NS_OK;
1962 0 : bool outerActivateEvent = !!(aVisitor.mItemFlags & NS_OUTER_ACTIVATE_EVENT);
1963 : bool originalCheckedValue =
1964 0 : !!(aVisitor.mItemFlags & NS_ORIGINAL_CHECKED_VALUE);
1965 0 : bool noContentDispatch = !!(aVisitor.mItemFlags & NS_NO_CONTENT_DISPATCH);
1966 0 : PRUint8 oldType = NS_CONTROL_TYPE(aVisitor.mItemFlags);
1967 : // Ideally we would make the default action for click and space just dispatch
1968 : // DOMActivate, and the default action for DOMActivate flip the checkbox/
1969 : // radio state and fire onchange. However, for backwards compatibility, we
1970 : // need to flip the state before firing click, and we need to fire click
1971 : // when space is pressed. So, we just nest the firing of DOMActivate inside
1972 : // the click event handling, and allow cancellation of DOMActivate to cancel
1973 : // the click.
1974 0 : if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault &&
1975 0 : !IsSingleLineTextControl(true) &&
1976 : NS_IS_MOUSE_LEFT_CLICK(aVisitor.mEvent)) {
1977 0 : nsUIEvent actEvent(NS_IS_TRUSTED_EVENT(aVisitor.mEvent), NS_UI_ACTIVATE, 1);
1978 :
1979 0 : nsCOMPtr<nsIPresShell> shell = aVisitor.mPresContext->GetPresShell();
1980 0 : if (shell) {
1981 0 : nsEventStatus status = nsEventStatus_eIgnore;
1982 0 : mInInternalActivate = true;
1983 0 : rv = shell->HandleDOMEventWithTarget(this, &actEvent, &status);
1984 0 : mInInternalActivate = false;
1985 :
1986 : // If activate is cancelled, we must do the same as when click is
1987 : // cancelled (revert the checkbox to its original value).
1988 0 : if (status == nsEventStatus_eConsumeNoDefault)
1989 0 : aVisitor.mEventStatus = status;
1990 : }
1991 : }
1992 :
1993 0 : if (outerActivateEvent) {
1994 0 : switch(oldType) {
1995 : case NS_FORM_INPUT_SUBMIT:
1996 : case NS_FORM_INPUT_IMAGE:
1997 0 : if(mForm) {
1998 : // tell the form that we are about to exit a click handler
1999 : // so the form knows not to defer subsequent submissions
2000 : // the pending ones that were created during the handler
2001 : // will be flushed or forgoten.
2002 0 : mForm->OnSubmitClickEnd();
2003 : }
2004 0 : break;
2005 : }
2006 : }
2007 :
2008 : // Reset the flag for other content besides this text field
2009 : aVisitor.mEvent->flags |=
2010 0 : noContentDispatch ? NS_EVENT_FLAG_NO_CONTENT_DISPATCH : NS_EVENT_FLAG_NONE;
2011 :
2012 : // now check to see if the event was "cancelled"
2013 0 : if (mCheckedIsToggled && outerActivateEvent) {
2014 0 : if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
2015 : // if it was cancelled and a radio button, then set the old
2016 : // selected btn to TRUE. if it is a checkbox then set it to its
2017 : // original value
2018 0 : if (oldType == NS_FORM_INPUT_RADIO) {
2019 : nsCOMPtr<nsIDOMHTMLInputElement> selectedRadioButton =
2020 0 : do_QueryInterface(aVisitor.mItemData);
2021 0 : if (selectedRadioButton) {
2022 0 : selectedRadioButton->SetChecked(true);
2023 : }
2024 : // If there was no checked radio button or this one is no longer a
2025 : // radio button we must reset it back to false to cancel the action.
2026 : // See how the web of hack grows?
2027 0 : if (!selectedRadioButton || mType != NS_FORM_INPUT_RADIO) {
2028 0 : DoSetChecked(false, true, true);
2029 : }
2030 0 : } else if (oldType == NS_FORM_INPUT_CHECKBOX) {
2031 : bool originalIndeterminateValue =
2032 0 : !!(aVisitor.mItemFlags & NS_ORIGINAL_INDETERMINATE_VALUE);
2033 0 : SetIndeterminateInternal(originalIndeterminateValue, false);
2034 0 : DoSetChecked(originalCheckedValue, true, true);
2035 : }
2036 : } else {
2037 : nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
2038 : static_cast<nsIDOMHTMLInputElement*>(this),
2039 0 : NS_LITERAL_STRING("change"), true,
2040 0 : false);
2041 : #ifdef ACCESSIBILITY
2042 : // Fire an event to notify accessibility
2043 0 : if (mType == NS_FORM_INPUT_CHECKBOX) {
2044 : FireEventForAccessibility(this, aVisitor.mPresContext,
2045 0 : NS_LITERAL_STRING("CheckboxStateChange"));
2046 : } else {
2047 : FireEventForAccessibility(this, aVisitor.mPresContext,
2048 0 : NS_LITERAL_STRING("RadioStateChange"));
2049 : // Fire event for the previous selected radio.
2050 : nsCOMPtr<nsIDOMHTMLInputElement> previous =
2051 0 : do_QueryInterface(aVisitor.mItemData);
2052 0 : if(previous) {
2053 : FireEventForAccessibility(previous, aVisitor.mPresContext,
2054 0 : NS_LITERAL_STRING("RadioStateChange"));
2055 : }
2056 : }
2057 : #endif
2058 : }
2059 : }
2060 :
2061 0 : if (NS_SUCCEEDED(rv)) {
2062 0 : if (nsEventStatus_eIgnore == aVisitor.mEventStatus) {
2063 0 : switch (aVisitor.mEvent->message) {
2064 :
2065 : case NS_FOCUS_CONTENT:
2066 : {
2067 : // see if we should select the contents of the textbox. This happens
2068 : // for text and password fields when the field was focused by the
2069 : // keyboard or a navigation, the platform allows it, and it wasn't
2070 : // just because we raised a window.
2071 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
2072 0 : if (fm && IsSingleLineTextControl(false) &&
2073 0 : !(static_cast<nsFocusEvent *>(aVisitor.mEvent))->fromRaise &&
2074 0 : SelectTextFieldOnFocus()) {
2075 0 : nsIDocument* document = GetCurrentDoc();
2076 0 : if (document) {
2077 : PRUint32 lastFocusMethod;
2078 0 : fm->GetLastFocusMethod(document->GetWindow(), &lastFocusMethod);
2079 0 : if (lastFocusMethod &
2080 : (nsIFocusManager::FLAG_BYKEY | nsIFocusManager::FLAG_BYMOVEFOCUS)) {
2081 0 : nsRefPtr<nsPresContext> presContext = GetPresContext();
2082 0 : if (DispatchSelectEvent(presContext)) {
2083 0 : SelectAll(presContext);
2084 : }
2085 : }
2086 : }
2087 : }
2088 0 : break;
2089 : }
2090 :
2091 : case NS_KEY_PRESS:
2092 : case NS_KEY_UP:
2093 : {
2094 : // For backwards compat, trigger checks/radios/buttons with
2095 : // space or enter (bug 25300)
2096 0 : nsKeyEvent * keyEvent = (nsKeyEvent *)aVisitor.mEvent;
2097 :
2098 0 : if ((aVisitor.mEvent->message == NS_KEY_PRESS &&
2099 : keyEvent->keyCode == NS_VK_RETURN) ||
2100 : (aVisitor.mEvent->message == NS_KEY_UP &&
2101 : keyEvent->keyCode == NS_VK_SPACE)) {
2102 0 : switch(mType) {
2103 : case NS_FORM_INPUT_CHECKBOX:
2104 : case NS_FORM_INPUT_RADIO:
2105 : {
2106 : // Checkbox and Radio try to submit on Enter press
2107 0 : if (keyEvent->keyCode != NS_VK_SPACE) {
2108 0 : MaybeSubmitForm(aVisitor.mPresContext);
2109 :
2110 0 : break; // If we are submitting, do not send click event
2111 : }
2112 : // else fall through and treat Space like click...
2113 : }
2114 : case NS_FORM_INPUT_BUTTON:
2115 : case NS_FORM_INPUT_RESET:
2116 : case NS_FORM_INPUT_SUBMIT:
2117 : case NS_FORM_INPUT_IMAGE: // Bug 34418
2118 : {
2119 : nsMouseEvent event(NS_IS_TRUSTED_EVENT(aVisitor.mEvent),
2120 0 : NS_MOUSE_CLICK, nsnull, nsMouseEvent::eReal);
2121 0 : event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
2122 0 : nsEventStatus status = nsEventStatus_eIgnore;
2123 :
2124 : nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this),
2125 : aVisitor.mPresContext, &event,
2126 0 : nsnull, &status);
2127 0 : aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
2128 : } // case
2129 : } // switch
2130 : }
2131 0 : if (aVisitor.mEvent->message == NS_KEY_PRESS &&
2132 0 : mType == NS_FORM_INPUT_RADIO && !keyEvent->isAlt &&
2133 0 : !keyEvent->isControl && !keyEvent->isMeta) {
2134 0 : bool isMovingBack = false;
2135 0 : switch (keyEvent->keyCode) {
2136 : case NS_VK_UP:
2137 : case NS_VK_LEFT:
2138 0 : isMovingBack = true;
2139 : // FALLTHROUGH
2140 : case NS_VK_DOWN:
2141 : case NS_VK_RIGHT:
2142 : // Arrow key pressed, focus+select prev/next radio button
2143 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
2144 0 : if (container) {
2145 0 : nsAutoString name;
2146 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
2147 0 : nsCOMPtr<nsIDOMHTMLInputElement> selectedRadioButton;
2148 : container->GetNextRadioButton(name, isMovingBack, this,
2149 0 : getter_AddRefs(selectedRadioButton));
2150 : nsCOMPtr<nsIContent> radioContent =
2151 0 : do_QueryInterface(selectedRadioButton);
2152 0 : if (radioContent) {
2153 0 : rv = selectedRadioButton->Focus();
2154 0 : if (NS_SUCCEEDED(rv)) {
2155 0 : nsEventStatus status = nsEventStatus_eIgnore;
2156 : nsMouseEvent event(NS_IS_TRUSTED_EVENT(aVisitor.mEvent),
2157 : NS_MOUSE_CLICK, nsnull,
2158 0 : nsMouseEvent::eReal);
2159 0 : event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
2160 : rv = nsEventDispatcher::Dispatch(radioContent,
2161 : aVisitor.mPresContext,
2162 0 : &event, nsnull, &status);
2163 0 : if (NS_SUCCEEDED(rv)) {
2164 0 : aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
2165 : }
2166 : }
2167 : }
2168 : }
2169 : }
2170 : }
2171 :
2172 : /*
2173 : * For some input types, if the user hits enter, the form is submitted.
2174 : *
2175 : * Bug 99920, bug 109463 and bug 147850:
2176 : * (a) if there is a submit control in the form, click the first
2177 : * submit control in the form.
2178 : * (b) if there is just one text control in the form, submit by
2179 : * sending a submit event directly to the form
2180 : * (c) if there is more than one text input and no submit buttons, do
2181 : * not submit, period.
2182 : */
2183 :
2184 0 : if (aVisitor.mEvent->message == NS_KEY_PRESS &&
2185 : (keyEvent->keyCode == NS_VK_RETURN ||
2186 : keyEvent->keyCode == NS_VK_ENTER) &&
2187 0 : IsSingleLineTextControl(false, mType)) {
2188 0 : nsIFrame* primaryFrame = GetPrimaryFrame();
2189 0 : if (primaryFrame) {
2190 0 : nsITextControlFrame* textFrame = do_QueryFrame(primaryFrame);
2191 :
2192 : // Fire onChange (if necessary)
2193 0 : if (textFrame) {
2194 0 : textFrame->CheckFireOnChange();
2195 : }
2196 : }
2197 :
2198 0 : rv = MaybeSubmitForm(aVisitor.mPresContext);
2199 0 : NS_ENSURE_SUCCESS(rv, rv);
2200 : }
2201 :
2202 0 : } break; // NS_KEY_PRESS || NS_KEY_UP
2203 :
2204 : case NS_MOUSE_BUTTON_DOWN:
2205 : case NS_MOUSE_BUTTON_UP:
2206 : case NS_MOUSE_DOUBLECLICK:
2207 : {
2208 : // cancel all of these events for buttons
2209 : //XXXsmaug Why?
2210 0 : if (aVisitor.mEvent->eventStructType == NS_MOUSE_EVENT &&
2211 : (static_cast<nsMouseEvent*>(aVisitor.mEvent)->button ==
2212 : nsMouseEvent::eMiddleButton ||
2213 : static_cast<nsMouseEvent*>(aVisitor.mEvent)->button ==
2214 : nsMouseEvent::eRightButton)) {
2215 0 : if (mType == NS_FORM_INPUT_BUTTON ||
2216 : mType == NS_FORM_INPUT_RESET ||
2217 : mType == NS_FORM_INPUT_SUBMIT) {
2218 0 : if (aVisitor.mDOMEvent) {
2219 0 : aVisitor.mDOMEvent->StopPropagation();
2220 : } else {
2221 0 : rv = NS_ERROR_FAILURE;
2222 : }
2223 : }
2224 :
2225 : }
2226 0 : break;
2227 : }
2228 : default:
2229 0 : break;
2230 : }
2231 :
2232 0 : if (outerActivateEvent) {
2233 0 : if (mForm && (oldType == NS_FORM_INPUT_SUBMIT ||
2234 : oldType == NS_FORM_INPUT_IMAGE)) {
2235 0 : if (mType != NS_FORM_INPUT_SUBMIT && mType != NS_FORM_INPUT_IMAGE) {
2236 : // If the type has changed to a non-submit type, then we want to
2237 : // flush the stored submission if there is one (as if the submit()
2238 : // was allowed to succeed)
2239 0 : mForm->FlushPendingSubmission();
2240 : }
2241 : }
2242 0 : switch(mType) {
2243 : case NS_FORM_INPUT_RESET:
2244 : case NS_FORM_INPUT_SUBMIT:
2245 : case NS_FORM_INPUT_IMAGE:
2246 0 : if (mForm) {
2247 : nsFormEvent event(true, (mType == NS_FORM_INPUT_RESET) ?
2248 0 : NS_FORM_RESET : NS_FORM_SUBMIT);
2249 0 : event.originator = this;
2250 0 : nsEventStatus status = nsEventStatus_eIgnore;
2251 :
2252 : nsCOMPtr<nsIPresShell> presShell =
2253 0 : aVisitor.mPresContext->GetPresShell();
2254 :
2255 : // If |nsIPresShell::Destroy| has been called due to
2256 : // handling the event the pres context will return a null
2257 : // pres shell. See bug 125624.
2258 : // TODO: removing this code and have the submit event sent by the
2259 : // form, see bug 592124.
2260 0 : if (presShell && (event.message != NS_FORM_SUBMIT ||
2261 0 : mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate) ||
2262 : // We know the element is a submit control, if this check is moved,
2263 : // make sure formnovalidate is used only if it's a submit control.
2264 0 : HasAttr(kNameSpaceID_None, nsGkAtoms::formnovalidate) ||
2265 0 : mForm->CheckValidFormSubmission())) {
2266 : // Hold a strong ref while dispatching
2267 0 : nsRefPtr<nsHTMLFormElement> form(mForm);
2268 0 : presShell->HandleDOMEventWithTarget(mForm, &event, &status);
2269 0 : aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
2270 : }
2271 : }
2272 0 : break;
2273 :
2274 : default:
2275 0 : break;
2276 : } //switch
2277 : } //click or outer activate event
2278 0 : } else if (outerActivateEvent &&
2279 : (oldType == NS_FORM_INPUT_SUBMIT ||
2280 : oldType == NS_FORM_INPUT_IMAGE) &&
2281 : mForm) {
2282 : // tell the form to flush a possible pending submission.
2283 : // the reason is that the script returned false (the event was
2284 : // not ignored) so if there is a stored submission, it needs to
2285 : // be submitted immediately.
2286 0 : mForm->FlushPendingSubmission();
2287 : }
2288 : } // if
2289 :
2290 0 : return rv;
2291 : }
2292 :
2293 : void
2294 0 : nsHTMLInputElement::MaybeLoadImage()
2295 : {
2296 : // Our base URI may have changed; claim that our URI changed, and the
2297 : // nsImageLoadingContent will decide whether a new image load is warranted.
2298 0 : nsAutoString uri;
2299 0 : if (mType == NS_FORM_INPUT_IMAGE &&
2300 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::src, uri) &&
2301 0 : (NS_FAILED(LoadImage(uri, false, true)) ||
2302 0 : !LoadingEnabled())) {
2303 0 : CancelImageRequests(true);
2304 : }
2305 0 : }
2306 :
2307 : nsresult
2308 1 : nsHTMLInputElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
2309 : nsIContent* aBindingParent,
2310 : bool aCompileEventHandlers)
2311 : {
2312 : nsresult rv = nsGenericHTMLFormElement::BindToTree(aDocument, aParent,
2313 : aBindingParent,
2314 1 : aCompileEventHandlers);
2315 1 : NS_ENSURE_SUCCESS(rv, rv);
2316 :
2317 1 : if (mType == NS_FORM_INPUT_IMAGE) {
2318 : // Our base URI may have changed; claim that our URI changed, and the
2319 : // nsImageLoadingContent will decide whether a new image load is warranted.
2320 0 : if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
2321 : // FIXME: Bug 660963 it would be nice if we could just have
2322 : // ClearBrokenState update our state and do it fast...
2323 0 : ClearBrokenState();
2324 0 : RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
2325 : nsContentUtils::AddScriptRunner(
2326 0 : NS_NewRunnableMethod(this, &nsHTMLInputElement::MaybeLoadImage));
2327 : }
2328 : }
2329 :
2330 : // Add radio to document if we don't have a form already (if we do it's
2331 : // already been added into that group)
2332 1 : if (aDocument && !mForm && mType == NS_FORM_INPUT_RADIO) {
2333 0 : AddedToRadioGroup();
2334 : }
2335 :
2336 : // An element can't suffer from value missing if it is not in a document.
2337 : // We have to check if we suffer from that as we are now in a document.
2338 1 : UpdateValueMissingValidityState();
2339 :
2340 : // If there is a disabled fieldset in the parent chain, the element is now
2341 : // barred from constraint validation and can't suffer from value missing
2342 : // (call done before).
2343 1 : UpdateBarredFromConstraintValidation();
2344 :
2345 : // And now make sure our state is up to date
2346 1 : UpdateState(false);
2347 :
2348 1 : return rv;
2349 : }
2350 :
2351 : void
2352 3 : nsHTMLInputElement::UnbindFromTree(bool aDeep, bool aNullParent)
2353 : {
2354 : // If we have a form and are unbound from it,
2355 : // nsGenericHTMLFormElement::UnbindFromTree() will unset the form and
2356 : // that takes care of form's WillRemove so we just have to take care
2357 : // of the case where we're removing from the document and we don't
2358 : // have a form
2359 3 : if (!mForm && mType == NS_FORM_INPUT_RADIO) {
2360 0 : WillRemoveFromRadioGroup();
2361 : }
2362 :
2363 3 : nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
2364 :
2365 : // GetCurrentDoc is returning nsnull so we can update the value
2366 : // missing validity state to reflect we are no longer into a doc.
2367 3 : UpdateValueMissingValidityState();
2368 : // We might be no longer disabled because of parent chain changed.
2369 3 : UpdateBarredFromConstraintValidation();
2370 :
2371 : // And now make sure our state is up to date
2372 3 : UpdateState(false);
2373 3 : }
2374 :
2375 : void
2376 0 : nsHTMLInputElement::HandleTypeChange(PRUint8 aNewType)
2377 : {
2378 0 : ValueModeType aOldValueMode = GetValueMode();
2379 0 : nsAutoString aOldValue;
2380 :
2381 0 : if (aOldValueMode == VALUE_MODE_VALUE && !mParserCreating) {
2382 0 : GetValue(aOldValue);
2383 : }
2384 :
2385 : // Only single line text inputs have a text editor state.
2386 0 : bool isNewTypeSingleLine = IsSingleLineTextControl(false, aNewType);
2387 0 : bool isCurrentTypeSingleLine = IsSingleLineTextControl(false, mType);
2388 :
2389 0 : if (isNewTypeSingleLine && !isCurrentTypeSingleLine) {
2390 0 : FreeData();
2391 0 : mInputData.mState = new nsTextEditorState(this);
2392 0 : NS_ADDREF(mInputData.mState);
2393 0 : } else if (isCurrentTypeSingleLine && !isNewTypeSingleLine) {
2394 0 : FreeData();
2395 : }
2396 :
2397 0 : mType = aNewType;
2398 :
2399 0 : if (!mParserCreating) {
2400 : /**
2401 : * The following code is trying to reproduce the algorithm described here:
2402 : * http://www.whatwg.org/specs/web-apps/current-work/complete.html#input-type-change
2403 : */
2404 0 : switch (GetValueMode()) {
2405 : case VALUE_MODE_DEFAULT:
2406 : case VALUE_MODE_DEFAULT_ON:
2407 : // If the previous value mode was value, we need to set the value content
2408 : // attribute to the previous value.
2409 : // There is no value sanitizing algorithm for elements in this mode.
2410 0 : if (aOldValueMode == VALUE_MODE_VALUE && !aOldValue.IsEmpty()) {
2411 0 : SetAttr(kNameSpaceID_None, nsGkAtoms::value, aOldValue, true);
2412 : }
2413 0 : break;
2414 : case VALUE_MODE_VALUE:
2415 : // If the previous value mode wasn't value, we have to set the value to
2416 : // the value content attribute.
2417 : // SetValueInternal is going to sanitize the value.
2418 : {
2419 0 : nsAutoString value;
2420 0 : if (aOldValueMode != VALUE_MODE_VALUE) {
2421 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::value, value);
2422 : } else {
2423 : // We get the current value so we can sanitize it.
2424 0 : GetValue(value);
2425 : }
2426 0 : SetValueInternal(value, false, false);
2427 : }
2428 0 : break;
2429 : case VALUE_MODE_FILENAME:
2430 : default:
2431 : // We don't care about the value.
2432 : // There is no value sanitizing algorithm for elements in this mode.
2433 0 : break;
2434 : }
2435 : }
2436 :
2437 : // Do not notify, it will be done after if needed.
2438 0 : UpdateAllValidityStates(false);
2439 0 : }
2440 :
2441 : void
2442 2 : nsHTMLInputElement::SanitizeValue(nsAString& aValue)
2443 : {
2444 2 : NS_ASSERTION(!mParserCreating, "The element parsing should be finished!");
2445 :
2446 2 : switch (mType) {
2447 : case NS_FORM_INPUT_TEXT:
2448 : case NS_FORM_INPUT_SEARCH:
2449 : case NS_FORM_INPUT_TEL:
2450 : case NS_FORM_INPUT_PASSWORD:
2451 : {
2452 2 : PRUnichar crlf[] = { PRUnichar('\r'), PRUnichar('\n'), 0 };
2453 2 : aValue.StripChars(crlf);
2454 : }
2455 2 : break;
2456 : case NS_FORM_INPUT_EMAIL:
2457 : case NS_FORM_INPUT_URL:
2458 : {
2459 0 : PRUnichar crlf[] = { PRUnichar('\r'), PRUnichar('\n'), 0 };
2460 0 : aValue.StripChars(crlf);
2461 :
2462 0 : aValue = nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(aValue);
2463 : }
2464 0 : break;
2465 : }
2466 2 : }
2467 :
2468 : bool
2469 0 : nsHTMLInputElement::ParseAttribute(PRInt32 aNamespaceID,
2470 : nsIAtom* aAttribute,
2471 : const nsAString& aValue,
2472 : nsAttrValue& aResult)
2473 : {
2474 0 : if (aNamespaceID == kNameSpaceID_None) {
2475 0 : if (aAttribute == nsGkAtoms::type) {
2476 : // XXX ARG!! This is major evilness. ParseAttribute
2477 : // shouldn't set members. Override SetAttr instead
2478 : PRInt32 newType;
2479 0 : bool success = aResult.ParseEnumValue(aValue, kInputTypeTable, false);
2480 0 : if (success) {
2481 0 : newType = aResult.GetEnumValue();
2482 : } else {
2483 0 : newType = kInputDefaultType->value;
2484 : }
2485 :
2486 0 : if (newType != mType) {
2487 : // Make sure to do the check for newType being NS_FORM_INPUT_FILE and
2488 : // the corresponding SetValueInternal() call _before_ we set mType.
2489 : // That way the logic in SetValueInternal() will work right (that logic
2490 : // makes assumptions about our frame based on mType, but we won't have
2491 : // had time to recreate frames yet -- that happens later in the
2492 : // SetAttr() process).
2493 0 : if (newType == NS_FORM_INPUT_FILE || mType == NS_FORM_INPUT_FILE) {
2494 : // This call isn't strictly needed any more since we'll never
2495 : // confuse values and filenames. However it's there for backwards
2496 : // compat.
2497 0 : ClearFiles(false);
2498 : }
2499 :
2500 0 : HandleTypeChange(newType);
2501 : }
2502 :
2503 0 : return success;
2504 : }
2505 0 : if (aAttribute == nsGkAtoms::width) {
2506 0 : return aResult.ParseSpecialIntValue(aValue);
2507 : }
2508 0 : if (aAttribute == nsGkAtoms::height) {
2509 0 : return aResult.ParseSpecialIntValue(aValue);
2510 : }
2511 0 : if (aAttribute == nsGkAtoms::maxlength) {
2512 0 : return aResult.ParseNonNegativeIntValue(aValue);
2513 : }
2514 0 : if (aAttribute == nsGkAtoms::size) {
2515 0 : return aResult.ParsePositiveIntValue(aValue);
2516 : }
2517 0 : if (aAttribute == nsGkAtoms::border) {
2518 0 : return aResult.ParseIntWithBounds(aValue, 0);
2519 : }
2520 0 : if (aAttribute == nsGkAtoms::align) {
2521 0 : return ParseAlignValue(aValue, aResult);
2522 : }
2523 0 : if (aAttribute == nsGkAtoms::formmethod) {
2524 0 : return aResult.ParseEnumValue(aValue, kFormMethodTable, false);
2525 : }
2526 0 : if (aAttribute == nsGkAtoms::formenctype) {
2527 0 : return aResult.ParseEnumValue(aValue, kFormEnctypeTable, false);
2528 : }
2529 0 : if (aAttribute == nsGkAtoms::autocomplete) {
2530 0 : return aResult.ParseEnumValue(aValue, kInputAutocompleteTable, false);
2531 : }
2532 0 : if (ParseImageAttribute(aAttribute, aValue, aResult)) {
2533 : // We have to call |ParseImageAttribute| unconditionally since we
2534 : // don't know if we're going to have a type="image" attribute yet,
2535 : // (or could have it set dynamically in the future). See bug
2536 : // 214077.
2537 0 : return true;
2538 : }
2539 : }
2540 :
2541 : return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
2542 0 : aResult);
2543 : }
2544 :
2545 : static void
2546 0 : MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
2547 : nsRuleData* aData)
2548 : {
2549 0 : const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::type);
2550 0 : if (value && value->Type() == nsAttrValue::eEnum &&
2551 0 : value->GetEnumValue() == NS_FORM_INPUT_IMAGE) {
2552 0 : nsGenericHTMLFormElement::MapImageBorderAttributeInto(aAttributes, aData);
2553 0 : nsGenericHTMLFormElement::MapImageMarginAttributeInto(aAttributes, aData);
2554 0 : nsGenericHTMLFormElement::MapImageSizeAttributesInto(aAttributes, aData);
2555 : // Images treat align as "float"
2556 0 : nsGenericHTMLFormElement::MapImageAlignAttributeInto(aAttributes, aData);
2557 : }
2558 :
2559 0 : nsGenericHTMLFormElement::MapCommonAttributesInto(aAttributes, aData);
2560 0 : }
2561 :
2562 : nsChangeHint
2563 0 : nsHTMLInputElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
2564 : PRInt32 aModType) const
2565 : {
2566 : nsChangeHint retval =
2567 0 : nsGenericHTMLFormElement::GetAttributeChangeHint(aAttribute, aModType);
2568 0 : if (aAttribute == nsGkAtoms::type) {
2569 0 : NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
2570 0 : } else if (mType == NS_FORM_INPUT_IMAGE &&
2571 : (aAttribute == nsGkAtoms::alt ||
2572 : aAttribute == nsGkAtoms::value)) {
2573 : // We might need to rebuild our alt text. Just go ahead and
2574 : // reconstruct our frame. This should be quite rare..
2575 0 : NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
2576 0 : } else if (aAttribute == nsGkAtoms::value) {
2577 0 : NS_UpdateHint(retval, NS_STYLE_HINT_REFLOW);
2578 0 : } else if (aAttribute == nsGkAtoms::size &&
2579 0 : IsSingleLineTextControl(false)) {
2580 0 : NS_UpdateHint(retval, NS_STYLE_HINT_REFLOW);
2581 0 : } else if (PlaceholderApplies() && aAttribute == nsGkAtoms::placeholder) {
2582 0 : NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
2583 : }
2584 0 : return retval;
2585 : }
2586 :
2587 : NS_IMETHODIMP_(bool)
2588 0 : nsHTMLInputElement::IsAttributeMapped(const nsIAtom* aAttribute) const
2589 : {
2590 : static const MappedAttributeEntry attributes[] = {
2591 : { &nsGkAtoms::align },
2592 : { &nsGkAtoms::type },
2593 : { nsnull },
2594 : };
2595 :
2596 : static const MappedAttributeEntry* const map[] = {
2597 : attributes,
2598 : sCommonAttributeMap,
2599 : sImageMarginSizeAttributeMap,
2600 : sImageBorderAttributeMap,
2601 : };
2602 :
2603 0 : return FindAttributeDependence(aAttribute, map);
2604 : }
2605 :
2606 : nsMapRuleToAttributesFunc
2607 0 : nsHTMLInputElement::GetAttributeMappingFunction() const
2608 : {
2609 0 : return &MapAttributesIntoRule;
2610 : }
2611 :
2612 :
2613 : // Controllers Methods
2614 :
2615 : NS_IMETHODIMP
2616 0 : nsHTMLInputElement::GetControllers(nsIControllers** aResult)
2617 : {
2618 0 : NS_ENSURE_ARG_POINTER(aResult);
2619 :
2620 : //XXX: what about type "file"?
2621 0 : if (IsSingleLineTextControl(false))
2622 : {
2623 0 : if (!mControllers)
2624 : {
2625 : nsresult rv;
2626 0 : mControllers = do_CreateInstance(kXULControllersCID, &rv);
2627 0 : NS_ENSURE_SUCCESS(rv, rv);
2628 :
2629 : nsCOMPtr<nsIController>
2630 : controller(do_CreateInstance("@mozilla.org/editor/editorcontroller;1",
2631 0 : &rv));
2632 0 : NS_ENSURE_SUCCESS(rv, rv);
2633 :
2634 0 : mControllers->AppendController(controller);
2635 :
2636 : controller = do_CreateInstance("@mozilla.org/editor/editingcontroller;1",
2637 0 : &rv);
2638 0 : NS_ENSURE_SUCCESS(rv, rv);
2639 :
2640 0 : mControllers->AppendController(controller);
2641 : }
2642 : }
2643 :
2644 0 : *aResult = mControllers;
2645 0 : NS_IF_ADDREF(*aResult);
2646 :
2647 0 : return NS_OK;
2648 : }
2649 :
2650 : NS_IMETHODIMP
2651 0 : nsHTMLInputElement::GetTextLength(PRInt32* aTextLength)
2652 : {
2653 0 : nsAutoString val;
2654 :
2655 0 : nsresult rv = GetValue(val);
2656 :
2657 0 : *aTextLength = val.Length();
2658 :
2659 0 : return rv;
2660 : }
2661 :
2662 : NS_IMETHODIMP
2663 0 : nsHTMLInputElement::SetSelectionRange(PRInt32 aSelectionStart,
2664 : PRInt32 aSelectionEnd,
2665 : const nsAString& aDirection)
2666 : {
2667 0 : nsresult rv = NS_ERROR_FAILURE;
2668 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
2669 :
2670 0 : if (formControlFrame) {
2671 0 : nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
2672 0 : if (textControlFrame) {
2673 : // Default to forward, even if not specified.
2674 : // Note that we don't currently support directionless selections, so
2675 : // "none" is treated like "forward".
2676 0 : nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eForward;
2677 0 : if (aDirection.EqualsLiteral("backward")) {
2678 0 : dir = nsITextControlFrame::eBackward;
2679 : }
2680 :
2681 0 : rv = textControlFrame->SetSelectionRange(aSelectionStart, aSelectionEnd, dir);
2682 0 : if (NS_SUCCEEDED(rv)) {
2683 0 : rv = textControlFrame->ScrollSelectionIntoView();
2684 : }
2685 : }
2686 : }
2687 :
2688 0 : return rv;
2689 : }
2690 :
2691 : NS_IMETHODIMP
2692 0 : nsHTMLInputElement::GetSelectionStart(PRInt32* aSelectionStart)
2693 : {
2694 0 : NS_ENSURE_ARG_POINTER(aSelectionStart);
2695 :
2696 : PRInt32 selEnd;
2697 0 : nsresult rv = GetSelectionRange(aSelectionStart, &selEnd);
2698 :
2699 0 : if (NS_FAILED(rv)) {
2700 0 : nsTextEditorState *state = GetEditorState();
2701 0 : if (state && state->IsSelectionCached()) {
2702 0 : *aSelectionStart = state->GetSelectionProperties().mStart;
2703 0 : return NS_OK;
2704 : }
2705 : }
2706 0 : return rv;
2707 : }
2708 :
2709 : NS_IMETHODIMP
2710 0 : nsHTMLInputElement::SetSelectionStart(PRInt32 aSelectionStart)
2711 : {
2712 0 : nsTextEditorState *state = GetEditorState();
2713 0 : if (state && state->IsSelectionCached()) {
2714 0 : state->GetSelectionProperties().mStart = aSelectionStart;
2715 0 : return NS_OK;
2716 : }
2717 :
2718 0 : nsAutoString direction;
2719 0 : nsresult rv = GetSelectionDirection(direction);
2720 0 : NS_ENSURE_SUCCESS(rv, rv);
2721 : PRInt32 start, end;
2722 0 : rv = GetSelectionRange(&start, &end);
2723 0 : NS_ENSURE_SUCCESS(rv, rv);
2724 0 : start = aSelectionStart;
2725 0 : if (end < start) {
2726 0 : end = start;
2727 : }
2728 0 : return SetSelectionRange(start, end, direction);
2729 : }
2730 :
2731 : NS_IMETHODIMP
2732 0 : nsHTMLInputElement::GetSelectionEnd(PRInt32* aSelectionEnd)
2733 : {
2734 0 : NS_ENSURE_ARG_POINTER(aSelectionEnd);
2735 :
2736 : PRInt32 selStart;
2737 0 : nsresult rv = GetSelectionRange(&selStart, aSelectionEnd);
2738 :
2739 0 : if (NS_FAILED(rv)) {
2740 0 : nsTextEditorState *state = GetEditorState();
2741 0 : if (state && state->IsSelectionCached()) {
2742 0 : *aSelectionEnd = state->GetSelectionProperties().mEnd;
2743 0 : return NS_OK;
2744 : }
2745 : }
2746 0 : return rv;
2747 : }
2748 :
2749 :
2750 : NS_IMETHODIMP
2751 0 : nsHTMLInputElement::SetSelectionEnd(PRInt32 aSelectionEnd)
2752 : {
2753 0 : nsTextEditorState *state = GetEditorState();
2754 0 : if (state && state->IsSelectionCached()) {
2755 0 : state->GetSelectionProperties().mEnd = aSelectionEnd;
2756 0 : return NS_OK;
2757 : }
2758 :
2759 0 : nsAutoString direction;
2760 0 : nsresult rv = GetSelectionDirection(direction);
2761 0 : NS_ENSURE_SUCCESS(rv, rv);
2762 : PRInt32 start, end;
2763 0 : rv = GetSelectionRange(&start, &end);
2764 0 : NS_ENSURE_SUCCESS(rv, rv);
2765 0 : end = aSelectionEnd;
2766 0 : if (start > end) {
2767 0 : start = end;
2768 : }
2769 0 : return SetSelectionRange(start, end, direction);
2770 : }
2771 :
2772 : NS_IMETHODIMP
2773 0 : nsHTMLInputElement::GetFiles(nsIDOMFileList** aFileList)
2774 : {
2775 0 : *aFileList = nsnull;
2776 :
2777 0 : if (mType != NS_FORM_INPUT_FILE) {
2778 0 : return NS_OK;
2779 : }
2780 :
2781 0 : if (!mFileList) {
2782 0 : mFileList = new nsDOMFileList(static_cast<nsIContent*>(this));
2783 0 : if (!mFileList) return NS_ERROR_OUT_OF_MEMORY;
2784 :
2785 0 : UpdateFileList();
2786 : }
2787 :
2788 0 : NS_ADDREF(*aFileList = mFileList);
2789 :
2790 0 : return NS_OK;
2791 : }
2792 :
2793 : nsresult
2794 0 : nsHTMLInputElement::GetSelectionRange(PRInt32* aSelectionStart,
2795 : PRInt32* aSelectionEnd)
2796 : {
2797 0 : nsresult rv = NS_ERROR_FAILURE;
2798 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
2799 :
2800 0 : if (formControlFrame) {
2801 0 : nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
2802 0 : if (textControlFrame)
2803 0 : rv = textControlFrame->GetSelectionRange(aSelectionStart, aSelectionEnd);
2804 : }
2805 :
2806 0 : return rv;
2807 : }
2808 :
2809 : static void
2810 0 : DirectionToName(nsITextControlFrame::SelectionDirection dir, nsAString& aDirection)
2811 : {
2812 0 : if (dir == nsITextControlFrame::eNone) {
2813 0 : aDirection.AssignLiteral("none");
2814 0 : } else if (dir == nsITextControlFrame::eForward) {
2815 0 : aDirection.AssignLiteral("forward");
2816 0 : } else if (dir == nsITextControlFrame::eBackward) {
2817 0 : aDirection.AssignLiteral("backward");
2818 : } else {
2819 0 : NS_NOTREACHED("Invalid SelectionDirection value");
2820 : }
2821 0 : }
2822 :
2823 : NS_IMETHODIMP
2824 0 : nsHTMLInputElement::GetSelectionDirection(nsAString& aDirection)
2825 : {
2826 0 : nsresult rv = NS_ERROR_FAILURE;
2827 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
2828 :
2829 0 : if (formControlFrame) {
2830 0 : nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
2831 0 : if (textControlFrame) {
2832 : nsITextControlFrame::SelectionDirection dir;
2833 0 : rv = textControlFrame->GetSelectionRange(nsnull, nsnull, &dir);
2834 0 : if (NS_SUCCEEDED(rv)) {
2835 0 : DirectionToName(dir, aDirection);
2836 : }
2837 : }
2838 : }
2839 :
2840 0 : if (NS_FAILED(rv)) {
2841 0 : nsTextEditorState *state = GetEditorState();
2842 0 : if (state && state->IsSelectionCached()) {
2843 0 : DirectionToName(state->GetSelectionProperties().mDirection, aDirection);
2844 0 : return NS_OK;
2845 : }
2846 : }
2847 :
2848 0 : return rv;
2849 : }
2850 :
2851 : NS_IMETHODIMP
2852 0 : nsHTMLInputElement::SetSelectionDirection(const nsAString& aDirection) {
2853 0 : nsTextEditorState *state = GetEditorState();
2854 0 : if (state && state->IsSelectionCached()) {
2855 0 : nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eNone;
2856 0 : if (aDirection.EqualsLiteral("forward")) {
2857 0 : dir = nsITextControlFrame::eForward;
2858 0 : } else if (aDirection.EqualsLiteral("backward")) {
2859 0 : dir = nsITextControlFrame::eBackward;
2860 : }
2861 0 : state->GetSelectionProperties().mDirection = dir;
2862 0 : return NS_OK;
2863 : }
2864 :
2865 : PRInt32 start, end;
2866 0 : nsresult rv = GetSelectionRange(&start, &end);
2867 0 : if (NS_SUCCEEDED(rv)) {
2868 0 : rv = SetSelectionRange(start, end, aDirection);
2869 : }
2870 :
2871 0 : return rv;
2872 : }
2873 :
2874 : NS_IMETHODIMP
2875 0 : nsHTMLInputElement::GetPhonetic(nsAString& aPhonetic)
2876 : {
2877 0 : aPhonetic.Truncate();
2878 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
2879 :
2880 0 : if (formControlFrame) {
2881 0 : nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
2882 0 : if (textControlFrame)
2883 0 : textControlFrame->GetPhonetic(aPhonetic);
2884 : }
2885 :
2886 0 : return NS_OK;
2887 : }
2888 :
2889 : #ifdef ACCESSIBILITY
2890 : /*static*/ nsresult
2891 0 : FireEventForAccessibility(nsIDOMHTMLInputElement* aTarget,
2892 : nsPresContext* aPresContext,
2893 : const nsAString& aEventType)
2894 : {
2895 0 : nsCOMPtr<nsIDOMEvent> event;
2896 0 : if (NS_SUCCEEDED(nsEventDispatcher::CreateEvent(aPresContext, nsnull,
2897 : NS_LITERAL_STRING("Events"),
2898 : getter_AddRefs(event)))) {
2899 0 : event->InitEvent(aEventType, true, true);
2900 :
2901 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
2902 0 : if (privateEvent) {
2903 0 : privateEvent->SetTrusted(true);
2904 : }
2905 :
2906 0 : nsEventDispatcher::DispatchDOMEvent(aTarget, nsnull, event, aPresContext, nsnull);
2907 : }
2908 :
2909 0 : return NS_OK;
2910 : }
2911 : #endif
2912 :
2913 : nsresult
2914 0 : nsHTMLInputElement::SetDefaultValueAsValue()
2915 : {
2916 0 : NS_ASSERTION(GetValueMode() == VALUE_MODE_VALUE,
2917 : "GetValueMode() should return VALUE_MODE_VALUE!");
2918 :
2919 : // The element has a content attribute value different from it's value when
2920 : // it's in the value mode value.
2921 0 : nsAutoString resetVal;
2922 0 : GetDefaultValue(resetVal);
2923 :
2924 : // SetValueInternal is going to sanitize the value.
2925 0 : return SetValueInternal(resetVal, false, false);
2926 : }
2927 :
2928 : NS_IMETHODIMP
2929 0 : nsHTMLInputElement::Reset()
2930 : {
2931 : // We should be able to reset all dirty flags regardless of the type.
2932 0 : SetCheckedChanged(false);
2933 0 : SetValueChanged(false);
2934 :
2935 0 : switch (GetValueMode()) {
2936 : case VALUE_MODE_VALUE:
2937 0 : return SetDefaultValueAsValue();
2938 : case VALUE_MODE_DEFAULT_ON:
2939 0 : return DoSetChecked(DefaultChecked(), true, false);
2940 : case VALUE_MODE_FILENAME:
2941 0 : ClearFiles(false);
2942 0 : return NS_OK;
2943 : case VALUE_MODE_DEFAULT:
2944 : default:
2945 0 : return NS_OK;
2946 : }
2947 : }
2948 :
2949 : NS_IMETHODIMP
2950 0 : nsHTMLInputElement::SubmitNamesValues(nsFormSubmission* aFormSubmission)
2951 : {
2952 : // Disabled elements don't submit
2953 : // For type=reset, and type=button, we just never submit, period.
2954 : // For type=image and type=button, we only submit if we were the button
2955 : // pressed
2956 : // For type=radio and type=checkbox, we only submit if checked=true
2957 0 : if (IsDisabled() || mType == NS_FORM_INPUT_RESET ||
2958 : mType == NS_FORM_INPUT_BUTTON ||
2959 : ((mType == NS_FORM_INPUT_SUBMIT || mType == NS_FORM_INPUT_IMAGE) &&
2960 0 : aFormSubmission->GetOriginatingElement() != this) ||
2961 : ((mType == NS_FORM_INPUT_RADIO || mType == NS_FORM_INPUT_CHECKBOX) &&
2962 0 : !mChecked)) {
2963 0 : return NS_OK;
2964 : }
2965 :
2966 : // Get the name
2967 0 : nsAutoString name;
2968 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
2969 :
2970 : // Submit .x, .y for input type=image
2971 0 : if (mType == NS_FORM_INPUT_IMAGE) {
2972 : // Get a property set by the frame to find out where it was clicked.
2973 : nsIntPoint* lastClickedPoint =
2974 0 : static_cast<nsIntPoint*>(GetProperty(nsGkAtoms::imageClickedPoint));
2975 : PRInt32 x, y;
2976 0 : if (lastClickedPoint) {
2977 : // Convert the values to strings for submission
2978 0 : x = lastClickedPoint->x;
2979 0 : y = lastClickedPoint->y;
2980 : } else {
2981 0 : x = y = 0;
2982 : }
2983 :
2984 0 : nsAutoString xVal, yVal;
2985 0 : xVal.AppendInt(x);
2986 0 : yVal.AppendInt(y);
2987 :
2988 0 : if (!name.IsEmpty()) {
2989 0 : aFormSubmission->AddNameValuePair(name + NS_LITERAL_STRING(".x"), xVal);
2990 0 : aFormSubmission->AddNameValuePair(name + NS_LITERAL_STRING(".y"), yVal);
2991 : } else {
2992 : // If the Image Element has no name, simply return x and y
2993 : // to Nav and IE compatibility.
2994 0 : aFormSubmission->AddNameValuePair(NS_LITERAL_STRING("x"), xVal);
2995 0 : aFormSubmission->AddNameValuePair(NS_LITERAL_STRING("y"), yVal);
2996 : }
2997 :
2998 0 : return NS_OK;
2999 : }
3000 :
3001 : //
3002 : // Submit name=value
3003 : //
3004 :
3005 : // If name not there, don't submit
3006 0 : if (name.IsEmpty()) {
3007 0 : return NS_OK;
3008 : }
3009 :
3010 : // Get the value
3011 0 : nsAutoString value;
3012 0 : nsresult rv = GetValue(value);
3013 0 : if (NS_FAILED(rv)) {
3014 0 : return rv;
3015 : }
3016 :
3017 0 : if (mType == NS_FORM_INPUT_SUBMIT && value.IsEmpty() &&
3018 0 : !HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
3019 : // Get our default value, which is the same as our default label
3020 0 : nsXPIDLString defaultValue;
3021 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
3022 0 : "Submit", defaultValue);
3023 0 : value = defaultValue;
3024 : }
3025 :
3026 : //
3027 : // Submit file if its input type=file and this encoding method accepts files
3028 : //
3029 0 : if (mType == NS_FORM_INPUT_FILE) {
3030 : // Submit files
3031 :
3032 0 : const nsCOMArray<nsIDOMFile>& files = GetFiles();
3033 :
3034 0 : for (PRInt32 i = 0; i < files.Count(); ++i) {
3035 0 : aFormSubmission->AddNameFilePair(name, files[i]);
3036 : }
3037 :
3038 0 : if (files.Count() == 0) {
3039 : // If no file was selected, pretend we had an empty file with an
3040 : // empty filename.
3041 0 : aFormSubmission->AddNameFilePair(name, nsnull);
3042 :
3043 : }
3044 :
3045 0 : return NS_OK;
3046 : }
3047 :
3048 0 : if (mType == NS_FORM_INPUT_HIDDEN && name.EqualsLiteral("_charset_")) {
3049 0 : nsCString charset;
3050 0 : aFormSubmission->GetCharset(charset);
3051 : return aFormSubmission->AddNameValuePair(name,
3052 0 : NS_ConvertASCIItoUTF16(charset));
3053 : }
3054 0 : if (IsSingleLineTextControl(true) &&
3055 0 : name.EqualsLiteral("isindex") &&
3056 0 : aFormSubmission->SupportsIsindexSubmission()) {
3057 0 : return aFormSubmission->AddIsindex(value);
3058 : }
3059 0 : return aFormSubmission->AddNameValuePair(name, value);
3060 : }
3061 :
3062 :
3063 : NS_IMETHODIMP
3064 3 : nsHTMLInputElement::SaveState()
3065 : {
3066 6 : nsRefPtr<nsHTMLInputElementState> inputState;
3067 3 : switch (mType) {
3068 : case NS_FORM_INPUT_CHECKBOX:
3069 : case NS_FORM_INPUT_RADIO:
3070 : {
3071 0 : if (mCheckedChanged) {
3072 0 : inputState = new nsHTMLInputElementState();
3073 0 : inputState->SetChecked(mChecked);
3074 : }
3075 0 : break;
3076 : }
3077 :
3078 : // Never save passwords in session history
3079 : case NS_FORM_INPUT_PASSWORD:
3080 0 : break;
3081 : case NS_FORM_INPUT_EMAIL:
3082 : case NS_FORM_INPUT_SEARCH:
3083 : case NS_FORM_INPUT_TEXT:
3084 : case NS_FORM_INPUT_TEL:
3085 : case NS_FORM_INPUT_URL:
3086 : case NS_FORM_INPUT_HIDDEN:
3087 : {
3088 3 : if (mValueChanged) {
3089 0 : inputState = new nsHTMLInputElementState();
3090 0 : nsAutoString value;
3091 0 : GetValue(value);
3092 : DebugOnly<nsresult> rv =
3093 : nsLinebreakConverter::ConvertStringLineBreaks(
3094 : value,
3095 : nsLinebreakConverter::eLinebreakPlatform,
3096 0 : nsLinebreakConverter::eLinebreakContent);
3097 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "Converting linebreaks failed!");
3098 0 : inputState->SetValue(value);
3099 : }
3100 3 : break;
3101 : }
3102 : case NS_FORM_INPUT_FILE:
3103 : {
3104 0 : if (mFiles.Count()) {
3105 0 : inputState = new nsHTMLInputElementState();
3106 0 : inputState->SetFiles(mFiles);
3107 : }
3108 0 : break;
3109 : }
3110 : }
3111 :
3112 3 : nsresult rv = NS_OK;
3113 3 : nsPresState* state = nsnull;
3114 3 : if (inputState) {
3115 0 : rv = GetPrimaryPresState(this, &state);
3116 0 : if (state) {
3117 0 : state->SetStateProperty(inputState);
3118 : }
3119 : }
3120 :
3121 3 : if (mDisabledChanged) {
3122 0 : rv |= GetPrimaryPresState(this, &state);
3123 0 : if (state) {
3124 : // We do not want to save the real disabled state but the disabled
3125 : // attribute.
3126 0 : state->SetDisabled(HasAttr(kNameSpaceID_None, nsGkAtoms::disabled));
3127 : }
3128 : }
3129 :
3130 3 : return rv;
3131 : }
3132 :
3133 : void
3134 1 : nsHTMLInputElement::DoneCreatingElement()
3135 : {
3136 1 : mParserCreating = false;
3137 :
3138 : //
3139 : // Restore state as needed. Note that disabled state applies to all control
3140 : // types.
3141 : //
3142 : bool restoredCheckedState =
3143 1 : !mInhibitRestoration && RestoreFormControlState(this, this);
3144 :
3145 : //
3146 : // If restore does not occur, we initialize .checked using the CHECKED
3147 : // property.
3148 : //
3149 1 : if (!restoredCheckedState && mShouldInitChecked) {
3150 0 : DoSetChecked(DefaultChecked(), false, true);
3151 0 : DoSetCheckedChanged(false, false);
3152 : }
3153 :
3154 : // Sanitize the value.
3155 1 : if (GetValueMode() == VALUE_MODE_VALUE) {
3156 2 : nsAutoString aValue;
3157 1 : GetValue(aValue);
3158 1 : SetValueInternal(aValue, false, false);
3159 : }
3160 :
3161 1 : mShouldInitChecked = false;
3162 1 : }
3163 :
3164 : nsEventStates
3165 5 : nsHTMLInputElement::IntrinsicState() const
3166 : {
3167 : // If you add states here, and they're type-dependent, you need to add them
3168 : // to the type case in AfterSetAttr.
3169 :
3170 5 : nsEventStates state = nsGenericHTMLFormElement::IntrinsicState();
3171 5 : if (mType == NS_FORM_INPUT_CHECKBOX || mType == NS_FORM_INPUT_RADIO) {
3172 : // Check current checked state (:checked)
3173 0 : if (mChecked) {
3174 0 : state |= NS_EVENT_STATE_CHECKED;
3175 : }
3176 :
3177 : // Check current indeterminate state (:indeterminate)
3178 0 : if (mType == NS_FORM_INPUT_CHECKBOX && mIndeterminate) {
3179 0 : state |= NS_EVENT_STATE_INDETERMINATE;
3180 : }
3181 :
3182 : // Check whether we are the default checked element (:default)
3183 0 : if (DefaultChecked()) {
3184 0 : state |= NS_EVENT_STATE_DEFAULT;
3185 : }
3186 5 : } else if (mType == NS_FORM_INPUT_IMAGE) {
3187 0 : state |= nsImageLoadingContent::ImageState();
3188 : }
3189 :
3190 5 : if (DoesRequiredApply() && HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
3191 0 : state |= NS_EVENT_STATE_REQUIRED;
3192 : } else {
3193 5 : state |= NS_EVENT_STATE_OPTIONAL;
3194 : }
3195 :
3196 5 : if (IsCandidateForConstraintValidation()) {
3197 5 : if (IsValid()) {
3198 5 : state |= NS_EVENT_STATE_VALID;
3199 : } else {
3200 0 : state |= NS_EVENT_STATE_INVALID;
3201 :
3202 0 : if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
3203 0 : (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
3204 0 : (mCanShowInvalidUI && ShouldShowValidityUI()))) {
3205 0 : state |= NS_EVENT_STATE_MOZ_UI_INVALID;
3206 : }
3207 : }
3208 :
3209 : // :-moz-ui-valid applies if all of the following conditions are true:
3210 : // 1. The element is not focused, or had either :-moz-ui-valid or
3211 : // :-moz-ui-invalid applying before it was focused ;
3212 : // 2. The element is either valid or isn't allowed to have
3213 : // :-moz-ui-invalid applying ;
3214 : // 3. The element has no form owner or its form owner doesn't have the
3215 : // novalidate attribute set ;
3216 : // 4. The element has already been modified or the user tried to submit the
3217 : // form owner while invalid.
3218 10 : if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
3219 5 : (mCanShowValidUI && ShouldShowValidityUI() &&
3220 0 : (IsValid() || (!state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
3221 0 : !mCanShowInvalidUI)))) {
3222 0 : state |= NS_EVENT_STATE_MOZ_UI_VALID;
3223 : }
3224 : }
3225 :
3226 5 : if (PlaceholderApplies() && HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
3227 0 : !nsContentUtils::IsFocusedContent((nsIContent*)(this)) &&
3228 0 : IsValueEmpty()) {
3229 0 : state |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
3230 : }
3231 :
3232 5 : if (mForm && !mForm->GetValidity() && IsSubmitControl()) {
3233 0 : state |= NS_EVENT_STATE_MOZ_SUBMITINVALID;
3234 : }
3235 :
3236 : return state;
3237 : }
3238 :
3239 : bool
3240 0 : nsHTMLInputElement::RestoreState(nsPresState* aState)
3241 : {
3242 0 : bool restoredCheckedState = false;
3243 :
3244 : nsCOMPtr<nsHTMLInputElementState> inputState
3245 0 : (do_QueryInterface(aState->GetStateProperty()));
3246 :
3247 0 : if (inputState) {
3248 0 : switch (mType) {
3249 : case NS_FORM_INPUT_CHECKBOX:
3250 : case NS_FORM_INPUT_RADIO:
3251 : {
3252 0 : if (inputState->IsCheckedSet()) {
3253 0 : restoredCheckedState = true;
3254 0 : DoSetChecked(inputState->GetChecked(), true, true);
3255 : }
3256 0 : break;
3257 : }
3258 :
3259 : case NS_FORM_INPUT_EMAIL:
3260 : case NS_FORM_INPUT_SEARCH:
3261 : case NS_FORM_INPUT_TEXT:
3262 : case NS_FORM_INPUT_TEL:
3263 : case NS_FORM_INPUT_URL:
3264 : case NS_FORM_INPUT_HIDDEN:
3265 : {
3266 0 : SetValueInternal(inputState->GetValue(), false, true);
3267 0 : break;
3268 : }
3269 : case NS_FORM_INPUT_FILE:
3270 : {
3271 0 : const nsCOMArray<nsIDOMFile>& files = inputState->GetFiles();
3272 0 : SetFiles(files, true);
3273 0 : break;
3274 : }
3275 : }
3276 : }
3277 :
3278 0 : if (aState->IsDisabledSet()) {
3279 0 : SetDisabled(aState->GetDisabled());
3280 : }
3281 :
3282 0 : return restoredCheckedState;
3283 : }
3284 :
3285 : bool
3286 0 : nsHTMLInputElement::AllowDrop()
3287 : {
3288 : // Allow drop on anything other than file inputs.
3289 :
3290 0 : return mType != NS_FORM_INPUT_FILE;
3291 : }
3292 :
3293 : /*
3294 : * Radio group stuff
3295 : */
3296 :
3297 : void
3298 0 : nsHTMLInputElement::AddedToRadioGroup()
3299 : {
3300 : // Make sure not to notify if we're still being created by the parser
3301 0 : bool notify = !mParserCreating;
3302 :
3303 : //
3304 : // If the input element is not in a form and
3305 : // not in a document, we just need to return.
3306 : //
3307 0 : if (!mForm && !(IsInDoc() && GetParent())) {
3308 0 : return;
3309 : }
3310 :
3311 : //
3312 : // If the input element is checked, and we add it to the group, it will
3313 : // deselect whatever is currently selected in that group
3314 : //
3315 0 : if (mChecked) {
3316 : //
3317 : // If it is checked, call "RadioSetChecked" to perform the selection/
3318 : // deselection ritual. This has the side effect of repainting the
3319 : // radio button, but as adding a checked radio button into the group
3320 : // should not be that common an occurrence, I think we can live with
3321 : // that.
3322 : //
3323 0 : RadioSetChecked(notify);
3324 : }
3325 :
3326 : //
3327 : // For integrity purposes, we have to ensure that "checkedChanged" is
3328 : // the same for this new element as for all the others in the group
3329 : //
3330 0 : bool checkedChanged = mCheckedChanged;
3331 :
3332 : nsCOMPtr<nsIRadioVisitor> visitor =
3333 0 : new nsRadioGetCheckedChangedVisitor(&checkedChanged, this);
3334 0 : VisitGroup(visitor, notify);
3335 :
3336 0 : SetCheckedChangedInternal(checkedChanged);
3337 :
3338 : //
3339 : // Add the radio to the radio group container.
3340 : //
3341 0 : nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
3342 0 : if (container) {
3343 0 : nsAutoString name;
3344 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
3345 0 : container->AddToRadioGroup(name, static_cast<nsIFormControl*>(this));
3346 :
3347 : // We initialize the validity of the element to the validity of the group
3348 : // because we assume UpdateValueMissingState() will be called after.
3349 : SetValidityState(VALIDITY_STATE_VALUE_MISSING,
3350 0 : container->GetValueMissingState(name));
3351 : }
3352 : }
3353 :
3354 : void
3355 0 : nsHTMLInputElement::WillRemoveFromRadioGroup()
3356 : {
3357 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
3358 0 : if (!container) {
3359 0 : return;
3360 : }
3361 :
3362 0 : nsAutoString name;
3363 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
3364 :
3365 : // If this button was checked, we need to notify the group that there is no
3366 : // longer a selected radio button
3367 0 : if (mChecked) {
3368 0 : container->SetCurrentRadioButton(name, nsnull);
3369 : }
3370 :
3371 : // Remove this radio from its group in the container.
3372 : // We need to call UpdateValueMissingValidityStateForRadio before to make sure
3373 : // the group validity is updated (with this element being ignored).
3374 0 : UpdateValueMissingValidityStateForRadio(true);
3375 0 : container->RemoveFromRadioGroup(name, static_cast<nsIFormControl*>(this));
3376 : }
3377 :
3378 : bool
3379 0 : nsHTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, PRInt32 *aTabIndex)
3380 : {
3381 0 : if (nsGenericHTMLFormElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
3382 0 : return true;
3383 : }
3384 :
3385 0 : if (IsDisabled()) {
3386 0 : *aIsFocusable = false;
3387 0 : return true;
3388 : }
3389 :
3390 0 : if (IsSingleLineTextControl(false)) {
3391 0 : *aIsFocusable = true;
3392 0 : return false;
3393 : }
3394 :
3395 : #ifdef XP_MACOSX
3396 : const bool defaultFocusable = !aWithMouse || nsFocusManager::sMouseFocusesFormControl;
3397 : #else
3398 0 : const bool defaultFocusable = true;
3399 : #endif
3400 :
3401 0 : if (mType == NS_FORM_INPUT_FILE) {
3402 0 : if (aTabIndex) {
3403 0 : *aTabIndex = -1;
3404 : }
3405 0 : *aIsFocusable = defaultFocusable;
3406 0 : return true;
3407 : }
3408 :
3409 0 : if (mType == NS_FORM_INPUT_HIDDEN) {
3410 0 : if (aTabIndex) {
3411 0 : *aTabIndex = -1;
3412 : }
3413 0 : *aIsFocusable = false;
3414 0 : return false;
3415 : }
3416 :
3417 0 : if (!aTabIndex) {
3418 : // The other controls are all focusable
3419 0 : *aIsFocusable = defaultFocusable;
3420 0 : return false;
3421 : }
3422 :
3423 0 : if (mType != NS_FORM_INPUT_RADIO) {
3424 0 : *aIsFocusable = defaultFocusable;
3425 0 : return false;
3426 : }
3427 :
3428 0 : if (mChecked) {
3429 : // Selected radio buttons are tabbable
3430 0 : *aIsFocusable = defaultFocusable;
3431 0 : return false;
3432 : }
3433 :
3434 : // Current radio button is not selected.
3435 : // But make it tabbable if nothing in group is selected.
3436 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
3437 0 : if (!container) {
3438 0 : *aIsFocusable = defaultFocusable;
3439 0 : return false;
3440 : }
3441 :
3442 0 : nsAutoString name;
3443 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
3444 :
3445 0 : nsCOMPtr<nsIDOMHTMLInputElement> currentRadio;
3446 0 : container->GetCurrentRadioButton(name, getter_AddRefs(currentRadio));
3447 0 : if (currentRadio) {
3448 0 : *aTabIndex = -1;
3449 : }
3450 0 : *aIsFocusable = defaultFocusable;
3451 0 : return false;
3452 : }
3453 :
3454 : nsresult
3455 0 : nsHTMLInputElement::VisitGroup(nsIRadioVisitor* aVisitor, bool aFlushContent)
3456 : {
3457 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
3458 0 : if (container) {
3459 0 : nsAutoString name;
3460 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
3461 0 : return container->WalkRadioGroup(name, aVisitor, aFlushContent);
3462 : }
3463 :
3464 0 : aVisitor->Visit(this);
3465 0 : return NS_OK;
3466 : }
3467 :
3468 : nsHTMLInputElement::ValueModeType
3469 9 : nsHTMLInputElement::GetValueMode() const
3470 : {
3471 9 : switch (mType)
3472 : {
3473 : case NS_FORM_INPUT_HIDDEN:
3474 : case NS_FORM_INPUT_SUBMIT:
3475 : case NS_FORM_INPUT_BUTTON:
3476 : case NS_FORM_INPUT_RESET:
3477 : case NS_FORM_INPUT_IMAGE:
3478 0 : return VALUE_MODE_DEFAULT;
3479 : case NS_FORM_INPUT_CHECKBOX:
3480 : case NS_FORM_INPUT_RADIO:
3481 0 : return VALUE_MODE_DEFAULT_ON;
3482 : case NS_FORM_INPUT_FILE:
3483 0 : return VALUE_MODE_FILENAME;
3484 : #ifdef DEBUG
3485 : case NS_FORM_INPUT_TEXT:
3486 : case NS_FORM_INPUT_PASSWORD:
3487 : case NS_FORM_INPUT_SEARCH:
3488 : case NS_FORM_INPUT_TEL:
3489 : case NS_FORM_INPUT_EMAIL:
3490 : case NS_FORM_INPUT_URL:
3491 9 : return VALUE_MODE_VALUE;
3492 : default:
3493 0 : NS_NOTYETIMPLEMENTED("Unexpected input type in GetValueMode()");
3494 0 : return VALUE_MODE_VALUE;
3495 : #else // DEBUG
3496 : default:
3497 : return VALUE_MODE_VALUE;
3498 : #endif // DEBUG
3499 : }
3500 : }
3501 :
3502 : bool
3503 0 : nsHTMLInputElement::IsMutable() const
3504 : {
3505 0 : return !IsDisabled() && GetCurrentDoc() &&
3506 0 : !(DoesReadOnlyApply() &&
3507 0 : HasAttr(kNameSpaceID_None, nsGkAtoms::readonly));
3508 : }
3509 :
3510 : bool
3511 0 : nsHTMLInputElement::DoesReadOnlyApply() const
3512 : {
3513 0 : switch (mType)
3514 : {
3515 : case NS_FORM_INPUT_HIDDEN:
3516 : case NS_FORM_INPUT_BUTTON:
3517 : case NS_FORM_INPUT_IMAGE:
3518 : case NS_FORM_INPUT_RESET:
3519 : case NS_FORM_INPUT_SUBMIT:
3520 : case NS_FORM_INPUT_RADIO:
3521 : case NS_FORM_INPUT_FILE:
3522 : case NS_FORM_INPUT_CHECKBOX:
3523 : // TODO:
3524 : // case NS_FORM_INPUT_COLOR:
3525 : // case NS_FORM_INPUT_RANGE:
3526 0 : return false;
3527 : #ifdef DEBUG
3528 : case NS_FORM_INPUT_TEXT:
3529 : case NS_FORM_INPUT_PASSWORD:
3530 : case NS_FORM_INPUT_SEARCH:
3531 : case NS_FORM_INPUT_TEL:
3532 : case NS_FORM_INPUT_EMAIL:
3533 : case NS_FORM_INPUT_URL:
3534 0 : return true;
3535 : default:
3536 0 : NS_NOTYETIMPLEMENTED("Unexpected input type in DoesReadOnlyApply()");
3537 0 : return true;
3538 : #else // DEBUG
3539 : default:
3540 : return true;
3541 : #endif // DEBUG
3542 : }
3543 : }
3544 :
3545 : bool
3546 5 : nsHTMLInputElement::DoesRequiredApply() const
3547 : {
3548 5 : switch (mType)
3549 : {
3550 : case NS_FORM_INPUT_HIDDEN:
3551 : case NS_FORM_INPUT_BUTTON:
3552 : case NS_FORM_INPUT_IMAGE:
3553 : case NS_FORM_INPUT_RESET:
3554 : case NS_FORM_INPUT_SUBMIT:
3555 : // TODO:
3556 : // case NS_FORM_INPUT_COLOR:
3557 : // case NS_FORM_INPUT_RANGE:
3558 0 : return false;
3559 : #ifdef DEBUG
3560 : case NS_FORM_INPUT_RADIO:
3561 : case NS_FORM_INPUT_CHECKBOX:
3562 : case NS_FORM_INPUT_FILE:
3563 : case NS_FORM_INPUT_TEXT:
3564 : case NS_FORM_INPUT_PASSWORD:
3565 : case NS_FORM_INPUT_SEARCH:
3566 : case NS_FORM_INPUT_TEL:
3567 : case NS_FORM_INPUT_EMAIL:
3568 : case NS_FORM_INPUT_URL:
3569 5 : return true;
3570 : default:
3571 0 : NS_NOTYETIMPLEMENTED("Unexpected input type in DoesRequiredApply()");
3572 0 : return true;
3573 : #else // DEBUG
3574 : default:
3575 : return true;
3576 : #endif // DEBUG
3577 : }
3578 : }
3579 :
3580 : bool
3581 1 : nsHTMLInputElement::DoesPatternApply() const
3582 : {
3583 1 : return IsSingleLineTextControl(false);
3584 : }
3585 :
3586 : // nsIConstraintValidation
3587 :
3588 : NS_IMETHODIMP
3589 0 : nsHTMLInputElement::SetCustomValidity(const nsAString& aError)
3590 : {
3591 0 : nsIConstraintValidation::SetCustomValidity(aError);
3592 :
3593 0 : UpdateState(true);
3594 :
3595 0 : return NS_OK;
3596 : }
3597 :
3598 : bool
3599 0 : nsHTMLInputElement::IsTooLong()
3600 : {
3601 0 : if (!MaxLengthApplies() ||
3602 0 : !HasAttr(kNameSpaceID_None, nsGkAtoms::maxlength) ||
3603 0 : !mValueChanged) {
3604 0 : return false;
3605 : }
3606 :
3607 0 : PRInt32 maxLength = -1;
3608 0 : GetMaxLength(&maxLength);
3609 :
3610 : // Maxlength of -1 means parsing error.
3611 0 : if (maxLength == -1) {
3612 0 : return false;
3613 : }
3614 :
3615 0 : PRInt32 textLength = -1;
3616 0 : GetTextLength(&textLength);
3617 :
3618 0 : return textLength > maxLength;
3619 : }
3620 :
3621 : bool
3622 5 : nsHTMLInputElement::IsValueMissing() const
3623 : {
3624 5 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) ||
3625 0 : !DoesRequiredApply()) {
3626 5 : return false;
3627 : }
3628 :
3629 0 : if (GetValueMode() == VALUE_MODE_VALUE) {
3630 0 : if (!IsMutable()) {
3631 0 : return false;
3632 : }
3633 :
3634 0 : return IsValueEmpty();
3635 : }
3636 :
3637 0 : switch (mType)
3638 : {
3639 : case NS_FORM_INPUT_CHECKBOX:
3640 0 : return !mChecked;
3641 : case NS_FORM_INPUT_FILE:
3642 : {
3643 0 : const nsCOMArray<nsIDOMFile>& files = GetFiles();
3644 0 : return !files.Count();
3645 : }
3646 : default:
3647 0 : return false;
3648 : }
3649 : }
3650 :
3651 : bool
3652 1 : nsHTMLInputElement::HasTypeMismatch() const
3653 : {
3654 1 : if (mType != NS_FORM_INPUT_EMAIL && mType != NS_FORM_INPUT_URL) {
3655 1 : return false;
3656 : }
3657 :
3658 0 : nsAutoString value;
3659 0 : NS_ENSURE_SUCCESS(GetValueInternal(value), false);
3660 :
3661 0 : if (value.IsEmpty()) {
3662 0 : return false;
3663 : }
3664 :
3665 0 : if (mType == NS_FORM_INPUT_EMAIL) {
3666 0 : return HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)
3667 0 : ? !IsValidEmailAddressList(value) : !IsValidEmailAddress(value);
3668 0 : } else if (mType == NS_FORM_INPUT_URL) {
3669 : /**
3670 : * TODO:
3671 : * The URL is not checked as the HTML5 specifications want it to be because
3672 : * there is no code to check for a valid URI/IRI according to 3986 and 3987
3673 : * RFC's at the moment, see bug 561586.
3674 : *
3675 : * RFC 3987 (IRI) implementation: bug 42899
3676 : *
3677 : * HTML5 specifications:
3678 : * http://dev.w3.org/html5/spec/infrastructure.html#valid-url
3679 : */
3680 0 : nsCOMPtr<nsIIOService> ioService = do_GetIOService();
3681 0 : nsCOMPtr<nsIURI> uri;
3682 :
3683 0 : return !NS_SUCCEEDED(ioService->NewURI(NS_ConvertUTF16toUTF8(value), nsnull,
3684 : nsnull, getter_AddRefs(uri)));
3685 : }
3686 :
3687 0 : return false;
3688 : }
3689 :
3690 : bool
3691 1 : nsHTMLInputElement::HasPatternMismatch() const
3692 : {
3693 2 : nsAutoString pattern;
3694 2 : if (!DoesPatternApply() ||
3695 1 : !GetAttr(kNameSpaceID_None, nsGkAtoms::pattern, pattern)) {
3696 1 : return false;
3697 : }
3698 :
3699 0 : nsAutoString value;
3700 0 : NS_ENSURE_SUCCESS(GetValueInternal(value), false);
3701 :
3702 0 : if (value.IsEmpty()) {
3703 0 : return false;
3704 : }
3705 :
3706 0 : nsIDocument* doc = OwnerDoc();
3707 :
3708 0 : return !nsContentUtils::IsPatternMatching(value, pattern, doc);
3709 : }
3710 :
3711 : void
3712 1 : nsHTMLInputElement::UpdateTooLongValidityState()
3713 : {
3714 : // TODO: this code will be re-enabled with bug 613016 and bug 613019.
3715 : #if 0
3716 : SetValidityState(VALIDITY_STATE_TOO_LONG, IsTooLong());
3717 : #endif
3718 1 : }
3719 :
3720 : void
3721 0 : nsHTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
3722 : {
3723 0 : bool notify = !mParserCreating;
3724 0 : nsCOMPtr<nsIDOMHTMLInputElement> selection = GetSelectedRadioButton();
3725 :
3726 : // If there is no selection, that might mean the radio is not in a group.
3727 : // In that case, we can look for the checked state of the radio.
3728 0 : bool selected = selection || (!aIgnoreSelf && mChecked);
3729 0 : bool required = !aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required);
3730 0 : bool valueMissing = false;
3731 :
3732 0 : nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
3733 :
3734 0 : if (!container) {
3735 0 : SetValidityState(VALIDITY_STATE_VALUE_MISSING, required && !selected);
3736 : return;
3737 : }
3738 :
3739 0 : nsAutoString name;
3740 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
3741 :
3742 : // If the current radio is required and not ignored, we can assume the entire
3743 : // group is required.
3744 0 : if (!required) {
3745 0 : required = (aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required))
3746 0 : ? container->GetRequiredRadioCount(name) - 1
3747 0 : : container->GetRequiredRadioCount(name);
3748 : }
3749 :
3750 0 : valueMissing = required && !selected;
3751 :
3752 0 : if (container->GetValueMissingState(name) != valueMissing) {
3753 0 : container->SetValueMissingState(name, valueMissing);
3754 :
3755 0 : SetValidityState(VALIDITY_STATE_VALUE_MISSING, valueMissing);
3756 :
3757 : // nsRadioSetValueMissingState will call ContentStateChanged while visiting.
3758 0 : nsAutoScriptBlocker scriptBlocker;
3759 : nsCOMPtr<nsIRadioVisitor> visitor =
3760 0 : new nsRadioSetValueMissingState(this, valueMissing, notify);
3761 0 : VisitGroup(visitor, notify);
3762 : }
3763 : }
3764 :
3765 : void
3766 5 : nsHTMLInputElement::UpdateValueMissingValidityState()
3767 : {
3768 5 : if (mType == NS_FORM_INPUT_RADIO) {
3769 0 : UpdateValueMissingValidityStateForRadio(false);
3770 0 : return;
3771 : }
3772 :
3773 5 : SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
3774 : }
3775 :
3776 : void
3777 1 : nsHTMLInputElement::UpdateTypeMismatchValidityState()
3778 : {
3779 1 : SetValidityState(VALIDITY_STATE_TYPE_MISMATCH, HasTypeMismatch());
3780 1 : }
3781 :
3782 : void
3783 1 : nsHTMLInputElement::UpdatePatternMismatchValidityState()
3784 : {
3785 1 : SetValidityState(VALIDITY_STATE_PATTERN_MISMATCH, HasPatternMismatch());
3786 1 : }
3787 :
3788 : void
3789 1 : nsHTMLInputElement::UpdateAllValidityStates(bool aNotify)
3790 : {
3791 1 : bool validBefore = IsValid();
3792 1 : UpdateTooLongValidityState();
3793 1 : UpdateValueMissingValidityState();
3794 1 : UpdateTypeMismatchValidityState();
3795 1 : UpdatePatternMismatchValidityState();
3796 :
3797 1 : if (validBefore != IsValid()) {
3798 0 : UpdateState(aNotify);
3799 : }
3800 1 : }
3801 :
3802 : void
3803 4 : nsHTMLInputElement::UpdateBarredFromConstraintValidation()
3804 : {
3805 : SetBarredFromConstraintValidation(mType == NS_FORM_INPUT_HIDDEN ||
3806 : mType == NS_FORM_INPUT_BUTTON ||
3807 : mType == NS_FORM_INPUT_RESET ||
3808 : mType == NS_FORM_INPUT_SUBMIT ||
3809 : mType == NS_FORM_INPUT_IMAGE ||
3810 4 : HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) ||
3811 8 : IsDisabled());
3812 4 : }
3813 :
3814 : nsresult
3815 0 : nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
3816 : ValidityStateType aType)
3817 : {
3818 0 : nsresult rv = NS_OK;
3819 :
3820 0 : switch (aType)
3821 : {
3822 : case VALIDITY_STATE_TOO_LONG:
3823 : {
3824 0 : nsXPIDLString message;
3825 0 : PRInt32 maxLength = -1;
3826 0 : PRInt32 textLength = -1;
3827 0 : nsAutoString strMaxLength;
3828 0 : nsAutoString strTextLength;
3829 :
3830 0 : GetMaxLength(&maxLength);
3831 0 : GetTextLength(&textLength);
3832 :
3833 0 : strMaxLength.AppendInt(maxLength);
3834 0 : strTextLength.AppendInt(textLength);
3835 :
3836 0 : const PRUnichar* params[] = { strMaxLength.get(), strTextLength.get() };
3837 : rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
3838 : "FormValidationTextTooLong",
3839 0 : params, message);
3840 0 : aValidationMessage = message;
3841 : break;
3842 : }
3843 : case VALIDITY_STATE_VALUE_MISSING:
3844 : {
3845 0 : nsXPIDLString message;
3846 0 : nsCAutoString key;
3847 0 : switch (mType)
3848 : {
3849 : case NS_FORM_INPUT_FILE:
3850 0 : key.Assign("FormValidationFileMissing");
3851 0 : break;
3852 : case NS_FORM_INPUT_CHECKBOX:
3853 0 : key.Assign("FormValidationCheckboxMissing");
3854 0 : break;
3855 : case NS_FORM_INPUT_RADIO:
3856 0 : key.Assign("FormValidationRadioMissing");
3857 0 : break;
3858 : default:
3859 0 : key.Assign("FormValidationValueMissing");
3860 : }
3861 : rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
3862 0 : key.get(), message);
3863 0 : aValidationMessage = message;
3864 : break;
3865 : }
3866 : case VALIDITY_STATE_TYPE_MISMATCH:
3867 : {
3868 0 : nsXPIDLString message;
3869 0 : nsCAutoString key;
3870 0 : if (mType == NS_FORM_INPUT_EMAIL) {
3871 0 : key.AssignLiteral("FormValidationInvalidEmail");
3872 0 : } else if (mType == NS_FORM_INPUT_URL) {
3873 0 : key.AssignLiteral("FormValidationInvalidURL");
3874 : } else {
3875 0 : return NS_ERROR_UNEXPECTED;
3876 : }
3877 : rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
3878 0 : key.get(), message);
3879 0 : aValidationMessage = message;
3880 0 : break;
3881 : }
3882 : case VALIDITY_STATE_PATTERN_MISMATCH:
3883 : {
3884 0 : nsXPIDLString message;
3885 0 : nsAutoString title;
3886 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::title, title);
3887 0 : if (title.IsEmpty()) {
3888 : rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
3889 : "FormValidationPatternMismatch",
3890 0 : message);
3891 : } else {
3892 0 : if (title.Length() > nsIConstraintValidation::sContentSpecifiedMaxLengthMessage) {
3893 0 : title.Truncate(nsIConstraintValidation::sContentSpecifiedMaxLengthMessage);
3894 : }
3895 0 : const PRUnichar* params[] = { title.get() };
3896 : rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
3897 : "FormValidationPatternMismatchWithTitle",
3898 0 : params, message);
3899 : }
3900 0 : aValidationMessage = message;
3901 : break;
3902 : }
3903 : default:
3904 0 : rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
3905 : }
3906 :
3907 0 : return rv;
3908 : }
3909 :
3910 : //static
3911 : bool
3912 0 : nsHTMLInputElement::IsValidEmailAddressList(const nsAString& aValue)
3913 : {
3914 0 : HTMLSplitOnSpacesTokenizer tokenizer(aValue, ',');
3915 :
3916 0 : while (tokenizer.hasMoreTokens()) {
3917 0 : if (!IsValidEmailAddress(tokenizer.nextToken())) {
3918 0 : return false;
3919 : }
3920 : }
3921 :
3922 0 : return !tokenizer.lastTokenEndedWithSeparator();
3923 : }
3924 :
3925 : //static
3926 : bool
3927 0 : nsHTMLInputElement::IsValidEmailAddress(const nsAString& aValue)
3928 : {
3929 0 : nsCAutoString value = NS_ConvertUTF16toUTF8(aValue);
3930 0 : PRUint32 i = 0;
3931 0 : PRUint32 length = value.Length();
3932 :
3933 : // Puny-encode the string if needed before running the validation algorithm.
3934 0 : nsCOMPtr<nsIIDNService> idnSrv = do_GetService(NS_IDNSERVICE_CONTRACTID);
3935 0 : if (idnSrv) {
3936 : bool ace;
3937 0 : if (NS_SUCCEEDED(idnSrv->IsACE(value, &ace)) && !ace) {
3938 0 : nsCAutoString punyCodedValue;
3939 0 : if (NS_SUCCEEDED(idnSrv->ConvertUTF8toACE(value, punyCodedValue))) {
3940 0 : value = punyCodedValue;
3941 0 : length = value.Length();
3942 : }
3943 : }
3944 : } else {
3945 0 : NS_ERROR("nsIIDNService isn't present!");
3946 : }
3947 :
3948 : // If the email address is empty, begins with a '@' or ends with a '.',
3949 : // we know it's invalid.
3950 0 : if (length == 0 || value[0] == '@' || value[length-1] == '.') {
3951 0 : return false;
3952 : }
3953 :
3954 : // Parsing the username.
3955 0 : for (; i < length && value[i] != '@'; ++i) {
3956 0 : PRUnichar c = value[i];
3957 :
3958 : // The username characters have to be in this list to be valid.
3959 0 : if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) ||
3960 : c == '.' || c == '!' || c == '#' || c == '$' || c == '%' ||
3961 : c == '&' || c == '\''|| c == '*' || c == '+' || c == '-' ||
3962 : c == '/' || c == '=' || c == '?' || c == '^' || c == '_' ||
3963 0 : c == '`' || c == '{' || c == '|' || c == '}' || c == '~' )) {
3964 0 : return false;
3965 : }
3966 : }
3967 :
3968 : // There is no domain name (or it's one-character long),
3969 : // that's not a valid email address.
3970 0 : if (++i >= length) {
3971 0 : return false;
3972 : }
3973 :
3974 : // The domain name can't begin with a dot.
3975 0 : if (value[i] == '.') {
3976 0 : return false;
3977 : }
3978 :
3979 : // Parsing the domain name.
3980 0 : for (; i < length; ++i) {
3981 0 : PRUnichar c = value[i];
3982 :
3983 0 : if (c == '.') {
3984 : // A dot can't follow a dot.
3985 0 : if (value[i-1] == '.') {
3986 0 : return false;
3987 : }
3988 0 : } else if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) ||
3989 0 : c == '-')) {
3990 : // The domain characters have to be in this list to be valid.
3991 0 : return false;
3992 : }
3993 : }
3994 :
3995 0 : return true;
3996 : }
3997 :
3998 : NS_IMETHODIMP_(bool)
3999 0 : nsHTMLInputElement::IsSingleLineTextControl() const
4000 : {
4001 0 : return IsSingleLineTextControl(false);
4002 : }
4003 :
4004 : NS_IMETHODIMP_(bool)
4005 0 : nsHTMLInputElement::IsTextArea() const
4006 : {
4007 0 : return false;
4008 : }
4009 :
4010 : NS_IMETHODIMP_(bool)
4011 0 : nsHTMLInputElement::IsPlainTextControl() const
4012 : {
4013 : // need to check our HTML attribute and/or CSS.
4014 0 : return true;
4015 : }
4016 :
4017 : NS_IMETHODIMP_(bool)
4018 0 : nsHTMLInputElement::IsPasswordTextControl() const
4019 : {
4020 0 : return mType == NS_FORM_INPUT_PASSWORD;
4021 : }
4022 :
4023 : NS_IMETHODIMP_(PRInt32)
4024 0 : nsHTMLInputElement::GetCols()
4025 : {
4026 : // Else we know (assume) it is an input with size attr
4027 0 : const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::size);
4028 0 : if (attr && attr->Type() == nsAttrValue::eInteger) {
4029 0 : PRInt32 cols = attr->GetIntegerValue();
4030 0 : if (cols > 0) {
4031 0 : return cols;
4032 : }
4033 : }
4034 :
4035 0 : return DEFAULT_COLS;
4036 : }
4037 :
4038 : NS_IMETHODIMP_(PRInt32)
4039 0 : nsHTMLInputElement::GetWrapCols()
4040 : {
4041 0 : return -1; // only textarea's can have wrap cols
4042 : }
4043 :
4044 : NS_IMETHODIMP_(PRInt32)
4045 0 : nsHTMLInputElement::GetRows()
4046 : {
4047 0 : return DEFAULT_ROWS;
4048 : }
4049 :
4050 : NS_IMETHODIMP_(void)
4051 1 : nsHTMLInputElement::GetDefaultValueFromContent(nsAString& aValue)
4052 : {
4053 1 : nsTextEditorState *state = GetEditorState();
4054 1 : if (state) {
4055 1 : GetDefaultValue(aValue);
4056 : // This is called by the frame to show the value.
4057 : // We have to sanitize it when needed.
4058 1 : if (!mParserCreating) {
4059 1 : SanitizeValue(aValue);
4060 : }
4061 : }
4062 1 : }
4063 :
4064 : NS_IMETHODIMP_(bool)
4065 1 : nsHTMLInputElement::ValueChanged() const
4066 : {
4067 1 : return mValueChanged;
4068 : }
4069 :
4070 : NS_IMETHODIMP_(void)
4071 0 : nsHTMLInputElement::GetTextEditorValue(nsAString& aValue,
4072 : bool aIgnoreWrap) const
4073 : {
4074 0 : nsTextEditorState *state = GetEditorState();
4075 0 : if (state) {
4076 0 : state->GetValue(aValue, aIgnoreWrap);
4077 : }
4078 0 : }
4079 :
4080 : NS_IMETHODIMP_(void)
4081 0 : nsHTMLInputElement::SetTextEditorValue(const nsAString& aValue,
4082 : bool aUserInput)
4083 : {
4084 0 : nsTextEditorState *state = GetEditorState();
4085 0 : if (state) {
4086 0 : state->SetValue(aValue, aUserInput);
4087 : }
4088 0 : }
4089 :
4090 : NS_IMETHODIMP_(void)
4091 0 : nsHTMLInputElement::InitializeKeyboardEventListeners()
4092 : {
4093 0 : nsTextEditorState *state = GetEditorState();
4094 0 : if (state) {
4095 0 : state->InitializeKeyboardEventListeners();
4096 : }
4097 0 : }
4098 :
4099 : NS_IMETHODIMP_(void)
4100 1 : nsHTMLInputElement::OnValueChanged(bool aNotify)
4101 : {
4102 1 : UpdateAllValidityStates(aNotify);
4103 :
4104 : // :-moz-placeholder pseudo-class may change when the value changes.
4105 : // However, we don't want to waste cycles if the state doesn't apply.
4106 2 : if (PlaceholderApplies() &&
4107 1 : HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
4108 0 : !nsContentUtils::IsFocusedContent(this)) {
4109 0 : UpdateState(aNotify);
4110 : }
4111 1 : }
4112 :
4113 : NS_IMETHODIMP_(bool)
4114 0 : nsHTMLInputElement::HasCachedSelection()
4115 : {
4116 0 : bool isCached = false;
4117 0 : nsTextEditorState *state = GetEditorState();
4118 0 : if (state) {
4119 0 : isCached = state->IsSelectionCached() &&
4120 0 : state->HasNeverInitializedBefore() &&
4121 0 : !state->GetSelectionProperties().IsDefault();
4122 0 : if (isCached) {
4123 0 : state->WillInitEagerly();
4124 : }
4125 : }
4126 0 : return isCached;
4127 : }
4128 :
4129 : void
4130 0 : nsHTMLInputElement::FieldSetDisabledChanged(bool aNotify)
4131 : {
4132 0 : UpdateValueMissingValidityState();
4133 0 : UpdateBarredFromConstraintValidation();
4134 :
4135 0 : nsGenericHTMLFormElement::FieldSetDisabledChanged(aNotify);
4136 0 : }
4137 :
4138 : PRInt32
4139 0 : nsHTMLInputElement::GetFilterFromAccept()
4140 : {
4141 0 : NS_ASSERTION(HasAttr(kNameSpaceID_None, nsGkAtoms::accept),
4142 : "You should not call GetFileFiltersFromAccept if the element"
4143 : " has no accept attribute!");
4144 :
4145 0 : PRInt32 filter = 0;
4146 0 : nsAutoString accept;
4147 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::accept, accept);
4148 :
4149 0 : HTMLSplitOnSpacesTokenizer tokenizer(accept, ',');
4150 :
4151 0 : while (tokenizer.hasMoreTokens()) {
4152 0 : const nsDependentSubstring token = tokenizer.nextToken();
4153 :
4154 0 : PRInt32 tokenFilter = 0;
4155 0 : if (token.EqualsLiteral("image/*")) {
4156 0 : tokenFilter = nsIFilePicker::filterImages;
4157 0 : } else if (token.EqualsLiteral("audio/*")) {
4158 0 : tokenFilter = nsIFilePicker::filterAudio;
4159 0 : } else if (token.EqualsLiteral("video/*")) {
4160 0 : tokenFilter = nsIFilePicker::filterVideo;
4161 : }
4162 :
4163 0 : if (tokenFilter) {
4164 : // We do not want to set more than one filter so if we found two different
4165 : // kwown tokens, we will return 0 (no filter).
4166 0 : if (filter && filter != tokenFilter) {
4167 0 : return 0;
4168 : }
4169 0 : filter = tokenFilter;
4170 : }
4171 : }
4172 :
4173 0 : return filter;
4174 : }
4175 :
4176 : void
4177 0 : nsHTMLInputElement::UpdateValidityUIBits(bool aIsFocused)
4178 : {
4179 0 : if (aIsFocused) {
4180 : // If the invalid UI is shown, we should show it while focusing (and
4181 : // update). Otherwise, we should not.
4182 0 : mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
4183 :
4184 : // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
4185 : // UI while typing.
4186 0 : mCanShowValidUI = ShouldShowValidityUI();
4187 : } else {
4188 0 : mCanShowInvalidUI = true;
4189 0 : mCanShowValidUI = true;
4190 : }
4191 4392 : }
4192 :
|