Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/dom/HTMLInputElement.h"
8 :
9 : #include "mozilla/ArrayUtils.h"
10 : #include "mozilla/AsyncEventDispatcher.h"
11 : #include "mozilla/DebugOnly.h"
12 : #include "mozilla/dom/Date.h"
13 : #include "mozilla/dom/Directory.h"
14 : #include "mozilla/dom/HTMLFormSubmission.h"
15 : #include "mozilla/dom/FileSystemUtils.h"
16 : #include "mozilla/dom/GetFilesHelper.h"
17 : #include "nsAttrValueInlines.h"
18 : #include "nsCRTGlue.h"
19 :
20 : #include "nsIDOMHTMLInputElement.h"
21 : #include "nsITextControlElement.h"
22 : #include "nsIDOMNSEditableElement.h"
23 : #include "nsIRadioVisitor.h"
24 : #include "InputType.h"
25 :
26 : #include "HTMLFormSubmissionConstants.h"
27 : #include "mozilla/Telemetry.h"
28 : #include "nsIControllers.h"
29 : #include "nsIStringBundle.h"
30 : #include "nsFocusManager.h"
31 : #include "nsColorControlFrame.h"
32 : #include "nsNumberControlFrame.h"
33 : #include "nsPIDOMWindow.h"
34 : #include "nsRepeatService.h"
35 : #include "nsContentCID.h"
36 : #include "nsIComponentManager.h"
37 : #include "nsIDOMHTMLFormElement.h"
38 : #include "mozilla/dom/ProgressEvent.h"
39 : #include "nsGkAtoms.h"
40 : #include "nsStyleConsts.h"
41 : #include "nsPresContext.h"
42 : #include "nsMappedAttributes.h"
43 : #include "nsIFormControl.h"
44 : #include "nsIDocument.h"
45 : #include "nsIPresShell.h"
46 : #include "nsIFormControlFrame.h"
47 : #include "nsITextControlFrame.h"
48 : #include "nsIFrame.h"
49 : #include "nsRangeFrame.h"
50 : #include "nsIServiceManager.h"
51 : #include "nsError.h"
52 : #include "nsIEditor.h"
53 : #include "nsDocument.h"
54 : #include "nsAttrValueOrString.h"
55 : #include "nsDateTimeControlFrame.h"
56 :
57 : #include "nsPresState.h"
58 : #include "nsIDOMEvent.h"
59 : #include "nsIDOMNodeList.h"
60 : #include "nsIDOMHTMLCollection.h"
61 : #include "nsLinebreakConverter.h" //to strip out carriage returns
62 : #include "nsReadableUtils.h"
63 : #include "nsUnicharUtils.h"
64 : #include "nsLayoutUtils.h"
65 : #include "nsVariant.h"
66 :
67 : #include "nsIDOMMutationEvent.h"
68 : #include "mozilla/ContentEvents.h"
69 : #include "mozilla/EventDispatcher.h"
70 : #include "mozilla/EventStates.h"
71 : #include "mozilla/GenericSpecifiedValuesInlines.h"
72 : #include "mozilla/InternalMutationEvent.h"
73 : #include "mozilla/TextEditor.h"
74 : #include "mozilla/TextEvents.h"
75 : #include "mozilla/TouchEvents.h"
76 :
77 : #include <algorithm>
78 :
79 : // input type=radio
80 : #include "nsIRadioGroupContainer.h"
81 :
82 : // input type=file
83 : #include "mozilla/dom/FileSystemEntry.h"
84 : #include "mozilla/dom/FileSystem.h"
85 : #include "mozilla/dom/File.h"
86 : #include "mozilla/dom/FileList.h"
87 : #include "nsIFile.h"
88 : #include "nsDirectoryServiceDefs.h"
89 : #include "nsIContentPrefService.h"
90 : #include "nsIMIMEService.h"
91 : #include "nsIObserverService.h"
92 : #include "nsIPopupWindowManager.h"
93 : #include "nsGlobalWindow.h"
94 :
95 : // input type=image
96 : #include "nsImageLoadingContent.h"
97 : #include "imgRequestProxy.h"
98 :
99 : #include "mozAutoDocUpdate.h"
100 : #include "nsContentCreatorFunctions.h"
101 : #include "nsContentUtils.h"
102 : #include "mozilla/dom/DirectionalityUtils.h"
103 : #include "nsRadioVisitor.h"
104 : #include "nsTextEditorState.h"
105 :
106 : #include "mozilla/LookAndFeel.h"
107 : #include "mozilla/Preferences.h"
108 : #include "mozilla/MathAlgorithms.h"
109 :
110 : #include "nsIIDNService.h"
111 :
112 : #include <limits>
113 :
114 : #include "nsIColorPicker.h"
115 : #include "nsIStringEnumerator.h"
116 : #include "HTMLSplitOnSpacesTokenizer.h"
117 : #include "nsIController.h"
118 : #include "nsIMIMEInfo.h"
119 : #include "nsFrameSelection.h"
120 :
121 : // input type=date
122 : #include "js/Date.h"
123 :
124 10 : NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Input)
125 :
126 : // XXX align=left, hspace, vspace, border? other nav4 attrs
127 :
128 : static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
129 :
130 : namespace mozilla {
131 : namespace dom {
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 : #define NS_PRE_HANDLE_BLUR_EVENT (1 << 13)
142 : #define NS_PRE_HANDLE_INPUT_EVENT (1 << 14)
143 :
144 : // whether textfields should be selected once focused:
145 : // -1: no, 1: yes, 0: uninitialized
146 : static int32_t gSelectTextFieldOnFocus;
147 : UploadLastDir* HTMLInputElement::gUploadLastDir;
148 :
149 : static const nsAttrValue::EnumTable kInputTypeTable[] = {
150 : { "button", NS_FORM_INPUT_BUTTON },
151 : { "checkbox", NS_FORM_INPUT_CHECKBOX },
152 : { "color", NS_FORM_INPUT_COLOR },
153 : { "date", NS_FORM_INPUT_DATE },
154 : { "datetime-local", NS_FORM_INPUT_DATETIME_LOCAL },
155 : { "email", NS_FORM_INPUT_EMAIL },
156 : { "file", NS_FORM_INPUT_FILE },
157 : { "hidden", NS_FORM_INPUT_HIDDEN },
158 : { "reset", NS_FORM_INPUT_RESET },
159 : { "image", NS_FORM_INPUT_IMAGE },
160 : { "month", NS_FORM_INPUT_MONTH },
161 : { "number", NS_FORM_INPUT_NUMBER },
162 : { "password", NS_FORM_INPUT_PASSWORD },
163 : { "radio", NS_FORM_INPUT_RADIO },
164 : { "range", NS_FORM_INPUT_RANGE },
165 : { "search", NS_FORM_INPUT_SEARCH },
166 : { "submit", NS_FORM_INPUT_SUBMIT },
167 : { "tel", NS_FORM_INPUT_TEL },
168 : { "time", NS_FORM_INPUT_TIME },
169 : { "url", NS_FORM_INPUT_URL },
170 : { "week", NS_FORM_INPUT_WEEK },
171 : // "text" must be last for ParseAttribute to work right. If you add things
172 : // before it, please update kInputDefaultType.
173 : { "text", NS_FORM_INPUT_TEXT },
174 : { nullptr, 0 }
175 : };
176 :
177 : // Default type is 'text'.
178 : static const nsAttrValue::EnumTable* kInputDefaultType =
179 : &kInputTypeTable[ArrayLength(kInputTypeTable) - 2];
180 :
181 : static const uint8_t NS_INPUT_INPUTMODE_AUTO = 0;
182 : static const uint8_t NS_INPUT_INPUTMODE_NUMERIC = 1;
183 : static const uint8_t NS_INPUT_INPUTMODE_DIGIT = 2;
184 : static const uint8_t NS_INPUT_INPUTMODE_UPPERCASE = 3;
185 : static const uint8_t NS_INPUT_INPUTMODE_LOWERCASE = 4;
186 : static const uint8_t NS_INPUT_INPUTMODE_TITLECASE = 5;
187 : static const uint8_t NS_INPUT_INPUTMODE_AUTOCAPITALIZED = 6;
188 :
189 : static const nsAttrValue::EnumTable kInputInputmodeTable[] = {
190 : { "auto", NS_INPUT_INPUTMODE_AUTO },
191 : { "numeric", NS_INPUT_INPUTMODE_NUMERIC },
192 : { "digit", NS_INPUT_INPUTMODE_DIGIT },
193 : { "uppercase", NS_INPUT_INPUTMODE_UPPERCASE },
194 : { "lowercase", NS_INPUT_INPUTMODE_LOWERCASE },
195 : { "titlecase", NS_INPUT_INPUTMODE_TITLECASE },
196 : { "autocapitalized", NS_INPUT_INPUTMODE_AUTOCAPITALIZED },
197 : { nullptr, 0 }
198 : };
199 :
200 : // Default inputmode value is "auto".
201 : static const nsAttrValue::EnumTable* kInputDefaultInputmode = &kInputInputmodeTable[0];
202 :
203 3 : const Decimal HTMLInputElement::kStepScaleFactorDate = Decimal(86400000);
204 3 : const Decimal HTMLInputElement::kStepScaleFactorNumberRange = Decimal(1);
205 3 : const Decimal HTMLInputElement::kStepScaleFactorTime = Decimal(1000);
206 3 : const Decimal HTMLInputElement::kStepScaleFactorMonth = Decimal(1);
207 3 : const Decimal HTMLInputElement::kStepScaleFactorWeek = Decimal(7 * 86400000);
208 3 : const Decimal HTMLInputElement::kDefaultStepBase = Decimal(0);
209 3 : const Decimal HTMLInputElement::kDefaultStepBaseWeek = Decimal(-259200000);
210 3 : const Decimal HTMLInputElement::kDefaultStep = Decimal(1);
211 3 : const Decimal HTMLInputElement::kDefaultStepTime = Decimal(60);
212 3 : const Decimal HTMLInputElement::kStepAny = Decimal(0);
213 :
214 : const double HTMLInputElement::kMinimumYear = 1;
215 : const double HTMLInputElement::kMaximumYear = 275760;
216 : const double HTMLInputElement::kMaximumWeekInMaximumYear = 37;
217 : const double HTMLInputElement::kMaximumDayInMaximumYear = 13;
218 : const double HTMLInputElement::kMaximumMonthInMaximumYear = 9;
219 : const double HTMLInputElement::kMaximumWeekInYear = 53;
220 : const double HTMLInputElement::kMsPerDay = 24 * 60 * 60 * 1000;
221 :
222 : #define NS_INPUT_ELEMENT_STATE_IID \
223 : { /* dc3b3d14-23e2-4479-b513-7b369343e3a0 */ \
224 : 0xdc3b3d14, \
225 : 0x23e2, \
226 : 0x4479, \
227 : {0xb5, 0x13, 0x7b, 0x36, 0x93, 0x43, 0xe3, 0xa0} \
228 : }
229 :
230 : #define PROGRESS_STR "progress"
231 : static const uint32_t kProgressEventInterval = 50; // ms
232 :
233 : // An helper class for the dispatching of the 'change' event.
234 : // This class is used when the FilePicker finished its task (or when files and
235 : // directories are set by some chrome/test only method).
236 : // The task of this class is to postpone the dispatching of 'change' and 'input'
237 : // events at the end of the exploration of the directories.
238 0 : class DispatchChangeEventCallback final : public GetFilesCallback
239 : {
240 : public:
241 0 : explicit DispatchChangeEventCallback(HTMLInputElement* aInputElement)
242 0 : : mInputElement(aInputElement)
243 : {
244 0 : MOZ_ASSERT(aInputElement);
245 0 : }
246 :
247 : virtual void
248 0 : Callback(nsresult aStatus, const Sequence<RefPtr<File>>& aFiles) override
249 : {
250 0 : nsTArray<OwningFileOrDirectory> array;
251 0 : for (uint32_t i = 0; i < aFiles.Length(); ++i) {
252 0 : OwningFileOrDirectory* element = array.AppendElement();
253 0 : element->SetAsFile() = aFiles[i];
254 : }
255 :
256 0 : mInputElement->SetFilesOrDirectories(array, true);
257 0 : Unused << NS_WARN_IF(NS_FAILED(DispatchEvents()));
258 0 : }
259 :
260 : nsresult
261 0 : DispatchEvents()
262 : {
263 0 : nsresult rv = NS_OK;
264 0 : rv = nsContentUtils::DispatchTrustedEvent(mInputElement->OwnerDoc(),
265 0 : static_cast<nsIDOMHTMLInputElement*>(mInputElement.get()),
266 0 : NS_LITERAL_STRING("input"), true,
267 0 : false);
268 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "DispatchTrustedEvent failed");
269 :
270 0 : rv = nsContentUtils::DispatchTrustedEvent(mInputElement->OwnerDoc(),
271 0 : static_cast<nsIDOMHTMLInputElement*>(mInputElement.get()),
272 0 : NS_LITERAL_STRING("change"), true,
273 0 : false);
274 :
275 0 : return rv;
276 : }
277 :
278 : private:
279 : RefPtr<HTMLInputElement> mInputElement;
280 : };
281 :
282 : class HTMLInputElementState final : public nsISupports
283 : {
284 : public:
285 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_INPUT_ELEMENT_STATE_IID)
286 : NS_DECL_ISUPPORTS
287 :
288 0 : bool IsCheckedSet()
289 : {
290 0 : return mCheckedSet;
291 : }
292 :
293 0 : bool GetChecked()
294 : {
295 0 : return mChecked;
296 : }
297 :
298 0 : void SetChecked(bool aChecked)
299 : {
300 0 : mChecked = aChecked;
301 0 : mCheckedSet = true;
302 0 : }
303 :
304 0 : const nsString& GetValue()
305 : {
306 0 : return mValue;
307 : }
308 :
309 0 : void SetValue(const nsAString& aValue)
310 : {
311 0 : mValue = aValue;
312 0 : }
313 :
314 : void
315 0 : GetFilesOrDirectories(nsPIDOMWindowInner* aWindow,
316 : nsTArray<OwningFileOrDirectory>& aResult) const
317 : {
318 0 : for (uint32_t i = 0; i < mBlobImplsOrDirectoryPaths.Length(); ++i) {
319 0 : if (mBlobImplsOrDirectoryPaths[i].mType == BlobImplOrDirectoryPath::eBlobImpl) {
320 : RefPtr<File> file =
321 : File::Create(aWindow,
322 0 : mBlobImplsOrDirectoryPaths[i].mBlobImpl);
323 0 : MOZ_ASSERT(file);
324 :
325 0 : OwningFileOrDirectory* element = aResult.AppendElement();
326 0 : element->SetAsFile() = file;
327 : } else {
328 0 : MOZ_ASSERT(mBlobImplsOrDirectoryPaths[i].mType == BlobImplOrDirectoryPath::eDirectoryPath);
329 :
330 0 : nsCOMPtr<nsIFile> file;
331 : nsresult rv =
332 0 : NS_NewLocalFile(mBlobImplsOrDirectoryPaths[i].mDirectoryPath,
333 0 : true, getter_AddRefs(file));
334 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
335 0 : continue;
336 : }
337 :
338 0 : RefPtr<Directory> directory = Directory::Create(aWindow, file);
339 0 : MOZ_ASSERT(directory);
340 :
341 0 : OwningFileOrDirectory* element = aResult.AppendElement();
342 0 : element->SetAsDirectory() = directory;
343 : }
344 : }
345 0 : }
346 :
347 0 : void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aArray)
348 : {
349 0 : mBlobImplsOrDirectoryPaths.Clear();
350 0 : for (uint32_t i = 0; i < aArray.Length(); ++i) {
351 0 : if (aArray[i].IsFile()) {
352 0 : BlobImplOrDirectoryPath* data = mBlobImplsOrDirectoryPaths.AppendElement();
353 :
354 0 : data->mBlobImpl = aArray[i].GetAsFile()->Impl();
355 0 : data->mType = BlobImplOrDirectoryPath::eBlobImpl;
356 : } else {
357 0 : MOZ_ASSERT(aArray[i].IsDirectory());
358 0 : nsAutoString fullPath;
359 0 : nsresult rv = aArray[i].GetAsDirectory()->GetFullRealPath(fullPath);
360 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
361 0 : continue;
362 : }
363 :
364 : BlobImplOrDirectoryPath* data =
365 0 : mBlobImplsOrDirectoryPaths.AppendElement();
366 :
367 0 : data->mDirectoryPath = fullPath;
368 0 : data->mType = BlobImplOrDirectoryPath::eDirectoryPath;
369 : }
370 : }
371 0 : }
372 :
373 0 : HTMLInputElementState()
374 0 : : mValue()
375 : , mChecked(false)
376 0 : , mCheckedSet(false)
377 0 : {}
378 :
379 : protected:
380 0 : ~HTMLInputElementState() {}
381 :
382 : nsString mValue;
383 :
384 0 : struct BlobImplOrDirectoryPath
385 : {
386 : RefPtr<BlobImpl> mBlobImpl;
387 : nsString mDirectoryPath;
388 :
389 : enum {
390 : eBlobImpl,
391 : eDirectoryPath
392 : } mType;
393 : };
394 :
395 : nsTArray<BlobImplOrDirectoryPath> mBlobImplsOrDirectoryPaths;
396 :
397 : bool mChecked;
398 : bool mCheckedSet;
399 : };
400 :
401 : NS_DEFINE_STATIC_IID_ACCESSOR(HTMLInputElementState, NS_INPUT_ELEMENT_STATE_IID)
402 :
403 0 : NS_IMPL_ISUPPORTS(HTMLInputElementState, HTMLInputElementState)
404 :
405 0 : struct HTMLInputElement::FileData
406 : {
407 : /**
408 : * The value of the input if it is a file input. This is the list of files or
409 : * directories DOM objects used when uploading a file. It is vital that this
410 : * is kept separate from mValue so that it won't be possible to 'leak' the
411 : * value from a text-input to a file-input. Additionally, the logic for this
412 : * value is kept as simple as possible to avoid accidental errors where the
413 : * wrong filename is used. Therefor the list of filenames is always owned by
414 : * this member, never by the frame. Whenever the frame wants to change the
415 : * filename it has to call SetFilesOrDirectories to update this member.
416 : */
417 : nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
418 :
419 : RefPtr<GetFilesHelper> mGetFilesRecursiveHelper;
420 : RefPtr<GetFilesHelper> mGetFilesNonRecursiveHelper;
421 :
422 : /**
423 : * Hack for bug 1086684: Stash the .value when we're a file picker.
424 : */
425 : nsString mFirstFilePath;
426 :
427 : RefPtr<FileList> mFileList;
428 : Sequence<RefPtr<FileSystemEntry>> mEntries;
429 :
430 : nsString mStaticDocFileList;
431 :
432 0 : void ClearGetFilesHelpers()
433 : {
434 0 : if (mGetFilesRecursiveHelper) {
435 0 : mGetFilesRecursiveHelper->Unlink();
436 0 : mGetFilesRecursiveHelper = nullptr;
437 : }
438 :
439 0 : if (mGetFilesNonRecursiveHelper) {
440 0 : mGetFilesNonRecursiveHelper->Unlink();
441 0 : mGetFilesNonRecursiveHelper = nullptr;
442 : }
443 0 : }
444 :
445 : // Cycle Collection support.
446 0 : void Traverse(nsCycleCollectionTraversalCallback &cb)
447 : {
448 0 : FileData* tmp = this;
449 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesOrDirectories)
450 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
451 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntries)
452 0 : if (mGetFilesRecursiveHelper) {
453 0 : mGetFilesRecursiveHelper->Traverse(cb);
454 : }
455 :
456 0 : if (mGetFilesNonRecursiveHelper) {
457 0 : mGetFilesNonRecursiveHelper->Traverse(cb);
458 : }
459 0 : }
460 :
461 0 : void Unlink()
462 : {
463 0 : FileData* tmp = this;
464 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories)
465 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
466 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntries)
467 0 : ClearGetFilesHelpers();
468 0 : }
469 : };
470 :
471 0 : HTMLInputElement::nsFilePickerShownCallback::nsFilePickerShownCallback(
472 0 : HTMLInputElement* aInput, nsIFilePicker* aFilePicker)
473 : : mFilePicker(aFilePicker)
474 0 : , mInput(aInput)
475 : {
476 0 : }
477 :
478 0 : NS_IMPL_ISUPPORTS(UploadLastDir::ContentPrefCallback, nsIContentPrefCallback2)
479 :
480 : NS_IMETHODIMP
481 0 : UploadLastDir::ContentPrefCallback::HandleCompletion(uint16_t aReason)
482 : {
483 0 : nsCOMPtr<nsIFile> localFile;
484 0 : nsAutoString prefStr;
485 :
486 0 : if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR || !mResult) {
487 0 : prefStr = Preferences::GetString("dom.input.fallbackUploadDir");
488 : }
489 :
490 0 : if (prefStr.IsEmpty() && mResult) {
491 0 : nsCOMPtr<nsIVariant> pref;
492 0 : mResult->GetValue(getter_AddRefs(pref));
493 0 : pref->GetAsAString(prefStr);
494 : }
495 :
496 0 : if (!prefStr.IsEmpty()) {
497 0 : localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
498 0 : if (localFile && NS_WARN_IF(NS_FAILED(localFile->InitWithPath(prefStr)))) {
499 0 : localFile = nullptr;
500 : }
501 : }
502 :
503 0 : if (localFile) {
504 0 : mFilePicker->SetDisplayDirectory(localFile);
505 : } else {
506 : // If no custom directory was set through the pref, default to
507 : // "desktop" directory for each platform.
508 0 : mFilePicker->SetDisplaySpecialDirectory(NS_LITERAL_STRING(NS_OS_DESKTOP_DIR));
509 : }
510 :
511 0 : mFilePicker->Open(mFpCallback);
512 0 : return NS_OK;
513 : }
514 :
515 : NS_IMETHODIMP
516 0 : UploadLastDir::ContentPrefCallback::HandleResult(nsIContentPref* pref)
517 : {
518 0 : mResult = pref;
519 0 : return NS_OK;
520 : }
521 :
522 : NS_IMETHODIMP
523 0 : UploadLastDir::ContentPrefCallback::HandleError(nsresult error)
524 : {
525 : // HandleCompletion is always called (even with HandleError was called),
526 : // so we don't need to do anything special here.
527 0 : return NS_OK;
528 : }
529 :
530 : namespace {
531 :
532 : /**
533 : * This may return nullptr if the DOM File's implementation of
534 : * File::mozFullPathInternal does not successfully return a non-empty
535 : * string that is a valid path. This can happen on Firefox OS, for example,
536 : * where the file picker can create Blobs.
537 : */
538 : static already_AddRefed<nsIFile>
539 0 : LastUsedDirectory(const OwningFileOrDirectory& aData)
540 : {
541 0 : if (aData.IsFile()) {
542 0 : nsAutoString path;
543 0 : ErrorResult error;
544 0 : aData.GetAsFile()->GetMozFullPathInternal(path, error);
545 0 : if (error.Failed() || path.IsEmpty()) {
546 0 : error.SuppressException();
547 0 : return nullptr;
548 : }
549 :
550 0 : nsCOMPtr<nsIFile> localFile;
551 0 : nsresult rv = NS_NewLocalFile(path, true, getter_AddRefs(localFile));
552 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
553 0 : return nullptr;
554 : }
555 :
556 0 : nsCOMPtr<nsIFile> parentFile;
557 0 : rv = localFile->GetParent(getter_AddRefs(parentFile));
558 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
559 0 : return nullptr;
560 : }
561 :
562 0 : return parentFile.forget();
563 : }
564 :
565 0 : MOZ_ASSERT(aData.IsDirectory());
566 :
567 0 : nsCOMPtr<nsIFile> localFile = aData.GetAsDirectory()->GetInternalNsIFile();
568 0 : MOZ_ASSERT(localFile);
569 :
570 0 : return localFile.forget();
571 : }
572 :
573 : void
574 0 : GetDOMFileOrDirectoryName(const OwningFileOrDirectory& aData,
575 : nsAString& aName)
576 : {
577 0 : if (aData.IsFile()) {
578 0 : aData.GetAsFile()->GetName(aName);
579 : } else {
580 0 : MOZ_ASSERT(aData.IsDirectory());
581 0 : ErrorResult rv;
582 0 : aData.GetAsDirectory()->GetName(aName, rv);
583 0 : if (NS_WARN_IF(rv.Failed())) {
584 0 : rv.SuppressException();
585 : }
586 : }
587 0 : }
588 :
589 : void
590 0 : GetDOMFileOrDirectoryPath(const OwningFileOrDirectory& aData,
591 : nsAString& aPath,
592 : ErrorResult& aRv)
593 : {
594 0 : if (aData.IsFile()) {
595 0 : aData.GetAsFile()->GetMozFullPathInternal(aPath, aRv);
596 : } else {
597 0 : MOZ_ASSERT(aData.IsDirectory());
598 0 : aData.GetAsDirectory()->GetFullRealPath(aPath);
599 : }
600 0 : }
601 :
602 : } // namespace
603 :
604 : /* static */
605 : bool
606 1 : HTMLInputElement::ValueAsDateEnabled(JSContext* cx, JSObject* obj)
607 : {
608 1 : return IsExperimentalFormsEnabled() || IsInputDateTimeEnabled() ||
609 1 : IsInputDateTimeOthersEnabled();
610 : }
611 :
612 : NS_IMETHODIMP
613 0 : HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
614 : {
615 0 : mInput->PickerClosed();
616 :
617 0 : if (aResult == nsIFilePicker::returnCancel) {
618 0 : return NS_OK;
619 : }
620 :
621 : int16_t mode;
622 0 : mFilePicker->GetMode(&mode);
623 :
624 : // Collect new selected filenames
625 0 : nsTArray<OwningFileOrDirectory> newFilesOrDirectories;
626 0 : if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple)) {
627 0 : nsCOMPtr<nsISimpleEnumerator> iter;
628 : nsresult rv =
629 0 : mFilePicker->GetDomFileOrDirectoryEnumerator(getter_AddRefs(iter));
630 0 : NS_ENSURE_SUCCESS(rv, rv);
631 :
632 0 : if (!iter) {
633 0 : return NS_OK;
634 : }
635 :
636 0 : nsCOMPtr<nsISupports> tmp;
637 0 : bool hasMore = true;
638 :
639 0 : while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
640 0 : iter->GetNext(getter_AddRefs(tmp));
641 0 : nsCOMPtr<nsIDOMBlob> domBlob = do_QueryInterface(tmp);
642 0 : MOZ_ASSERT(domBlob,
643 : "Null file object from FilePicker's file enumerator?");
644 0 : if (!domBlob) {
645 0 : continue;
646 : }
647 :
648 0 : OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
649 0 : element->SetAsFile() = static_cast<File*>(domBlob.get());
650 : }
651 : } else {
652 0 : MOZ_ASSERT(mode == static_cast<int16_t>(nsIFilePicker::modeOpen) ||
653 : mode == static_cast<int16_t>(nsIFilePicker::modeGetFolder));
654 0 : nsCOMPtr<nsISupports> tmp;
655 0 : nsresult rv = mFilePicker->GetDomFileOrDirectory(getter_AddRefs(tmp));
656 0 : NS_ENSURE_SUCCESS(rv, rv);
657 :
658 0 : nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(tmp);
659 0 : if (blob) {
660 0 : RefPtr<File> file = static_cast<Blob*>(blob.get())->ToFile();
661 0 : MOZ_ASSERT(file);
662 :
663 0 : OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
664 0 : element->SetAsFile() = file;
665 0 : } else if (tmp) {
666 0 : RefPtr<Directory> directory = static_cast<Directory*>(tmp.get());
667 0 : OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
668 0 : element->SetAsDirectory() = directory;
669 : }
670 : }
671 :
672 0 : if (newFilesOrDirectories.IsEmpty()) {
673 0 : return NS_OK;
674 : }
675 :
676 : // Store the last used directory using the content pref service:
677 0 : nsCOMPtr<nsIFile> lastUsedDir = LastUsedDirectory(newFilesOrDirectories[0]);
678 :
679 0 : if (lastUsedDir) {
680 0 : HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(
681 0 : mInput->OwnerDoc(), lastUsedDir);
682 : }
683 :
684 : // The text control frame (if there is one) isn't going to send a change
685 : // event because it will think this is done by a script.
686 : // So, we can safely send one by ourself.
687 0 : mInput->SetFilesOrDirectories(newFilesOrDirectories, true);
688 :
689 : RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback =
690 0 : new DispatchChangeEventCallback(mInput);
691 :
692 0 : if (IsWebkitDirPickerEnabled() &&
693 0 : mInput->HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)) {
694 0 : ErrorResult error;
695 0 : GetFilesHelper* helper = mInput->GetOrCreateGetFilesHelper(true, error);
696 0 : if (NS_WARN_IF(error.Failed())) {
697 0 : return error.StealNSResult();
698 : }
699 :
700 0 : helper->AddCallback(dispatchChangeEventCallback);
701 0 : return NS_OK;
702 : }
703 :
704 0 : return dispatchChangeEventCallback->DispatchEvents();
705 : }
706 :
707 0 : NS_IMPL_ISUPPORTS(HTMLInputElement::nsFilePickerShownCallback,
708 : nsIFilePickerShownCallback)
709 :
710 : class nsColorPickerShownCallback final
711 : : public nsIColorPickerShownCallback
712 : {
713 0 : ~nsColorPickerShownCallback() {}
714 :
715 : public:
716 0 : nsColorPickerShownCallback(HTMLInputElement* aInput,
717 : nsIColorPicker* aColorPicker)
718 0 : : mInput(aInput)
719 : , mColorPicker(aColorPicker)
720 0 : , mValueChanged(false)
721 0 : {}
722 :
723 : NS_DECL_ISUPPORTS
724 :
725 : NS_IMETHOD Update(const nsAString& aColor) override;
726 : NS_IMETHOD Done(const nsAString& aColor) override;
727 :
728 : private:
729 : /**
730 : * Updates the internals of the object using aColor as the new value.
731 : * If aTrustedUpdate is true, it will consider that aColor is a new value.
732 : * Otherwise, it will check that aColor is different from the current value.
733 : */
734 : nsresult UpdateInternal(const nsAString& aColor, bool aTrustedUpdate);
735 :
736 : RefPtr<HTMLInputElement> mInput;
737 : nsCOMPtr<nsIColorPicker> mColorPicker;
738 : bool mValueChanged;
739 : };
740 :
741 : nsresult
742 0 : nsColorPickerShownCallback::UpdateInternal(const nsAString& aColor,
743 : bool aTrustedUpdate)
744 : {
745 0 : bool valueChanged = false;
746 :
747 0 : nsAutoString oldValue;
748 0 : if (aTrustedUpdate) {
749 0 : valueChanged = true;
750 : } else {
751 0 : mInput->GetValue(oldValue, CallerType::System);
752 : }
753 :
754 0 : IgnoredErrorResult rv;
755 0 : mInput->SetValue(aColor, CallerType::System, rv);
756 :
757 0 : if (!aTrustedUpdate) {
758 0 : nsAutoString newValue;
759 0 : mInput->GetValue(newValue, CallerType::System);
760 0 : if (!oldValue.Equals(newValue)) {
761 0 : valueChanged = true;
762 : }
763 : }
764 :
765 0 : if (valueChanged) {
766 0 : mValueChanged = true;
767 0 : return nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
768 0 : static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
769 0 : NS_LITERAL_STRING("input"), true,
770 0 : false);
771 : }
772 :
773 0 : return NS_OK;
774 : }
775 :
776 : NS_IMETHODIMP
777 0 : nsColorPickerShownCallback::Update(const nsAString& aColor)
778 : {
779 0 : return UpdateInternal(aColor, true);
780 : }
781 :
782 : NS_IMETHODIMP
783 0 : nsColorPickerShownCallback::Done(const nsAString& aColor)
784 : {
785 : /**
786 : * When Done() is called, we might be at the end of a serie of Update() calls
787 : * in which case mValueChanged is set to true and a change event will have to
788 : * be fired but we might also be in a one shot Done() call situation in which
789 : * case we should fire a change event iif the value actually changed.
790 : * UpdateInternal(bool) is taking care of that logic for us.
791 : */
792 0 : nsresult rv = NS_OK;
793 :
794 0 : mInput->PickerClosed();
795 :
796 0 : if (!aColor.IsEmpty()) {
797 0 : UpdateInternal(aColor, false);
798 : }
799 :
800 0 : if (mValueChanged) {
801 0 : rv = nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
802 0 : static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
803 0 : NS_LITERAL_STRING("change"), true,
804 0 : false);
805 : }
806 :
807 0 : return rv;
808 : }
809 :
810 0 : NS_IMPL_ISUPPORTS(nsColorPickerShownCallback, nsIColorPickerShownCallback)
811 :
812 : bool
813 0 : HTMLInputElement::IsPopupBlocked() const
814 : {
815 0 : nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
816 0 : MOZ_ASSERT(win, "window should not be null");
817 0 : if (!win) {
818 0 : return true;
819 : }
820 :
821 : // Check if page is allowed to open the popup
822 0 : if (win->GetPopupControlState() <= openControlled) {
823 0 : return false;
824 : }
825 :
826 0 : nsCOMPtr<nsIPopupWindowManager> pm = do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
827 0 : if (!pm) {
828 0 : return true;
829 : }
830 :
831 : uint32_t permission;
832 0 : pm->TestPermission(OwnerDoc()->NodePrincipal(), &permission);
833 0 : return permission == nsIPopupWindowManager::DENY_POPUP;
834 : }
835 :
836 : nsresult
837 0 : HTMLInputElement::InitColorPicker()
838 : {
839 0 : if (mPickerRunning) {
840 0 : NS_WARNING("Just one nsIColorPicker is allowed");
841 0 : return NS_ERROR_FAILURE;
842 : }
843 :
844 0 : nsCOMPtr<nsIDocument> doc = OwnerDoc();
845 :
846 0 : nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
847 0 : if (!win) {
848 0 : return NS_ERROR_FAILURE;
849 : }
850 :
851 0 : if (IsPopupBlocked()) {
852 0 : win->FirePopupBlockedEvent(doc, nullptr, EmptyString(), EmptyString());
853 0 : return NS_OK;
854 : }
855 :
856 : // Get Loc title
857 0 : nsXPIDLString title;
858 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
859 0 : "ColorPicker", title);
860 :
861 0 : nsCOMPtr<nsIColorPicker> colorPicker = do_CreateInstance("@mozilla.org/colorpicker;1");
862 0 : if (!colorPicker) {
863 0 : return NS_ERROR_FAILURE;
864 : }
865 :
866 0 : nsAutoString initialValue;
867 0 : GetNonFileValueInternal(initialValue);
868 0 : nsresult rv = colorPicker->Init(win, title, initialValue);
869 0 : NS_ENSURE_SUCCESS(rv, rv);
870 :
871 : nsCOMPtr<nsIColorPickerShownCallback> callback =
872 0 : new nsColorPickerShownCallback(this, colorPicker);
873 :
874 0 : rv = colorPicker->Open(callback);
875 0 : if (NS_SUCCEEDED(rv)) {
876 0 : mPickerRunning = true;
877 : }
878 :
879 0 : return rv;
880 : }
881 :
882 : nsresult
883 0 : HTMLInputElement::InitFilePicker(FilePickerType aType)
884 : {
885 0 : if (mPickerRunning) {
886 0 : NS_WARNING("Just one nsIFilePicker is allowed");
887 0 : return NS_ERROR_FAILURE;
888 : }
889 :
890 : // Get parent nsPIDOMWindow object.
891 0 : nsCOMPtr<nsIDocument> doc = OwnerDoc();
892 :
893 0 : nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
894 0 : if (!win) {
895 0 : return NS_ERROR_FAILURE;
896 : }
897 :
898 0 : if (IsPopupBlocked()) {
899 0 : win->FirePopupBlockedEvent(doc, nullptr, EmptyString(), EmptyString());
900 0 : return NS_OK;
901 : }
902 :
903 : // Get Loc title
904 0 : nsXPIDLString title;
905 0 : nsXPIDLString okButtonLabel;
906 0 : if (aType == FILE_PICKER_DIRECTORY) {
907 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
908 0 : "DirectoryUpload", title);
909 :
910 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
911 : "DirectoryPickerOkButtonLabel",
912 0 : okButtonLabel);
913 : } else {
914 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
915 0 : "FileUpload", title);
916 : }
917 :
918 0 : nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1");
919 0 : if (!filePicker)
920 0 : return NS_ERROR_FAILURE;
921 :
922 : int16_t mode;
923 :
924 0 : if (aType == FILE_PICKER_DIRECTORY) {
925 0 : mode = static_cast<int16_t>(nsIFilePicker::modeGetFolder);
926 0 : } else if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
927 0 : mode = static_cast<int16_t>(nsIFilePicker::modeOpenMultiple);
928 : } else {
929 0 : mode = static_cast<int16_t>(nsIFilePicker::modeOpen);
930 : }
931 :
932 0 : nsresult rv = filePicker->Init(win, title, mode);
933 0 : NS_ENSURE_SUCCESS(rv, rv);
934 :
935 0 : if (!okButtonLabel.IsEmpty()) {
936 0 : filePicker->SetOkButtonLabel(okButtonLabel);
937 : }
938 :
939 : // Native directory pickers ignore file type filters, so we don't spend
940 : // cycles adding them for FILE_PICKER_DIRECTORY.
941 0 : if (HasAttr(kNameSpaceID_None, nsGkAtoms::accept) &&
942 : aType != FILE_PICKER_DIRECTORY) {
943 0 : SetFilePickerFiltersFromAccept(filePicker);
944 : } else {
945 0 : filePicker->AppendFilters(nsIFilePicker::filterAll);
946 : }
947 :
948 : // Set default directory and filename
949 0 : nsAutoString defaultName;
950 :
951 : const nsTArray<OwningFileOrDirectory>& oldFiles =
952 0 : GetFilesOrDirectoriesInternal();
953 :
954 : nsCOMPtr<nsIFilePickerShownCallback> callback =
955 0 : new HTMLInputElement::nsFilePickerShownCallback(this, filePicker);
956 :
957 0 : if (!oldFiles.IsEmpty() &&
958 : aType != FILE_PICKER_DIRECTORY) {
959 0 : nsAutoString path;
960 :
961 0 : nsCOMPtr<nsIFile> parentFile = LastUsedDirectory(oldFiles[0]);
962 0 : if (parentFile) {
963 0 : filePicker->SetDisplayDirectory(parentFile);
964 : }
965 :
966 : // Unfortunately nsIFilePicker doesn't allow multiple files to be
967 : // default-selected, so only select something by default if exactly
968 : // one file was selected before.
969 0 : if (oldFiles.Length() == 1) {
970 0 : nsAutoString leafName;
971 0 : GetDOMFileOrDirectoryName(oldFiles[0], leafName);
972 :
973 0 : if (!leafName.IsEmpty()) {
974 0 : filePicker->SetDefaultString(leafName);
975 : }
976 : }
977 :
978 0 : rv = filePicker->Open(callback);
979 0 : if (NS_SUCCEEDED(rv)) {
980 0 : mPickerRunning = true;
981 : }
982 :
983 0 : return rv;
984 : }
985 :
986 0 : HTMLInputElement::gUploadLastDir->FetchDirectoryAndDisplayPicker(doc, filePicker, callback);
987 0 : mPickerRunning = true;
988 0 : return NS_OK;
989 : }
990 :
991 : #define CPS_PREF_NAME NS_LITERAL_STRING("browser.upload.lastDir")
992 :
993 4 : NS_IMPL_ISUPPORTS(UploadLastDir, nsIObserver, nsISupportsWeakReference)
994 :
995 : void
996 1 : HTMLInputElement::InitUploadLastDir() {
997 1 : gUploadLastDir = new UploadLastDir();
998 1 : NS_ADDREF(gUploadLastDir);
999 :
1000 : nsCOMPtr<nsIObserverService> observerService =
1001 2 : mozilla::services::GetObserverService();
1002 1 : if (observerService && gUploadLastDir) {
1003 1 : observerService->AddObserver(gUploadLastDir, "browser:purge-session-history", true);
1004 : }
1005 1 : }
1006 :
1007 : void
1008 0 : HTMLInputElement::DestroyUploadLastDir() {
1009 0 : NS_IF_RELEASE(gUploadLastDir);
1010 0 : }
1011 :
1012 : nsresult
1013 0 : UploadLastDir::FetchDirectoryAndDisplayPicker(nsIDocument* aDoc,
1014 : nsIFilePicker* aFilePicker,
1015 : nsIFilePickerShownCallback* aFpCallback)
1016 : {
1017 0 : NS_PRECONDITION(aDoc, "aDoc is null");
1018 0 : NS_PRECONDITION(aFilePicker, "aFilePicker is null");
1019 0 : NS_PRECONDITION(aFpCallback, "aFpCallback is null");
1020 :
1021 0 : nsIURI* docURI = aDoc->GetDocumentURI();
1022 0 : NS_PRECONDITION(docURI, "docURI is null");
1023 :
1024 0 : nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
1025 : nsCOMPtr<nsIContentPrefCallback2> prefCallback =
1026 0 : new UploadLastDir::ContentPrefCallback(aFilePicker, aFpCallback);
1027 :
1028 : #ifdef MOZ_B2G
1029 : if (XRE_IsContentProcess()) {
1030 : prefCallback->HandleCompletion(nsIContentPrefCallback2::COMPLETE_ERROR);
1031 : return NS_OK;
1032 : }
1033 : #endif
1034 :
1035 : // Attempt to get the CPS, if it's not present we'll fallback to use the Desktop folder
1036 : nsCOMPtr<nsIContentPrefService2> contentPrefService =
1037 0 : do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
1038 0 : if (!contentPrefService) {
1039 0 : prefCallback->HandleCompletion(nsIContentPrefCallback2::COMPLETE_ERROR);
1040 0 : return NS_OK;
1041 : }
1042 :
1043 0 : nsAutoCString cstrSpec;
1044 0 : docURI->GetSpec(cstrSpec);
1045 0 : NS_ConvertUTF8toUTF16 spec(cstrSpec);
1046 :
1047 0 : contentPrefService->GetByDomainAndName(spec, CPS_PREF_NAME, loadContext, prefCallback);
1048 0 : return NS_OK;
1049 : }
1050 :
1051 : nsresult
1052 0 : UploadLastDir::StoreLastUsedDirectory(nsIDocument* aDoc, nsIFile* aDir)
1053 : {
1054 0 : NS_PRECONDITION(aDoc, "aDoc is null");
1055 0 : if (!aDir) {
1056 0 : return NS_OK;
1057 : }
1058 :
1059 : #ifdef MOZ_B2G
1060 : if (XRE_IsContentProcess()) {
1061 : return NS_OK;
1062 : }
1063 : #endif
1064 :
1065 0 : nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
1066 0 : NS_PRECONDITION(docURI, "docURI is null");
1067 :
1068 : // Attempt to get the CPS, if it's not present we'll just return
1069 : nsCOMPtr<nsIContentPrefService2> contentPrefService =
1070 0 : do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
1071 0 : if (!contentPrefService)
1072 0 : return NS_ERROR_NOT_AVAILABLE;
1073 :
1074 0 : nsAutoCString cstrSpec;
1075 0 : docURI->GetSpec(cstrSpec);
1076 0 : NS_ConvertUTF8toUTF16 spec(cstrSpec);
1077 :
1078 : // Find the parent of aFile, and store it
1079 0 : nsString unicodePath;
1080 0 : aDir->GetPath(unicodePath);
1081 0 : if (unicodePath.IsEmpty()) // nothing to do
1082 0 : return NS_OK;
1083 0 : RefPtr<nsVariantCC> prefValue = new nsVariantCC();
1084 0 : prefValue->SetAsAString(unicodePath);
1085 :
1086 : // Use the document's current load context to ensure that the content pref
1087 : // service doesn't persistently store this directory for this domain if the
1088 : // user is using private browsing:
1089 0 : nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
1090 0 : return contentPrefService->Set(spec, CPS_PREF_NAME, prefValue, loadContext, nullptr);
1091 : }
1092 :
1093 : NS_IMETHODIMP
1094 0 : UploadLastDir::Observe(nsISupports* aSubject, char const* aTopic, char16_t const* aData)
1095 : {
1096 0 : if (strcmp(aTopic, "browser:purge-session-history") == 0) {
1097 : nsCOMPtr<nsIContentPrefService2> contentPrefService =
1098 0 : do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
1099 0 : if (contentPrefService)
1100 0 : contentPrefService->RemoveByName(CPS_PREF_NAME, nullptr, nullptr);
1101 : }
1102 0 : return NS_OK;
1103 : }
1104 :
1105 : #ifdef ACCESSIBILITY
1106 : //Helper method
1107 : static nsresult FireEventForAccessibility(nsIDOMHTMLInputElement* aTarget,
1108 : nsPresContext* aPresContext,
1109 : EventMessage aEventMessage);
1110 : #endif
1111 :
1112 : nsTextEditorState* HTMLInputElement::sCachedTextEditorState = nullptr;
1113 : bool HTMLInputElement::sShutdown = false;
1114 :
1115 : /* static */ void
1116 1 : HTMLInputElement::ReleaseTextEditorState(nsTextEditorState* aState)
1117 : {
1118 1 : if (!sShutdown && !sCachedTextEditorState) {
1119 1 : aState->PrepareForReuse();
1120 1 : sCachedTextEditorState = aState;
1121 : } else {
1122 0 : delete aState;
1123 : }
1124 1 : }
1125 :
1126 : /* static */ void
1127 0 : HTMLInputElement::Shutdown()
1128 : {
1129 0 : sShutdown = true;
1130 0 : delete sCachedTextEditorState;
1131 0 : sCachedTextEditorState = nullptr;
1132 0 : }
1133 :
1134 : //
1135 : // construction, destruction
1136 : //
1137 :
1138 7 : HTMLInputElement::HTMLInputElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
1139 7 : FromParser aFromParser, FromClone aFromClone)
1140 7 : : nsGenericHTMLFormElementWithState(aNodeInfo, kInputDefaultType->value)
1141 : , mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown)
1142 : , mAutocompleteInfoState(nsContentUtils::eAutocompleteAttrState_Unknown)
1143 : , mDisabledChanged(false)
1144 : , mValueChanged(false)
1145 : , mLastValueChangeWasInteractive(false)
1146 : , mCheckedChanged(false)
1147 : , mChecked(false)
1148 : , mHandlingSelectEvent(false)
1149 : , mShouldInitChecked(false)
1150 7 : , mDoneCreating(aFromParser == NOT_FROM_PARSER &&
1151 : aFromClone == FromClone::no)
1152 : , mInInternalActivate(false)
1153 : , mCheckedIsToggled(false)
1154 : , mIndeterminate(false)
1155 7 : , mInhibitRestoration(aFromParser & FROM_PARSER_FRAGMENT)
1156 : , mCanShowValidUI(true)
1157 : , mCanShowInvalidUI(true)
1158 : , mHasRange(false)
1159 : , mIsDraggingRange(false)
1160 : , mNumberControlSpinnerIsSpinning(false)
1161 : , mNumberControlSpinnerSpinsUp(false)
1162 : , mPickerRunning(false)
1163 : , mSelectionCached(true)
1164 21 : , mIsPreviewEnabled(false)
1165 : {
1166 : // If size is above 512, mozjemalloc allocates 1kB, see
1167 : // memory/mozjemalloc/jemalloc.c
1168 : static_assert(sizeof(HTMLInputElement) <= 512,
1169 : "Keep the size of HTMLInputElement under 512 to avoid "
1170 : "performance regression!");
1171 :
1172 : // We are in a type=text so we now we currenty need a nsTextEditorState.
1173 7 : mInputData.mState =
1174 7 : nsTextEditorState::Construct(this, &sCachedTextEditorState);
1175 :
1176 7 : void* memory = mInputTypeMem;
1177 7 : mInputType = InputType::Create(this, mType, memory);
1178 :
1179 7 : if (!gUploadLastDir)
1180 1 : HTMLInputElement::InitUploadLastDir();
1181 :
1182 : // Set up our default state. By default we're enabled (since we're
1183 : // a control type that can be disabled but not actually disabled
1184 : // right now), optional, and valid. We are NOT readwrite by default
1185 : // until someone calls UpdateEditableState on us, apparently! Also
1186 : // by default we don't have to show validity UI and so forth.
1187 14 : AddStatesSilently(NS_EVENT_STATE_ENABLED |
1188 14 : NS_EVENT_STATE_OPTIONAL |
1189 21 : NS_EVENT_STATE_VALID);
1190 7 : UpdateApzAwareFlag();
1191 7 : }
1192 :
1193 0 : HTMLInputElement::~HTMLInputElement()
1194 : {
1195 0 : if (mNumberControlSpinnerIsSpinning) {
1196 0 : StopNumberControlSpinnerSpin(eDisallowDispatchingEvents);
1197 : }
1198 0 : DestroyImageLoadingContent();
1199 0 : FreeData();
1200 0 : }
1201 :
1202 : void
1203 1 : HTMLInputElement::FreeData()
1204 : {
1205 1 : if (!IsSingleLineTextControl(false)) {
1206 0 : free(mInputData.mValue);
1207 0 : mInputData.mValue = nullptr;
1208 : } else {
1209 1 : UnbindFromFrame(nullptr);
1210 1 : ReleaseTextEditorState(mInputData.mState);
1211 1 : mInputData.mState = nullptr;
1212 : }
1213 :
1214 1 : if (mInputType) {
1215 1 : mInputType->DropReference();
1216 1 : mInputType = nullptr;
1217 : }
1218 1 : }
1219 :
1220 : nsTextEditorState*
1221 343 : HTMLInputElement::GetEditorState() const
1222 : {
1223 343 : if (!IsSingleLineTextControl(false)) {
1224 0 : return nullptr;
1225 : }
1226 :
1227 343 : MOZ_ASSERT(mInputData.mState, "Single line text controls need to have a state"
1228 : " associated with them");
1229 :
1230 343 : return mInputData.mState;
1231 : }
1232 :
1233 :
1234 : // nsISupports
1235 :
1236 : NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLInputElement)
1237 :
1238 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement,
1239 : nsGenericHTMLFormElementWithState)
1240 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
1241 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
1242 2 : if (tmp->IsSingleLineTextControl(false)) {
1243 2 : tmp->mInputData.mState->Traverse(cb);
1244 : }
1245 :
1246 2 : if (tmp->mFileData) {
1247 0 : tmp->mFileData->Traverse(cb);
1248 : }
1249 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1250 :
1251 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement,
1252 : nsGenericHTMLFormElementWithState)
1253 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
1254 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
1255 0 : if (tmp->IsSingleLineTextControl(false)) {
1256 0 : tmp->mInputData.mState->Unlink();
1257 : }
1258 :
1259 0 : if (tmp->mFileData) {
1260 0 : tmp->mFileData->Unlink();
1261 : }
1262 : //XXX should unlink more?
1263 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1264 :
1265 434 : NS_IMPL_ADDREF_INHERITED(HTMLInputElement, Element)
1266 410 : NS_IMPL_RELEASE_INHERITED(HTMLInputElement, Element)
1267 :
1268 : // QueryInterface implementation for HTMLInputElement
1269 370 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLInputElement)
1270 353 : NS_INTERFACE_TABLE_INHERITED(HTMLInputElement,
1271 : nsIDOMHTMLInputElement,
1272 : nsITextControlElement,
1273 : imgINotificationObserver,
1274 : nsIImageLoadingContent,
1275 : imgIOnloadBlocker,
1276 : nsIDOMNSEditableElement,
1277 : nsIConstraintValidation)
1278 353 : NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLFormElementWithState)
1279 :
1280 : // nsIConstraintValidation
1281 0 : NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(HTMLInputElement)
1282 :
1283 : // nsIDOMNode
1284 :
1285 : nsresult
1286 2 : HTMLInputElement::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult,
1287 : bool aPreallocateArrays) const
1288 : {
1289 2 : *aResult = nullptr;
1290 :
1291 4 : already_AddRefed<mozilla::dom::NodeInfo> ni = RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();
1292 : RefPtr<HTMLInputElement> it = new HTMLInputElement(ni, NOT_FROM_PARSER,
1293 4 : FromClone::yes);
1294 :
1295 2 : nsresult rv = const_cast<HTMLInputElement*>(this)->CopyInnerTo(it, aPreallocateArrays);
1296 2 : NS_ENSURE_SUCCESS(rv, rv);
1297 :
1298 2 : switch (GetValueMode()) {
1299 : case VALUE_MODE_VALUE:
1300 2 : if (mValueChanged) {
1301 : // We don't have our default value anymore. Set our value on
1302 : // the clone.
1303 0 : nsAutoString value;
1304 0 : GetNonFileValueInternal(value);
1305 : // SetValueInternal handles setting the VALUE_CHANGED bit for us
1306 0 : rv = it->SetValueInternal(value, nsTextEditorState::eSetValue_Notify);
1307 0 : NS_ENSURE_SUCCESS(rv, rv);
1308 : }
1309 2 : break;
1310 : case VALUE_MODE_FILENAME:
1311 0 : if (it->OwnerDoc()->IsStaticDocument()) {
1312 : // We're going to be used in print preview. Since the doc is static
1313 : // we can just grab the pretty string and use it as wallpaper
1314 0 : GetDisplayFileName(it->mFileData->mStaticDocFileList);
1315 : } else {
1316 0 : it->mFileData->ClearGetFilesHelpers();
1317 0 : it->mFileData->mFilesOrDirectories.Clear();
1318 0 : it->mFileData->mFilesOrDirectories.AppendElements(
1319 0 : mFileData->mFilesOrDirectories);
1320 : }
1321 0 : break;
1322 : case VALUE_MODE_DEFAULT_ON:
1323 0 : if (mCheckedChanged) {
1324 : // We no longer have our original checked state. Set our
1325 : // checked state on the clone.
1326 0 : it->DoSetChecked(mChecked, false, true);
1327 : // Then tell DoneCreatingElement() not to overwrite:
1328 0 : it->mShouldInitChecked = false;
1329 : }
1330 0 : break;
1331 : case VALUE_MODE_DEFAULT:
1332 0 : if (mType == NS_FORM_INPUT_IMAGE && it->OwnerDoc()->IsStaticDocument()) {
1333 0 : CreateStaticImageClone(it);
1334 : }
1335 0 : break;
1336 : }
1337 :
1338 2 : it->DoneCreatingElement();
1339 :
1340 2 : it->mLastValueChangeWasInteractive = mLastValueChangeWasInteractive;
1341 2 : it.forget(aResult);
1342 2 : return NS_OK;
1343 : }
1344 :
1345 : nsresult
1346 32 : HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
1347 : const nsAttrValueOrString* aValue,
1348 : bool aNotify)
1349 : {
1350 32 : if (aNameSpaceID == kNameSpaceID_None) {
1351 : //
1352 : // When name or type changes, radio should be removed from radio group.
1353 : // (type changes are handled in the form itself currently)
1354 : // If we are not done creating the radio, we also should not do it.
1355 : //
1356 50 : if ((aName == nsGkAtoms::name ||
1357 27 : (aName == nsGkAtoms::type && !mForm)) &&
1358 1 : mType == NS_FORM_INPUT_RADIO &&
1359 0 : (mForm || mDoneCreating)) {
1360 0 : WillRemoveFromRadioGroup();
1361 25 : } else if (aNotify && aName == nsGkAtoms::src &&
1362 0 : mType == NS_FORM_INPUT_IMAGE) {
1363 0 : if (aValue) {
1364 : // Mark channel as urgent-start before load image if the image load is
1365 : // initaiated by a user interaction.
1366 0 : mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
1367 :
1368 0 : LoadImage(aValue->String(), true, aNotify, eImageLoadType_Normal);
1369 : } else {
1370 : // Null value means the attr got unset; drop the image
1371 0 : CancelImageRequests(aNotify);
1372 : }
1373 25 : } else if (aNotify && aName == nsGkAtoms::disabled) {
1374 0 : mDisabledChanged = true;
1375 25 : } else if (mType == NS_FORM_INPUT_RADIO && aName == nsGkAtoms::required) {
1376 0 : nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
1377 :
1378 0 : if (container &&
1379 0 : ((aValue && !HasAttr(aNameSpaceID, aName)) ||
1380 0 : (!aValue && HasAttr(aNameSpaceID, aName)))) {
1381 0 : nsAutoString name;
1382 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
1383 0 : container->RadioRequiredWillChange(name, !!aValue);
1384 : }
1385 : }
1386 :
1387 25 : if (aName == nsGkAtoms::webkitdirectory) {
1388 0 : Telemetry::Accumulate(Telemetry::WEBKIT_DIRECTORY_USED, true);
1389 : }
1390 : }
1391 :
1392 32 : return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
1393 32 : aValue, aNotify);
1394 : }
1395 :
1396 : nsresult
1397 32 : HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
1398 : const nsAttrValue* aValue,
1399 : const nsAttrValue* aOldValue, bool aNotify)
1400 : {
1401 32 : if (aNameSpaceID == kNameSpaceID_None) {
1402 : //
1403 : // When name or type changes, radio should be added to radio group.
1404 : // (type changes are handled in the form itself currently)
1405 : // If we are not done creating the radio, we also should not do it.
1406 : //
1407 50 : if ((aName == nsGkAtoms::name ||
1408 27 : (aName == nsGkAtoms::type && !mForm)) &&
1409 1 : mType == NS_FORM_INPUT_RADIO &&
1410 0 : (mForm || mDoneCreating)) {
1411 0 : AddedToRadioGroup();
1412 0 : UpdateValueMissingValidityStateForRadio(false);
1413 : }
1414 :
1415 : // If @value is changed and BF_VALUE_CHANGED is false, @value is the value
1416 : // of the element so, if the value of the element is different than @value,
1417 : // we have to re-set it. This is only the case when GetValueMode() returns
1418 : // VALUE_MODE_VALUE.
1419 50 : if (aName == nsGkAtoms::value &&
1420 25 : !mValueChanged && GetValueMode() == VALUE_MODE_VALUE) {
1421 0 : SetDefaultValueAsValue();
1422 : }
1423 :
1424 : //
1425 : // Checked must be set no matter what type of control it is, since
1426 : // mChecked must reflect the new value
1427 25 : if (aName == nsGkAtoms::checked && !mCheckedChanged) {
1428 : // Delay setting checked if we are creating this element (wait
1429 : // until everything is set)
1430 0 : if (!mDoneCreating) {
1431 0 : mShouldInitChecked = true;
1432 : } else {
1433 0 : DoSetChecked(DefaultChecked(), true, false);
1434 : }
1435 : }
1436 :
1437 25 : if (aName == nsGkAtoms::type) {
1438 : uint8_t newType;
1439 1 : if (!aValue) {
1440 : // We're now a text input.
1441 0 : newType = kInputDefaultType->value;
1442 : } else {
1443 1 : newType = aValue->GetEnumValue();
1444 : }
1445 1 : if (newType != mType) {
1446 1 : HandleTypeChange(newType, aNotify);
1447 : }
1448 : }
1449 :
1450 50 : if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
1451 25 : aName == nsGkAtoms::readonly) {
1452 0 : UpdateValueMissingValidityState();
1453 :
1454 : // This *has* to be called *after* validity has changed.
1455 0 : if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
1456 0 : UpdateBarredFromConstraintValidation();
1457 : }
1458 25 : } else if (aName == nsGkAtoms::maxlength) {
1459 0 : UpdateTooLongValidityState();
1460 25 : } else if (aName == nsGkAtoms::minlength) {
1461 0 : UpdateTooShortValidityState();
1462 25 : } else if (aName == nsGkAtoms::pattern && mDoneCreating) {
1463 0 : UpdatePatternMismatchValidityState();
1464 25 : } else if (aName == nsGkAtoms::multiple) {
1465 0 : UpdateTypeMismatchValidityState();
1466 25 : } else if (aName == nsGkAtoms::max) {
1467 0 : UpdateHasRange();
1468 0 : nsresult rv = mInputType->MinMaxStepAttrChanged();
1469 0 : NS_ENSURE_SUCCESS(rv, rv);
1470 : // Validity state must be updated *after* the UpdateValueDueToAttrChange
1471 : // call above or else the following assert will not be valid.
1472 : // We don't assert the state of underflow during creation since
1473 : // DoneCreatingElement sanitizes.
1474 0 : UpdateRangeOverflowValidityState();
1475 0 : MOZ_ASSERT(!mDoneCreating ||
1476 : mType != NS_FORM_INPUT_RANGE ||
1477 : !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
1478 : "HTML5 spec does not allow underflow for type=range");
1479 25 : } else if (aName == nsGkAtoms::min) {
1480 0 : UpdateHasRange();
1481 0 : nsresult rv = mInputType->MinMaxStepAttrChanged();
1482 0 : NS_ENSURE_SUCCESS(rv, rv);
1483 : // See corresponding @max comment
1484 0 : UpdateRangeUnderflowValidityState();
1485 0 : UpdateStepMismatchValidityState();
1486 0 : MOZ_ASSERT(!mDoneCreating ||
1487 : mType != NS_FORM_INPUT_RANGE ||
1488 : !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
1489 : "HTML5 spec does not allow underflow for type=range");
1490 25 : } else if (aName == nsGkAtoms::step) {
1491 0 : nsresult rv = mInputType->MinMaxStepAttrChanged();
1492 0 : NS_ENSURE_SUCCESS(rv, rv);
1493 : // See corresponding @max comment
1494 0 : UpdateStepMismatchValidityState();
1495 0 : MOZ_ASSERT(!mDoneCreating ||
1496 : mType != NS_FORM_INPUT_RANGE ||
1497 : !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
1498 : "HTML5 spec does not allow underflow for type=range");
1499 50 : } else if (aName == nsGkAtoms::dir &&
1500 25 : aValue && aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
1501 0 : SetDirectionFromValue(aNotify);
1502 25 : } else if (aName == nsGkAtoms::lang) {
1503 0 : if (mType == NS_FORM_INPUT_NUMBER) {
1504 : // Update the value that is displayed to the user to the new locale:
1505 0 : nsAutoString value;
1506 0 : GetNonFileValueInternal(value);
1507 : nsNumberControlFrame* numberControlFrame =
1508 0 : do_QueryFrame(GetPrimaryFrame());
1509 0 : if (numberControlFrame) {
1510 0 : numberControlFrame->SetValueOfAnonTextControl(value);
1511 : }
1512 : }
1513 25 : } else if (aName == nsGkAtoms::autocomplete) {
1514 : // Clear the cached @autocomplete attribute and autocompleteInfo state.
1515 0 : mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown;
1516 0 : mAutocompleteInfoState = nsContentUtils::eAutocompleteAttrState_Unknown;
1517 : }
1518 : }
1519 :
1520 32 : return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName,
1521 : aValue, aOldValue,
1522 32 : aNotify);
1523 : }
1524 :
1525 : void
1526 0 : HTMLInputElement::BeforeSetForm(bool aBindToTree)
1527 : {
1528 : // No need to remove from radio group if we are just binding to tree.
1529 0 : if (mType == NS_FORM_INPUT_RADIO && !aBindToTree) {
1530 0 : WillRemoveFromRadioGroup();
1531 : }
1532 0 : }
1533 :
1534 : void
1535 0 : HTMLInputElement::AfterClearForm(bool aUnbindOrDelete)
1536 : {
1537 0 : MOZ_ASSERT(!mForm);
1538 :
1539 : // Do not add back to radio group if we are releasing or unbinding from tree.
1540 0 : if (mType == NS_FORM_INPUT_RADIO && !aUnbindOrDelete) {
1541 0 : AddedToRadioGroup();
1542 0 : UpdateValueMissingValidityStateForRadio(false);
1543 : }
1544 0 : }
1545 :
1546 : // nsIDOMHTMLInputElement
1547 :
1548 : NS_IMETHODIMP
1549 0 : HTMLInputElement::GetForm(nsIDOMHTMLFormElement** aForm)
1550 : {
1551 0 : return nsGenericHTMLFormElementWithState::GetForm(aForm);
1552 : }
1553 :
1554 30 : NS_IMPL_STRING_ATTR(HTMLInputElement, DefaultValue, value)
1555 0 : NS_IMPL_BOOL_ATTR(HTMLInputElement, DefaultChecked, checked)
1556 0 : NS_IMPL_STRING_ATTR(HTMLInputElement, Accept, accept)
1557 0 : NS_IMPL_STRING_ATTR(HTMLInputElement, Align, align)
1558 0 : NS_IMPL_STRING_ATTR(HTMLInputElement, Alt, alt)
1559 0 : NS_IMPL_BOOL_ATTR(HTMLInputElement, Autofocus, autofocus)
1560 : //NS_IMPL_BOOL_ATTR(HTMLInputElement, Checked, checked)
1561 0 : NS_IMPL_BOOL_ATTR(HTMLInputElement, Disabled, disabled)
1562 0 : NS_IMPL_STRING_ATTR(HTMLInputElement, Max, max)
1563 0 : NS_IMPL_STRING_ATTR(HTMLInputElement, Min, min)
1564 0 : NS_IMPL_ACTION_ATTR(HTMLInputElement, FormAction, formaction)
1565 0 : NS_IMPL_ENUM_ATTR_DEFAULT_MISSING_INVALID_VALUES(HTMLInputElement, FormEnctype, formenctype,
1566 : "", kFormDefaultEnctype->tag)
1567 0 : NS_IMPL_ENUM_ATTR_DEFAULT_MISSING_INVALID_VALUES(HTMLInputElement, FormMethod, formmethod,
1568 : "", kFormDefaultMethod->tag)
1569 0 : NS_IMPL_BOOL_ATTR(HTMLInputElement, FormNoValidate, formnovalidate)
1570 0 : NS_IMPL_STRING_ATTR(HTMLInputElement, FormTarget, formtarget)
1571 0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLInputElement, InputMode, inputmode,
1572 : kInputDefaultInputmode->tag)
1573 0 : NS_IMPL_BOOL_ATTR(HTMLInputElement, Multiple, multiple)
1574 0 : NS_IMPL_NON_NEGATIVE_INT_ATTR(HTMLInputElement, MaxLength, maxlength)
1575 0 : NS_IMPL_NON_NEGATIVE_INT_ATTR(HTMLInputElement, MinLength, minlength)
1576 0 : NS_IMPL_STRING_ATTR(HTMLInputElement, Name, name)
1577 0 : NS_IMPL_BOOL_ATTR(HTMLInputElement, ReadOnly, readonly)
1578 0 : NS_IMPL_BOOL_ATTR(HTMLInputElement, Required, required)
1579 0 : NS_IMPL_URI_ATTR(HTMLInputElement, Src, src)
1580 0 : NS_IMPL_STRING_ATTR(HTMLInputElement, Step, step)
1581 0 : NS_IMPL_STRING_ATTR(HTMLInputElement, UseMap, usemap)
1582 : //NS_IMPL_STRING_ATTR(HTMLInputElement, Value, value)
1583 0 : NS_IMPL_UINT_ATTR_NON_ZERO_DEFAULT_VALUE(HTMLInputElement, Size, size, DEFAULT_COLS)
1584 0 : NS_IMPL_STRING_ATTR(HTMLInputElement, Pattern, pattern)
1585 0 : NS_IMPL_STRING_ATTR(HTMLInputElement, Placeholder, placeholder)
1586 0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLInputElement, Type, type,
1587 : kInputDefaultType->tag)
1588 :
1589 : NS_IMETHODIMP
1590 0 : HTMLInputElement::GetAutocomplete(nsAString& aValue)
1591 : {
1592 0 : if (!DoesAutocompleteApply()) {
1593 0 : return NS_OK;
1594 : }
1595 :
1596 0 : aValue.Truncate();
1597 0 : const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
1598 :
1599 0 : mAutocompleteAttrState =
1600 0 : nsContentUtils::SerializeAutocompleteAttribute(attributeVal, aValue,
1601 0 : mAutocompleteAttrState);
1602 0 : return NS_OK;
1603 : }
1604 :
1605 : NS_IMETHODIMP
1606 0 : HTMLInputElement::SetAutocomplete(const nsAString& aValue)
1607 : {
1608 0 : return SetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete, nullptr, aValue, true);
1609 : }
1610 :
1611 : void
1612 0 : HTMLInputElement::GetAutocompleteInfo(Nullable<AutocompleteInfo>& aInfo)
1613 : {
1614 0 : if (!DoesAutocompleteApply()) {
1615 0 : aInfo.SetNull();
1616 0 : return;
1617 : }
1618 :
1619 0 : const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
1620 0 : mAutocompleteInfoState =
1621 0 : nsContentUtils::SerializeAutocompleteAttribute(attributeVal, aInfo.SetValue(),
1622 0 : mAutocompleteInfoState,
1623 : true);
1624 : }
1625 :
1626 : int32_t
1627 0 : HTMLInputElement::TabIndexDefault()
1628 : {
1629 0 : return 0;
1630 : }
1631 :
1632 : uint32_t
1633 0 : HTMLInputElement::Height()
1634 : {
1635 0 : if (mType != NS_FORM_INPUT_IMAGE) {
1636 0 : return 0;
1637 : }
1638 0 : return GetWidthHeightForImage(mCurrentRequest).height;
1639 : }
1640 :
1641 : NS_IMETHODIMP
1642 0 : HTMLInputElement::GetHeight(uint32_t* aHeight)
1643 : {
1644 0 : *aHeight = Height();
1645 0 : return NS_OK;
1646 : }
1647 :
1648 : NS_IMETHODIMP
1649 0 : HTMLInputElement::SetHeight(uint32_t aHeight)
1650 : {
1651 0 : ErrorResult rv;
1652 0 : SetHeight(aHeight, rv);
1653 0 : return rv.StealNSResult();
1654 : }
1655 :
1656 : NS_IMETHODIMP
1657 0 : HTMLInputElement::GetIndeterminate(bool* aValue)
1658 : {
1659 0 : *aValue = Indeterminate();
1660 0 : return NS_OK;
1661 : }
1662 :
1663 : void
1664 0 : HTMLInputElement::SetIndeterminateInternal(bool aValue,
1665 : bool aShouldInvalidate)
1666 : {
1667 0 : mIndeterminate = aValue;
1668 :
1669 0 : if (aShouldInvalidate) {
1670 : // Repaint the frame
1671 0 : nsIFrame* frame = GetPrimaryFrame();
1672 0 : if (frame)
1673 0 : frame->InvalidateFrameSubtree();
1674 : }
1675 :
1676 0 : UpdateState(true);
1677 0 : }
1678 :
1679 : NS_IMETHODIMP
1680 0 : HTMLInputElement::SetIndeterminate(bool aValue)
1681 : {
1682 0 : SetIndeterminateInternal(aValue, true);
1683 0 : return NS_OK;
1684 : }
1685 :
1686 : uint32_t
1687 0 : HTMLInputElement::Width()
1688 : {
1689 0 : if (mType != NS_FORM_INPUT_IMAGE) {
1690 0 : return 0;
1691 : }
1692 0 : return GetWidthHeightForImage(mCurrentRequest).width;
1693 : }
1694 :
1695 : NS_IMETHODIMP
1696 0 : HTMLInputElement::GetWidth(uint32_t* aWidth)
1697 : {
1698 0 : *aWidth = Width();
1699 0 : return NS_OK;
1700 : }
1701 :
1702 : NS_IMETHODIMP
1703 0 : HTMLInputElement::SetWidth(uint32_t aWidth)
1704 : {
1705 0 : ErrorResult rv;
1706 0 : SetWidth(aWidth, rv);
1707 0 : return rv.StealNSResult();
1708 : }
1709 :
1710 : void
1711 5 : HTMLInputElement::GetValue(nsAString& aValue, CallerType aCallerType)
1712 : {
1713 5 : GetValueInternal(aValue, aCallerType);
1714 :
1715 : // Don't return non-sanitized value for types that are experimental on mobile
1716 : // or datetime types
1717 5 : if (IsExperimentalMobileType(mType) || IsDateTimeInputType(mType)) {
1718 0 : SanitizeValue(aValue);
1719 : }
1720 5 : }
1721 :
1722 : void
1723 5 : HTMLInputElement::GetValueInternal(nsAString& aValue,
1724 : CallerType aCallerType) const
1725 : {
1726 5 : if (mType != NS_FORM_INPUT_FILE) {
1727 5 : GetNonFileValueInternal(aValue);
1728 5 : return;
1729 : }
1730 :
1731 0 : if (aCallerType == CallerType::System) {
1732 0 : aValue.Assign(mFileData->mFirstFilePath);
1733 0 : return;
1734 : }
1735 :
1736 0 : if (mFileData->mFilesOrDirectories.IsEmpty()) {
1737 0 : aValue.Truncate();
1738 0 : return;
1739 : }
1740 :
1741 0 : nsAutoString file;
1742 0 : GetDOMFileOrDirectoryName(mFileData->mFilesOrDirectories[0], file);
1743 0 : if (file.IsEmpty()) {
1744 0 : aValue.Truncate();
1745 0 : return;
1746 : }
1747 :
1748 0 : aValue.AssignLiteral("C:\\fakepath\\");
1749 0 : aValue.Append(file);
1750 : }
1751 :
1752 : void
1753 5 : HTMLInputElement::GetNonFileValueInternal(nsAString& aValue) const
1754 : {
1755 5 : switch (GetValueMode()) {
1756 : case VALUE_MODE_VALUE:
1757 5 : if (IsSingleLineTextControl(false)) {
1758 5 : mInputData.mState->GetValue(aValue, true);
1759 0 : } else if (!aValue.Assign(mInputData.mValue, fallible)) {
1760 0 : aValue.Truncate();
1761 : }
1762 5 : return;
1763 :
1764 : case VALUE_MODE_FILENAME:
1765 0 : NS_NOTREACHED("Someone screwed up here");
1766 : // We'll just return empty string if someone does screw up.
1767 0 : aValue.Truncate();
1768 0 : return;
1769 :
1770 : case VALUE_MODE_DEFAULT:
1771 : // Treat defaultValue as value.
1772 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue);
1773 0 : return;
1774 :
1775 : case VALUE_MODE_DEFAULT_ON:
1776 : // Treat default value as value and returns "on" if no value.
1777 0 : if (!GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue)) {
1778 0 : aValue.AssignLiteral("on");
1779 : }
1780 0 : return;
1781 : }
1782 : }
1783 :
1784 : bool
1785 7 : HTMLInputElement::IsValueEmpty() const
1786 : {
1787 7 : if (GetValueMode() == VALUE_MODE_VALUE && IsSingleLineTextControl(false)) {
1788 7 : return !mInputData.mState->HasNonEmptyValue();
1789 : }
1790 :
1791 0 : nsAutoString value;
1792 0 : GetNonFileValueInternal(value);
1793 :
1794 0 : return value.IsEmpty();
1795 : }
1796 :
1797 : void
1798 0 : HTMLInputElement::ClearFiles(bool aSetValueChanged)
1799 : {
1800 0 : nsTArray<OwningFileOrDirectory> data;
1801 0 : SetFilesOrDirectories(data, aSetValueChanged);
1802 0 : }
1803 :
1804 : int32_t
1805 0 : HTMLInputElement::MonthsSinceJan1970(uint32_t aYear, uint32_t aMonth) const
1806 : {
1807 0 : return (aYear - 1970) * 12 + aMonth - 1;
1808 : }
1809 :
1810 : /* static */ Decimal
1811 0 : HTMLInputElement::StringToDecimal(const nsAString& aValue)
1812 : {
1813 0 : if (!IsASCII(aValue)) {
1814 0 : return Decimal::nan();
1815 : }
1816 0 : NS_LossyConvertUTF16toASCII asciiString(aValue);
1817 0 : std::string stdString = asciiString.get();
1818 0 : return Decimal::fromString(stdString);
1819 : }
1820 :
1821 : Decimal
1822 0 : HTMLInputElement::GetValueAsDecimal() const
1823 : {
1824 0 : Decimal decimalValue;
1825 0 : nsAutoString stringValue;
1826 :
1827 0 : GetNonFileValueInternal(stringValue);
1828 :
1829 0 : return !mInputType->ConvertStringToNumber(stringValue, decimalValue) ?
1830 0 : Decimal::nan() : decimalValue;
1831 : }
1832 :
1833 : void
1834 1 : HTMLInputElement::SetValue(const nsAString& aValue, CallerType aCallerType,
1835 : ErrorResult& aRv)
1836 : {
1837 : // check security. Note that setting the value to the empty string is always
1838 : // OK and gives pages a way to clear a file input if necessary.
1839 1 : if (mType == NS_FORM_INPUT_FILE) {
1840 0 : if (!aValue.IsEmpty()) {
1841 0 : if (aCallerType != CallerType::System) {
1842 : // setting the value of a "FILE" input widget requires
1843 : // chrome privilege
1844 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1845 0 : return;
1846 : }
1847 0 : Sequence<nsString> list;
1848 0 : if (!list.AppendElement(aValue, fallible)) {
1849 0 : aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
1850 0 : return;
1851 : }
1852 :
1853 0 : MozSetFileNameArray(list, aRv);
1854 0 : return;
1855 : }
1856 : else {
1857 0 : ClearFiles(true);
1858 : }
1859 : }
1860 : else {
1861 1 : if (MayFireChangeOnBlur()) {
1862 : // If the value has been set by a script, we basically want to keep the
1863 : // current change event state. If the element is ready to fire a change
1864 : // event, we should keep it that way. Otherwise, we should make sure the
1865 : // element will not fire any event because of the script interaction.
1866 : //
1867 : // NOTE: this is currently quite expensive work (too much string
1868 : // manipulation). We should probably optimize that.
1869 2 : nsAutoString currentValue;
1870 1 : GetValue(currentValue, aCallerType);
1871 :
1872 : // Some types sanitize value, so GetValue doesn't return pure
1873 : // previous value correctly.
1874 : nsresult rv =
1875 2 : SetValueInternal(aValue,
1876 2 : (IsExperimentalMobileType(mType) || IsDateTimeInputType(mType)) ?
1877 : nullptr : ¤tValue,
1878 : nsTextEditorState::eSetValue_ByContent |
1879 : nsTextEditorState::eSetValue_Notify |
1880 1 : nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
1881 1 : if (NS_FAILED(rv)) {
1882 0 : aRv.Throw(rv);
1883 0 : return;
1884 : }
1885 :
1886 1 : if (mFocusedValue.Equals(currentValue)) {
1887 1 : GetValue(mFocusedValue, aCallerType);
1888 : }
1889 : } else {
1890 : nsresult rv =
1891 : SetValueInternal(aValue,
1892 : nsTextEditorState::eSetValue_ByContent |
1893 : nsTextEditorState::eSetValue_Notify |
1894 0 : nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
1895 0 : if (NS_FAILED(rv)) {
1896 0 : aRv.Throw(rv);
1897 0 : return;
1898 : }
1899 : }
1900 : }
1901 : }
1902 :
1903 : nsGenericHTMLElement*
1904 0 : HTMLInputElement::GetList() const
1905 : {
1906 0 : nsAutoString dataListId;
1907 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::list, dataListId);
1908 0 : if (dataListId.IsEmpty()) {
1909 0 : return nullptr;
1910 : }
1911 :
1912 : //XXXsmaug How should this all work in case input element is in Shadow DOM.
1913 0 : nsIDocument* doc = GetUncomposedDoc();
1914 0 : if (!doc) {
1915 0 : return nullptr;
1916 : }
1917 :
1918 0 : Element* element = doc->GetElementById(dataListId);
1919 0 : if (!element || !element->IsHTMLElement(nsGkAtoms::datalist)) {
1920 0 : return nullptr;
1921 : }
1922 :
1923 0 : return static_cast<nsGenericHTMLElement*>(element);
1924 : }
1925 :
1926 : NS_IMETHODIMP
1927 0 : HTMLInputElement::GetList(nsIDOMHTMLElement** aValue)
1928 : {
1929 0 : *aValue = nullptr;
1930 :
1931 0 : RefPtr<nsGenericHTMLElement> element = GetList();
1932 0 : if (!element) {
1933 0 : return NS_OK;
1934 : }
1935 :
1936 0 : element.forget(aValue);
1937 0 : return NS_OK;
1938 : }
1939 :
1940 : void
1941 0 : HTMLInputElement::SetValue(Decimal aValue, CallerType aCallerType)
1942 : {
1943 0 : MOZ_ASSERT(!aValue.isInfinity(), "aValue must not be Infinity!");
1944 :
1945 0 : if (aValue.isNaN()) {
1946 0 : IgnoredErrorResult rv;
1947 0 : SetValue(EmptyString(), aCallerType, rv);
1948 0 : return;
1949 : }
1950 :
1951 0 : nsAutoString value;
1952 0 : mInputType->ConvertNumberToString(aValue, value);
1953 0 : IgnoredErrorResult rv;
1954 0 : SetValue(value, aCallerType, rv);
1955 : }
1956 :
1957 : Nullable<Date>
1958 0 : HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
1959 : {
1960 0 : if (!IsDateTimeInputType(mType)) {
1961 0 : return Nullable<Date>();
1962 : }
1963 :
1964 0 : switch (mType) {
1965 : case NS_FORM_INPUT_DATE:
1966 : {
1967 : uint32_t year, month, day;
1968 0 : nsAutoString value;
1969 0 : GetNonFileValueInternal(value);
1970 0 : if (!ParseDate(value, &year, &month, &day)) {
1971 0 : return Nullable<Date>();
1972 : }
1973 :
1974 0 : JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day));
1975 0 : return Nullable<Date>(Date(time));
1976 : }
1977 : case NS_FORM_INPUT_TIME:
1978 : {
1979 : uint32_t millisecond;
1980 0 : nsAutoString value;
1981 0 : GetNonFileValueInternal(value);
1982 0 : if (!ParseTime(value, &millisecond)) {
1983 0 : return Nullable<Date>();
1984 : }
1985 :
1986 0 : JS::ClippedTime time = JS::TimeClip(millisecond);
1987 0 : MOZ_ASSERT(time.toDouble() == millisecond,
1988 : "HTML times are restricted to the day after the epoch and "
1989 : "never clip");
1990 0 : return Nullable<Date>(Date(time));
1991 : }
1992 : case NS_FORM_INPUT_MONTH:
1993 : {
1994 : uint32_t year, month;
1995 0 : nsAutoString value;
1996 0 : GetNonFileValueInternal(value);
1997 0 : if (!ParseMonth(value, &year, &month)) {
1998 0 : return Nullable<Date>();
1999 : }
2000 :
2001 0 : JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, 1));
2002 0 : return Nullable<Date>(Date(time));
2003 : }
2004 : case NS_FORM_INPUT_WEEK:
2005 : {
2006 : uint32_t year, week;
2007 0 : nsAutoString value;
2008 0 : GetNonFileValueInternal(value);
2009 0 : if (!ParseWeek(value, &year, &week)) {
2010 0 : return Nullable<Date>();
2011 : }
2012 :
2013 0 : double days = DaysSinceEpochFromWeek(year, week);
2014 0 : JS::ClippedTime time = JS::TimeClip(days * kMsPerDay);
2015 :
2016 0 : return Nullable<Date>(Date(time));
2017 : }
2018 : case NS_FORM_INPUT_DATETIME_LOCAL:
2019 : {
2020 : uint32_t year, month, day, timeInMs;
2021 0 : nsAutoString value;
2022 0 : GetNonFileValueInternal(value);
2023 0 : if (!ParseDateTimeLocal(value, &year, &month, &day, &timeInMs)) {
2024 0 : return Nullable<Date>();
2025 : }
2026 :
2027 : JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day,
2028 0 : timeInMs));
2029 0 : return Nullable<Date>(Date(time));
2030 : }
2031 : }
2032 :
2033 0 : MOZ_ASSERT(false, "Unrecognized input type");
2034 : aRv.Throw(NS_ERROR_UNEXPECTED);
2035 : return Nullable<Date>();
2036 : }
2037 :
2038 : void
2039 0 : HTMLInputElement::SetValueAsDate(const Nullable<Date>& aDate, ErrorResult& aRv)
2040 : {
2041 0 : if (!IsDateTimeInputType(mType)) {
2042 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2043 0 : return;
2044 : }
2045 :
2046 : // At this point we know we're not a file input, so we can just pass "not
2047 : // system" as the caller type, since the caller type only matters in the file
2048 : // input case.
2049 0 : if (aDate.IsNull() || aDate.Value().IsUndefined()) {
2050 0 : SetValue(EmptyString(), CallerType::NonSystem, aRv);
2051 0 : return;
2052 : }
2053 :
2054 0 : double milliseconds = aDate.Value().TimeStamp().toDouble();
2055 :
2056 0 : if (mType != NS_FORM_INPUT_MONTH) {
2057 0 : SetValue(Decimal::fromDouble(milliseconds), CallerType::NonSystem);
2058 0 : return;
2059 : }
2060 :
2061 : // type=month expects the value to be number of months.
2062 0 : double year = JS::YearFromTime(milliseconds);
2063 0 : double month = JS::MonthFromTime(milliseconds);
2064 :
2065 0 : if (IsNaN(year) || IsNaN(month)) {
2066 0 : SetValue(EmptyString(), CallerType::NonSystem, aRv);
2067 0 : return;
2068 : }
2069 :
2070 0 : int32_t months = MonthsSinceJan1970(year, month + 1);
2071 0 : SetValue(Decimal(int32_t(months)), CallerType::NonSystem);
2072 : }
2073 :
2074 : void
2075 0 : HTMLInputElement::SetValueAsNumber(double aValueAsNumber, ErrorResult& aRv)
2076 : {
2077 : // TODO: return TypeError when HTMLInputElement is converted to WebIDL, see
2078 : // bug 825197.
2079 0 : if (IsInfinite(aValueAsNumber)) {
2080 0 : aRv.Throw(NS_ERROR_INVALID_ARG);
2081 0 : return;
2082 : }
2083 :
2084 0 : if (!DoesValueAsNumberApply()) {
2085 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2086 0 : return;
2087 : }
2088 :
2089 : // At this point we know we're not a file input, so we can just pass "not
2090 : // system" as the caller type, since the caller type only matters in the file
2091 : // input case.
2092 0 : SetValue(Decimal::fromDouble(aValueAsNumber), CallerType::NonSystem);
2093 : }
2094 :
2095 : Decimal
2096 0 : HTMLInputElement::GetMinimum() const
2097 : {
2098 0 : MOZ_ASSERT(DoesValueAsNumberApply(),
2099 : "GetMinimum() should only be used for types that allow .valueAsNumber");
2100 :
2101 : // Only type=range has a default minimum
2102 : Decimal defaultMinimum =
2103 0 : mType == NS_FORM_INPUT_RANGE ? Decimal(0) : Decimal::nan();
2104 :
2105 0 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::min)) {
2106 0 : return defaultMinimum;
2107 : }
2108 :
2109 0 : nsAutoString minStr;
2110 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr);
2111 :
2112 0 : Decimal min;
2113 0 : return mInputType->ConvertStringToNumber(minStr, min) ? min : defaultMinimum;
2114 : }
2115 :
2116 : Decimal
2117 0 : HTMLInputElement::GetMaximum() const
2118 : {
2119 0 : MOZ_ASSERT(DoesValueAsNumberApply(),
2120 : "GetMaximum() should only be used for types that allow .valueAsNumber");
2121 :
2122 : // Only type=range has a default maximum
2123 : Decimal defaultMaximum =
2124 0 : mType == NS_FORM_INPUT_RANGE ? Decimal(100) : Decimal::nan();
2125 :
2126 0 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::max)) {
2127 0 : return defaultMaximum;
2128 : }
2129 :
2130 0 : nsAutoString maxStr;
2131 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::max, maxStr);
2132 :
2133 0 : Decimal max;
2134 0 : return mInputType->ConvertStringToNumber(maxStr, max) ? max : defaultMaximum;
2135 : }
2136 :
2137 : Decimal
2138 0 : HTMLInputElement::GetStepBase() const
2139 : {
2140 0 : MOZ_ASSERT(IsDateTimeInputType(mType) ||
2141 : mType == NS_FORM_INPUT_NUMBER ||
2142 : mType == NS_FORM_INPUT_RANGE,
2143 : "Check that kDefaultStepBase is correct for this new type");
2144 :
2145 0 : Decimal stepBase;
2146 :
2147 : // Do NOT use GetMinimum here - the spec says to use "the min content
2148 : // attribute", not "the minimum".
2149 0 : nsAutoString minStr;
2150 0 : if (GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr) &&
2151 0 : mInputType->ConvertStringToNumber(minStr, stepBase)) {
2152 0 : return stepBase;
2153 : }
2154 :
2155 : // If @min is not a double, we should use @value.
2156 0 : nsAutoString valueStr;
2157 0 : if (GetAttr(kNameSpaceID_None, nsGkAtoms::value, valueStr) &&
2158 0 : mInputType->ConvertStringToNumber(valueStr, stepBase)) {
2159 0 : return stepBase;
2160 : }
2161 :
2162 0 : if (mType == NS_FORM_INPUT_WEEK) {
2163 0 : return kDefaultStepBaseWeek;
2164 : }
2165 :
2166 0 : return kDefaultStepBase;
2167 : }
2168 :
2169 : nsresult
2170 0 : HTMLInputElement::GetValueIfStepped(int32_t aStep,
2171 : StepCallerType aCallerType,
2172 : Decimal* aNextStep)
2173 : {
2174 0 : if (!DoStepDownStepUpApply()) {
2175 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
2176 : }
2177 :
2178 0 : Decimal stepBase = GetStepBase();
2179 0 : Decimal step = GetStep();
2180 0 : if (step == kStepAny) {
2181 0 : if (aCallerType != CALLED_FOR_USER_EVENT) {
2182 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
2183 : }
2184 : // Allow the spin buttons and up/down arrow keys to do something sensible:
2185 0 : step = GetDefaultStep();
2186 : }
2187 :
2188 0 : Decimal minimum = GetMinimum();
2189 0 : Decimal maximum = GetMaximum();
2190 :
2191 0 : if (!maximum.isNaN()) {
2192 : // "max - (max - stepBase) % step" is the nearest valid value to max.
2193 0 : maximum = maximum - NS_floorModulo(maximum - stepBase, step);
2194 0 : if (!minimum.isNaN()) {
2195 0 : if (minimum > maximum) {
2196 : // Either the minimum was greater than the maximum prior to our
2197 : // adjustment to align maximum on a step, or else (if we adjusted
2198 : // maximum) there is no valid step between minimum and the unadjusted
2199 : // maximum.
2200 0 : return NS_OK;
2201 : }
2202 : }
2203 : }
2204 :
2205 0 : Decimal value = GetValueAsDecimal();
2206 0 : bool valueWasNaN = false;
2207 0 : if (value.isNaN()) {
2208 0 : value = Decimal(0);
2209 0 : valueWasNaN = true;
2210 : }
2211 0 : Decimal valueBeforeStepping = value;
2212 :
2213 0 : Decimal deltaFromStep = NS_floorModulo(value - stepBase, step);
2214 :
2215 0 : if (deltaFromStep != Decimal(0)) {
2216 0 : if (aStep > 0) {
2217 0 : value += step - deltaFromStep; // partial step
2218 0 : value += step * Decimal(aStep - 1); // then remaining steps
2219 0 : } else if (aStep < 0) {
2220 0 : value -= deltaFromStep; // partial step
2221 0 : value += step * Decimal(aStep + 1); // then remaining steps
2222 : }
2223 : } else {
2224 0 : value += step * Decimal(aStep);
2225 : }
2226 :
2227 0 : if (value < minimum) {
2228 0 : value = minimum;
2229 0 : deltaFromStep = NS_floorModulo(value - stepBase, step);
2230 0 : if (deltaFromStep != Decimal(0)) {
2231 0 : value += step - deltaFromStep;
2232 : }
2233 : }
2234 0 : if (value > maximum) {
2235 0 : value = maximum;
2236 0 : deltaFromStep = NS_floorModulo(value - stepBase, step);
2237 0 : if (deltaFromStep != Decimal(0)) {
2238 0 : value -= deltaFromStep;
2239 : }
2240 : }
2241 :
2242 0 : if (!valueWasNaN && // value="", resulting in us using "0"
2243 0 : ((aStep > 0 && value < valueBeforeStepping) ||
2244 0 : (aStep < 0 && value > valueBeforeStepping))) {
2245 : // We don't want step-up to effectively step down, or step-down to
2246 : // effectively step up, so return;
2247 0 : return NS_OK;
2248 : }
2249 :
2250 0 : *aNextStep = value;
2251 0 : return NS_OK;
2252 : }
2253 :
2254 : nsresult
2255 0 : HTMLInputElement::ApplyStep(int32_t aStep)
2256 : {
2257 0 : Decimal nextStep = Decimal::nan(); // unchanged if value will not change
2258 :
2259 0 : nsresult rv = GetValueIfStepped(aStep, CALLED_FOR_SCRIPT, &nextStep);
2260 :
2261 0 : if (NS_SUCCEEDED(rv) && nextStep.isFinite()) {
2262 : // We know we're not a file input, so the caller type does not matter; just
2263 : // pass "not system" to be safe.
2264 0 : SetValue(nextStep, CallerType::NonSystem);
2265 : }
2266 :
2267 0 : return rv;
2268 : }
2269 :
2270 : /* static */
2271 : bool
2272 6 : HTMLInputElement::IsExperimentalMobileType(uint8_t aType)
2273 : {
2274 6 : return (aType == NS_FORM_INPUT_DATE || aType == NS_FORM_INPUT_TIME) &&
2275 6 : !IsInputDateTimeEnabled();
2276 : }
2277 :
2278 : bool
2279 77 : HTMLInputElement::IsDateTimeInputType(uint8_t aType)
2280 : {
2281 77 : return aType == NS_FORM_INPUT_DATE ||
2282 77 : aType == NS_FORM_INPUT_TIME ||
2283 77 : aType == NS_FORM_INPUT_MONTH ||
2284 154 : aType == NS_FORM_INPUT_WEEK ||
2285 77 : aType == NS_FORM_INPUT_DATETIME_LOCAL;
2286 : }
2287 :
2288 : NS_IMETHODIMP
2289 0 : HTMLInputElement::StepDown(int32_t n, uint8_t optional_argc)
2290 : {
2291 0 : return ApplyStep(optional_argc ? -n : -1);
2292 : }
2293 :
2294 : NS_IMETHODIMP
2295 0 : HTMLInputElement::StepUp(int32_t n, uint8_t optional_argc)
2296 : {
2297 0 : return ApplyStep(optional_argc ? n : 1);
2298 : }
2299 :
2300 : void
2301 0 : HTMLInputElement::FlushFrames()
2302 : {
2303 0 : if (GetComposedDoc()) {
2304 0 : GetComposedDoc()->FlushPendingNotifications(FlushType::Frames);
2305 : }
2306 0 : }
2307 :
2308 : void
2309 0 : HTMLInputElement::MozGetFileNameArray(nsTArray<nsString>& aArray,
2310 : ErrorResult& aRv)
2311 : {
2312 0 : if (NS_WARN_IF(mType != NS_FORM_INPUT_FILE)) {
2313 0 : return;
2314 : }
2315 :
2316 : const nsTArray<OwningFileOrDirectory>& filesOrDirs =
2317 0 : GetFilesOrDirectoriesInternal();
2318 0 : for (uint32_t i = 0; i < filesOrDirs.Length(); i++) {
2319 0 : nsAutoString str;
2320 0 : GetDOMFileOrDirectoryPath(filesOrDirs[i], str, aRv);
2321 0 : if (NS_WARN_IF(aRv.Failed())) {
2322 0 : return;
2323 : }
2324 :
2325 0 : aArray.AppendElement(str);
2326 : }
2327 : }
2328 :
2329 : void
2330 0 : HTMLInputElement::MozSetFileArray(const Sequence<OwningNonNull<File>>& aFiles)
2331 : {
2332 0 : if (NS_WARN_IF(mType != NS_FORM_INPUT_FILE)) {
2333 0 : return;
2334 : }
2335 :
2336 0 : nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
2337 0 : MOZ_ASSERT(global);
2338 0 : if (!global) {
2339 0 : return;
2340 : }
2341 :
2342 0 : nsTArray<OwningFileOrDirectory> files;
2343 0 : for (uint32_t i = 0; i < aFiles.Length(); ++i) {
2344 0 : RefPtr<File> file = File::Create(global, aFiles[i].get()->Impl());
2345 0 : MOZ_ASSERT(file);
2346 :
2347 0 : OwningFileOrDirectory* element = files.AppendElement();
2348 0 : element->SetAsFile() = file;
2349 : }
2350 :
2351 0 : SetFilesOrDirectories(files, true);
2352 : }
2353 :
2354 : void
2355 0 : HTMLInputElement::MozSetFileNameArray(const Sequence<nsString>& aFileNames,
2356 : ErrorResult& aRv)
2357 : {
2358 0 : if (NS_WARN_IF(mType != NS_FORM_INPUT_FILE)) {
2359 0 : return;
2360 : }
2361 :
2362 0 : if (XRE_IsContentProcess()) {
2363 0 : aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2364 0 : return;
2365 : }
2366 :
2367 0 : nsTArray<OwningFileOrDirectory> files;
2368 0 : for (uint32_t i = 0; i < aFileNames.Length(); ++i) {
2369 0 : nsCOMPtr<nsIFile> file;
2370 :
2371 0 : if (StringBeginsWith(aFileNames[i], NS_LITERAL_STRING("file:"),
2372 0 : nsASCIICaseInsensitiveStringComparator())) {
2373 : // Converts the URL string into the corresponding nsIFile if possible
2374 : // A local file will be created if the URL string begins with file://
2375 0 : NS_GetFileFromURLSpec(NS_ConvertUTF16toUTF8(aFileNames[i]),
2376 0 : getter_AddRefs(file));
2377 : }
2378 :
2379 0 : if (!file) {
2380 : // this is no "file://", try as local file
2381 0 : NS_NewLocalFile(aFileNames[i], false, getter_AddRefs(file));
2382 : }
2383 :
2384 0 : if (!file) {
2385 0 : continue; // Not much we can do if the file doesn't exist
2386 : }
2387 :
2388 0 : nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
2389 0 : if (!global) {
2390 0 : aRv.Throw(NS_ERROR_FAILURE);
2391 0 : return;
2392 : }
2393 :
2394 0 : RefPtr<File> domFile = File::CreateFromFile(global, file);
2395 :
2396 0 : OwningFileOrDirectory* element = files.AppendElement();
2397 0 : element->SetAsFile() = domFile;
2398 : }
2399 :
2400 0 : SetFilesOrDirectories(files, true);
2401 : }
2402 :
2403 : void
2404 0 : HTMLInputElement::MozSetDirectory(const nsAString& aDirectoryPath,
2405 : ErrorResult& aRv)
2406 : {
2407 0 : if (NS_WARN_IF(mType != NS_FORM_INPUT_FILE)) {
2408 0 : return;
2409 : }
2410 :
2411 0 : nsCOMPtr<nsIFile> file;
2412 0 : aRv = NS_NewLocalFile(aDirectoryPath, true, getter_AddRefs(file));
2413 0 : if (NS_WARN_IF(aRv.Failed())) {
2414 0 : return;
2415 : }
2416 :
2417 0 : nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
2418 0 : if (NS_WARN_IF(!window)) {
2419 0 : aRv.Throw(NS_ERROR_FAILURE);
2420 0 : return;
2421 : }
2422 :
2423 0 : RefPtr<Directory> directory = Directory::Create(window, file);
2424 0 : MOZ_ASSERT(directory);
2425 :
2426 0 : nsTArray<OwningFileOrDirectory> array;
2427 0 : OwningFileOrDirectory* element = array.AppendElement();
2428 0 : element->SetAsDirectory() = directory;
2429 :
2430 0 : SetFilesOrDirectories(array, true);
2431 : }
2432 :
2433 0 : void HTMLInputElement::GetDateTimeInputBoxValue(DateTimeValue& aValue)
2434 : {
2435 0 : if (NS_WARN_IF(!IsDateTimeInputType(mType)) || !mDateTimeInputBoxValue) {
2436 0 : return;
2437 : }
2438 :
2439 0 : aValue = *mDateTimeInputBoxValue;
2440 : }
2441 :
2442 : void
2443 0 : HTMLInputElement::UpdateDateTimeInputBox(const DateTimeValue& aValue)
2444 : {
2445 0 : if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
2446 0 : return;
2447 : }
2448 :
2449 0 : nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
2450 0 : if (frame) {
2451 0 : frame->SetValueFromPicker(aValue);
2452 : }
2453 : }
2454 :
2455 : void
2456 0 : HTMLInputElement::SetDateTimePickerState(bool aOpen)
2457 : {
2458 0 : if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
2459 0 : return;
2460 : }
2461 :
2462 0 : nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
2463 0 : if (frame) {
2464 0 : frame->SetPickerState(aOpen);
2465 : }
2466 : }
2467 :
2468 : void
2469 0 : HTMLInputElement::OpenDateTimePicker(const DateTimeValue& aInitialValue)
2470 : {
2471 0 : if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
2472 0 : return;
2473 : }
2474 :
2475 0 : mDateTimeInputBoxValue = new DateTimeValue(aInitialValue);
2476 0 : nsContentUtils::DispatchChromeEvent(OwnerDoc(),
2477 : static_cast<nsIDOMHTMLInputElement*>(this),
2478 0 : NS_LITERAL_STRING("MozOpenDateTimePicker"),
2479 0 : true, true);
2480 : }
2481 :
2482 : void
2483 0 : HTMLInputElement::UpdateDateTimePicker(const DateTimeValue& aValue)
2484 : {
2485 0 : if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
2486 0 : return;
2487 : }
2488 :
2489 0 : mDateTimeInputBoxValue = new DateTimeValue(aValue);
2490 0 : nsContentUtils::DispatchChromeEvent(OwnerDoc(),
2491 : static_cast<nsIDOMHTMLInputElement*>(this),
2492 0 : NS_LITERAL_STRING("MozUpdateDateTimePicker"),
2493 0 : true, true);
2494 : }
2495 :
2496 : void
2497 0 : HTMLInputElement::CloseDateTimePicker()
2498 : {
2499 0 : if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
2500 0 : return;
2501 : }
2502 :
2503 0 : nsContentUtils::DispatchChromeEvent(OwnerDoc(),
2504 : static_cast<nsIDOMHTMLInputElement*>(this),
2505 0 : NS_LITERAL_STRING("MozCloseDateTimePicker"),
2506 0 : true, true);
2507 : }
2508 :
2509 : void
2510 0 : HTMLInputElement::SetFocusState(bool aIsFocused)
2511 : {
2512 0 : if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
2513 0 : return;
2514 : }
2515 :
2516 0 : EventStates focusStates = NS_EVENT_STATE_FOCUS | NS_EVENT_STATE_FOCUSRING;
2517 0 : if (aIsFocused) {
2518 0 : AddStates(focusStates);
2519 : } else {
2520 0 : RemoveStates(focusStates);
2521 : }
2522 : }
2523 :
2524 : void
2525 0 : HTMLInputElement::UpdateValidityState()
2526 : {
2527 0 : if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
2528 0 : return;
2529 : }
2530 :
2531 : // For now, datetime input box call this function only when the value may
2532 : // become valid/invalid. For other validity states, they will be updated when
2533 : // .value is actually changed.
2534 0 : UpdateBadInputValidityState();
2535 0 : UpdateState(true);
2536 : }
2537 :
2538 : bool
2539 0 : HTMLInputElement::MozIsTextField(bool aExcludePassword)
2540 : {
2541 : // TODO: temporary until bug 888320 is fixed.
2542 0 : if (IsExperimentalMobileType(mType) || IsDateTimeInputType(mType)) {
2543 0 : return false;
2544 : }
2545 :
2546 0 : return IsSingleLineTextControl(aExcludePassword);
2547 : }
2548 :
2549 : HTMLInputElement*
2550 0 : HTMLInputElement::GetOwnerNumberControl()
2551 : {
2552 0 : if (IsInNativeAnonymousSubtree() &&
2553 0 : mType == NS_FORM_INPUT_TEXT &&
2554 0 : GetParent() && GetParent()->GetParent()) {
2555 : HTMLInputElement* grandparent =
2556 0 : HTMLInputElement::FromContentOrNull(GetParent()->GetParent());
2557 0 : if (grandparent && grandparent->mType == NS_FORM_INPUT_NUMBER) {
2558 0 : return grandparent;
2559 : }
2560 : }
2561 0 : return nullptr;
2562 : }
2563 :
2564 : NS_IMETHODIMP
2565 0 : HTMLInputElement::MozIsTextField(bool aExcludePassword, bool* aResult)
2566 : {
2567 0 : *aResult = MozIsTextField(aExcludePassword);
2568 0 : return NS_OK;
2569 : }
2570 :
2571 : void
2572 0 : HTMLInputElement::SetUserInput(const nsAString& aInput,
2573 : nsIPrincipal& aSubjectPrincipal) {
2574 0 : if (mType == NS_FORM_INPUT_FILE &&
2575 0 : !nsContentUtils::IsSystemPrincipal(&aSubjectPrincipal)) {
2576 0 : return;
2577 : }
2578 :
2579 0 : SetUserInput(aInput);
2580 : }
2581 :
2582 : NS_IMETHODIMP
2583 0 : HTMLInputElement::SetUserInput(const nsAString& aValue)
2584 : {
2585 0 : if (mType == NS_FORM_INPUT_FILE)
2586 : {
2587 0 : Sequence<nsString> list;
2588 0 : if (!list.AppendElement(aValue, fallible)) {
2589 0 : return NS_ERROR_OUT_OF_MEMORY;
2590 : }
2591 :
2592 0 : ErrorResult rv;
2593 0 : MozSetFileNameArray(list, rv);
2594 0 : return rv.StealNSResult();
2595 : } else {
2596 : nsresult rv =
2597 : SetValueInternal(aValue,
2598 : nsTextEditorState::eSetValue_BySetUserInput |
2599 : nsTextEditorState::eSetValue_Notify|
2600 0 : nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
2601 0 : NS_ENSURE_SUCCESS(rv, rv);
2602 : }
2603 :
2604 0 : nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
2605 : static_cast<nsIDOMHTMLInputElement*>(this),
2606 0 : NS_LITERAL_STRING("input"), true,
2607 0 : true);
2608 :
2609 : // If this element is not currently focused, it won't receive a change event for this
2610 : // update through the normal channels. So fire a change event immediately, instead.
2611 0 : if (!ShouldBlur(this)) {
2612 0 : FireChangeEventIfNeeded();
2613 : }
2614 :
2615 0 : return NS_OK;
2616 : }
2617 :
2618 : nsIEditor*
2619 1 : HTMLInputElement::GetEditor()
2620 : {
2621 1 : return GetTextEditorFromState();
2622 : }
2623 :
2624 : TextEditor*
2625 8 : HTMLInputElement::GetTextEditorFromState()
2626 : {
2627 8 : nsTextEditorState* state = GetEditorState();
2628 8 : if (state) {
2629 8 : return state->GetTextEditor();
2630 : }
2631 0 : return nullptr;
2632 : }
2633 :
2634 : NS_IMETHODIMP_(TextEditor*)
2635 7 : HTMLInputElement::GetTextEditor()
2636 : {
2637 7 : return GetTextEditorFromState();
2638 : }
2639 :
2640 : NS_IMETHODIMP_(nsISelectionController*)
2641 8 : HTMLInputElement::GetSelectionController()
2642 : {
2643 8 : nsTextEditorState* state = GetEditorState();
2644 8 : if (state) {
2645 8 : return state->GetSelectionController();
2646 : }
2647 0 : return nullptr;
2648 : }
2649 :
2650 : nsFrameSelection*
2651 6 : HTMLInputElement::GetConstFrameSelection()
2652 : {
2653 6 : nsTextEditorState* state = GetEditorState();
2654 6 : if (state) {
2655 6 : return state->GetConstFrameSelection();
2656 : }
2657 0 : return nullptr;
2658 : }
2659 :
2660 : NS_IMETHODIMP
2661 4 : HTMLInputElement::BindToFrame(nsTextControlFrame* aFrame)
2662 : {
2663 4 : nsTextEditorState* state = GetEditorState();
2664 4 : if (state) {
2665 4 : return state->BindToFrame(aFrame);
2666 : }
2667 0 : return NS_ERROR_FAILURE;
2668 : }
2669 :
2670 : NS_IMETHODIMP_(void)
2671 3 : HTMLInputElement::UnbindFromFrame(nsTextControlFrame* aFrame)
2672 : {
2673 3 : nsTextEditorState* state = GetEditorState();
2674 3 : if (state && aFrame) {
2675 2 : state->UnbindFromFrame(aFrame);
2676 : }
2677 3 : }
2678 :
2679 : NS_IMETHODIMP
2680 2 : HTMLInputElement::CreateEditor()
2681 : {
2682 2 : nsTextEditorState* state = GetEditorState();
2683 2 : if (state) {
2684 2 : return state->PrepareEditor();
2685 : }
2686 0 : return NS_ERROR_FAILURE;
2687 : }
2688 :
2689 : NS_IMETHODIMP_(Element*)
2690 16 : HTMLInputElement::GetRootEditorNode()
2691 : {
2692 16 : nsTextEditorState* state = GetEditorState();
2693 16 : if (state) {
2694 16 : return state->GetRootNode();
2695 : }
2696 0 : return nullptr;
2697 : }
2698 :
2699 : NS_IMETHODIMP_(Element*)
2700 4 : HTMLInputElement::CreatePlaceholderNode()
2701 : {
2702 4 : nsTextEditorState* state = GetEditorState();
2703 4 : if (state) {
2704 4 : NS_ENSURE_SUCCESS(state->CreatePlaceholderNode(), nullptr);
2705 4 : return state->GetPlaceholderNode();
2706 : }
2707 0 : return nullptr;
2708 : }
2709 :
2710 : NS_IMETHODIMP_(Element*)
2711 102 : HTMLInputElement::GetPlaceholderNode()
2712 : {
2713 102 : nsTextEditorState* state = GetEditorState();
2714 102 : if (state) {
2715 102 : return state->GetPlaceholderNode();
2716 : }
2717 0 : return nullptr;
2718 : }
2719 :
2720 : NS_IMETHODIMP_(void)
2721 5 : HTMLInputElement::UpdateOverlayTextVisibility(bool aNotify)
2722 : {
2723 5 : nsTextEditorState* state = GetEditorState();
2724 5 : if (state) {
2725 5 : state->UpdateOverlayTextVisibility(aNotify);
2726 : }
2727 5 : }
2728 :
2729 : NS_IMETHODIMP_(bool)
2730 48 : HTMLInputElement::GetPlaceholderVisibility()
2731 : {
2732 48 : nsTextEditorState* state = GetEditorState();
2733 48 : if (!state) {
2734 0 : return false;
2735 : }
2736 :
2737 48 : return state->GetPlaceholderVisibility();
2738 : }
2739 :
2740 : NS_IMETHODIMP_(Element*)
2741 0 : HTMLInputElement::CreatePreviewNode()
2742 : {
2743 0 : nsTextEditorState* state = GetEditorState();
2744 0 : if (state) {
2745 0 : NS_ENSURE_SUCCESS(state->CreatePreviewNode(), nullptr);
2746 0 : return state->GetPreviewNode();
2747 : }
2748 0 : return nullptr;
2749 : }
2750 :
2751 : NS_IMETHODIMP_(Element*)
2752 85 : HTMLInputElement::GetPreviewNode()
2753 : {
2754 85 : nsTextEditorState* state = GetEditorState();
2755 85 : if (state) {
2756 85 : return state->GetPreviewNode();
2757 : }
2758 0 : return nullptr;
2759 : }
2760 :
2761 : NS_IMETHODIMP_(void)
2762 0 : HTMLInputElement::SetPreviewValue(const nsAString& aValue)
2763 : {
2764 0 : nsTextEditorState* state = GetEditorState();
2765 0 : if (state) {
2766 0 : state->SetPreviewText(aValue, true);
2767 : }
2768 0 : }
2769 :
2770 : NS_IMETHODIMP_(void)
2771 0 : HTMLInputElement::GetPreviewValue(nsAString& aValue)
2772 : {
2773 0 : nsTextEditorState* state = GetEditorState();
2774 0 : if (state) {
2775 0 : state->GetPreviewText(aValue);
2776 : }
2777 0 : }
2778 :
2779 : NS_IMETHODIMP_(void)
2780 0 : HTMLInputElement::EnablePreview()
2781 : {
2782 0 : if (mIsPreviewEnabled) {
2783 0 : return;
2784 : }
2785 :
2786 0 : mIsPreviewEnabled = true;
2787 : // Reconstruct the frame to append an anonymous preview node
2788 0 : nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), nsChangeHint_ReconstructFrame);
2789 : }
2790 :
2791 : NS_IMETHODIMP_(bool)
2792 4 : HTMLInputElement::IsPreviewEnabled()
2793 : {
2794 4 : return mIsPreviewEnabled;
2795 : }
2796 :
2797 : NS_IMETHODIMP_(bool)
2798 0 : HTMLInputElement::GetPreviewVisibility()
2799 : {
2800 0 : nsTextEditorState* state = GetEditorState();
2801 0 : if (!state) {
2802 0 : return false;
2803 : }
2804 :
2805 0 : return state->GetPreviewVisibility();
2806 : }
2807 :
2808 : void
2809 0 : HTMLInputElement::GetDisplayFileName(nsAString& aValue) const
2810 : {
2811 0 : MOZ_ASSERT(mFileData);
2812 :
2813 0 : if (OwnerDoc()->IsStaticDocument()) {
2814 0 : aValue = mFileData->mStaticDocFileList;
2815 0 : return;
2816 : }
2817 :
2818 0 : if (mFileData->mFilesOrDirectories.Length() == 1) {
2819 0 : GetDOMFileOrDirectoryName(mFileData->mFilesOrDirectories[0], aValue);
2820 0 : return;
2821 : }
2822 :
2823 0 : nsXPIDLString value;
2824 :
2825 0 : if (mFileData->mFilesOrDirectories.IsEmpty()) {
2826 0 : if ((IsDirPickerEnabled() && Allowdirs()) ||
2827 0 : (IsWebkitDirPickerEnabled() &&
2828 0 : HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory))) {
2829 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
2830 0 : "NoDirSelected", value);
2831 0 : } else if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
2832 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
2833 0 : "NoFilesSelected", value);
2834 : } else {
2835 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
2836 0 : "NoFileSelected", value);
2837 : }
2838 : } else {
2839 0 : nsString count;
2840 0 : count.AppendInt(int(mFileData->mFilesOrDirectories.Length()));
2841 :
2842 0 : const char16_t* params[] = { count.get() };
2843 : nsContentUtils::FormatLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
2844 0 : "XFilesSelected", params, value);
2845 : }
2846 :
2847 0 : aValue = value;
2848 : }
2849 :
2850 : const nsTArray<OwningFileOrDirectory>&
2851 0 : HTMLInputElement::GetFilesOrDirectoriesInternal() const
2852 : {
2853 0 : return mFileData->mFilesOrDirectories;
2854 : }
2855 :
2856 : void
2857 0 : HTMLInputElement::SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
2858 : bool aSetValueChanged)
2859 : {
2860 0 : MOZ_ASSERT(mFileData);
2861 :
2862 0 : mFileData->ClearGetFilesHelpers();
2863 :
2864 0 : if (IsWebkitFileSystemEnabled()) {
2865 0 : HTMLInputElementBinding::ClearCachedWebkitEntriesValue(this);
2866 0 : mFileData->mEntries.Clear();
2867 : }
2868 :
2869 0 : mFileData->mFilesOrDirectories.Clear();
2870 0 : mFileData->mFilesOrDirectories.AppendElements(aFilesOrDirectories);
2871 :
2872 0 : AfterSetFilesOrDirectories(aSetValueChanged);
2873 0 : }
2874 :
2875 : void
2876 0 : HTMLInputElement::SetFiles(nsIDOMFileList* aFiles,
2877 : bool aSetValueChanged)
2878 : {
2879 0 : MOZ_ASSERT(mFileData);
2880 :
2881 0 : RefPtr<FileList> files = static_cast<FileList*>(aFiles);
2882 0 : mFileData->mFilesOrDirectories.Clear();
2883 0 : mFileData->ClearGetFilesHelpers();
2884 :
2885 0 : if (IsWebkitFileSystemEnabled()) {
2886 0 : HTMLInputElementBinding::ClearCachedWebkitEntriesValue(this);
2887 0 : mFileData->mEntries.Clear();
2888 : }
2889 :
2890 0 : if (aFiles) {
2891 : uint32_t listLength;
2892 0 : aFiles->GetLength(&listLength);
2893 0 : for (uint32_t i = 0; i < listLength; i++) {
2894 : OwningFileOrDirectory* element =
2895 0 : mFileData->mFilesOrDirectories.AppendElement();
2896 0 : element->SetAsFile() = files->Item(i);
2897 : }
2898 : }
2899 :
2900 0 : AfterSetFilesOrDirectories(aSetValueChanged);
2901 0 : }
2902 :
2903 : // This method is used for testing only.
2904 : void
2905 0 : HTMLInputElement::MozSetDndFilesAndDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
2906 : {
2907 0 : if (NS_WARN_IF(mType != NS_FORM_INPUT_FILE)) {
2908 0 : return;
2909 : }
2910 :
2911 0 : SetFilesOrDirectories(aFilesOrDirectories, true);
2912 :
2913 0 : if (IsWebkitFileSystemEnabled()) {
2914 0 : UpdateEntries(aFilesOrDirectories);
2915 : }
2916 :
2917 : RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback =
2918 0 : new DispatchChangeEventCallback(this);
2919 :
2920 0 : if (IsWebkitDirPickerEnabled() &&
2921 0 : HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)) {
2922 0 : ErrorResult rv;
2923 : GetFilesHelper* helper = GetOrCreateGetFilesHelper(true /* recursionFlag */,
2924 0 : rv);
2925 0 : if (NS_WARN_IF(rv.Failed())) {
2926 0 : rv.SuppressException();
2927 0 : return;
2928 : }
2929 :
2930 0 : helper->AddCallback(dispatchChangeEventCallback);
2931 : } else {
2932 0 : dispatchChangeEventCallback->DispatchEvents();
2933 : }
2934 : }
2935 :
2936 : void
2937 0 : HTMLInputElement::AfterSetFilesOrDirectories(bool aSetValueChanged)
2938 : {
2939 : // No need to flush here, if there's no frame at this point we
2940 : // don't need to force creation of one just to tell it about this
2941 : // new value. We just want the display to update as needed.
2942 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
2943 0 : if (formControlFrame) {
2944 0 : nsAutoString readableValue;
2945 0 : GetDisplayFileName(readableValue);
2946 0 : formControlFrame->SetFormProperty(nsGkAtoms::value, readableValue);
2947 : }
2948 :
2949 : // Grab the full path here for any chrome callers who access our .value via a
2950 : // CPOW. This path won't be called from a CPOW meaning the potential sync IPC
2951 : // call under GetMozFullPath won't be rejected for not being urgent.
2952 : // XXX Protected by the ifndef because the blob code doesn't allow us to send
2953 : // this message in b2g.
2954 0 : if (mFileData->mFilesOrDirectories.IsEmpty()) {
2955 0 : mFileData->mFirstFilePath.Truncate();
2956 : } else {
2957 0 : ErrorResult rv;
2958 0 : GetDOMFileOrDirectoryPath(mFileData->mFilesOrDirectories[0],
2959 0 : mFileData->mFirstFilePath, rv);
2960 0 : if (NS_WARN_IF(rv.Failed())) {
2961 0 : rv.SuppressException();
2962 : }
2963 : }
2964 :
2965 0 : UpdateFileList();
2966 :
2967 0 : if (aSetValueChanged) {
2968 0 : SetValueChanged(true);
2969 : }
2970 :
2971 0 : UpdateAllValidityStates(true);
2972 0 : }
2973 :
2974 : void
2975 0 : HTMLInputElement::FireChangeEventIfNeeded()
2976 : {
2977 : // We're not exposing the GetValue return value anywhere here, so it's safe to
2978 : // claim to be a system caller.
2979 0 : nsAutoString value;
2980 0 : GetValue(value, CallerType::System);
2981 :
2982 0 : if (!MayFireChangeOnBlur() || mFocusedValue.Equals(value)) {
2983 0 : return;
2984 : }
2985 :
2986 : // Dispatch the change event.
2987 0 : mFocusedValue = value;
2988 0 : nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
2989 : static_cast<nsIContent*>(this),
2990 0 : NS_LITERAL_STRING("change"), true,
2991 0 : false);
2992 : }
2993 :
2994 : FileList*
2995 0 : HTMLInputElement::GetFiles()
2996 : {
2997 0 : if (mType != NS_FORM_INPUT_FILE) {
2998 0 : return nullptr;
2999 : }
3000 :
3001 0 : if (IsDirPickerEnabled() && Allowdirs() &&
3002 0 : (!IsWebkitDirPickerEnabled() ||
3003 0 : !HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory))) {
3004 0 : return nullptr;
3005 : }
3006 :
3007 0 : if (!mFileData->mFileList) {
3008 0 : mFileData->mFileList = new FileList(static_cast<nsIContent*>(this));
3009 0 : UpdateFileList();
3010 : }
3011 :
3012 0 : return mFileData->mFileList;
3013 : }
3014 :
3015 : /* static */ void
3016 0 : HTMLInputElement::HandleNumberControlSpin(void* aData)
3017 : {
3018 0 : HTMLInputElement* input = static_cast<HTMLInputElement*>(aData);
3019 :
3020 0 : NS_ASSERTION(input->mNumberControlSpinnerIsSpinning,
3021 : "Should have called nsRepeatService::Stop()");
3022 :
3023 : nsNumberControlFrame* numberControlFrame =
3024 0 : do_QueryFrame(input->GetPrimaryFrame());
3025 0 : if (input->mType != NS_FORM_INPUT_NUMBER || !numberControlFrame) {
3026 : // Type has changed (and possibly our frame type hasn't been updated yet)
3027 : // or else we've lost our frame. Either way, stop the timer and don't do
3028 : // anything else.
3029 0 : input->StopNumberControlSpinnerSpin();
3030 : } else {
3031 0 : input->StepNumberControlForUserEvent(input->mNumberControlSpinnerSpinsUp ? 1 : -1);
3032 : }
3033 0 : }
3034 :
3035 : void
3036 0 : HTMLInputElement::UpdateFileList()
3037 : {
3038 0 : MOZ_ASSERT(mFileData);
3039 :
3040 0 : if (mFileData->mFileList) {
3041 0 : mFileData->mFileList->Clear();
3042 :
3043 : const nsTArray<OwningFileOrDirectory>& array =
3044 0 : GetFilesOrDirectoriesInternal();
3045 :
3046 0 : for (uint32_t i = 0; i < array.Length(); ++i) {
3047 0 : if (array[i].IsFile()) {
3048 0 : mFileData->mFileList->Append(array[i].GetAsFile());
3049 : }
3050 : }
3051 : }
3052 0 : }
3053 :
3054 : nsresult
3055 4 : HTMLInputElement::SetValueInternal(const nsAString& aValue,
3056 : const nsAString* aOldValue,
3057 : uint32_t aFlags)
3058 : {
3059 4 : NS_PRECONDITION(GetValueMode() != VALUE_MODE_FILENAME,
3060 : "Don't call SetValueInternal for file inputs");
3061 :
3062 4 : switch (GetValueMode()) {
3063 : case VALUE_MODE_VALUE:
3064 : {
3065 : // At the moment, only single line text control have to sanitize their value
3066 : // Because we have to create a new string for that, we should prevent doing
3067 : // it if it's useless.
3068 8 : nsAutoString value(aValue);
3069 :
3070 4 : if (mDoneCreating) {
3071 4 : SanitizeValue(value);
3072 : }
3073 : // else DoneCreatingElement calls us again once mDoneCreating is true
3074 :
3075 4 : bool setValueChanged = !!(aFlags & nsTextEditorState::eSetValue_Notify);
3076 4 : if (setValueChanged) {
3077 1 : SetValueChanged(true);
3078 : }
3079 :
3080 4 : if (IsSingleLineTextControl(false)) {
3081 4 : if (!mInputData.mState->SetValue(value, aOldValue, aFlags)) {
3082 0 : return NS_ERROR_OUT_OF_MEMORY;
3083 : }
3084 4 : if (mType == NS_FORM_INPUT_EMAIL) {
3085 0 : UpdateAllValidityStates(!mDoneCreating);
3086 : }
3087 : } else {
3088 0 : free(mInputData.mValue);
3089 0 : mInputData.mValue = ToNewUnicode(value);
3090 0 : if (setValueChanged) {
3091 0 : SetValueChanged(true);
3092 : }
3093 0 : if (mType == NS_FORM_INPUT_NUMBER) {
3094 : // This has to happen before OnValueChanged is called because that
3095 : // method needs the new value of our frame's anon text control.
3096 : nsNumberControlFrame* numberControlFrame =
3097 0 : do_QueryFrame(GetPrimaryFrame());
3098 0 : if (numberControlFrame) {
3099 0 : numberControlFrame->SetValueOfAnonTextControl(value);
3100 : }
3101 0 : } else if (mType == NS_FORM_INPUT_RANGE) {
3102 0 : nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
3103 0 : if (frame) {
3104 0 : frame->UpdateForValueChange();
3105 : }
3106 0 : } else if ((mType == NS_FORM_INPUT_TIME ||
3107 0 : mType == NS_FORM_INPUT_DATE) &&
3108 0 : !IsExperimentalMobileType(mType) &&
3109 0 : !(aFlags & nsTextEditorState::eSetValue_BySetUserInput)) {
3110 0 : nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
3111 0 : if (frame) {
3112 0 : frame->OnValueChanged();
3113 : }
3114 : }
3115 0 : if (mDoneCreating) {
3116 : OnValueChanged(/* aNotify = */ true,
3117 0 : /* aWasInteractiveUserChange = */ false);
3118 : }
3119 : // else DoneCreatingElement calls us again once mDoneCreating is true
3120 : }
3121 :
3122 4 : if (mType == NS_FORM_INPUT_COLOR) {
3123 : // Update color frame, to reflect color changes
3124 0 : nsColorControlFrame* colorControlFrame = do_QueryFrame(GetPrimaryFrame());
3125 0 : if (colorControlFrame) {
3126 0 : colorControlFrame->UpdateColor();
3127 : }
3128 : }
3129 :
3130 : // This call might be useless in some situations because if the element is
3131 : // a single line text control, nsTextEditorState::SetValue will call
3132 : // nsHTMLInputElement::OnValueChanged which is going to call UpdateState()
3133 : // if the element is focused. This bug 665547.
3134 8 : if (PlaceholderApplies() &&
3135 4 : HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
3136 1 : UpdateState(true);
3137 : }
3138 :
3139 4 : return NS_OK;
3140 : }
3141 :
3142 : case VALUE_MODE_DEFAULT:
3143 : case VALUE_MODE_DEFAULT_ON:
3144 : // If the value of a hidden input was changed, we mark it changed so that we
3145 : // will know we need to save / restore the value. Yes, we are overloading
3146 : // the meaning of ValueChanged just a teensy bit to save a measly byte of
3147 : // storage space in HTMLInputElement. Yes, you are free to make a new flag,
3148 : // NEED_TO_SAVE_VALUE, at such time as mBitField becomes a 16-bit value.
3149 0 : if (mType == NS_FORM_INPUT_HIDDEN) {
3150 0 : SetValueChanged(true);
3151 : }
3152 :
3153 : // Treat value == defaultValue for other input elements.
3154 0 : return nsGenericHTMLFormElementWithState::SetAttr(kNameSpaceID_None,
3155 : nsGkAtoms::value, aValue,
3156 0 : true);
3157 :
3158 : case VALUE_MODE_FILENAME:
3159 0 : return NS_ERROR_UNEXPECTED;
3160 : }
3161 :
3162 : // This return statement is required for some compilers.
3163 0 : return NS_OK;
3164 : }
3165 :
3166 : NS_IMETHODIMP
3167 2 : HTMLInputElement::SetValueChanged(bool aValueChanged)
3168 : {
3169 2 : bool valueChangedBefore = mValueChanged;
3170 :
3171 2 : mValueChanged = aValueChanged;
3172 :
3173 2 : if (valueChangedBefore != aValueChanged) {
3174 1 : UpdateState(true);
3175 : }
3176 :
3177 2 : return NS_OK;
3178 : }
3179 :
3180 : NS_IMETHODIMP
3181 0 : HTMLInputElement::GetChecked(bool* aChecked)
3182 : {
3183 0 : *aChecked = Checked();
3184 0 : return NS_OK;
3185 : }
3186 :
3187 : void
3188 0 : HTMLInputElement::SetCheckedChanged(bool aCheckedChanged)
3189 : {
3190 0 : DoSetCheckedChanged(aCheckedChanged, true);
3191 0 : }
3192 :
3193 : void
3194 0 : HTMLInputElement::DoSetCheckedChanged(bool aCheckedChanged,
3195 : bool aNotify)
3196 : {
3197 0 : if (mType == NS_FORM_INPUT_RADIO) {
3198 0 : if (mCheckedChanged != aCheckedChanged) {
3199 : nsCOMPtr<nsIRadioVisitor> visitor =
3200 0 : new nsRadioSetCheckedChangedVisitor(aCheckedChanged);
3201 0 : VisitGroup(visitor, aNotify);
3202 : }
3203 : } else {
3204 0 : SetCheckedChangedInternal(aCheckedChanged);
3205 : }
3206 0 : }
3207 :
3208 : void
3209 0 : HTMLInputElement::SetCheckedChangedInternal(bool aCheckedChanged)
3210 : {
3211 0 : bool checkedChangedBefore = mCheckedChanged;
3212 :
3213 0 : mCheckedChanged = aCheckedChanged;
3214 :
3215 : // This method can't be called when we are not authorized to notify
3216 : // so we do not need a aNotify parameter.
3217 0 : if (checkedChangedBefore != aCheckedChanged) {
3218 0 : UpdateState(true);
3219 : }
3220 0 : }
3221 :
3222 : NS_IMETHODIMP
3223 0 : HTMLInputElement::SetChecked(bool aChecked)
3224 : {
3225 0 : DoSetChecked(aChecked, true, true);
3226 0 : return NS_OK;
3227 : }
3228 :
3229 : void
3230 0 : HTMLInputElement::DoSetChecked(bool aChecked, bool aNotify,
3231 : bool aSetValueChanged)
3232 : {
3233 : // If the user or JS attempts to set checked, whether it actually changes the
3234 : // value or not, we say the value was changed so that defaultValue don't
3235 : // affect it no more.
3236 0 : if (aSetValueChanged) {
3237 0 : DoSetCheckedChanged(true, aNotify);
3238 : }
3239 :
3240 : // Don't do anything if we're not changing whether it's checked (it would
3241 : // screw up state actually, especially when you are setting radio button to
3242 : // false)
3243 0 : if (mChecked == aChecked) {
3244 0 : return;
3245 : }
3246 :
3247 : // Set checked
3248 0 : if (mType != NS_FORM_INPUT_RADIO) {
3249 0 : SetCheckedInternal(aChecked, aNotify);
3250 0 : return;
3251 : }
3252 :
3253 : // For radio button, we need to do some extra fun stuff
3254 0 : if (aChecked) {
3255 0 : RadioSetChecked(aNotify);
3256 0 : return;
3257 : }
3258 :
3259 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
3260 0 : if (container) {
3261 0 : nsAutoString name;
3262 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
3263 0 : container->SetCurrentRadioButton(name, nullptr);
3264 : }
3265 : // SetCheckedInternal is going to ask all radios to update their
3266 : // validity state. We have to be sure the radio group container knows
3267 : // the currently selected radio.
3268 0 : SetCheckedInternal(false, aNotify);
3269 : }
3270 :
3271 : void
3272 0 : HTMLInputElement::RadioSetChecked(bool aNotify)
3273 : {
3274 : // Find the selected radio button so we can deselect it
3275 0 : nsCOMPtr<nsIDOMHTMLInputElement> currentlySelected = GetSelectedRadioButton();
3276 :
3277 : // Deselect the currently selected radio button
3278 0 : if (currentlySelected) {
3279 : // Pass true for the aNotify parameter since the currently selected
3280 : // button is already in the document.
3281 0 : static_cast<HTMLInputElement*>(currentlySelected.get())
3282 0 : ->SetCheckedInternal(false, true);
3283 : }
3284 :
3285 : // Let the group know that we are now the One True Radio Button
3286 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
3287 0 : if (container) {
3288 0 : nsAutoString name;
3289 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
3290 0 : container->SetCurrentRadioButton(name, this);
3291 : }
3292 :
3293 : // SetCheckedInternal is going to ask all radios to update their
3294 : // validity state.
3295 0 : SetCheckedInternal(true, aNotify);
3296 0 : }
3297 :
3298 : nsIRadioGroupContainer*
3299 0 : HTMLInputElement::GetRadioGroupContainer() const
3300 : {
3301 0 : NS_ASSERTION(mType == NS_FORM_INPUT_RADIO,
3302 : "GetRadioGroupContainer should only be called when type='radio'");
3303 :
3304 0 : nsAutoString name;
3305 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
3306 :
3307 0 : if (name.IsEmpty()) {
3308 0 : return nullptr;
3309 : }
3310 :
3311 0 : if (mForm) {
3312 0 : return mForm;
3313 : }
3314 :
3315 : //XXXsmaug It isn't clear how this should work in Shadow DOM.
3316 0 : return static_cast<nsDocument*>(GetUncomposedDoc());
3317 : }
3318 :
3319 : already_AddRefed<nsIDOMHTMLInputElement>
3320 0 : HTMLInputElement::GetSelectedRadioButton() const
3321 : {
3322 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
3323 0 : if (!container) {
3324 0 : return nullptr;
3325 : }
3326 :
3327 0 : nsAutoString name;
3328 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
3329 :
3330 0 : nsCOMPtr<nsIDOMHTMLInputElement> selected = container->GetCurrentRadioButton(name);
3331 0 : return selected.forget();
3332 : }
3333 :
3334 : nsresult
3335 0 : HTMLInputElement::MaybeSubmitForm(nsPresContext* aPresContext)
3336 : {
3337 0 : if (!mForm) {
3338 : // Nothing to do here.
3339 0 : return NS_OK;
3340 : }
3341 :
3342 0 : nsCOMPtr<nsIPresShell> shell = aPresContext->GetPresShell();
3343 0 : if (!shell) {
3344 0 : return NS_OK;
3345 : }
3346 :
3347 : // Get the default submit element
3348 0 : nsIFormControl* submitControl = mForm->GetDefaultSubmitElement();
3349 0 : if (submitControl) {
3350 0 : nsCOMPtr<nsIContent> submitContent = do_QueryInterface(submitControl);
3351 0 : NS_ASSERTION(submitContent, "Form control not implementing nsIContent?!");
3352 : // Fire the button's onclick handler and let the button handle
3353 : // submitting the form.
3354 0 : WidgetMouseEvent event(true, eMouseClick, nullptr, WidgetMouseEvent::eReal);
3355 0 : nsEventStatus status = nsEventStatus_eIgnore;
3356 0 : shell->HandleDOMEventWithTarget(submitContent, &event, &status);
3357 0 : } else if (!mForm->ImplicitSubmissionIsDisabled() &&
3358 0 : mForm->SubmissionCanProceed(nullptr)) {
3359 : // TODO: removing this code and have the submit event sent by the form,
3360 : // bug 592124.
3361 : // If there's only one text control, just submit the form
3362 : // Hold strong ref across the event
3363 0 : RefPtr<mozilla::dom::HTMLFormElement> form = mForm;
3364 0 : InternalFormEvent event(true, eFormSubmit);
3365 0 : nsEventStatus status = nsEventStatus_eIgnore;
3366 0 : shell->HandleDOMEventWithTarget(form, &event, &status);
3367 : }
3368 :
3369 0 : return NS_OK;
3370 : }
3371 :
3372 : void
3373 0 : HTMLInputElement::SetCheckedInternal(bool aChecked, bool aNotify)
3374 : {
3375 : // Set the value
3376 0 : mChecked = aChecked;
3377 :
3378 : // Notify the frame
3379 0 : if (mType == NS_FORM_INPUT_CHECKBOX || mType == NS_FORM_INPUT_RADIO) {
3380 0 : nsIFrame* frame = GetPrimaryFrame();
3381 0 : if (frame) {
3382 0 : frame->InvalidateFrameSubtree();
3383 : }
3384 : }
3385 :
3386 0 : UpdateAllValidityStates(aNotify);
3387 :
3388 : // Notify the document that the CSS :checked pseudoclass for this element
3389 : // has changed state.
3390 0 : UpdateState(aNotify);
3391 :
3392 : // Notify all radios in the group that value has changed, this is to let
3393 : // radios to have the chance to update its states, e.g., :indeterminate.
3394 0 : if (mType == NS_FORM_INPUT_RADIO) {
3395 0 : nsCOMPtr<nsIRadioVisitor> visitor = new nsRadioUpdateStateVisitor(this);
3396 0 : VisitGroup(visitor, aNotify);
3397 : }
3398 0 : }
3399 :
3400 : void
3401 0 : HTMLInputElement::Blur(ErrorResult& aError)
3402 : {
3403 0 : if (mType == NS_FORM_INPUT_NUMBER) {
3404 : // Blur our anonymous text control, if we have one. (DOM 'change' event
3405 : // firing and other things depend on this.)
3406 : nsNumberControlFrame* numberControlFrame =
3407 0 : do_QueryFrame(GetPrimaryFrame());
3408 0 : if (numberControlFrame) {
3409 0 : HTMLInputElement* textControl = numberControlFrame->GetAnonTextControl();
3410 0 : if (textControl) {
3411 0 : textControl->Blur(aError);
3412 0 : return;
3413 : }
3414 : }
3415 : }
3416 :
3417 0 : if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
3418 0 : !IsExperimentalMobileType(mType)) {
3419 0 : nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
3420 0 : if (frame) {
3421 0 : frame->HandleBlurEvent();
3422 0 : return;
3423 : }
3424 : }
3425 :
3426 0 : nsGenericHTMLElement::Blur(aError);
3427 : }
3428 :
3429 : void
3430 0 : HTMLInputElement::Focus(ErrorResult& aError)
3431 : {
3432 0 : if (mType == NS_FORM_INPUT_NUMBER) {
3433 : // Focus our anonymous text control, if we have one.
3434 : nsNumberControlFrame* numberControlFrame =
3435 0 : do_QueryFrame(GetPrimaryFrame());
3436 0 : if (numberControlFrame) {
3437 0 : HTMLInputElement* textControl = numberControlFrame->GetAnonTextControl();
3438 0 : if (textControl) {
3439 0 : textControl->Focus(aError);
3440 0 : return;
3441 : }
3442 : }
3443 : }
3444 :
3445 0 : if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
3446 0 : !IsExperimentalMobileType(mType)) {
3447 0 : nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
3448 0 : if (frame) {
3449 0 : frame->HandleFocusEvent();
3450 0 : return;
3451 : }
3452 : }
3453 :
3454 0 : if (mType != NS_FORM_INPUT_FILE) {
3455 0 : nsGenericHTMLElement::Focus(aError);
3456 0 : return;
3457 : }
3458 :
3459 : // For file inputs, focus the first button instead. In the case of there
3460 : // being two buttons (when the picker is a directory picker) the user can
3461 : // tab to the next one.
3462 0 : nsIFrame* frame = GetPrimaryFrame();
3463 0 : if (frame) {
3464 0 : for (nsIFrame* childFrame : frame->PrincipalChildList()) {
3465 : // See if the child is a button control.
3466 : nsCOMPtr<nsIFormControl> formCtrl =
3467 0 : do_QueryInterface(childFrame->GetContent());
3468 0 : if (formCtrl && formCtrl->ControlType() == NS_FORM_BUTTON_BUTTON) {
3469 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(formCtrl);
3470 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3471 0 : if (fm && element) {
3472 0 : fm->SetFocus(element, 0);
3473 : }
3474 0 : break;
3475 : }
3476 : }
3477 : }
3478 :
3479 0 : return;
3480 : }
3481 :
3482 : #if !defined(ANDROID) && !defined(XP_MACOSX)
3483 : bool
3484 0 : HTMLInputElement::IsNodeApzAwareInternal() const
3485 : {
3486 : // Tell APZC we may handle mouse wheel event and do preventDefault when input
3487 : // type is number.
3488 0 : return (mType == NS_FORM_INPUT_NUMBER) || (mType == NS_FORM_INPUT_RANGE) ||
3489 0 : nsINode::IsNodeApzAwareInternal();
3490 : }
3491 : #endif
3492 :
3493 : bool
3494 0 : HTMLInputElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
3495 : {
3496 0 : return mType != NS_FORM_INPUT_HIDDEN ||
3497 0 : nsGenericHTMLFormElementWithState::IsInteractiveHTMLContent(aIgnoreTabindex);
3498 : }
3499 :
3500 : void
3501 2 : HTMLInputElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
3502 : {
3503 2 : nsImageLoadingContent::AsyncEventRunning(aEvent);
3504 2 : }
3505 :
3506 : NS_IMETHODIMP
3507 0 : HTMLInputElement::Select()
3508 : {
3509 0 : if (mType == NS_FORM_INPUT_NUMBER) {
3510 : nsNumberControlFrame* numberControlFrame =
3511 0 : do_QueryFrame(GetPrimaryFrame());
3512 0 : if (numberControlFrame) {
3513 0 : return numberControlFrame->HandleSelectCall();
3514 : }
3515 0 : return NS_OK;
3516 : }
3517 :
3518 0 : if (!IsSingleLineTextControl(false)) {
3519 0 : return NS_OK;
3520 : }
3521 :
3522 : // XXX Bug? We have to give the input focus before contents can be
3523 : // selected
3524 :
3525 0 : FocusTristate state = FocusState();
3526 0 : if (state == eUnfocusable) {
3527 0 : return NS_OK;
3528 : }
3529 :
3530 0 : nsTextEditorState* tes = GetEditorState();
3531 0 : if (tes) {
3532 0 : RefPtr<nsFrameSelection> fs = tes->GetConstFrameSelection();
3533 0 : if (fs && fs->MouseDownRecorded()) {
3534 : // This means that we're being called while the frame selection has a mouse
3535 : // down event recorded to adjust the caret during the mouse up event.
3536 : // We are probably called from the focus event handler. We should override
3537 : // the delayed caret data in this case to ensure that this select() call
3538 : // takes effect.
3539 0 : fs->SetDelayedCaretData(nullptr);
3540 : }
3541 : }
3542 :
3543 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3544 :
3545 0 : RefPtr<nsPresContext> presContext = GetPresContext(eForComposedDoc);
3546 0 : if (state == eInactiveWindow) {
3547 0 : if (fm)
3548 0 : fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
3549 0 : SelectAll(presContext);
3550 0 : return NS_OK;
3551 : }
3552 :
3553 0 : if (DispatchSelectEvent(presContext) && fm) {
3554 0 : fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
3555 :
3556 : // ensure that the element is actually focused
3557 0 : nsCOMPtr<nsIDOMElement> focusedElement;
3558 0 : fm->GetFocusedElement(getter_AddRefs(focusedElement));
3559 0 : if (SameCOMIdentity(static_cast<nsIDOMNode*>(this), focusedElement)) {
3560 : // Now Select all the text!
3561 0 : SelectAll(presContext);
3562 : }
3563 : }
3564 :
3565 0 : return NS_OK;
3566 : }
3567 :
3568 : bool
3569 0 : HTMLInputElement::DispatchSelectEvent(nsPresContext* aPresContext)
3570 : {
3571 0 : nsEventStatus status = nsEventStatus_eIgnore;
3572 :
3573 : // If already handling select event, don't dispatch a second.
3574 0 : if (!mHandlingSelectEvent) {
3575 0 : WidgetEvent event(true, eFormSelect);
3576 :
3577 0 : mHandlingSelectEvent = true;
3578 0 : EventDispatcher::Dispatch(static_cast<nsIContent*>(this),
3579 0 : aPresContext, &event, nullptr, &status);
3580 0 : mHandlingSelectEvent = false;
3581 : }
3582 :
3583 : // If the DOM event was not canceled (e.g. by a JS event handler
3584 : // returning false)
3585 0 : return (status == nsEventStatus_eIgnore);
3586 : }
3587 :
3588 : void
3589 0 : HTMLInputElement::SelectAll(nsPresContext* aPresContext)
3590 : {
3591 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
3592 :
3593 0 : if (formControlFrame) {
3594 0 : formControlFrame->SetFormProperty(nsGkAtoms::select, EmptyString());
3595 : }
3596 0 : }
3597 :
3598 : bool
3599 4 : HTMLInputElement::NeedToInitializeEditorForEvent(
3600 : EventChainPreVisitor& aVisitor) const
3601 : {
3602 : // We only need to initialize the editor for single line input controls because they
3603 : // are lazily initialized. We don't need to initialize the control for
3604 : // certain types of events, because we know that those events are safe to be
3605 : // handled without the editor being initialized. These events include:
3606 : // mousein/move/out, overflow/underflow, and DOM mutation events.
3607 8 : if (!IsSingleLineTextControl(false) ||
3608 4 : aVisitor.mEvent->mClass == eMutationEventClass) {
3609 0 : return false;
3610 : }
3611 :
3612 4 : switch (aVisitor.mEvent->mMessage) {
3613 : case eMouseMove:
3614 : case eMouseEnterIntoWidget:
3615 : case eMouseExitFromWidget:
3616 : case eMouseOver:
3617 : case eMouseOut:
3618 : case eScrollPortUnderflow:
3619 : case eScrollPortOverflow:
3620 1 : return false;
3621 : default:
3622 3 : return true;
3623 : }
3624 : }
3625 :
3626 : bool
3627 4 : HTMLInputElement::IsDisabledForEvents(EventMessage aMessage)
3628 : {
3629 4 : return IsElementDisabledForEvents(aMessage, GetPrimaryFrame());
3630 : }
3631 :
3632 : nsresult
3633 4 : HTMLInputElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
3634 : {
3635 : // Do not process any DOM events if the element is disabled
3636 4 : aVisitor.mCanHandle = false;
3637 4 : if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) {
3638 0 : return NS_OK;
3639 : }
3640 :
3641 : // Initialize the editor if needed.
3642 4 : if (NeedToInitializeEditorForEvent(aVisitor)) {
3643 3 : nsITextControlFrame* textControlFrame = do_QueryFrame(GetPrimaryFrame());
3644 3 : if (textControlFrame)
3645 3 : textControlFrame->EnsureEditorInitialized();
3646 : }
3647 :
3648 : //FIXME Allow submission etc. also when there is no prescontext, Bug 329509.
3649 4 : if (!aVisitor.mPresContext) {
3650 0 : return nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor);
3651 : }
3652 : //
3653 : // Web pages expect the value of a radio button or checkbox to be set
3654 : // *before* onclick and DOMActivate fire, and they expect that if they set
3655 : // the value explicitly during onclick or DOMActivate it will not be toggled
3656 : // or any such nonsense.
3657 : // In order to support that (bug 57137 and 58460 are examples) we toggle
3658 : // the checked attribute *first*, and then fire onclick. If the user
3659 : // returns false, we reset the control to the old checked value. Otherwise,
3660 : // we dispatch DOMActivate. If DOMActivate is cancelled, we also reset
3661 : // the control to the old checked value. We need to keep track of whether
3662 : // we've already toggled the state from onclick since the user could
3663 : // explicitly dispatch DOMActivate on the element.
3664 : //
3665 : // This is a compatibility hack.
3666 : //
3667 :
3668 : // Track whether we're in the outermost Dispatch invocation that will
3669 : // cause activation of the input. That is, if we're a click event, or a
3670 : // DOMActivate that was dispatched directly, this will be set, but if we're
3671 : // a DOMActivate dispatched from click handling, it will not be set.
3672 4 : WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
3673 : bool outerActivateEvent =
3674 8 : ((mouseEvent && mouseEvent->IsLeftClickEvent()) ||
3675 8 : (aVisitor.mEvent->mMessage == eLegacyDOMActivate && !mInInternalActivate));
3676 :
3677 4 : if (outerActivateEvent) {
3678 0 : aVisitor.mItemFlags |= NS_OUTER_ACTIVATE_EVENT;
3679 : }
3680 :
3681 4 : bool originalCheckedValue = false;
3682 :
3683 4 : if (outerActivateEvent) {
3684 0 : mCheckedIsToggled = false;
3685 :
3686 0 : switch(mType) {
3687 : case NS_FORM_INPUT_CHECKBOX:
3688 : {
3689 0 : if (mIndeterminate) {
3690 : // indeterminate is always set to FALSE when the checkbox is toggled
3691 0 : SetIndeterminateInternal(false, false);
3692 0 : aVisitor.mItemFlags |= NS_ORIGINAL_INDETERMINATE_VALUE;
3693 : }
3694 :
3695 0 : GetChecked(&originalCheckedValue);
3696 0 : DoSetChecked(!originalCheckedValue, true, true);
3697 0 : mCheckedIsToggled = true;
3698 : }
3699 0 : break;
3700 :
3701 : case NS_FORM_INPUT_RADIO:
3702 : {
3703 0 : nsCOMPtr<nsIDOMHTMLInputElement> selectedRadioButton = GetSelectedRadioButton();
3704 0 : aVisitor.mItemData = selectedRadioButton;
3705 :
3706 0 : originalCheckedValue = mChecked;
3707 0 : if (!originalCheckedValue) {
3708 0 : DoSetChecked(true, true, true);
3709 0 : mCheckedIsToggled = true;
3710 : }
3711 : }
3712 0 : break;
3713 :
3714 : case NS_FORM_INPUT_SUBMIT:
3715 : case NS_FORM_INPUT_IMAGE:
3716 0 : if (mForm) {
3717 : // tell the form that we are about to enter a click handler.
3718 : // that means that if there are scripted submissions, the
3719 : // latest one will be deferred until after the exit point of the handler.
3720 0 : mForm->OnSubmitClickBegin(this);
3721 : }
3722 0 : break;
3723 :
3724 : default:
3725 0 : break;
3726 : }
3727 : }
3728 :
3729 4 : if (originalCheckedValue) {
3730 0 : aVisitor.mItemFlags |= NS_ORIGINAL_CHECKED_VALUE;
3731 : }
3732 :
3733 : // If mNoContentDispatch is true we will not allow content to handle
3734 : // this event. But to allow middle mouse button paste to work we must allow
3735 : // middle clicks to go to text fields anyway.
3736 4 : if (aVisitor.mEvent->mFlags.mNoContentDispatch) {
3737 0 : aVisitor.mItemFlags |= NS_NO_CONTENT_DISPATCH;
3738 : }
3739 12 : if (IsSingleLineTextControl(false) &&
3740 4 : aVisitor.mEvent->mMessage == eMouseClick &&
3741 0 : aVisitor.mEvent->AsMouseEvent()->button ==
3742 : WidgetMouseEvent::eMiddleButton) {
3743 0 : aVisitor.mEvent->mFlags.mNoContentDispatch = false;
3744 : }
3745 :
3746 : // We must cache type because mType may change during JS event (bug 2369)
3747 4 : aVisitor.mItemFlags |= mType;
3748 :
3749 8 : if (aVisitor.mEvent->mMessage == eFocus &&
3750 0 : aVisitor.mEvent->IsTrusted() &&
3751 4 : MayFireChangeOnBlur() &&
3752 : // StartRangeThumbDrag already set mFocusedValue on 'mousedown' before
3753 : // we get the 'focus' event.
3754 0 : !mIsDraggingRange) {
3755 0 : GetValue(mFocusedValue, CallerType::System);
3756 : }
3757 :
3758 : // Fire onchange (if necessary), before we do the blur, bug 357684.
3759 4 : if (aVisitor.mEvent->mMessage == eBlur) {
3760 : // We set NS_PRE_HANDLE_BLUR_EVENT here and handle it in PreHandleEvent to
3761 : // prevent breaking event target chain creation.
3762 0 : aVisitor.mWantsPreHandleEvent = true;
3763 0 : aVisitor.mItemFlags |= NS_PRE_HANDLE_BLUR_EVENT;
3764 : }
3765 :
3766 4 : if (mType == NS_FORM_INPUT_RANGE &&
3767 0 : (aVisitor.mEvent->mMessage == eFocus ||
3768 0 : aVisitor.mEvent->mMessage == eBlur)) {
3769 : // Just as nsGenericHTMLFormElementWithState::GetEventTargetParent calls
3770 : // nsIFormControlFrame::SetFocus, we handle focus here.
3771 0 : nsIFrame* frame = GetPrimaryFrame();
3772 0 : if (frame) {
3773 0 : frame->InvalidateFrameSubtree();
3774 : }
3775 : }
3776 :
3777 12 : if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
3778 0 : !IsExperimentalMobileType(mType) &&
3779 4 : aVisitor.mEvent->mMessage == eFocus &&
3780 0 : aVisitor.mEvent->mOriginalTarget == this) {
3781 : // If original target is this and not the anonymous text control, we should
3782 : // pass the focus to the anonymous text control.
3783 0 : nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
3784 0 : if (frame) {
3785 0 : frame->HandleFocusEvent();
3786 : }
3787 : }
3788 :
3789 4 : if (mType == NS_FORM_INPUT_NUMBER && aVisitor.mEvent->IsTrusted()) {
3790 0 : if (mNumberControlSpinnerIsSpinning) {
3791 : // If the timer is running the user has depressed the mouse on one of the
3792 : // spin buttons. If the mouse exits the button we either want to reverse
3793 : // the direction of spin if it has moved over the other button, or else
3794 : // we want to end the spin. We do this here (rather than in
3795 : // PostHandleEvent) because we don't want to let content preventDefault()
3796 : // the end of the spin.
3797 0 : if (aVisitor.mEvent->mMessage == eMouseMove) {
3798 : // Be aggressive about stopping the spin:
3799 0 : bool stopSpin = true;
3800 : nsNumberControlFrame* numberControlFrame =
3801 0 : do_QueryFrame(GetPrimaryFrame());
3802 0 : if (numberControlFrame) {
3803 : bool oldNumberControlSpinTimerSpinsUpValue =
3804 0 : mNumberControlSpinnerSpinsUp;
3805 0 : switch (numberControlFrame->GetSpinButtonForPointerEvent(
3806 0 : aVisitor.mEvent->AsMouseEvent())) {
3807 : case nsNumberControlFrame::eSpinButtonUp:
3808 0 : mNumberControlSpinnerSpinsUp = true;
3809 0 : stopSpin = false;
3810 0 : break;
3811 : case nsNumberControlFrame::eSpinButtonDown:
3812 0 : mNumberControlSpinnerSpinsUp = false;
3813 0 : stopSpin = false;
3814 0 : break;
3815 : }
3816 0 : if (mNumberControlSpinnerSpinsUp !=
3817 : oldNumberControlSpinTimerSpinsUpValue) {
3818 : nsNumberControlFrame* numberControlFrame =
3819 0 : do_QueryFrame(GetPrimaryFrame());
3820 0 : if (numberControlFrame) {
3821 0 : numberControlFrame->SpinnerStateChanged();
3822 : }
3823 : }
3824 : }
3825 0 : if (stopSpin) {
3826 0 : StopNumberControlSpinnerSpin();
3827 : }
3828 0 : } else if (aVisitor.mEvent->mMessage == eMouseUp) {
3829 0 : StopNumberControlSpinnerSpin();
3830 : }
3831 : }
3832 0 : if (aVisitor.mEvent->mMessage == eFocus ||
3833 0 : aVisitor.mEvent->mMessage == eBlur) {
3834 0 : if (aVisitor.mEvent->mMessage == eFocus) {
3835 : // Tell our frame it's getting focus so that it can make sure focus
3836 : // is moved to our anonymous text control.
3837 : nsNumberControlFrame* numberControlFrame =
3838 0 : do_QueryFrame(GetPrimaryFrame());
3839 0 : if (numberControlFrame) {
3840 : // This could kill the frame!
3841 0 : numberControlFrame->HandleFocusEvent(aVisitor.mEvent);
3842 : }
3843 : }
3844 0 : nsIFrame* frame = GetPrimaryFrame();
3845 0 : if (frame && frame->IsThemed()) {
3846 : // Our frame's nested <input type=text> will be invalidated when it
3847 : // loses focus, but since we are also native themed we need to make
3848 : // sure that our entire area is repainted since any focus highlight
3849 : // from the theme should be removed from us (the repainting of the
3850 : // sub-area occupied by the anon text control is not enough to do
3851 : // that).
3852 0 : frame->InvalidateFrame();
3853 : }
3854 : }
3855 : }
3856 :
3857 4 : nsresult rv = nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor);
3858 :
3859 : // We do this after calling the base class' GetEventTargetParent so that
3860 : // nsIContent::GetEventTargetParent doesn't reset any change we make to
3861 : // mCanHandle.
3862 8 : if (mType == NS_FORM_INPUT_NUMBER &&
3863 4 : aVisitor.mEvent->IsTrusted() &&
3864 0 : aVisitor.mEvent->mOriginalTarget != this) {
3865 : // <input type=number> has an anonymous <input type=text> descendant. If
3866 : // 'input' or 'change' events are fired at that text control then we need
3867 : // to do some special handling here.
3868 0 : HTMLInputElement* textControl = nullptr;
3869 : nsNumberControlFrame* numberControlFrame =
3870 0 : do_QueryFrame(GetPrimaryFrame());
3871 0 : if (numberControlFrame) {
3872 0 : textControl = numberControlFrame->GetAnonTextControl();
3873 : }
3874 0 : if (textControl && aVisitor.mEvent->mOriginalTarget == textControl) {
3875 0 : if (aVisitor.mEvent->mMessage == eEditorInput) {
3876 0 : aVisitor.mWantsPreHandleEvent = true;
3877 : // We set NS_PRE_HANDLE_INPUT_EVENT here and handle it in PreHandleEvent
3878 : // to prevent breaking event target chain creation.
3879 0 : aVisitor.mItemFlags |= NS_PRE_HANDLE_INPUT_EVENT;
3880 : }
3881 0 : else if (aVisitor.mEvent->mMessage == eFormChange) {
3882 : // We cancel the DOM 'change' event that is fired for any change to our
3883 : // anonymous text control since we fire our own 'change' events and
3884 : // content shouldn't be seeing two 'change' events. Besides that we
3885 : // (as a number) control have tighter restrictions on when our internal
3886 : // value changes than our anon text control does, so in some cases
3887 : // (if our text control's value doesn't parse as a number) we don't
3888 : // want to fire a 'change' event at all.
3889 0 : aVisitor.mCanHandle = false;
3890 : }
3891 : }
3892 : }
3893 :
3894 : // Stop the event if the related target's first non-native ancestor is the
3895 : // same as the original target's first non-native ancestor (we are moving
3896 : // inside of the same element).
3897 12 : if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
3898 0 : !IsExperimentalMobileType(mType) &&
3899 4 : aVisitor.mEvent->IsTrusted() &&
3900 0 : (aVisitor.mEvent->mMessage == eFocus ||
3901 0 : aVisitor.mEvent->mMessage == eFocusIn ||
3902 0 : aVisitor.mEvent->mMessage == eFocusOut ||
3903 0 : aVisitor.mEvent->mMessage == eBlur)) {
3904 : nsCOMPtr<nsIContent> originalTarget =
3905 0 : do_QueryInterface(aVisitor.mEvent->AsFocusEvent()->mOriginalTarget);
3906 : nsCOMPtr<nsIContent> relatedTarget =
3907 0 : do_QueryInterface(aVisitor.mEvent->AsFocusEvent()->mRelatedTarget);
3908 :
3909 0 : if (originalTarget && relatedTarget &&
3910 0 : originalTarget->FindFirstNonChromeOnlyAccessContent() ==
3911 0 : relatedTarget->FindFirstNonChromeOnlyAccessContent()) {
3912 0 : aVisitor.mCanHandle = false;
3913 : }
3914 : }
3915 :
3916 4 : return rv;
3917 : }
3918 :
3919 : nsresult
3920 0 : HTMLInputElement::PreHandleEvent(EventChainVisitor& aVisitor)
3921 : {
3922 0 : if (!aVisitor.mPresContext) {
3923 0 : return nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor);
3924 : }
3925 : nsresult rv;
3926 0 : if (aVisitor.mItemFlags & NS_PRE_HANDLE_BLUR_EVENT) {
3927 0 : MOZ_ASSERT(aVisitor.mEvent->mMessage == eBlur);
3928 : // Experimental mobile types rely on the system UI to prevent users to not
3929 : // set invalid values but we have to be extra-careful. Especially if the
3930 : // option has been enabled on desktop.
3931 0 : if (IsExperimentalMobileType(mType)) {
3932 0 : nsAutoString aValue;
3933 0 : GetNonFileValueInternal(aValue);
3934 0 : rv = SetValueInternal(aValue, nsTextEditorState::eSetValue_Internal);
3935 0 : NS_ENSURE_SUCCESS(rv, rv);
3936 : }
3937 0 : FireChangeEventIfNeeded();
3938 : }
3939 0 : rv = nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor);
3940 0 : if (aVisitor.mItemFlags & NS_PRE_HANDLE_INPUT_EVENT) {
3941 0 : nsNumberControlFrame* numberControlFrame = do_QueryFrame(GetPrimaryFrame());
3942 0 : MOZ_ASSERT(aVisitor.mEvent->mMessage == eEditorInput);
3943 0 : MOZ_ASSERT(numberControlFrame);
3944 0 : MOZ_ASSERT(numberControlFrame->GetAnonTextControl() ==
3945 : aVisitor.mEvent->mOriginalTarget);
3946 : // Propogate the anon text control's new value to our HTMLInputElement:
3947 0 : nsAutoString value;
3948 0 : numberControlFrame->GetValueOfAnonTextControl(value);
3949 0 : numberControlFrame->HandlingInputEvent(true);
3950 0 : AutoWeakFrame weakNumberControlFrame(numberControlFrame);
3951 : rv = SetValueInternal(value,
3952 : nsTextEditorState::eSetValue_BySetUserInput |
3953 0 : nsTextEditorState::eSetValue_Notify);
3954 0 : NS_ENSURE_SUCCESS(rv, rv);
3955 0 : if (weakNumberControlFrame.IsAlive()) {
3956 0 : numberControlFrame->HandlingInputEvent(false);
3957 : }
3958 : }
3959 0 : return rv;
3960 : }
3961 :
3962 : void
3963 0 : HTMLInputElement::StartRangeThumbDrag(WidgetGUIEvent* aEvent)
3964 : {
3965 0 : mIsDraggingRange = true;
3966 0 : mRangeThumbDragStartValue = GetValueAsDecimal();
3967 : // Don't use CAPTURE_RETARGETTOELEMENT, as that breaks pseudo-class styling
3968 : // of the thumb.
3969 0 : nsIPresShell::SetCapturingContent(this, CAPTURE_IGNOREALLOWED);
3970 0 : nsRangeFrame* rangeFrame = do_QueryFrame(GetPrimaryFrame());
3971 :
3972 : // Before we change the value, record the current value so that we'll
3973 : // correctly send a 'change' event if appropriate. We need to do this here
3974 : // because the 'focus' event is handled after the 'mousedown' event that
3975 : // we're being called for (i.e. too late to update mFocusedValue, since we'll
3976 : // have changed it by then).
3977 0 : GetValue(mFocusedValue, CallerType::System);
3978 :
3979 0 : SetValueOfRangeForUserEvent(rangeFrame->GetValueAtEventPoint(aEvent));
3980 0 : }
3981 :
3982 : void
3983 0 : HTMLInputElement::FinishRangeThumbDrag(WidgetGUIEvent* aEvent)
3984 : {
3985 0 : MOZ_ASSERT(mIsDraggingRange);
3986 :
3987 0 : if (nsIPresShell::GetCapturingContent() == this) {
3988 0 : nsIPresShell::SetCapturingContent(nullptr, 0); // cancel capture
3989 : }
3990 0 : if (aEvent) {
3991 0 : nsRangeFrame* rangeFrame = do_QueryFrame(GetPrimaryFrame());
3992 0 : SetValueOfRangeForUserEvent(rangeFrame->GetValueAtEventPoint(aEvent));
3993 : }
3994 0 : mIsDraggingRange = false;
3995 0 : FireChangeEventIfNeeded();
3996 0 : }
3997 :
3998 : void
3999 0 : HTMLInputElement::CancelRangeThumbDrag(bool aIsForUserEvent)
4000 : {
4001 0 : MOZ_ASSERT(mIsDraggingRange);
4002 :
4003 0 : mIsDraggingRange = false;
4004 0 : if (nsIPresShell::GetCapturingContent() == this) {
4005 0 : nsIPresShell::SetCapturingContent(nullptr, 0); // cancel capture
4006 : }
4007 0 : if (aIsForUserEvent) {
4008 0 : SetValueOfRangeForUserEvent(mRangeThumbDragStartValue);
4009 : } else {
4010 : // Don't dispatch an 'input' event - at least not using
4011 : // DispatchTrustedEvent.
4012 : // TODO: decide what we should do here - bug 851782.
4013 0 : nsAutoString val;
4014 0 : mInputType->ConvertNumberToString(mRangeThumbDragStartValue, val);
4015 : // TODO: What should we do if SetValueInternal fails? (The allocation
4016 : // is small, so we should be fine here.)
4017 : SetValueInternal(val, nsTextEditorState::eSetValue_BySetUserInput |
4018 0 : nsTextEditorState::eSetValue_Notify);
4019 0 : nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
4020 0 : if (frame) {
4021 0 : frame->UpdateForValueChange();
4022 : }
4023 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
4024 0 : new AsyncEventDispatcher(this, NS_LITERAL_STRING("input"), true, false);
4025 0 : asyncDispatcher->RunDOMEventWhenSafe();
4026 : }
4027 0 : }
4028 :
4029 : void
4030 0 : HTMLInputElement::SetValueOfRangeForUserEvent(Decimal aValue)
4031 : {
4032 0 : MOZ_ASSERT(aValue.isFinite());
4033 :
4034 0 : Decimal oldValue = GetValueAsDecimal();
4035 :
4036 0 : nsAutoString val;
4037 0 : mInputType->ConvertNumberToString(aValue, val);
4038 : // TODO: What should we do if SetValueInternal fails? (The allocation
4039 : // is small, so we should be fine here.)
4040 : SetValueInternal(val, nsTextEditorState::eSetValue_BySetUserInput |
4041 0 : nsTextEditorState::eSetValue_Notify);
4042 0 : nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
4043 0 : if (frame) {
4044 0 : frame->UpdateForValueChange();
4045 : }
4046 :
4047 0 : if (GetValueAsDecimal() != oldValue) {
4048 0 : nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
4049 : static_cast<nsIDOMHTMLInputElement*>(this),
4050 0 : NS_LITERAL_STRING("input"), true,
4051 0 : false);
4052 : }
4053 0 : }
4054 :
4055 : void
4056 0 : HTMLInputElement::StartNumberControlSpinnerSpin()
4057 : {
4058 0 : MOZ_ASSERT(!mNumberControlSpinnerIsSpinning);
4059 :
4060 0 : mNumberControlSpinnerIsSpinning = true;
4061 :
4062 0 : nsRepeatService::GetInstance()->Start(HandleNumberControlSpin, this, OwnerDoc(),
4063 0 : NS_LITERAL_CSTRING("HandleNumberControlSpin"));
4064 :
4065 : // Capture the mouse so that we can tell if the pointer moves from one
4066 : // spin button to the other, or to some other element:
4067 0 : nsIPresShell::SetCapturingContent(this, CAPTURE_IGNOREALLOWED);
4068 :
4069 : nsNumberControlFrame* numberControlFrame =
4070 0 : do_QueryFrame(GetPrimaryFrame());
4071 0 : if (numberControlFrame) {
4072 0 : numberControlFrame->SpinnerStateChanged();
4073 : }
4074 0 : }
4075 :
4076 : void
4077 0 : HTMLInputElement::StopNumberControlSpinnerSpin(SpinnerStopState aState)
4078 : {
4079 0 : if (mNumberControlSpinnerIsSpinning) {
4080 0 : if (nsIPresShell::GetCapturingContent() == this) {
4081 0 : nsIPresShell::SetCapturingContent(nullptr, 0); // cancel capture
4082 : }
4083 :
4084 0 : nsRepeatService::GetInstance()->Stop(HandleNumberControlSpin, this);
4085 :
4086 0 : mNumberControlSpinnerIsSpinning = false;
4087 :
4088 0 : if (aState == eAllowDispatchingEvents) {
4089 0 : FireChangeEventIfNeeded();
4090 : }
4091 :
4092 : nsNumberControlFrame* numberControlFrame =
4093 0 : do_QueryFrame(GetPrimaryFrame());
4094 0 : if (numberControlFrame) {
4095 0 : MOZ_ASSERT(aState == eAllowDispatchingEvents,
4096 : "Shouldn't have primary frame for the element when we're not "
4097 : "allowed to dispatch events to it anymore.");
4098 0 : numberControlFrame->SpinnerStateChanged();
4099 : }
4100 : }
4101 0 : }
4102 :
4103 : void
4104 0 : HTMLInputElement::StepNumberControlForUserEvent(int32_t aDirection)
4105 : {
4106 : // We can't use GetValidityState here because the validity state is not set
4107 : // if the user hasn't previously taken an action to set or change the value,
4108 : // according to the specs.
4109 0 : if (HasBadInput()) {
4110 : // If the user has typed a value into the control and inadvertently made a
4111 : // mistake (e.g. put a thousand separator at the wrong point) we do not
4112 : // want to wipe out what they typed if they try to increment/decrement the
4113 : // value. Better is to highlight the value as being invalid so that they
4114 : // can correct what they typed.
4115 : // We only do this if there actually is a value typed in by/displayed to
4116 : // the user. (IsValid() can return false if the 'required' attribute is
4117 : // set and the value is the empty string.)
4118 : nsNumberControlFrame* numberControlFrame =
4119 0 : do_QueryFrame(GetPrimaryFrame());
4120 0 : if (numberControlFrame &&
4121 0 : !numberControlFrame->AnonTextControlIsEmpty()) {
4122 : // We pass 'true' for UpdateValidityUIBits' aIsFocused argument
4123 : // regardless because we need the UI to update _now_ or the user will
4124 : // wonder why the step behavior isn't functioning.
4125 0 : UpdateValidityUIBits(true);
4126 0 : UpdateState(true);
4127 0 : return;
4128 : }
4129 : }
4130 :
4131 0 : Decimal newValue = Decimal::nan(); // unchanged if value will not change
4132 :
4133 0 : nsresult rv = GetValueIfStepped(aDirection, CALLED_FOR_USER_EVENT, &newValue);
4134 :
4135 0 : if (NS_FAILED(rv) || !newValue.isFinite()) {
4136 0 : return; // value should not or will not change
4137 : }
4138 :
4139 0 : nsAutoString newVal;
4140 0 : mInputType->ConvertNumberToString(newValue, newVal);
4141 : // TODO: What should we do if SetValueInternal fails? (The allocation
4142 : // is small, so we should be fine here.)
4143 : SetValueInternal(newVal, nsTextEditorState::eSetValue_BySetUserInput |
4144 0 : nsTextEditorState::eSetValue_Notify);
4145 :
4146 0 : nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
4147 : static_cast<nsIDOMHTMLInputElement*>(this),
4148 0 : NS_LITERAL_STRING("input"), true,
4149 0 : false);
4150 : }
4151 :
4152 : static bool
4153 0 : SelectTextFieldOnFocus()
4154 : {
4155 0 : if (!gSelectTextFieldOnFocus) {
4156 0 : int32_t selectTextfieldsOnKeyFocus = -1;
4157 : nsresult rv =
4158 : LookAndFeel::GetInt(LookAndFeel::eIntID_SelectTextfieldsOnKeyFocus,
4159 0 : &selectTextfieldsOnKeyFocus);
4160 0 : if (NS_FAILED(rv)) {
4161 0 : gSelectTextFieldOnFocus = -1;
4162 : } else {
4163 0 : gSelectTextFieldOnFocus = selectTextfieldsOnKeyFocus != 0 ? 1 : -1;
4164 : }
4165 : }
4166 :
4167 0 : return gSelectTextFieldOnFocus == 1;
4168 : }
4169 :
4170 : bool
4171 0 : HTMLInputElement::ShouldPreventDOMActivateDispatch(EventTarget* aOriginalTarget)
4172 : {
4173 : /*
4174 : * For the moment, there is only one situation where we actually want to
4175 : * prevent firing a DOMActivate event:
4176 : * - we are a <input type='file'> that just got a click event,
4177 : * - the event was targeted to our button which should have sent a
4178 : * DOMActivate event.
4179 : */
4180 :
4181 0 : if (mType != NS_FORM_INPUT_FILE) {
4182 0 : return false;
4183 : }
4184 :
4185 0 : nsCOMPtr<nsIContent> target = do_QueryInterface(aOriginalTarget);
4186 0 : if (!target) {
4187 0 : return false;
4188 : }
4189 :
4190 0 : return target->GetParent() == this &&
4191 0 : target->IsRootOfNativeAnonymousSubtree() &&
4192 0 : target->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
4193 0 : nsGkAtoms::button, eCaseMatters);
4194 : }
4195 :
4196 : nsresult
4197 4 : HTMLInputElement::MaybeInitPickers(EventChainPostVisitor& aVisitor)
4198 : {
4199 : // Open a file picker when we receive a click on a <input type='file'>, or
4200 : // open a color picker when we receive a click on a <input type='color'>.
4201 : // A click is handled in the following cases:
4202 : // - preventDefault() has not been called (or something similar);
4203 : // - it's the left mouse button.
4204 : // We do not prevent non-trusted click because authors can already use
4205 : // .click(). However, the pickers will follow the rules of popup-blocking.
4206 4 : if (aVisitor.mEvent->DefaultPrevented()) {
4207 0 : return NS_OK;
4208 : }
4209 4 : WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
4210 4 : if (!(mouseEvent && mouseEvent->IsLeftClickEvent())) {
4211 4 : return NS_OK;
4212 : }
4213 0 : if (mType == NS_FORM_INPUT_FILE) {
4214 : // If the user clicked on the "Choose folder..." button we open the
4215 : // directory picker, else we open the file picker.
4216 0 : FilePickerType type = FILE_PICKER_FILE;
4217 : nsCOMPtr<nsIContent> target =
4218 0 : do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
4219 0 : if (target &&
4220 0 : target->FindFirstNonChromeOnlyAccessContent() == this &&
4221 0 : ((IsDirPickerEnabled() && Allowdirs()) ||
4222 0 : (IsWebkitDirPickerEnabled() &&
4223 0 : HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)))) {
4224 0 : type = FILE_PICKER_DIRECTORY;
4225 : }
4226 0 : return InitFilePicker(type);
4227 : }
4228 0 : if (mType == NS_FORM_INPUT_COLOR) {
4229 0 : return InitColorPicker();
4230 : }
4231 :
4232 0 : return NS_OK;
4233 : }
4234 :
4235 : /**
4236 : * Return true if the input event should be ignore because of it's modifiers
4237 : */
4238 : static bool
4239 0 : IgnoreInputEventWithModifier(WidgetInputEvent* aEvent)
4240 : {
4241 0 : return aEvent->IsShift() || aEvent->IsControl() || aEvent->IsAlt() ||
4242 0 : aEvent->IsMeta() || aEvent->IsAltGraph() || aEvent->IsFn() ||
4243 0 : aEvent->IsOS();
4244 : }
4245 :
4246 : nsresult
4247 4 : HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
4248 : {
4249 4 : if (!aVisitor.mPresContext) {
4250 : // Hack alert! In order to open file picker even in case the element isn't
4251 : // in document, try to init picker even without PresContext.
4252 0 : return MaybeInitPickers(aVisitor);
4253 : }
4254 :
4255 8 : if (aVisitor.mEvent->mMessage == eFocus ||
4256 4 : aVisitor.mEvent->mMessage == eBlur) {
4257 0 : if (aVisitor.mEvent->mMessage == eBlur) {
4258 0 : if (mIsDraggingRange) {
4259 0 : FinishRangeThumbDrag();
4260 0 : } else if (mNumberControlSpinnerIsSpinning) {
4261 0 : StopNumberControlSpinnerSpin();
4262 : }
4263 : }
4264 :
4265 0 : UpdateValidityUIBits(aVisitor.mEvent->mMessage == eFocus);
4266 :
4267 0 : UpdateState(true);
4268 : }
4269 :
4270 4 : nsresult rv = NS_OK;
4271 4 : bool outerActivateEvent = !!(aVisitor.mItemFlags & NS_OUTER_ACTIVATE_EVENT);
4272 : bool originalCheckedValue =
4273 4 : !!(aVisitor.mItemFlags & NS_ORIGINAL_CHECKED_VALUE);
4274 4 : bool noContentDispatch = !!(aVisitor.mItemFlags & NS_NO_CONTENT_DISPATCH);
4275 4 : uint8_t oldType = NS_CONTROL_TYPE(aVisitor.mItemFlags);
4276 :
4277 : // Ideally we would make the default action for click and space just dispatch
4278 : // DOMActivate, and the default action for DOMActivate flip the checkbox/
4279 : // radio state and fire onchange. However, for backwards compatibility, we
4280 : // need to flip the state before firing click, and we need to fire click
4281 : // when space is pressed. So, we just nest the firing of DOMActivate inside
4282 : // the click event handling, and allow cancellation of DOMActivate to cancel
4283 : // the click.
4284 12 : if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault &&
4285 4 : !IsSingleLineTextControl(true) &&
4286 0 : mType != NS_FORM_INPUT_NUMBER) {
4287 0 : WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
4288 0 : if (mouseEvent && mouseEvent->IsLeftClickEvent() &&
4289 0 : !ShouldPreventDOMActivateDispatch(aVisitor.mEvent->mOriginalTarget)) {
4290 : // DOMActive event should be trusted since the activation is actually
4291 : // occurred even if the cause is an untrusted click event.
4292 0 : InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent);
4293 0 : actEvent.mDetail = 1;
4294 :
4295 0 : nsCOMPtr<nsIPresShell> shell = aVisitor.mPresContext->GetPresShell();
4296 0 : if (shell) {
4297 0 : nsEventStatus status = nsEventStatus_eIgnore;
4298 0 : mInInternalActivate = true;
4299 0 : rv = shell->HandleDOMEventWithTarget(this, &actEvent, &status);
4300 0 : mInInternalActivate = false;
4301 :
4302 : // If activate is cancelled, we must do the same as when click is
4303 : // cancelled (revert the checkbox to its original value).
4304 0 : if (status == nsEventStatus_eConsumeNoDefault) {
4305 0 : aVisitor.mEventStatus = status;
4306 : }
4307 : }
4308 : }
4309 : }
4310 :
4311 4 : if (outerActivateEvent) {
4312 0 : switch(oldType) {
4313 : case NS_FORM_INPUT_SUBMIT:
4314 : case NS_FORM_INPUT_IMAGE:
4315 0 : if (mForm) {
4316 : // tell the form that we are about to exit a click handler
4317 : // so the form knows not to defer subsequent submissions
4318 : // the pending ones that were created during the handler
4319 : // will be flushed or forgoten.
4320 0 : mForm->OnSubmitClickEnd();
4321 : }
4322 0 : break;
4323 : default:
4324 0 : break;
4325 : }
4326 : }
4327 :
4328 : // Reset the flag for other content besides this text field
4329 4 : aVisitor.mEvent->mFlags.mNoContentDispatch = noContentDispatch;
4330 :
4331 : // now check to see if the event was "cancelled"
4332 4 : if (mCheckedIsToggled && outerActivateEvent) {
4333 0 : if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
4334 : // if it was cancelled and a radio button, then set the old
4335 : // selected btn to TRUE. if it is a checkbox then set it to its
4336 : // original value
4337 0 : if (oldType == NS_FORM_INPUT_RADIO) {
4338 : nsCOMPtr<nsIDOMHTMLInputElement> selectedRadioButton =
4339 0 : do_QueryInterface(aVisitor.mItemData);
4340 0 : if (selectedRadioButton) {
4341 0 : selectedRadioButton->SetChecked(true);
4342 : }
4343 : // If there was no checked radio button or this one is no longer a
4344 : // radio button we must reset it back to false to cancel the action.
4345 : // See how the web of hack grows?
4346 0 : if (!selectedRadioButton || mType != NS_FORM_INPUT_RADIO) {
4347 0 : DoSetChecked(false, true, true);
4348 : }
4349 0 : } else if (oldType == NS_FORM_INPUT_CHECKBOX) {
4350 : bool originalIndeterminateValue =
4351 0 : !!(aVisitor.mItemFlags & NS_ORIGINAL_INDETERMINATE_VALUE);
4352 0 : SetIndeterminateInternal(originalIndeterminateValue, false);
4353 0 : DoSetChecked(originalCheckedValue, true, true);
4354 : }
4355 : } else {
4356 : // Fire input event and then change event.
4357 : nsContentUtils::DispatchTrustedEvent<InternalEditorInputEvent>
4358 0 : (OwnerDoc(), static_cast<nsIDOMHTMLInputElement*>(this),
4359 0 : eEditorInput, true, false);
4360 :
4361 : nsContentUtils::DispatchTrustedEvent<WidgetEvent>
4362 0 : (OwnerDoc(), static_cast<nsIDOMHTMLInputElement*>(this),
4363 0 : eFormChange, true, false);
4364 : #ifdef ACCESSIBILITY
4365 : // Fire an event to notify accessibility
4366 0 : if (mType == NS_FORM_INPUT_CHECKBOX) {
4367 0 : FireEventForAccessibility(this, aVisitor.mPresContext,
4368 0 : eFormCheckboxStateChange);
4369 : } else {
4370 0 : FireEventForAccessibility(this, aVisitor.mPresContext,
4371 0 : eFormRadioStateChange);
4372 : // Fire event for the previous selected radio.
4373 : nsCOMPtr<nsIDOMHTMLInputElement> previous =
4374 0 : do_QueryInterface(aVisitor.mItemData);
4375 0 : if (previous) {
4376 0 : FireEventForAccessibility(previous, aVisitor.mPresContext,
4377 0 : eFormRadioStateChange);
4378 : }
4379 : }
4380 : #endif
4381 : }
4382 : }
4383 :
4384 4 : if (NS_SUCCEEDED(rv)) {
4385 4 : WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
4386 8 : if (mType == NS_FORM_INPUT_NUMBER &&
4387 0 : keyEvent && keyEvent->mMessage == eKeyPress &&
4388 0 : aVisitor.mEvent->IsTrusted() &&
4389 4 : (keyEvent->mKeyCode == NS_VK_UP || keyEvent->mKeyCode == NS_VK_DOWN) &&
4390 0 : !IgnoreInputEventWithModifier(keyEvent)) {
4391 : // We handle the up/down arrow keys specially for <input type=number>.
4392 : // On some platforms the editor for the nested text control will
4393 : // process these keys to send the cursor to the start/end of the text
4394 : // control and as a result aVisitor.mEventStatus will already have been
4395 : // set to nsEventStatus_eConsumeNoDefault. However, we know that
4396 : // whenever the up/down arrow keys cause the value of the number
4397 : // control to change the string in the text control will change, and
4398 : // the cursor will be moved to the end of the text control, overwriting
4399 : // the editor's handling of up/down keypress events. For that reason we
4400 : // just ignore aVisitor.mEventStatus here and go ahead and handle the
4401 : // event to increase/decrease the value of the number control.
4402 0 : if (!aVisitor.mEvent->DefaultPreventedByContent() && IsMutable()) {
4403 0 : StepNumberControlForUserEvent(keyEvent->mKeyCode == NS_VK_UP ? 1 : -1);
4404 0 : FireChangeEventIfNeeded();
4405 0 : aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
4406 : }
4407 4 : } else if (nsEventStatus_eIgnore == aVisitor.mEventStatus) {
4408 4 : switch (aVisitor.mEvent->mMessage) {
4409 : case eFocus: {
4410 : // see if we should select the contents of the textbox. This happens
4411 : // for text and password fields when the field was focused by the
4412 : // keyboard or a navigation, the platform allows it, and it wasn't
4413 : // just because we raised a window.
4414 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
4415 0 : if (fm && IsSingleLineTextControl(false) &&
4416 0 : !aVisitor.mEvent->AsFocusEvent()->mFromRaise &&
4417 0 : SelectTextFieldOnFocus()) {
4418 0 : nsIDocument* document = GetComposedDoc();
4419 0 : if (document) {
4420 : uint32_t lastFocusMethod;
4421 0 : fm->GetLastFocusMethod(document->GetWindow(), &lastFocusMethod);
4422 0 : if (lastFocusMethod &
4423 : (nsIFocusManager::FLAG_BYKEY | nsIFocusManager::FLAG_BYMOVEFOCUS)) {
4424 : RefPtr<nsPresContext> presContext =
4425 0 : GetPresContext(eForComposedDoc);
4426 0 : if (DispatchSelectEvent(presContext)) {
4427 0 : SelectAll(presContext);
4428 : }
4429 : }
4430 : }
4431 : }
4432 0 : break;
4433 : }
4434 :
4435 : case eKeyPress:
4436 : case eKeyUp:
4437 : {
4438 : // For backwards compat, trigger checks/radios/buttons with
4439 : // space or enter (bug 25300)
4440 0 : WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
4441 0 : if ((aVisitor.mEvent->mMessage == eKeyPress &&
4442 0 : keyEvent->mKeyCode == NS_VK_RETURN) ||
4443 0 : (aVisitor.mEvent->mMessage == eKeyUp &&
4444 0 : keyEvent->mKeyCode == NS_VK_SPACE)) {
4445 0 : switch(mType) {
4446 : case NS_FORM_INPUT_CHECKBOX:
4447 : case NS_FORM_INPUT_RADIO:
4448 : {
4449 : // Checkbox and Radio try to submit on Enter press
4450 0 : if (keyEvent->mKeyCode != NS_VK_SPACE) {
4451 0 : MaybeSubmitForm(aVisitor.mPresContext);
4452 :
4453 0 : break; // If we are submitting, do not send click event
4454 : }
4455 : // else fall through and treat Space like click...
4456 : MOZ_FALLTHROUGH;
4457 : }
4458 : case NS_FORM_INPUT_BUTTON:
4459 : case NS_FORM_INPUT_RESET:
4460 : case NS_FORM_INPUT_SUBMIT:
4461 : case NS_FORM_INPUT_IMAGE: // Bug 34418
4462 : case NS_FORM_INPUT_COLOR:
4463 : {
4464 0 : DispatchSimulatedClick(this, aVisitor.mEvent->IsTrusted(),
4465 0 : aVisitor.mPresContext);
4466 0 : aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
4467 : } // case
4468 : } // switch
4469 : }
4470 0 : if (aVisitor.mEvent->mMessage == eKeyPress &&
4471 0 : mType == NS_FORM_INPUT_RADIO && !keyEvent->IsAlt() &&
4472 0 : !keyEvent->IsControl() && !keyEvent->IsMeta()) {
4473 0 : bool isMovingBack = false;
4474 0 : switch (keyEvent->mKeyCode) {
4475 : case NS_VK_UP:
4476 : case NS_VK_LEFT:
4477 0 : isMovingBack = true;
4478 : MOZ_FALLTHROUGH;
4479 : case NS_VK_DOWN:
4480 : case NS_VK_RIGHT:
4481 : // Arrow key pressed, focus+select prev/next radio button
4482 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
4483 0 : if (container) {
4484 0 : nsAutoString name;
4485 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
4486 0 : RefPtr<HTMLInputElement> selectedRadioButton;
4487 0 : container->GetNextRadioButton(name, isMovingBack, this,
4488 0 : getter_AddRefs(selectedRadioButton));
4489 0 : if (selectedRadioButton) {
4490 0 : rv = selectedRadioButton->Focus();
4491 0 : if (NS_SUCCEEDED(rv)) {
4492 0 : rv = DispatchSimulatedClick(selectedRadioButton,
4493 0 : aVisitor.mEvent->IsTrusted(),
4494 0 : aVisitor.mPresContext);
4495 0 : if (NS_SUCCEEDED(rv)) {
4496 0 : aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
4497 : }
4498 : }
4499 : }
4500 : }
4501 : }
4502 : }
4503 :
4504 : /*
4505 : * For some input types, if the user hits enter, the form is submitted.
4506 : *
4507 : * Bug 99920, bug 109463 and bug 147850:
4508 : * (a) if there is a submit control in the form, click the first
4509 : * submit control in the form.
4510 : * (b) if there is just one text control in the form, submit by
4511 : * sending a submit event directly to the form
4512 : * (c) if there is more than one text input and no submit buttons, do
4513 : * not submit, period.
4514 : */
4515 :
4516 0 : if (aVisitor.mEvent->mMessage == eKeyPress &&
4517 0 : keyEvent->mKeyCode == NS_VK_RETURN &&
4518 0 : (IsSingleLineTextControl(false, mType) ||
4519 0 : mType == NS_FORM_INPUT_NUMBER ||
4520 0 : IsExperimentalMobileType(mType) ||
4521 0 : IsDateTimeInputType(mType))) {
4522 0 : FireChangeEventIfNeeded();
4523 0 : rv = MaybeSubmitForm(aVisitor.mPresContext);
4524 0 : NS_ENSURE_SUCCESS(rv, rv);
4525 : }
4526 :
4527 0 : if (aVisitor.mEvent->mMessage == eKeyPress &&
4528 0 : mType == NS_FORM_INPUT_RANGE && !keyEvent->IsAlt() &&
4529 0 : !keyEvent->IsControl() && !keyEvent->IsMeta() &&
4530 0 : (keyEvent->mKeyCode == NS_VK_LEFT ||
4531 0 : keyEvent->mKeyCode == NS_VK_RIGHT ||
4532 0 : keyEvent->mKeyCode == NS_VK_UP ||
4533 0 : keyEvent->mKeyCode == NS_VK_DOWN ||
4534 0 : keyEvent->mKeyCode == NS_VK_PAGE_UP ||
4535 0 : keyEvent->mKeyCode == NS_VK_PAGE_DOWN ||
4536 0 : keyEvent->mKeyCode == NS_VK_HOME ||
4537 0 : keyEvent->mKeyCode == NS_VK_END)) {
4538 0 : Decimal minimum = GetMinimum();
4539 0 : Decimal maximum = GetMaximum();
4540 0 : MOZ_ASSERT(minimum.isFinite() && maximum.isFinite());
4541 0 : if (minimum < maximum) { // else the value is locked to the minimum
4542 0 : Decimal value = GetValueAsDecimal();
4543 0 : Decimal step = GetStep();
4544 0 : if (step == kStepAny) {
4545 0 : step = GetDefaultStep();
4546 : }
4547 0 : MOZ_ASSERT(value.isFinite() && step.isFinite());
4548 0 : Decimal newValue;
4549 0 : switch (keyEvent->mKeyCode) {
4550 : case NS_VK_LEFT:
4551 0 : newValue = value + (GetComputedDirectionality() == eDir_RTL
4552 0 : ? step : -step);
4553 0 : break;
4554 : case NS_VK_RIGHT:
4555 0 : newValue = value + (GetComputedDirectionality() == eDir_RTL
4556 0 : ? -step : step);
4557 0 : break;
4558 : case NS_VK_UP:
4559 : // Even for horizontal range, "up" means "increase"
4560 0 : newValue = value + step;
4561 0 : break;
4562 : case NS_VK_DOWN:
4563 : // Even for horizontal range, "down" means "decrease"
4564 0 : newValue = value - step;
4565 0 : break;
4566 : case NS_VK_HOME:
4567 0 : newValue = minimum;
4568 0 : break;
4569 : case NS_VK_END:
4570 0 : newValue = maximum;
4571 0 : break;
4572 : case NS_VK_PAGE_UP:
4573 : // For PgUp/PgDn we jump 10% of the total range, unless step
4574 : // requires us to jump more.
4575 0 : newValue = value + std::max(step, (maximum - minimum) / Decimal(10));
4576 0 : break;
4577 : case NS_VK_PAGE_DOWN:
4578 0 : newValue = value - std::max(step, (maximum - minimum) / Decimal(10));
4579 0 : break;
4580 : }
4581 0 : SetValueOfRangeForUserEvent(newValue);
4582 0 : FireChangeEventIfNeeded();
4583 0 : aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
4584 : }
4585 : }
4586 :
4587 0 : } break; // eKeyPress || eKeyUp
4588 :
4589 : case eMouseDown:
4590 : case eMouseUp:
4591 : case eMouseDoubleClick: {
4592 : // cancel all of these events for buttons
4593 : //XXXsmaug Why?
4594 0 : WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
4595 0 : if (mouseEvent->button == WidgetMouseEvent::eMiddleButton ||
4596 0 : mouseEvent->button == WidgetMouseEvent::eRightButton) {
4597 0 : if (mType == NS_FORM_INPUT_BUTTON ||
4598 0 : mType == NS_FORM_INPUT_RESET ||
4599 0 : mType == NS_FORM_INPUT_SUBMIT) {
4600 0 : if (aVisitor.mDOMEvent) {
4601 0 : aVisitor.mDOMEvent->StopPropagation();
4602 : } else {
4603 0 : rv = NS_ERROR_FAILURE;
4604 : }
4605 : }
4606 : }
4607 0 : if (mType == NS_FORM_INPUT_NUMBER && aVisitor.mEvent->IsTrusted()) {
4608 0 : if (mouseEvent->button == WidgetMouseEvent::eLeftButton &&
4609 0 : !IgnoreInputEventWithModifier(mouseEvent)) {
4610 : nsNumberControlFrame* numberControlFrame =
4611 0 : do_QueryFrame(GetPrimaryFrame());
4612 0 : if (numberControlFrame) {
4613 0 : if (aVisitor.mEvent->mMessage == eMouseDown &&
4614 0 : IsMutable()) {
4615 0 : switch (numberControlFrame->GetSpinButtonForPointerEvent(
4616 0 : aVisitor.mEvent->AsMouseEvent())) {
4617 : case nsNumberControlFrame::eSpinButtonUp:
4618 0 : StepNumberControlForUserEvent(1);
4619 0 : mNumberControlSpinnerSpinsUp = true;
4620 0 : StartNumberControlSpinnerSpin();
4621 0 : aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
4622 0 : break;
4623 : case nsNumberControlFrame::eSpinButtonDown:
4624 0 : StepNumberControlForUserEvent(-1);
4625 0 : mNumberControlSpinnerSpinsUp = false;
4626 0 : StartNumberControlSpinnerSpin();
4627 0 : aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
4628 0 : break;
4629 : }
4630 : }
4631 : }
4632 : }
4633 0 : if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
4634 : // We didn't handle this to step up/down. Whatever this was, be
4635 : // aggressive about stopping the spin. (And don't set
4636 : // nsEventStatus_eConsumeNoDefault after doing so, since that
4637 : // might prevent, say, the context menu from opening.)
4638 0 : StopNumberControlSpinnerSpin();
4639 : }
4640 : }
4641 0 : break;
4642 : }
4643 : #if !defined(ANDROID) && !defined(XP_MACOSX)
4644 : case eWheel: {
4645 : // Handle wheel events as increasing / decreasing the input element's
4646 : // value when it's focused and it's type is number or range.
4647 0 : WidgetWheelEvent* wheelEvent = aVisitor.mEvent->AsWheelEvent();
4648 0 : if (!aVisitor.mEvent->DefaultPrevented() &&
4649 0 : aVisitor.mEvent->IsTrusted() && IsMutable() && wheelEvent &&
4650 0 : wheelEvent->mDeltaY != 0 &&
4651 0 : wheelEvent->mDeltaMode != nsIDOMWheelEvent::DOM_DELTA_PIXEL) {
4652 0 : if (mType == NS_FORM_INPUT_NUMBER) {
4653 : nsNumberControlFrame* numberControlFrame =
4654 0 : do_QueryFrame(GetPrimaryFrame());
4655 0 : if (numberControlFrame && numberControlFrame->IsFocused()) {
4656 0 : StepNumberControlForUserEvent(wheelEvent->mDeltaY > 0 ? -1 : 1);
4657 0 : FireChangeEventIfNeeded();
4658 0 : aVisitor.mEvent->PreventDefault();
4659 : }
4660 0 : } else if (mType == NS_FORM_INPUT_RANGE &&
4661 0 : nsContentUtils::IsFocusedContent(this) &&
4662 0 : GetMinimum() < GetMaximum()) {
4663 0 : Decimal value = GetValueAsDecimal();
4664 0 : Decimal step = GetStep();
4665 0 : if (step == kStepAny) {
4666 0 : step = GetDefaultStep();
4667 : }
4668 0 : MOZ_ASSERT(value.isFinite() && step.isFinite());
4669 0 : SetValueOfRangeForUserEvent(wheelEvent->mDeltaY < 0 ?
4670 0 : value + step : value - step);
4671 0 : FireChangeEventIfNeeded();
4672 0 : aVisitor.mEvent->PreventDefault();
4673 : }
4674 : }
4675 0 : break;
4676 : }
4677 : #endif
4678 : default:
4679 4 : break;
4680 : }
4681 :
4682 4 : if (outerActivateEvent) {
4683 0 : if (mForm && (oldType == NS_FORM_INPUT_SUBMIT ||
4684 : oldType == NS_FORM_INPUT_IMAGE)) {
4685 0 : if (mType != NS_FORM_INPUT_SUBMIT && mType != NS_FORM_INPUT_IMAGE) {
4686 : // If the type has changed to a non-submit type, then we want to
4687 : // flush the stored submission if there is one (as if the submit()
4688 : // was allowed to succeed)
4689 0 : mForm->FlushPendingSubmission();
4690 : }
4691 : }
4692 0 : switch(mType) {
4693 : case NS_FORM_INPUT_RESET:
4694 : case NS_FORM_INPUT_SUBMIT:
4695 : case NS_FORM_INPUT_IMAGE:
4696 0 : if (mForm) {
4697 : InternalFormEvent event(true,
4698 0 : (mType == NS_FORM_INPUT_RESET) ? eFormReset : eFormSubmit);
4699 0 : event.mOriginator = this;
4700 0 : nsEventStatus status = nsEventStatus_eIgnore;
4701 :
4702 : nsCOMPtr<nsIPresShell> presShell =
4703 0 : aVisitor.mPresContext->GetPresShell();
4704 :
4705 : // If |nsIPresShell::Destroy| has been called due to
4706 : // handling the event the pres context will return a null
4707 : // pres shell. See bug 125624.
4708 : // TODO: removing this code and have the submit event sent by the
4709 : // form, see bug 592124.
4710 0 : if (presShell && (event.mMessage != eFormSubmit ||
4711 0 : mForm->SubmissionCanProceed(this))) {
4712 : // Hold a strong ref while dispatching
4713 0 : RefPtr<mozilla::dom::HTMLFormElement> form(mForm);
4714 0 : presShell->HandleDOMEventWithTarget(form, &event, &status);
4715 0 : aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
4716 : }
4717 : }
4718 0 : break;
4719 :
4720 : default:
4721 0 : break;
4722 : } //switch
4723 : } //click or outer activate event
4724 0 : } else if (outerActivateEvent &&
4725 0 : (oldType == NS_FORM_INPUT_SUBMIT ||
4726 0 : oldType == NS_FORM_INPUT_IMAGE) &&
4727 0 : mForm) {
4728 : // tell the form to flush a possible pending submission.
4729 : // the reason is that the script returned false (the event was
4730 : // not ignored) so if there is a stored submission, it needs to
4731 : // be submitted immediately.
4732 0 : mForm->FlushPendingSubmission();
4733 : }
4734 : } // if
4735 :
4736 4 : if (NS_SUCCEEDED(rv) && mType == NS_FORM_INPUT_RANGE) {
4737 0 : PostHandleEventForRangeThumb(aVisitor);
4738 : }
4739 :
4740 4 : return MaybeInitPickers(aVisitor);
4741 : }
4742 :
4743 : void
4744 0 : HTMLInputElement::PostHandleEventForRangeThumb(EventChainPostVisitor& aVisitor)
4745 : {
4746 0 : MOZ_ASSERT(mType == NS_FORM_INPUT_RANGE);
4747 :
4748 0 : if ((nsEventStatus_eConsumeNoDefault == aVisitor.mEventStatus &&
4749 0 : !MozInputRangeIgnorePreventDefault()) ||
4750 0 : !(aVisitor.mEvent->mClass == eMouseEventClass ||
4751 0 : aVisitor.mEvent->mClass == eTouchEventClass ||
4752 0 : aVisitor.mEvent->mClass == eKeyboardEventClass)) {
4753 0 : return;
4754 : }
4755 :
4756 0 : nsRangeFrame* rangeFrame = do_QueryFrame(GetPrimaryFrame());
4757 0 : if (!rangeFrame && mIsDraggingRange) {
4758 0 : CancelRangeThumbDrag();
4759 0 : return;
4760 : }
4761 :
4762 0 : switch (aVisitor.mEvent->mMessage)
4763 : {
4764 : case eMouseDown:
4765 : case eTouchStart: {
4766 0 : if (mIsDraggingRange) {
4767 0 : break;
4768 : }
4769 0 : if (nsIPresShell::GetCapturingContent()) {
4770 0 : break; // don't start drag if someone else is already capturing
4771 : }
4772 0 : WidgetInputEvent* inputEvent = aVisitor.mEvent->AsInputEvent();
4773 0 : if (IgnoreInputEventWithModifier(inputEvent)) {
4774 0 : break; // ignore
4775 : }
4776 0 : if (aVisitor.mEvent->mMessage == eMouseDown) {
4777 0 : if (aVisitor.mEvent->AsMouseEvent()->buttons ==
4778 : WidgetMouseEvent::eLeftButtonFlag) {
4779 0 : StartRangeThumbDrag(inputEvent);
4780 0 : } else if (mIsDraggingRange) {
4781 0 : CancelRangeThumbDrag();
4782 : }
4783 : } else {
4784 0 : if (aVisitor.mEvent->AsTouchEvent()->mTouches.Length() == 1) {
4785 0 : StartRangeThumbDrag(inputEvent);
4786 0 : } else if (mIsDraggingRange) {
4787 0 : CancelRangeThumbDrag();
4788 : }
4789 : }
4790 0 : aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
4791 0 : } break;
4792 :
4793 : case eMouseMove:
4794 : case eTouchMove:
4795 0 : if (!mIsDraggingRange) {
4796 0 : break;
4797 : }
4798 0 : if (nsIPresShell::GetCapturingContent() != this) {
4799 : // Someone else grabbed capture.
4800 0 : CancelRangeThumbDrag();
4801 0 : break;
4802 : }
4803 : SetValueOfRangeForUserEvent(
4804 0 : rangeFrame->GetValueAtEventPoint(aVisitor.mEvent->AsInputEvent()));
4805 0 : aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
4806 0 : break;
4807 :
4808 : case eMouseUp:
4809 : case eTouchEnd:
4810 0 : if (!mIsDraggingRange) {
4811 0 : break;
4812 : }
4813 : // We don't check to see whether we are the capturing content here and
4814 : // call CancelRangeThumbDrag() if that is the case. We just finish off
4815 : // the drag and set our final value (unless someone has called
4816 : // preventDefault() and prevents us getting here).
4817 0 : FinishRangeThumbDrag(aVisitor.mEvent->AsInputEvent());
4818 0 : aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
4819 0 : break;
4820 :
4821 : case eKeyPress:
4822 0 : if (mIsDraggingRange &&
4823 0 : aVisitor.mEvent->AsKeyboardEvent()->mKeyCode == NS_VK_ESCAPE) {
4824 0 : CancelRangeThumbDrag();
4825 : }
4826 0 : break;
4827 :
4828 : case eTouchCancel:
4829 0 : if (mIsDraggingRange) {
4830 0 : CancelRangeThumbDrag();
4831 : }
4832 0 : break;
4833 :
4834 : default:
4835 0 : break;
4836 : }
4837 : }
4838 :
4839 : void
4840 0 : HTMLInputElement::MaybeLoadImage()
4841 : {
4842 : // Our base URI may have changed; claim that our URI changed, and the
4843 : // nsImageLoadingContent will decide whether a new image load is warranted.
4844 0 : nsAutoString uri;
4845 0 : if (mType == NS_FORM_INPUT_IMAGE &&
4846 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::src, uri) &&
4847 0 : (NS_FAILED(LoadImage(uri, false, true, eImageLoadType_Normal)) ||
4848 0 : !LoadingEnabled())) {
4849 0 : CancelImageRequests(true);
4850 : }
4851 0 : }
4852 :
4853 : nsresult
4854 21 : HTMLInputElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
4855 : nsIContent* aBindingParent,
4856 : bool aCompileEventHandlers)
4857 : {
4858 21 : nsresult rv = nsGenericHTMLFormElementWithState::BindToTree(aDocument, aParent,
4859 : aBindingParent,
4860 21 : aCompileEventHandlers);
4861 21 : NS_ENSURE_SUCCESS(rv, rv);
4862 :
4863 21 : nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
4864 21 : aCompileEventHandlers);
4865 :
4866 21 : if (mType == NS_FORM_INPUT_IMAGE) {
4867 : // Our base URI may have changed; claim that our URI changed, and the
4868 : // nsImageLoadingContent will decide whether a new image load is warranted.
4869 0 : if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
4870 : // Mark channel as urgent-start before load image if the image load is
4871 : // initaiated by a user interaction.
4872 0 : mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
4873 :
4874 : // FIXME: Bug 660963 it would be nice if we could just have
4875 : // ClearBrokenState update our state and do it fast...
4876 0 : ClearBrokenState();
4877 0 : RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
4878 0 : nsContentUtils::AddScriptRunner(
4879 0 : NewRunnableMethod("dom::HTMLInputElement::MaybeLoadImage",
4880 : this,
4881 0 : &HTMLInputElement::MaybeLoadImage));
4882 : }
4883 : }
4884 :
4885 : // Add radio to document if we don't have a form already (if we do it's
4886 : // already been added into that group)
4887 21 : if (aDocument && !mForm && mType == NS_FORM_INPUT_RADIO) {
4888 0 : AddedToRadioGroup();
4889 : }
4890 :
4891 : // Set direction based on value if dir=auto
4892 21 : if (HasDirAuto()) {
4893 0 : SetDirectionFromValue(false);
4894 : }
4895 :
4896 : // An element can't suffer from value missing if it is not in a document.
4897 : // We have to check if we suffer from that as we are now in a document.
4898 21 : UpdateValueMissingValidityState();
4899 :
4900 : // If there is a disabled fieldset in the parent chain, the element is now
4901 : // barred from constraint validation and can't suffer from value missing
4902 : // (call done before).
4903 21 : UpdateBarredFromConstraintValidation();
4904 :
4905 : // And now make sure our state is up to date
4906 21 : UpdateState(false);
4907 :
4908 21 : if (mType == NS_FORM_INPUT_PASSWORD) {
4909 0 : if (IsInComposedDoc()) {
4910 : AsyncEventDispatcher* dispatcher =
4911 : new AsyncEventDispatcher(this,
4912 0 : NS_LITERAL_STRING("DOMInputPasswordAdded"),
4913 : true,
4914 0 : true);
4915 0 : dispatcher->PostDOMEvent();
4916 : }
4917 :
4918 : #ifdef EARLY_BETA_OR_EARLIER
4919 0 : Telemetry::Accumulate(Telemetry::PWMGR_PASSWORD_INPUT_IN_FORM, !!mForm);
4920 : #endif
4921 : }
4922 :
4923 21 : return rv;
4924 : }
4925 :
4926 : void
4927 2 : HTMLInputElement::UnbindFromTree(bool aDeep, bool aNullParent)
4928 : {
4929 : // If we have a form and are unbound from it,
4930 : // nsGenericHTMLFormElementWithState::UnbindFromTree() will unset the form and
4931 : // that takes care of form's WillRemove so we just have to take care
4932 : // of the case where we're removing from the document and we don't
4933 : // have a form
4934 2 : if (!mForm && mType == NS_FORM_INPUT_RADIO) {
4935 0 : WillRemoveFromRadioGroup();
4936 : }
4937 :
4938 2 : nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
4939 2 : nsGenericHTMLFormElementWithState::UnbindFromTree(aDeep, aNullParent);
4940 :
4941 : // GetCurrentDoc is returning nullptr so we can update the value
4942 : // missing validity state to reflect we are no longer into a doc.
4943 2 : UpdateValueMissingValidityState();
4944 : // We might be no longer disabled because of parent chain changed.
4945 2 : UpdateBarredFromConstraintValidation();
4946 :
4947 : // And now make sure our state is up to date
4948 2 : UpdateState(false);
4949 2 : }
4950 :
4951 : void
4952 1 : HTMLInputElement::HandleTypeChange(uint8_t aNewType, bool aNotify)
4953 : {
4954 1 : uint8_t oldType = mType;
4955 1 : MOZ_ASSERT(oldType != aNewType);
4956 :
4957 1 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
4958 1 : if (fm) {
4959 : // Input element can represent very different kinds of UIs, and we may
4960 : // need to flush styling even when focusing the already focused input
4961 : // element.
4962 1 : fm->NeedsFlushBeforeEventHandling(this);
4963 : }
4964 :
4965 1 : if (aNewType == NS_FORM_INPUT_FILE || oldType == NS_FORM_INPUT_FILE) {
4966 0 : if (aNewType == NS_FORM_INPUT_FILE) {
4967 0 : mFileData.reset(new FileData());
4968 : } else {
4969 0 : mFileData->Unlink();
4970 0 : mFileData = nullptr;
4971 : }
4972 : }
4973 :
4974 1 : if (oldType == NS_FORM_INPUT_RANGE && mIsDraggingRange) {
4975 0 : CancelRangeThumbDrag(false);
4976 : }
4977 :
4978 1 : ValueModeType aOldValueMode = GetValueMode();
4979 2 : nsAutoString aOldValue;
4980 :
4981 1 : if (aOldValueMode == VALUE_MODE_VALUE) {
4982 : // Doesn't matter what caller type we pass here, since we know we're not a
4983 : // file input anyway.
4984 1 : GetValue(aOldValue, CallerType::NonSystem);
4985 : }
4986 :
4987 1 : nsTextEditorState::SelectionProperties sp;
4988 :
4989 1 : if (GetEditorState()) {
4990 1 : mInputData.mState->SyncUpSelectionPropertiesBeforeDestruction();
4991 1 : sp = mInputData.mState->GetSelectionProperties();
4992 : }
4993 :
4994 : // We already have a copy of the value, lets free it and changes the type.
4995 1 : FreeData();
4996 1 : mType = aNewType;
4997 1 : void* memory = mInputTypeMem;
4998 1 : mInputType = InputType::Create(this, mType, memory);
4999 :
5000 1 : if (IsSingleLineTextControl()) {
5001 :
5002 1 : mInputData.mState =
5003 1 : nsTextEditorState::Construct(this, &sCachedTextEditorState);
5004 1 : if (!sp.IsDefault()) {
5005 0 : mInputData.mState->SetSelectionProperties(sp);
5006 : }
5007 : }
5008 :
5009 : /**
5010 : * The following code is trying to reproduce the algorithm described here:
5011 : * http://www.whatwg.org/specs/web-apps/current-work/complete.html#input-type-change
5012 : */
5013 1 : switch (GetValueMode()) {
5014 : case VALUE_MODE_DEFAULT:
5015 : case VALUE_MODE_DEFAULT_ON:
5016 : // If the previous value mode was value, we need to set the value content
5017 : // attribute to the previous value.
5018 : // There is no value sanitizing algorithm for elements in this mode.
5019 0 : if (aOldValueMode == VALUE_MODE_VALUE && !aOldValue.IsEmpty()) {
5020 0 : SetAttr(kNameSpaceID_None, nsGkAtoms::value, aOldValue, true);
5021 : }
5022 0 : break;
5023 : case VALUE_MODE_VALUE:
5024 : // If the previous value mode wasn't value, we have to set the value to
5025 : // the value content attribute.
5026 : // SetValueInternal is going to sanitize the value.
5027 : {
5028 2 : nsAutoString value;
5029 1 : if (aOldValueMode != VALUE_MODE_VALUE) {
5030 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::value, value);
5031 : } else {
5032 1 : value = aOldValue;
5033 : }
5034 : // TODO: What should we do if SetValueInternal fails? (The allocation
5035 : // may potentially be big, but most likely we've failed to allocate
5036 : // before the type change.)
5037 1 : SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
5038 : }
5039 1 : break;
5040 : case VALUE_MODE_FILENAME:
5041 : default:
5042 : // We don't care about the value.
5043 : // There is no value sanitizing algorithm for elements in this mode.
5044 0 : break;
5045 : }
5046 :
5047 : // Updating mFocusedValue in consequence:
5048 : // If the new type fires a change event on blur, but the previous type
5049 : // doesn't, we should set mFocusedValue to the current value.
5050 : // Otherwise, if the new type doesn't fire a change event on blur, but the
5051 : // previous type does, we should clear out mFocusedValue.
5052 1 : if (MayFireChangeOnBlur(mType) && !MayFireChangeOnBlur(oldType)) {
5053 0 : GetValue(mFocusedValue, CallerType::System);
5054 1 : } else if (!IsSingleLineTextControl(false, mType) &&
5055 0 : IsSingleLineTextControl(false, oldType)) {
5056 0 : mFocusedValue.Truncate();
5057 : }
5058 :
5059 1 : UpdateHasRange();
5060 :
5061 : // Do not notify, it will be done after if needed.
5062 1 : UpdateAllValidityStates(false);
5063 :
5064 1 : UpdateApzAwareFlag();
5065 :
5066 1 : UpdateBarredFromConstraintValidation();
5067 :
5068 1 : if (oldType == NS_FORM_INPUT_IMAGE) {
5069 : // We're no longer an image input. Cancel our image requests, if we have
5070 : // any.
5071 0 : CancelImageRequests(aNotify);
5072 1 : } else if (aNotify && mType == NS_FORM_INPUT_IMAGE) {
5073 : // We just got switched to be an image input; we should see
5074 : // whether we have an image to load;
5075 0 : nsAutoString src;
5076 0 : if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
5077 : // Mark channel as urgent-start before load image if the image load is
5078 : // initaiated by a user interaction.
5079 0 : mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
5080 :
5081 0 : LoadImage(src, false, aNotify, eImageLoadType_Normal);
5082 : }
5083 : }
5084 :
5085 1 : if (mType == NS_FORM_INPUT_PASSWORD && IsInComposedDoc()) {
5086 : AsyncEventDispatcher* dispatcher =
5087 : new AsyncEventDispatcher(this,
5088 0 : NS_LITERAL_STRING("DOMInputPasswordAdded"),
5089 : true,
5090 0 : true);
5091 0 : dispatcher->PostDOMEvent();
5092 : }
5093 1 : }
5094 :
5095 : void
5096 34 : HTMLInputElement::SanitizeValue(nsAString& aValue)
5097 : {
5098 34 : NS_ASSERTION(mDoneCreating, "The element creation should be finished!");
5099 :
5100 34 : switch (mType) {
5101 : case NS_FORM_INPUT_TEXT:
5102 : case NS_FORM_INPUT_SEARCH:
5103 : case NS_FORM_INPUT_TEL:
5104 : case NS_FORM_INPUT_PASSWORD:
5105 : {
5106 34 : aValue.StripCRLF();
5107 : }
5108 34 : break;
5109 : case NS_FORM_INPUT_EMAIL:
5110 : case NS_FORM_INPUT_URL:
5111 : {
5112 0 : aValue.StripCRLF();
5113 :
5114 0 : aValue = nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(aValue);
5115 : }
5116 0 : break;
5117 : case NS_FORM_INPUT_NUMBER:
5118 : {
5119 0 : Decimal value;
5120 0 : bool ok = mInputType->ConvertStringToNumber(aValue, value);
5121 0 : if (!ok) {
5122 0 : aValue.Truncate();
5123 : }
5124 : }
5125 0 : break;
5126 : case NS_FORM_INPUT_RANGE:
5127 : {
5128 0 : Decimal minimum = GetMinimum();
5129 0 : Decimal maximum = GetMaximum();
5130 0 : MOZ_ASSERT(minimum.isFinite() && maximum.isFinite(),
5131 : "type=range should have a default maximum/minimum");
5132 :
5133 : // We use this to avoid modifying the string unnecessarily, since that
5134 : // may introduce rounding. This is set to true only if the value we
5135 : // parse out from aValue needs to be sanitized.
5136 0 : bool needSanitization = false;
5137 :
5138 0 : Decimal value;
5139 0 : bool ok = mInputType->ConvertStringToNumber(aValue, value);
5140 0 : if (!ok) {
5141 0 : needSanitization = true;
5142 : // Set value to midway between minimum and maximum.
5143 0 : value = maximum <= minimum ? minimum : minimum + (maximum - minimum)/Decimal(2);
5144 0 : } else if (value < minimum || maximum < minimum) {
5145 0 : needSanitization = true;
5146 0 : value = minimum;
5147 0 : } else if (value > maximum) {
5148 0 : needSanitization = true;
5149 0 : value = maximum;
5150 : }
5151 :
5152 0 : Decimal step = GetStep();
5153 0 : if (step != kStepAny) {
5154 0 : Decimal stepBase = GetStepBase();
5155 : // There could be rounding issues below when dealing with fractional
5156 : // numbers, but let's ignore that until ECMAScript supplies us with a
5157 : // decimal number type.
5158 0 : Decimal deltaToStep = NS_floorModulo(value - stepBase, step);
5159 0 : if (deltaToStep != Decimal(0)) {
5160 : // "suffering from a step mismatch"
5161 : // Round the element's value to the nearest number for which the
5162 : // element would not suffer from a step mismatch, and which is
5163 : // greater than or equal to the minimum, and, if the maximum is not
5164 : // less than the minimum, which is less than or equal to the
5165 : // maximum, if there is a number that matches these constraints:
5166 0 : MOZ_ASSERT(deltaToStep > Decimal(0), "stepBelow/stepAbove will be wrong");
5167 0 : Decimal stepBelow = value - deltaToStep;
5168 0 : Decimal stepAbove = value - deltaToStep + step;
5169 0 : Decimal halfStep = step / Decimal(2);
5170 0 : bool stepAboveIsClosest = (stepAbove - value) <= halfStep;
5171 0 : bool stepAboveInRange = stepAbove >= minimum &&
5172 0 : stepAbove <= maximum;
5173 0 : bool stepBelowInRange = stepBelow >= minimum &&
5174 0 : stepBelow <= maximum;
5175 :
5176 0 : if ((stepAboveIsClosest || !stepBelowInRange) && stepAboveInRange) {
5177 0 : needSanitization = true;
5178 0 : value = stepAbove;
5179 0 : } else if ((!stepAboveIsClosest || !stepAboveInRange) && stepBelowInRange) {
5180 0 : needSanitization = true;
5181 0 : value = stepBelow;
5182 : }
5183 : }
5184 : }
5185 :
5186 0 : if (needSanitization) {
5187 : char buf[32];
5188 0 : DebugOnly<bool> ok = value.toString(buf, ArrayLength(buf));
5189 0 : aValue.AssignASCII(buf);
5190 0 : MOZ_ASSERT(ok, "buf not big enough");
5191 : }
5192 : }
5193 0 : break;
5194 : case NS_FORM_INPUT_DATE:
5195 : {
5196 0 : if (!aValue.IsEmpty() && !IsValidDate(aValue)) {
5197 0 : aValue.Truncate();
5198 : }
5199 : }
5200 0 : break;
5201 : case NS_FORM_INPUT_TIME:
5202 : {
5203 0 : if (!aValue.IsEmpty() && !IsValidTime(aValue)) {
5204 0 : aValue.Truncate();
5205 : }
5206 : }
5207 0 : break;
5208 : case NS_FORM_INPUT_MONTH:
5209 : {
5210 0 : if (!aValue.IsEmpty() && !IsValidMonth(aValue)) {
5211 0 : aValue.Truncate();
5212 : }
5213 : }
5214 0 : break;
5215 : case NS_FORM_INPUT_WEEK:
5216 : {
5217 0 : if (!aValue.IsEmpty() && !IsValidWeek(aValue)) {
5218 0 : aValue.Truncate();
5219 : }
5220 : }
5221 0 : break;
5222 : case NS_FORM_INPUT_DATETIME_LOCAL:
5223 : {
5224 0 : if (!aValue.IsEmpty() && !IsValidDateTimeLocal(aValue)) {
5225 0 : aValue.Truncate();
5226 : } else {
5227 0 : NormalizeDateTimeLocal(aValue);
5228 : }
5229 : }
5230 0 : break;
5231 : case NS_FORM_INPUT_COLOR:
5232 : {
5233 0 : if (IsValidSimpleColor(aValue)) {
5234 0 : ToLowerCase(aValue);
5235 : } else {
5236 : // Set default (black) color, if aValue wasn't parsed correctly.
5237 0 : aValue.AssignLiteral("#000000");
5238 : }
5239 : }
5240 0 : break;
5241 : }
5242 34 : }
5243 :
5244 0 : bool HTMLInputElement::IsValidSimpleColor(const nsAString& aValue) const
5245 : {
5246 0 : if (aValue.Length() != 7 || aValue.First() != '#') {
5247 0 : return false;
5248 : }
5249 :
5250 0 : for (int i = 1; i < 7; ++i) {
5251 0 : if (!nsCRT::IsAsciiDigit(aValue[i]) &&
5252 0 : !(aValue[i] >= 'a' && aValue[i] <= 'f') &&
5253 0 : !(aValue[i] >= 'A' && aValue[i] <= 'F')) {
5254 0 : return false;
5255 : }
5256 : }
5257 0 : return true;
5258 : }
5259 :
5260 : bool
5261 0 : HTMLInputElement::IsLeapYear(uint32_t aYear) const
5262 : {
5263 0 : if ((aYear % 4 == 0 && aYear % 100 != 0) || ( aYear % 400 == 0)) {
5264 0 : return true;
5265 : }
5266 0 : return false;
5267 : }
5268 :
5269 : uint32_t
5270 0 : HTMLInputElement::DayOfWeek(uint32_t aYear, uint32_t aMonth, uint32_t aDay,
5271 : bool isoWeek) const
5272 : {
5273 : // Tomohiko Sakamoto algorithm.
5274 0 : int monthTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
5275 0 : aYear -= aMonth < 3;
5276 :
5277 0 : uint32_t day = (aYear + aYear / 4 - aYear / 100 + aYear / 400 +
5278 0 : monthTable[aMonth - 1] + aDay) % 7;
5279 :
5280 0 : if (isoWeek) {
5281 0 : return ((day + 6) % 7) + 1;
5282 : }
5283 :
5284 0 : return day;
5285 : }
5286 :
5287 : uint32_t
5288 0 : HTMLInputElement::MaximumWeekInYear(uint32_t aYear) const
5289 : {
5290 0 : int day = DayOfWeek(aYear, 1, 1, true); // January 1.
5291 : // A year starting on Thursday or a leap year starting on Wednesday has 53
5292 : // weeks. All other years have 52 weeks.
5293 0 : return day == 4 || (day == 3 && IsLeapYear(aYear)) ?
5294 0 : kMaximumWeekInYear : kMaximumWeekInYear - 1;
5295 : }
5296 :
5297 : bool
5298 0 : HTMLInputElement::IsValidWeek(const nsAString& aValue) const
5299 : {
5300 : uint32_t year, week;
5301 0 : return ParseWeek(aValue, &year, &week);
5302 : }
5303 :
5304 : bool
5305 0 : HTMLInputElement::IsValidMonth(const nsAString& aValue) const
5306 : {
5307 : uint32_t year, month;
5308 0 : return ParseMonth(aValue, &year, &month);
5309 : }
5310 :
5311 : bool
5312 0 : HTMLInputElement::IsValidDate(const nsAString& aValue) const
5313 : {
5314 : uint32_t year, month, day;
5315 0 : return ParseDate(aValue, &year, &month, &day);
5316 : }
5317 :
5318 : bool
5319 0 : HTMLInputElement::IsValidDateTimeLocal(const nsAString& aValue) const
5320 : {
5321 : uint32_t year, month, day, time;
5322 0 : return ParseDateTimeLocal(aValue, &year, &month, &day, &time);
5323 : }
5324 :
5325 : bool
5326 0 : HTMLInputElement::ParseYear(const nsAString& aValue, uint32_t* aYear) const
5327 : {
5328 0 : if (aValue.Length() < 4) {
5329 0 : return false;
5330 : }
5331 :
5332 0 : return DigitSubStringToNumber(aValue, 0, aValue.Length(), aYear) &&
5333 0 : *aYear > 0;
5334 : }
5335 :
5336 : bool
5337 0 : HTMLInputElement::ParseMonth(const nsAString& aValue, uint32_t* aYear,
5338 : uint32_t* aMonth) const
5339 : {
5340 : // Parse the year, month values out a string formatted as 'yyyy-mm'.
5341 0 : if (aValue.Length() < 7) {
5342 0 : return false;
5343 : }
5344 :
5345 0 : uint32_t endOfYearOffset = aValue.Length() - 3;
5346 0 : if (aValue[endOfYearOffset] != '-') {
5347 0 : return false;
5348 : }
5349 :
5350 0 : const nsAString& yearStr = Substring(aValue, 0, endOfYearOffset);
5351 0 : if (!ParseYear(yearStr, aYear)) {
5352 0 : return false;
5353 : }
5354 :
5355 0 : return DigitSubStringToNumber(aValue, endOfYearOffset + 1, 2, aMonth) &&
5356 0 : *aMonth > 0 && *aMonth <= 12;
5357 : }
5358 :
5359 : bool
5360 0 : HTMLInputElement::ParseWeek(const nsAString& aValue, uint32_t* aYear,
5361 : uint32_t* aWeek) const
5362 : {
5363 : // Parse the year, month values out a string formatted as 'yyyy-Www'.
5364 0 : if (aValue.Length() < 8) {
5365 0 : return false;
5366 : }
5367 :
5368 0 : uint32_t endOfYearOffset = aValue.Length() - 4;
5369 0 : if (aValue[endOfYearOffset] != '-') {
5370 0 : return false;
5371 : }
5372 :
5373 0 : if (aValue[endOfYearOffset + 1] != 'W') {
5374 0 : return false;
5375 : }
5376 :
5377 0 : const nsAString& yearStr = Substring(aValue, 0, endOfYearOffset);
5378 0 : if (!ParseYear(yearStr, aYear)) {
5379 0 : return false;
5380 : }
5381 :
5382 0 : return DigitSubStringToNumber(aValue, endOfYearOffset + 2, 2, aWeek) &&
5383 0 : *aWeek > 0 && *aWeek <= MaximumWeekInYear(*aYear);
5384 :
5385 : }
5386 :
5387 : bool
5388 0 : HTMLInputElement::ParseDate(const nsAString& aValue, uint32_t* aYear,
5389 : uint32_t* aMonth, uint32_t* aDay) const
5390 : {
5391 : /*
5392 : * Parse the year, month, day values out a date string formatted as 'yyyy-mm-dd'.
5393 : * -The year must be 4 or more digits long, and year > 0
5394 : * -The month must be exactly 2 digits long, and 01 <= month <= 12
5395 : * -The day must be exactly 2 digit long, and 01 <= day <= maxday
5396 : * Where maxday is the number of days in the month 'month' and year 'year'
5397 : */
5398 0 : if (aValue.Length() < 10) {
5399 0 : return false;
5400 : }
5401 :
5402 0 : uint32_t endOfMonthOffset = aValue.Length() - 3;
5403 0 : if (aValue[endOfMonthOffset] != '-') {
5404 0 : return false;
5405 : }
5406 :
5407 0 : const nsAString& yearMonthStr = Substring(aValue, 0, endOfMonthOffset);
5408 0 : if (!ParseMonth(yearMonthStr, aYear, aMonth)) {
5409 0 : return false;
5410 : }
5411 :
5412 0 : return DigitSubStringToNumber(aValue, endOfMonthOffset + 1, 2, aDay) &&
5413 0 : *aDay > 0 && *aDay <= NumberOfDaysInMonth(*aMonth, *aYear);
5414 : }
5415 :
5416 : bool
5417 0 : HTMLInputElement::ParseDateTimeLocal(const nsAString& aValue, uint32_t* aYear,
5418 : uint32_t* aMonth, uint32_t* aDay,
5419 : uint32_t* aTime) const
5420 : {
5421 : // Parse the year, month, day and time values out a string formatted as
5422 : // 'yyyy-mm-ddThh:mm[:ss.s] or 'yyyy-mm-dd hh:mm[:ss.s]', where fractions of
5423 : // seconds can be 1 to 3 digits.
5424 : // The minimum length allowed is 16, which is of the form 'yyyy-mm-ddThh:mm'
5425 : // or 'yyyy-mm-dd hh:mm'.
5426 0 : if (aValue.Length() < 16) {
5427 0 : return false;
5428 : }
5429 :
5430 0 : int32_t sepIndex = aValue.FindChar('T');
5431 0 : if (sepIndex == -1) {
5432 0 : sepIndex = aValue.FindChar(' ');
5433 :
5434 0 : if (sepIndex == -1) {
5435 0 : return false;
5436 : }
5437 : }
5438 :
5439 0 : const nsAString& dateStr = Substring(aValue, 0, sepIndex);
5440 0 : if (!ParseDate(dateStr, aYear, aMonth, aDay)) {
5441 0 : return false;
5442 : }
5443 :
5444 0 : const nsAString& timeStr = Substring(aValue, sepIndex + 1,
5445 0 : aValue.Length() - sepIndex + 1);
5446 0 : if (!ParseTime(timeStr, aTime)) {
5447 0 : return false;
5448 : }
5449 :
5450 0 : return true;
5451 : }
5452 :
5453 : void
5454 0 : HTMLInputElement::NormalizeDateTimeLocal(nsAString& aValue) const
5455 : {
5456 0 : if (aValue.IsEmpty()) {
5457 0 : return;
5458 : }
5459 :
5460 : // Use 'T' as the separator between date string and time string.
5461 0 : int32_t sepIndex = aValue.FindChar(' ');
5462 0 : if (sepIndex != -1) {
5463 0 : aValue.Replace(sepIndex, 1, NS_LITERAL_STRING("T"));
5464 : } else {
5465 0 : sepIndex = aValue.FindChar('T');
5466 : }
5467 :
5468 : // Time expressed as the shortest possible string, which is hh:mm.
5469 0 : if ((aValue.Length() - sepIndex) == 6) {
5470 0 : return;
5471 : }
5472 :
5473 : // Fractions of seconds part is optional, ommit it if it's 0.
5474 0 : if ((aValue.Length() - sepIndex) > 9) {
5475 0 : const uint32_t millisecSepIndex = sepIndex + 9;
5476 : uint32_t milliseconds;
5477 0 : if (!DigitSubStringToNumber(aValue, millisecSepIndex + 1,
5478 0 : aValue.Length() - (millisecSepIndex + 1),
5479 : &milliseconds)) {
5480 0 : return;
5481 : }
5482 :
5483 0 : if (milliseconds != 0) {
5484 0 : return;
5485 : }
5486 :
5487 0 : aValue.Cut(millisecSepIndex, aValue.Length() - millisecSepIndex);
5488 : }
5489 :
5490 : // Seconds part is optional, ommit it if it's 0.
5491 0 : const uint32_t secondSepIndex = sepIndex + 6;
5492 : uint32_t seconds;
5493 0 : if (!DigitSubStringToNumber(aValue, secondSepIndex + 1,
5494 0 : aValue.Length() - (secondSepIndex + 1),
5495 : &seconds)) {
5496 0 : return;
5497 : }
5498 :
5499 0 : if (seconds != 0) {
5500 0 : return;
5501 : }
5502 :
5503 0 : aValue.Cut(secondSepIndex, aValue.Length() - secondSepIndex);
5504 : }
5505 :
5506 : double
5507 0 : HTMLInputElement::DaysSinceEpochFromWeek(uint32_t aYear, uint32_t aWeek) const
5508 : {
5509 0 : double days = JS::DayFromYear(aYear) + (aWeek - 1) * 7;
5510 0 : uint32_t dayOneIsoWeekday = DayOfWeek(aYear, 1, 1, true);
5511 :
5512 : // If day one of that year is on/before Thursday, we should subtract the
5513 : // days that belong to last year in our first week, otherwise, our first
5514 : // days belong to last year's last week, and we should add those days
5515 : // back.
5516 0 : if (dayOneIsoWeekday <= 4) {
5517 0 : days -= (dayOneIsoWeekday - 1);
5518 : } else {
5519 0 : days += (7 - dayOneIsoWeekday + 1);
5520 : }
5521 :
5522 0 : return days;
5523 : }
5524 :
5525 : uint32_t
5526 0 : HTMLInputElement::NumberOfDaysInMonth(uint32_t aMonth, uint32_t aYear) const
5527 : {
5528 : /*
5529 : * Returns the number of days in a month.
5530 : * Months that are |longMonths| always have 31 days.
5531 : * Months that are not |longMonths| have 30 days except February (month 2).
5532 : * February has 29 days during leap years which are years that are divisible by 400.
5533 : * or divisible by 100 and 4. February has 28 days otherwise.
5534 : */
5535 :
5536 : static const bool longMonths[] = { true, false, true, false, true, false,
5537 : true, true, false, true, false, true };
5538 0 : MOZ_ASSERT(aMonth <= 12 && aMonth > 0);
5539 :
5540 0 : if (longMonths[aMonth-1]) {
5541 0 : return 31;
5542 : }
5543 :
5544 0 : if (aMonth != 2) {
5545 0 : return 30;
5546 : }
5547 :
5548 0 : return IsLeapYear(aYear) ? 29 : 28;
5549 : }
5550 :
5551 : /* static */ bool
5552 0 : HTMLInputElement::DigitSubStringToNumber(const nsAString& aStr,
5553 : uint32_t aStart, uint32_t aLen,
5554 : uint32_t* aRetVal)
5555 : {
5556 0 : MOZ_ASSERT(aStr.Length() > (aStart + aLen - 1));
5557 :
5558 0 : for (uint32_t offset = 0; offset < aLen; ++offset) {
5559 0 : if (!NS_IsAsciiDigit(aStr[aStart + offset])) {
5560 0 : return false;
5561 : }
5562 : }
5563 :
5564 : nsresult ec;
5565 0 : *aRetVal = static_cast<uint32_t>(PromiseFlatString(Substring(aStr, aStart, aLen)).ToInteger(&ec));
5566 :
5567 0 : return NS_SUCCEEDED(ec);
5568 : }
5569 :
5570 : bool
5571 0 : HTMLInputElement::IsValidTime(const nsAString& aValue) const
5572 : {
5573 0 : return ParseTime(aValue, nullptr);
5574 : }
5575 :
5576 : /* static */ bool
5577 0 : HTMLInputElement::ParseTime(const nsAString& aValue, uint32_t* aResult)
5578 : {
5579 : /* The string must have the following parts:
5580 : * - HOURS: two digits, value being in [0, 23];
5581 : * - Colon (:);
5582 : * - MINUTES: two digits, value being in [0, 59];
5583 : * - Optional:
5584 : * - Colon (:);
5585 : * - SECONDS: two digits, value being in [0, 59];
5586 : * - Optional:
5587 : * - DOT (.);
5588 : * - FRACTIONAL SECONDS: one to three digits, no value range.
5589 : */
5590 :
5591 : // The following format is the shorter one allowed: "HH:MM".
5592 0 : if (aValue.Length() < 5) {
5593 0 : return false;
5594 : }
5595 :
5596 : uint32_t hours;
5597 0 : if (!DigitSubStringToNumber(aValue, 0, 2, &hours) || hours > 23) {
5598 0 : return false;
5599 : }
5600 :
5601 : // Hours/minutes separator.
5602 0 : if (aValue[2] != ':') {
5603 0 : return false;
5604 : }
5605 :
5606 : uint32_t minutes;
5607 0 : if (!DigitSubStringToNumber(aValue, 3, 2, &minutes) || minutes > 59) {
5608 0 : return false;
5609 : }
5610 :
5611 0 : if (aValue.Length() == 5) {
5612 0 : if (aResult) {
5613 0 : *aResult = ((hours * 60) + minutes) * 60000;
5614 : }
5615 0 : return true;
5616 : }
5617 :
5618 : // The following format is the next shorter one: "HH:MM:SS".
5619 0 : if (aValue.Length() < 8 || aValue[5] != ':') {
5620 0 : return false;
5621 : }
5622 :
5623 : uint32_t seconds;
5624 0 : if (!DigitSubStringToNumber(aValue, 6, 2, &seconds) || seconds > 59) {
5625 0 : return false;
5626 : }
5627 :
5628 0 : if (aValue.Length() == 8) {
5629 0 : if (aResult) {
5630 0 : *aResult = (((hours * 60) + minutes) * 60 + seconds) * 1000;
5631 : }
5632 0 : return true;
5633 : }
5634 :
5635 : // The string must follow this format now: "HH:MM:SS.{s,ss,sss}".
5636 : // There can be 1 to 3 digits for the fractions of seconds.
5637 0 : if (aValue.Length() == 9 || aValue.Length() > 12 || aValue[8] != '.') {
5638 0 : return false;
5639 : }
5640 :
5641 : uint32_t fractionsSeconds;
5642 0 : if (!DigitSubStringToNumber(aValue, 9, aValue.Length() - 9, &fractionsSeconds)) {
5643 0 : return false;
5644 : }
5645 :
5646 0 : if (aResult) {
5647 0 : *aResult = (((hours * 60) + minutes) * 60 + seconds) * 1000 +
5648 : // NOTE: there is 10.0 instead of 10 and static_cast<int> because
5649 : // some old [and stupid] compilers can't just do the right thing.
5650 0 : fractionsSeconds * pow(10.0, static_cast<int>(3 - (aValue.Length() - 9)));
5651 : }
5652 :
5653 0 : return true;
5654 : }
5655 :
5656 : /* static */ bool
5657 0 : HTMLInputElement::IsDateTimeTypeSupported(uint8_t aDateTimeInputType)
5658 : {
5659 0 : return ((aDateTimeInputType == NS_FORM_INPUT_DATE ||
5660 0 : aDateTimeInputType == NS_FORM_INPUT_TIME) &&
5661 0 : (IsInputDateTimeEnabled() || IsExperimentalFormsEnabled())) ||
5662 0 : ((aDateTimeInputType == NS_FORM_INPUT_MONTH ||
5663 0 : aDateTimeInputType == NS_FORM_INPUT_WEEK ||
5664 0 : aDateTimeInputType == NS_FORM_INPUT_DATETIME_LOCAL) &&
5665 0 : IsInputDateTimeOthersEnabled());
5666 : }
5667 :
5668 : /* static */ bool
5669 0 : HTMLInputElement::IsWebkitDirPickerEnabled()
5670 : {
5671 : static bool sWebkitDirPickerEnabled = false;
5672 : static bool sWebkitDirPickerPrefCached = false;
5673 0 : if (!sWebkitDirPickerPrefCached) {
5674 0 : sWebkitDirPickerPrefCached = true;
5675 : Preferences::AddBoolVarCache(&sWebkitDirPickerEnabled,
5676 : "dom.webkitBlink.dirPicker.enabled",
5677 0 : false);
5678 : }
5679 :
5680 0 : return sWebkitDirPickerEnabled;
5681 : }
5682 :
5683 : /* static */ bool
5684 0 : HTMLInputElement::IsWebkitFileSystemEnabled()
5685 : {
5686 : static bool sWebkitFileSystemEnabled = false;
5687 : static bool sWebkitFileSystemPrefCached = false;
5688 0 : if (!sWebkitFileSystemPrefCached) {
5689 0 : sWebkitFileSystemPrefCached = true;
5690 : Preferences::AddBoolVarCache(&sWebkitFileSystemEnabled,
5691 : "dom.webkitBlink.filesystem.enabled",
5692 0 : false);
5693 : }
5694 :
5695 0 : return sWebkitFileSystemEnabled;
5696 : }
5697 :
5698 : /* static */ bool
5699 0 : HTMLInputElement::IsDirPickerEnabled()
5700 : {
5701 : static bool sDirPickerEnabled = false;
5702 : static bool sDirPickerPrefCached = false;
5703 0 : if (!sDirPickerPrefCached) {
5704 0 : sDirPickerPrefCached = true;
5705 : Preferences::AddBoolVarCache(&sDirPickerEnabled, "dom.input.dirpicker",
5706 0 : false);
5707 : }
5708 :
5709 0 : return sDirPickerEnabled;
5710 : }
5711 :
5712 : /* static */ bool
5713 1 : HTMLInputElement::IsExperimentalFormsEnabled()
5714 : {
5715 : static bool sExperimentalFormsEnabled = false;
5716 : static bool sExperimentalFormsPrefCached = false;
5717 1 : if (!sExperimentalFormsPrefCached) {
5718 1 : sExperimentalFormsPrefCached = true;
5719 : Preferences::AddBoolVarCache(&sExperimentalFormsEnabled,
5720 : "dom.experimental_forms",
5721 1 : false);
5722 : }
5723 :
5724 1 : return sExperimentalFormsEnabled;
5725 : }
5726 :
5727 : /* static */ bool
5728 1 : HTMLInputElement::IsInputDateTimeEnabled()
5729 : {
5730 : static bool sDateTimeEnabled = false;
5731 : static bool sDateTimePrefCached = false;
5732 1 : if (!sDateTimePrefCached) {
5733 1 : sDateTimePrefCached = true;
5734 : Preferences::AddBoolVarCache(&sDateTimeEnabled, "dom.forms.datetime",
5735 1 : false);
5736 : }
5737 :
5738 1 : return sDateTimeEnabled;
5739 : }
5740 :
5741 : /* static */ bool
5742 0 : HTMLInputElement::IsInputDateTimeOthersEnabled()
5743 : {
5744 : static bool sDateTimeOthersEnabled = false;
5745 : static bool sDateTimeOthersPrefCached = false;
5746 0 : if (!sDateTimeOthersPrefCached) {
5747 0 : sDateTimeOthersPrefCached = true;
5748 : Preferences::AddBoolVarCache(&sDateTimeOthersEnabled,
5749 0 : "dom.forms.datetime.others", false);
5750 : }
5751 :
5752 0 : return sDateTimeOthersEnabled;
5753 : }
5754 :
5755 : /* static */ bool
5756 0 : HTMLInputElement::IsInputNumberEnabled()
5757 : {
5758 : static bool sInputNumberEnabled = false;
5759 : static bool sInputNumberPrefCached = false;
5760 0 : if (!sInputNumberPrefCached) {
5761 0 : sInputNumberPrefCached = true;
5762 : Preferences::AddBoolVarCache(&sInputNumberEnabled, "dom.forms.number",
5763 0 : false);
5764 : }
5765 :
5766 0 : return sInputNumberEnabled;
5767 : }
5768 :
5769 : /* static */ bool
5770 0 : HTMLInputElement::IsInputColorEnabled()
5771 : {
5772 : static bool sInputColorEnabled = false;
5773 : static bool sInputColorPrefCached = false;
5774 0 : if (!sInputColorPrefCached) {
5775 0 : sInputColorPrefCached = true;
5776 : Preferences::AddBoolVarCache(&sInputColorEnabled, "dom.forms.color",
5777 0 : false);
5778 : }
5779 :
5780 0 : return sInputColorEnabled;
5781 : }
5782 :
5783 : bool
5784 25 : HTMLInputElement::ParseAttribute(int32_t aNamespaceID,
5785 : nsIAtom* aAttribute,
5786 : const nsAString& aValue,
5787 : nsAttrValue& aResult)
5788 : {
5789 : // We can't make these static_asserts because kInputDefaultType and
5790 : // kInputTypeTable aren't constexpr.
5791 25 : MOZ_ASSERT(kInputDefaultType->value == NS_FORM_INPUT_TEXT,
5792 : "Someone forgot to update kInputDefaultType when adding a new "
5793 : "input type.");
5794 25 : MOZ_ASSERT(kInputTypeTable[ArrayLength(kInputTypeTable) - 1].tag == nullptr,
5795 : "Last entry in the table must be the nullptr guard");
5796 25 : MOZ_ASSERT(kInputTypeTable[ArrayLength(kInputTypeTable) - 2].value ==
5797 : NS_FORM_INPUT_TEXT,
5798 : "Next to last entry in the table must be the \"text\" entry");
5799 :
5800 25 : if (aNamespaceID == kNameSpaceID_None) {
5801 18 : if (aAttribute == nsGkAtoms::type) {
5802 1 : aResult.ParseEnumValue(aValue, kInputTypeTable, false, kInputDefaultType);
5803 1 : int32_t newType = aResult.GetEnumValue();
5804 1 : if ((newType == NS_FORM_INPUT_NUMBER && !IsInputNumberEnabled()) ||
5805 2 : (newType == NS_FORM_INPUT_COLOR && !IsInputColorEnabled()) ||
5806 1 : (IsDateTimeInputType(newType) && !IsDateTimeTypeSupported(newType))) {
5807 : // There's no public way to set an nsAttrValue to an enum value, but we
5808 : // can just re-parse with a table that doesn't have any types other than
5809 : // "text" in it.
5810 0 : aResult.ParseEnumValue(aValue, kInputDefaultType, false, kInputDefaultType);
5811 : }
5812 :
5813 1 : return true;
5814 : }
5815 17 : if (aAttribute == nsGkAtoms::width) {
5816 0 : return aResult.ParseSpecialIntValue(aValue);
5817 : }
5818 17 : if (aAttribute == nsGkAtoms::height) {
5819 0 : return aResult.ParseSpecialIntValue(aValue);
5820 : }
5821 17 : if (aAttribute == nsGkAtoms::maxlength) {
5822 0 : return aResult.ParseNonNegativeIntValue(aValue);
5823 : }
5824 17 : if (aAttribute == nsGkAtoms::minlength) {
5825 0 : return aResult.ParseNonNegativeIntValue(aValue);
5826 : }
5827 17 : if (aAttribute == nsGkAtoms::size) {
5828 0 : return aResult.ParsePositiveIntValue(aValue);
5829 : }
5830 17 : if (aAttribute == nsGkAtoms::border) {
5831 0 : return aResult.ParseIntWithBounds(aValue, 0);
5832 : }
5833 17 : if (aAttribute == nsGkAtoms::align) {
5834 0 : return ParseAlignValue(aValue, aResult);
5835 : }
5836 17 : if (aAttribute == nsGkAtoms::formmethod) {
5837 0 : return aResult.ParseEnumValue(aValue, kFormMethodTable, false);
5838 : }
5839 17 : if (aAttribute == nsGkAtoms::formenctype) {
5840 0 : return aResult.ParseEnumValue(aValue, kFormEnctypeTable, false);
5841 : }
5842 17 : if (aAttribute == nsGkAtoms::autocomplete) {
5843 0 : aResult.ParseAtomArray(aValue);
5844 0 : return true;
5845 : }
5846 17 : if (aAttribute == nsGkAtoms::inputmode) {
5847 2 : return aResult.ParseEnumValue(aValue, kInputInputmodeTable, false);
5848 : }
5849 15 : if (ParseImageAttribute(aAttribute, aValue, aResult)) {
5850 : // We have to call |ParseImageAttribute| unconditionally since we
5851 : // don't know if we're going to have a type="image" attribute yet,
5852 : // (or could have it set dynamically in the future). See bug
5853 : // 214077.
5854 0 : return true;
5855 : }
5856 : }
5857 :
5858 22 : return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
5859 22 : aResult);
5860 : }
5861 :
5862 : void
5863 33 : HTMLInputElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
5864 : GenericSpecifiedValues* aData)
5865 : {
5866 33 : const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::type);
5867 66 : if (value && value->Type() == nsAttrValue::eEnum &&
5868 33 : value->GetEnumValue() == NS_FORM_INPUT_IMAGE) {
5869 0 : nsGenericHTMLFormElementWithState::MapImageBorderAttributeInto(aAttributes, aData);
5870 0 : nsGenericHTMLFormElementWithState::MapImageMarginAttributeInto(aAttributes, aData);
5871 0 : nsGenericHTMLFormElementWithState::MapImageSizeAttributesInto(aAttributes, aData);
5872 : // Images treat align as "float"
5873 0 : nsGenericHTMLFormElementWithState::MapImageAlignAttributeInto(aAttributes, aData);
5874 : }
5875 :
5876 33 : nsGenericHTMLFormElementWithState::MapCommonAttributesInto(aAttributes, aData);
5877 33 : }
5878 :
5879 : nsChangeHint
5880 0 : HTMLInputElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
5881 : int32_t aModType) const
5882 : {
5883 : nsChangeHint retval =
5884 0 : nsGenericHTMLFormElementWithState::GetAttributeChangeHint(aAttribute, aModType);
5885 0 : if (aAttribute == nsGkAtoms::type ||
5886 : // The presence or absence of the 'directory' attribute determines what
5887 : // buttons we show for type=file.
5888 0 : aAttribute == nsGkAtoms::allowdirs ||
5889 0 : aAttribute == nsGkAtoms::webkitdirectory) {
5890 0 : retval |= nsChangeHint_ReconstructFrame;
5891 0 : } else if (mType == NS_FORM_INPUT_IMAGE &&
5892 0 : (aAttribute == nsGkAtoms::alt ||
5893 0 : aAttribute == nsGkAtoms::value)) {
5894 : // We might need to rebuild our alt text. Just go ahead and
5895 : // reconstruct our frame. This should be quite rare..
5896 0 : retval |= nsChangeHint_ReconstructFrame;
5897 0 : } else if (aAttribute == nsGkAtoms::value) {
5898 0 : retval |= NS_STYLE_HINT_REFLOW;
5899 0 : } else if (aAttribute == nsGkAtoms::size &&
5900 0 : IsSingleLineTextControl(false)) {
5901 0 : retval |= NS_STYLE_HINT_REFLOW;
5902 0 : } else if (PlaceholderApplies() && aAttribute == nsGkAtoms::placeholder) {
5903 0 : retval |= nsChangeHint_ReconstructFrame;
5904 : }
5905 0 : return retval;
5906 : }
5907 :
5908 : NS_IMETHODIMP_(bool)
5909 25 : HTMLInputElement::IsAttributeMapped(const nsIAtom* aAttribute) const
5910 : {
5911 : static const MappedAttributeEntry attributes[] = {
5912 : { &nsGkAtoms::align },
5913 : { &nsGkAtoms::type },
5914 : { nullptr },
5915 : };
5916 :
5917 : static const MappedAttributeEntry* const map[] = {
5918 : attributes,
5919 : sCommonAttributeMap,
5920 : sImageMarginSizeAttributeMap,
5921 : sImageBorderAttributeMap,
5922 : };
5923 :
5924 25 : return FindAttributeDependence(aAttribute, map);
5925 : }
5926 :
5927 : nsMapRuleToAttributesFunc
5928 1 : HTMLInputElement::GetAttributeMappingFunction() const
5929 : {
5930 1 : return &MapAttributesIntoRule;
5931 : }
5932 :
5933 :
5934 : // Directory picking methods:
5935 :
5936 : bool
5937 0 : HTMLInputElement::IsFilesAndDirectoriesSupported() const
5938 : {
5939 : // This method is supposed to return true if a file and directory picker
5940 : // supports the selection of both files and directories *at the same time*.
5941 : // Only Mac currently supports that. We could implement it for Mac, but
5942 : // currently we do not.
5943 0 : return false;
5944 : }
5945 :
5946 : void
5947 0 : HTMLInputElement::ChooseDirectory(ErrorResult& aRv)
5948 : {
5949 0 : if (mType != NS_FORM_INPUT_FILE) {
5950 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
5951 0 : return;
5952 : }
5953 : // Script can call this method directly, so even though we don't show the
5954 : // "Pick Folder..." button on platforms that don't have a directory picker
5955 : // we have to redirect to the file picker here.
5956 : InitFilePicker(
5957 : #if defined(ANDROID) || defined(MOZ_B2G)
5958 : // No native directory picker - redirect to plain file picker
5959 : FILE_PICKER_FILE
5960 : #else
5961 : FILE_PICKER_DIRECTORY
5962 : #endif
5963 0 : );
5964 : }
5965 :
5966 : already_AddRefed<Promise>
5967 0 : HTMLInputElement::GetFilesAndDirectories(ErrorResult& aRv)
5968 : {
5969 0 : if (mType != NS_FORM_INPUT_FILE) {
5970 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
5971 0 : return nullptr;
5972 : }
5973 :
5974 0 : nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
5975 0 : MOZ_ASSERT(global);
5976 0 : if (!global) {
5977 0 : return nullptr;
5978 : }
5979 :
5980 0 : RefPtr<Promise> p = Promise::Create(global, aRv);
5981 0 : if (aRv.Failed()) {
5982 0 : return nullptr;
5983 : }
5984 :
5985 : const nsTArray<OwningFileOrDirectory>& filesAndDirs =
5986 0 : GetFilesOrDirectoriesInternal();
5987 :
5988 0 : Sequence<OwningFileOrDirectory> filesAndDirsSeq;
5989 :
5990 0 : if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(),
5991 0 : mozilla::fallible_t())) {
5992 0 : p->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
5993 0 : return p.forget();
5994 : }
5995 :
5996 0 : for (uint32_t i = 0; i < filesAndDirs.Length(); ++i) {
5997 0 : if (filesAndDirs[i].IsDirectory()) {
5998 0 : RefPtr<Directory> directory = filesAndDirs[i].GetAsDirectory();
5999 :
6000 : // In future we could refactor SetFilePickerFiltersFromAccept to return a
6001 : // semicolon separated list of file extensions and include that in the
6002 : // filter string passed here.
6003 0 : directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive"));
6004 0 : filesAndDirsSeq[i].SetAsDirectory() = directory;
6005 : } else {
6006 0 : MOZ_ASSERT(filesAndDirs[i].IsFile());
6007 :
6008 : // This file was directly selected by the user, so don't filter it.
6009 0 : filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i].GetAsFile();
6010 : }
6011 : }
6012 :
6013 0 : p->MaybeResolve(filesAndDirsSeq);
6014 0 : return p.forget();
6015 : }
6016 :
6017 : already_AddRefed<Promise>
6018 0 : HTMLInputElement::GetFiles(bool aRecursiveFlag, ErrorResult& aRv)
6019 : {
6020 0 : if (mType != NS_FORM_INPUT_FILE) {
6021 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
6022 0 : return nullptr;
6023 : }
6024 :
6025 0 : GetFilesHelper* helper = GetOrCreateGetFilesHelper(aRecursiveFlag, aRv);
6026 0 : if (NS_WARN_IF(aRv.Failed())) {
6027 0 : return nullptr;
6028 : }
6029 0 : MOZ_ASSERT(helper);
6030 :
6031 0 : nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
6032 0 : MOZ_ASSERT(global);
6033 0 : if (!global) {
6034 0 : return nullptr;
6035 : }
6036 :
6037 0 : RefPtr<Promise> p = Promise::Create(global, aRv);
6038 0 : if (aRv.Failed()) {
6039 0 : return nullptr;
6040 : }
6041 :
6042 0 : helper->AddPromise(p);
6043 0 : return p.forget();
6044 : }
6045 :
6046 :
6047 : // Controllers Methods
6048 :
6049 : nsIControllers*
6050 7 : HTMLInputElement::GetControllers(ErrorResult& aRv)
6051 : {
6052 : //XXX: what about type "file"?
6053 7 : if (IsSingleLineTextControl(false))
6054 : {
6055 7 : if (!mControllers)
6056 : {
6057 : nsresult rv;
6058 2 : mControllers = do_CreateInstance(kXULControllersCID, &rv);
6059 2 : if (NS_FAILED(rv)) {
6060 0 : aRv.Throw(rv);
6061 0 : return nullptr;
6062 : }
6063 :
6064 : nsCOMPtr<nsIController>
6065 4 : controller(do_CreateInstance("@mozilla.org/editor/editorcontroller;1",
6066 4 : &rv));
6067 2 : if (NS_FAILED(rv)) {
6068 0 : aRv.Throw(rv);
6069 0 : return nullptr;
6070 : }
6071 :
6072 2 : mControllers->AppendController(controller);
6073 :
6074 4 : controller = do_CreateInstance("@mozilla.org/editor/editingcontroller;1",
6075 2 : &rv);
6076 2 : if (NS_FAILED(rv)) {
6077 0 : aRv.Throw(rv);
6078 0 : return nullptr;
6079 : }
6080 :
6081 2 : mControllers->AppendController(controller);
6082 : }
6083 : }
6084 :
6085 7 : return mControllers;
6086 : }
6087 :
6088 : NS_IMETHODIMP
6089 4 : HTMLInputElement::GetControllers(nsIControllers** aResult)
6090 : {
6091 4 : NS_ENSURE_ARG_POINTER(aResult);
6092 :
6093 8 : ErrorResult rv;
6094 8 : RefPtr<nsIControllers> controller = GetControllers(rv);
6095 4 : controller.forget(aResult);
6096 4 : return rv.StealNSResult();
6097 : }
6098 :
6099 : int32_t
6100 0 : HTMLInputElement::InputTextLength(CallerType aCallerType)
6101 : {
6102 0 : nsAutoString val;
6103 0 : GetValue(val, aCallerType);
6104 0 : return val.Length();
6105 : }
6106 :
6107 : void
6108 0 : HTMLInputElement::SetSelectionRange(uint32_t aSelectionStart,
6109 : uint32_t aSelectionEnd,
6110 : const Optional<nsAString>& aDirection,
6111 : ErrorResult& aRv)
6112 : {
6113 0 : if (!SupportsTextSelection()) {
6114 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
6115 0 : return;
6116 : }
6117 :
6118 0 : nsTextEditorState* state = GetEditorState();
6119 0 : MOZ_ASSERT(state, "SupportsTextSelection() returned true!");
6120 0 : state->SetSelectionRange(aSelectionStart, aSelectionEnd, aDirection, aRv);
6121 : }
6122 :
6123 : void
6124 0 : HTMLInputElement::SetRangeText(const nsAString& aReplacement, ErrorResult& aRv)
6125 : {
6126 0 : if (!SupportsTextSelection()) {
6127 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
6128 0 : return;
6129 : }
6130 :
6131 0 : nsTextEditorState* state = GetEditorState();
6132 0 : MOZ_ASSERT(state, "SupportsTextSelection() returned true!");
6133 0 : state->SetRangeText(aReplacement, aRv);
6134 : }
6135 :
6136 : void
6137 0 : HTMLInputElement::SetRangeText(const nsAString& aReplacement, uint32_t aStart,
6138 : uint32_t aEnd, SelectionMode aSelectMode,
6139 : ErrorResult& aRv)
6140 : {
6141 0 : if (!SupportsTextSelection()) {
6142 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
6143 0 : return;
6144 : }
6145 :
6146 0 : nsTextEditorState* state = GetEditorState();
6147 0 : MOZ_ASSERT(state, "SupportsTextSelection() returned true!");
6148 0 : state->SetRangeText(aReplacement, aStart, aEnd, aSelectMode, aRv);
6149 : }
6150 :
6151 : void
6152 0 : HTMLInputElement::GetValueFromSetRangeText(nsAString& aValue)
6153 : {
6154 0 : GetNonFileValueInternal(aValue);
6155 0 : }
6156 :
6157 : nsresult
6158 0 : HTMLInputElement::SetValueFromSetRangeText(const nsAString& aValue)
6159 : {
6160 : return SetValueInternal(aValue,
6161 : nsTextEditorState::eSetValue_ByContent |
6162 0 : nsTextEditorState::eSetValue_Notify);
6163 : }
6164 :
6165 : Nullable<uint32_t>
6166 0 : HTMLInputElement::GetSelectionStart(ErrorResult& aRv)
6167 : {
6168 0 : if (!SupportsTextSelection()) {
6169 0 : return Nullable<uint32_t>();
6170 : }
6171 :
6172 0 : uint32_t selStart = GetSelectionStartIgnoringType(aRv);
6173 0 : if (aRv.Failed()) {
6174 0 : return Nullable<uint32_t>();
6175 : }
6176 :
6177 0 : return Nullable<uint32_t>(selStart);
6178 : }
6179 :
6180 : uint32_t
6181 0 : HTMLInputElement::GetSelectionStartIgnoringType(ErrorResult& aRv)
6182 : {
6183 : uint32_t selEnd, selStart;
6184 0 : GetSelectionRange(&selStart, &selEnd, aRv);
6185 0 : return selStart;
6186 : }
6187 :
6188 : void
6189 1 : HTMLInputElement::SetSelectionStart(const Nullable<uint32_t>& aSelectionStart,
6190 : ErrorResult& aRv)
6191 : {
6192 1 : if (!SupportsTextSelection()) {
6193 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
6194 0 : return;
6195 : }
6196 :
6197 1 : nsTextEditorState* state = GetEditorState();
6198 1 : MOZ_ASSERT(state, "SupportsTextSelection() returned true!");
6199 1 : state->SetSelectionStart(aSelectionStart, aRv);
6200 : }
6201 :
6202 : Nullable<uint32_t>
6203 0 : HTMLInputElement::GetSelectionEnd(ErrorResult& aRv)
6204 : {
6205 0 : if (!SupportsTextSelection()) {
6206 0 : return Nullable<uint32_t>();
6207 : }
6208 :
6209 0 : uint32_t selEnd = GetSelectionEndIgnoringType(aRv);
6210 0 : if (aRv.Failed()) {
6211 0 : return Nullable<uint32_t>();
6212 : }
6213 :
6214 0 : return Nullable<uint32_t>(selEnd);
6215 : }
6216 :
6217 : uint32_t
6218 0 : HTMLInputElement::GetSelectionEndIgnoringType(ErrorResult& aRv)
6219 : {
6220 : uint32_t selEnd, selStart;
6221 0 : GetSelectionRange(&selStart, &selEnd, aRv);
6222 0 : return selEnd;
6223 : }
6224 :
6225 : void
6226 1 : HTMLInputElement::SetSelectionEnd(const Nullable<uint32_t>& aSelectionEnd,
6227 : ErrorResult& aRv)
6228 : {
6229 1 : if (!SupportsTextSelection()) {
6230 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
6231 0 : return;
6232 : }
6233 :
6234 1 : nsTextEditorState* state = GetEditorState();
6235 1 : MOZ_ASSERT(state, "SupportsTextSelection() returned true!");
6236 1 : state->SetSelectionEnd(aSelectionEnd, aRv);
6237 : }
6238 :
6239 : NS_IMETHODIMP
6240 0 : HTMLInputElement::GetFiles(nsIDOMFileList** aFileList)
6241 : {
6242 0 : RefPtr<FileList> list = GetFiles();
6243 0 : list.forget(aFileList);
6244 0 : return NS_OK;
6245 : }
6246 :
6247 : void
6248 0 : HTMLInputElement::GetSelectionRange(uint32_t* aSelectionStart,
6249 : uint32_t* aSelectionEnd,
6250 : ErrorResult& aRv)
6251 : {
6252 0 : nsTextEditorState* state = GetEditorState();
6253 0 : if (!state) {
6254 : // Not a text control.
6255 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
6256 0 : return;
6257 : }
6258 :
6259 0 : state->GetSelectionRange(aSelectionStart, aSelectionEnd, aRv);
6260 : }
6261 :
6262 : void
6263 0 : HTMLInputElement::GetSelectionDirection(nsAString& aDirection, ErrorResult& aRv)
6264 : {
6265 0 : if (!SupportsTextSelection()) {
6266 0 : aDirection.SetIsVoid(true);
6267 0 : return;
6268 : }
6269 :
6270 0 : nsTextEditorState* state = GetEditorState();
6271 0 : MOZ_ASSERT(state, "SupportsTextSelection came back true!");
6272 0 : state->GetSelectionDirectionString(aDirection, aRv);
6273 : }
6274 :
6275 : void
6276 0 : HTMLInputElement::SetSelectionDirection(const nsAString& aDirection, ErrorResult& aRv)
6277 : {
6278 0 : if (!SupportsTextSelection()) {
6279 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
6280 0 : return;
6281 : }
6282 :
6283 0 : nsTextEditorState* state = GetEditorState();
6284 0 : MOZ_ASSERT(state, "SupportsTextSelection came back true!");
6285 0 : state->SetSelectionDirection(aDirection, aRv);
6286 : }
6287 :
6288 : #ifdef ACCESSIBILITY
6289 : /*static*/ nsresult
6290 0 : FireEventForAccessibility(nsIDOMHTMLInputElement* aTarget,
6291 : nsPresContext* aPresContext,
6292 : EventMessage aEventMessage)
6293 : {
6294 0 : nsCOMPtr<mozilla::dom::Element> element = do_QueryInterface(aTarget);
6295 : return nsContentUtils::DispatchTrustedEvent<WidgetEvent>
6296 0 : (element->OwnerDoc(), aTarget, aEventMessage, true, true);
6297 : }
6298 : #endif
6299 :
6300 : void
6301 8 : HTMLInputElement::UpdateApzAwareFlag()
6302 : {
6303 : #if !defined(ANDROID) && !defined(XP_MACOSX)
6304 8 : if ((mType == NS_FORM_INPUT_NUMBER) || (mType == NS_FORM_INPUT_RANGE)) {
6305 0 : SetMayBeApzAware();
6306 : }
6307 : #endif
6308 8 : }
6309 :
6310 : nsresult
6311 0 : HTMLInputElement::SetDefaultValueAsValue()
6312 : {
6313 0 : NS_ASSERTION(GetValueMode() == VALUE_MODE_VALUE,
6314 : "GetValueMode() should return VALUE_MODE_VALUE!");
6315 :
6316 : // The element has a content attribute value different from it's value when
6317 : // it's in the value mode value.
6318 0 : nsAutoString resetVal;
6319 0 : GetDefaultValue(resetVal);
6320 :
6321 : // SetValueInternal is going to sanitize the value.
6322 0 : return SetValueInternal(resetVal, nsTextEditorState::eSetValue_Internal);
6323 : }
6324 :
6325 : void
6326 0 : HTMLInputElement::SetDirectionFromValue(bool aNotify)
6327 : {
6328 0 : if (IsSingleLineTextControl(true)) {
6329 0 : nsAutoString value;
6330 0 : GetValue(value, CallerType::System);
6331 0 : SetDirectionalityFromValue(this, value, aNotify);
6332 : }
6333 0 : }
6334 :
6335 : NS_IMETHODIMP
6336 0 : HTMLInputElement::Reset()
6337 : {
6338 : // We should be able to reset all dirty flags regardless of the type.
6339 0 : SetCheckedChanged(false);
6340 0 : SetValueChanged(false);
6341 0 : mLastValueChangeWasInteractive = false;
6342 :
6343 0 : switch (GetValueMode()) {
6344 : case VALUE_MODE_VALUE:
6345 0 : return SetDefaultValueAsValue();
6346 : case VALUE_MODE_DEFAULT_ON:
6347 0 : DoSetChecked(DefaultChecked(), true, false);
6348 0 : return NS_OK;
6349 : case VALUE_MODE_FILENAME:
6350 0 : ClearFiles(false);
6351 0 : return NS_OK;
6352 : case VALUE_MODE_DEFAULT:
6353 : default:
6354 0 : return NS_OK;
6355 : }
6356 : }
6357 :
6358 : NS_IMETHODIMP
6359 0 : HTMLInputElement::SubmitNamesValues(HTMLFormSubmission* aFormSubmission)
6360 : {
6361 : // Disabled elements don't submit
6362 : // For type=reset, and type=button, we just never submit, period.
6363 : // For type=image and type=button, we only submit if we were the button
6364 : // pressed
6365 : // For type=radio and type=checkbox, we only submit if checked=true
6366 0 : if (IsDisabled() || mType == NS_FORM_INPUT_RESET ||
6367 0 : mType == NS_FORM_INPUT_BUTTON ||
6368 0 : ((mType == NS_FORM_INPUT_SUBMIT || mType == NS_FORM_INPUT_IMAGE) &&
6369 0 : aFormSubmission->GetOriginatingElement() != this) ||
6370 0 : ((mType == NS_FORM_INPUT_RADIO || mType == NS_FORM_INPUT_CHECKBOX) &&
6371 0 : !mChecked)) {
6372 0 : return NS_OK;
6373 : }
6374 :
6375 : // Get the name
6376 0 : nsAutoString name;
6377 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
6378 :
6379 : // Submit .x, .y for input type=image
6380 0 : if (mType == NS_FORM_INPUT_IMAGE) {
6381 : // Get a property set by the frame to find out where it was clicked.
6382 : nsIntPoint* lastClickedPoint =
6383 0 : static_cast<nsIntPoint*>(GetProperty(nsGkAtoms::imageClickedPoint));
6384 : int32_t x, y;
6385 0 : if (lastClickedPoint) {
6386 : // Convert the values to strings for submission
6387 0 : x = lastClickedPoint->x;
6388 0 : y = lastClickedPoint->y;
6389 : } else {
6390 0 : x = y = 0;
6391 : }
6392 :
6393 0 : nsAutoString xVal, yVal;
6394 0 : xVal.AppendInt(x);
6395 0 : yVal.AppendInt(y);
6396 :
6397 0 : if (!name.IsEmpty()) {
6398 0 : aFormSubmission->AddNameValuePair(name + NS_LITERAL_STRING(".x"), xVal);
6399 0 : aFormSubmission->AddNameValuePair(name + NS_LITERAL_STRING(".y"), yVal);
6400 : } else {
6401 : // If the Image Element has no name, simply return x and y
6402 : // to Nav and IE compatibility.
6403 0 : aFormSubmission->AddNameValuePair(NS_LITERAL_STRING("x"), xVal);
6404 0 : aFormSubmission->AddNameValuePair(NS_LITERAL_STRING("y"), yVal);
6405 : }
6406 :
6407 0 : return NS_OK;
6408 : }
6409 :
6410 : // If name not there, don't submit
6411 0 : if (name.IsEmpty()) {
6412 0 : return NS_OK;
6413 : }
6414 :
6415 : //
6416 : // Submit file if its input type=file and this encoding method accepts files
6417 : //
6418 0 : if (mType == NS_FORM_INPUT_FILE) {
6419 : // Submit files
6420 :
6421 : const nsTArray<OwningFileOrDirectory>& files =
6422 0 : GetFilesOrDirectoriesInternal();
6423 :
6424 0 : if (files.IsEmpty()) {
6425 0 : aFormSubmission->AddNameBlobOrNullPair(name, nullptr);
6426 0 : return NS_OK;
6427 : }
6428 :
6429 0 : for (uint32_t i = 0; i < files.Length(); ++i) {
6430 0 : if (files[i].IsFile()) {
6431 0 : aFormSubmission->AddNameBlobOrNullPair(name, files[i].GetAsFile());
6432 : } else {
6433 0 : MOZ_ASSERT(files[i].IsDirectory());
6434 0 : aFormSubmission->AddNameDirectoryPair(name, files[i].GetAsDirectory());
6435 : }
6436 : }
6437 :
6438 0 : return NS_OK;
6439 : }
6440 :
6441 0 : if (mType == NS_FORM_INPUT_HIDDEN && name.EqualsLiteral("_charset_")) {
6442 0 : nsCString charset;
6443 0 : aFormSubmission->GetCharset(charset);
6444 : return aFormSubmission->AddNameValuePair(name,
6445 0 : NS_ConvertASCIItoUTF16(charset));
6446 : }
6447 :
6448 : //
6449 : // Submit name=value
6450 : //
6451 :
6452 : // Get the value
6453 0 : nsAutoString value;
6454 0 : GetValue(value, CallerType::System);
6455 :
6456 0 : if (mType == NS_FORM_INPUT_SUBMIT && value.IsEmpty() &&
6457 0 : !HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
6458 : // Get our default value, which is the same as our default label
6459 0 : nsXPIDLString defaultValue;
6460 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
6461 0 : "Submit", defaultValue);
6462 0 : value = defaultValue;
6463 : }
6464 :
6465 0 : if (IsSingleLineTextControl(true) &&
6466 0 : name.EqualsLiteral("isindex") &&
6467 0 : aFormSubmission->SupportsIsindexSubmission()) {
6468 0 : return aFormSubmission->AddIsindex(value);
6469 : }
6470 0 : return aFormSubmission->AddNameValuePair(name, value);
6471 : }
6472 :
6473 :
6474 : NS_IMETHODIMP
6475 2 : HTMLInputElement::SaveState()
6476 : {
6477 4 : RefPtr<HTMLInputElementState> inputState;
6478 2 : switch (GetValueMode()) {
6479 : case VALUE_MODE_DEFAULT_ON:
6480 0 : if (mCheckedChanged) {
6481 0 : inputState = new HTMLInputElementState();
6482 0 : inputState->SetChecked(mChecked);
6483 : }
6484 2 : break;
6485 : case VALUE_MODE_FILENAME:
6486 0 : if (!mFileData->mFilesOrDirectories.IsEmpty()) {
6487 0 : inputState = new HTMLInputElementState();
6488 0 : inputState->SetFilesOrDirectories(mFileData->mFilesOrDirectories);
6489 : }
6490 0 : break;
6491 : case VALUE_MODE_VALUE:
6492 : case VALUE_MODE_DEFAULT:
6493 : // VALUE_MODE_DEFAULT shouldn't have their value saved except 'hidden',
6494 : // mType shouldn't be NS_FORM_INPUT_PASSWORD and value should have changed.
6495 4 : if ((GetValueMode() == VALUE_MODE_DEFAULT &&
6496 2 : mType != NS_FORM_INPUT_HIDDEN) ||
6497 6 : mType == NS_FORM_INPUT_PASSWORD || !mValueChanged) {
6498 2 : break;
6499 : }
6500 :
6501 0 : inputState = new HTMLInputElementState();
6502 0 : nsAutoString value;
6503 0 : GetValue(value, CallerType::System);
6504 :
6505 0 : if (!IsSingleLineTextControl(false)) {
6506 : nsresult rv = nsLinebreakConverter::ConvertStringLineBreaks(
6507 : value,
6508 : nsLinebreakConverter::eLinebreakPlatform,
6509 0 : nsLinebreakConverter::eLinebreakContent);
6510 :
6511 0 : if (NS_FAILED(rv)) {
6512 0 : NS_ERROR("Converting linebreaks failed!");
6513 0 : return rv;
6514 : }
6515 : }
6516 :
6517 0 : inputState->SetValue(value);
6518 0 : break;
6519 : }
6520 :
6521 2 : if (inputState) {
6522 0 : nsPresState* state = GetPrimaryPresState();
6523 0 : if (state) {
6524 0 : state->SetStateProperty(inputState);
6525 : }
6526 : }
6527 :
6528 2 : if (mDisabledChanged) {
6529 0 : nsPresState* state = GetPrimaryPresState();
6530 0 : if (state) {
6531 : // We do not want to save the real disabled state but the disabled
6532 : // attribute.
6533 0 : state->SetDisabled(HasAttr(kNameSpaceID_None, nsGkAtoms::disabled));
6534 : }
6535 : }
6536 :
6537 2 : return NS_OK;
6538 : }
6539 :
6540 : void
6541 2 : HTMLInputElement::DoneCreatingElement()
6542 : {
6543 2 : mDoneCreating = true;
6544 :
6545 : //
6546 : // Restore state as needed. Note that disabled state applies to all control
6547 : // types.
6548 : //
6549 : bool restoredCheckedState =
6550 2 : !mInhibitRestoration && NS_SUCCEEDED(GenerateStateKey()) && RestoreFormControlState();
6551 :
6552 : //
6553 : // If restore does not occur, we initialize .checked using the CHECKED
6554 : // property.
6555 : //
6556 2 : if (!restoredCheckedState && mShouldInitChecked) {
6557 0 : DoSetChecked(DefaultChecked(), false, false);
6558 : }
6559 :
6560 : // Sanitize the value.
6561 2 : if (GetValueMode() == VALUE_MODE_VALUE) {
6562 4 : nsAutoString aValue;
6563 2 : GetValue(aValue, CallerType::System);
6564 : // TODO: What should we do if SetValueInternal fails? (The allocation
6565 : // may potentially be big, but most likely we've failed to allocate
6566 : // before the type change.)
6567 2 : SetValueInternal(aValue, nsTextEditorState::eSetValue_Internal);
6568 : }
6569 :
6570 2 : mShouldInitChecked = false;
6571 2 : }
6572 :
6573 : EventStates
6574 60 : HTMLInputElement::IntrinsicState() const
6575 : {
6576 : // If you add states here, and they're type-dependent, you need to add them
6577 : // to the type case in AfterSetAttr.
6578 :
6579 60 : EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
6580 60 : if (mType == NS_FORM_INPUT_CHECKBOX || mType == NS_FORM_INPUT_RADIO) {
6581 : // Check current checked state (:checked)
6582 0 : if (mChecked) {
6583 0 : state |= NS_EVENT_STATE_CHECKED;
6584 : }
6585 :
6586 : // Check current indeterminate state (:indeterminate)
6587 0 : if (mType == NS_FORM_INPUT_CHECKBOX && mIndeterminate) {
6588 0 : state |= NS_EVENT_STATE_INDETERMINATE;
6589 : }
6590 :
6591 0 : if (mType == NS_FORM_INPUT_RADIO) {
6592 0 : nsCOMPtr<nsIDOMHTMLInputElement> selected = GetSelectedRadioButton();
6593 0 : bool indeterminate = !selected && !mChecked;
6594 :
6595 0 : if (indeterminate) {
6596 0 : state |= NS_EVENT_STATE_INDETERMINATE;
6597 : }
6598 : }
6599 :
6600 : // Check whether we are the default checked element (:default)
6601 0 : if (DefaultChecked()) {
6602 0 : state |= NS_EVENT_STATE_DEFAULT;
6603 0 : }
6604 60 : } else if (mType == NS_FORM_INPUT_IMAGE) {
6605 0 : state |= nsImageLoadingContent::ImageState();
6606 : }
6607 :
6608 60 : if (DoesRequiredApply() && HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
6609 0 : state |= NS_EVENT_STATE_REQUIRED;
6610 : } else {
6611 60 : state |= NS_EVENT_STATE_OPTIONAL;
6612 : }
6613 :
6614 60 : if (IsCandidateForConstraintValidation()) {
6615 60 : if (IsValid()) {
6616 60 : state |= NS_EVENT_STATE_VALID;
6617 : } else {
6618 0 : state |= NS_EVENT_STATE_INVALID;
6619 :
6620 0 : if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
6621 0 : (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
6622 0 : (mCanShowInvalidUI && ShouldShowValidityUI()))) {
6623 0 : state |= NS_EVENT_STATE_MOZ_UI_INVALID;
6624 : }
6625 : }
6626 :
6627 : // :-moz-ui-valid applies if all of the following conditions are true:
6628 : // 1. The element is not focused, or had either :-moz-ui-valid or
6629 : // :-moz-ui-invalid applying before it was focused ;
6630 : // 2. The element is either valid or isn't allowed to have
6631 : // :-moz-ui-invalid applying ;
6632 : // 3. The element has no form owner or its form owner doesn't have the
6633 : // novalidate attribute set ;
6634 : // 4. The element has already been modified or the user tried to submit the
6635 : // form owner while invalid.
6636 243 : if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
6637 123 : (mCanShowValidUI && ShouldShowValidityUI() &&
6638 63 : (IsValid() || (!state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
6639 0 : !mCanShowInvalidUI)))) {
6640 3 : state |= NS_EVENT_STATE_MOZ_UI_VALID;
6641 : }
6642 :
6643 : // :in-range and :out-of-range only apply if the element currently has a range
6644 60 : if (mHasRange) {
6645 0 : state |= (GetValidityState(VALIDITY_STATE_RANGE_OVERFLOW) ||
6646 0 : GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW))
6647 0 : ? NS_EVENT_STATE_OUTOFRANGE
6648 0 : : NS_EVENT_STATE_INRANGE;
6649 : }
6650 : }
6651 :
6652 180 : if (PlaceholderApplies() &&
6653 67 : HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
6654 7 : IsValueEmpty()) {
6655 5 : state |= NS_EVENT_STATE_PLACEHOLDERSHOWN;
6656 : }
6657 :
6658 60 : if (mForm && !mForm->GetValidity() && IsSubmitControl()) {
6659 0 : state |= NS_EVENT_STATE_MOZ_SUBMITINVALID;
6660 : }
6661 :
6662 60 : return state;
6663 : }
6664 :
6665 : void
6666 0 : HTMLInputElement::AddStates(EventStates aStates)
6667 : {
6668 0 : if (mType == NS_FORM_INPUT_TEXT) {
6669 0 : EventStates focusStates(aStates & (NS_EVENT_STATE_FOCUS |
6670 0 : NS_EVENT_STATE_FOCUSRING));
6671 0 : if (!focusStates.IsEmpty()) {
6672 0 : HTMLInputElement* ownerNumberControl = GetOwnerNumberControl();
6673 0 : if (ownerNumberControl) {
6674 0 : ownerNumberControl->AddStates(focusStates);
6675 : }
6676 : }
6677 : }
6678 0 : nsGenericHTMLFormElementWithState::AddStates(aStates);
6679 0 : }
6680 :
6681 : void
6682 0 : HTMLInputElement::RemoveStates(EventStates aStates)
6683 : {
6684 0 : if (mType == NS_FORM_INPUT_TEXT) {
6685 0 : EventStates focusStates(aStates & (NS_EVENT_STATE_FOCUS |
6686 0 : NS_EVENT_STATE_FOCUSRING));
6687 0 : if (!focusStates.IsEmpty()) {
6688 0 : HTMLInputElement* ownerNumberControl = GetOwnerNumberControl();
6689 0 : if (ownerNumberControl) {
6690 0 : ownerNumberControl->RemoveStates(focusStates);
6691 : }
6692 : }
6693 : }
6694 0 : nsGenericHTMLFormElementWithState::RemoveStates(aStates);
6695 0 : }
6696 :
6697 : bool
6698 0 : HTMLInputElement::RestoreState(nsPresState* aState)
6699 : {
6700 0 : bool restoredCheckedState = false;
6701 :
6702 : nsCOMPtr<HTMLInputElementState> inputState
6703 0 : (do_QueryInterface(aState->GetStateProperty()));
6704 :
6705 0 : if (inputState) {
6706 0 : switch (GetValueMode()) {
6707 : case VALUE_MODE_DEFAULT_ON:
6708 0 : if (inputState->IsCheckedSet()) {
6709 0 : restoredCheckedState = true;
6710 0 : DoSetChecked(inputState->GetChecked(), true, true);
6711 : }
6712 0 : break;
6713 : case VALUE_MODE_FILENAME:
6714 : {
6715 0 : nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
6716 0 : if (window) {
6717 0 : nsTArray<OwningFileOrDirectory> array;
6718 0 : inputState->GetFilesOrDirectories(window, array);
6719 :
6720 0 : SetFilesOrDirectories(array, true);
6721 : }
6722 : }
6723 0 : break;
6724 : case VALUE_MODE_VALUE:
6725 : case VALUE_MODE_DEFAULT:
6726 0 : if (GetValueMode() == VALUE_MODE_DEFAULT &&
6727 0 : mType != NS_FORM_INPUT_HIDDEN) {
6728 0 : break;
6729 : }
6730 :
6731 : // TODO: What should we do if SetValueInternal fails? (The allocation
6732 : // may potentially be big, but most likely we've failed to allocate
6733 : // before the type change.)
6734 0 : SetValueInternal(inputState->GetValue(),
6735 0 : nsTextEditorState::eSetValue_Notify);
6736 0 : break;
6737 : }
6738 : }
6739 :
6740 0 : if (aState->IsDisabledSet() && !aState->GetDisabled()) {
6741 0 : SetDisabled(false);
6742 : }
6743 :
6744 0 : return restoredCheckedState;
6745 : }
6746 :
6747 : bool
6748 0 : HTMLInputElement::AllowDrop()
6749 : {
6750 : // Allow drop on anything other than file inputs.
6751 :
6752 0 : return mType != NS_FORM_INPUT_FILE;
6753 : }
6754 :
6755 : /*
6756 : * Radio group stuff
6757 : */
6758 :
6759 : void
6760 0 : HTMLInputElement::AddedToRadioGroup()
6761 : {
6762 : // If the element is neither in a form nor a document, there is no group so we
6763 : // should just stop here.
6764 0 : if (!mForm && !IsInUncomposedDoc()) {
6765 0 : return;
6766 : }
6767 :
6768 : // Make sure not to notify if we're still being created
6769 0 : bool notify = mDoneCreating;
6770 :
6771 : //
6772 : // If the input element is checked, and we add it to the group, it will
6773 : // deselect whatever is currently selected in that group
6774 : //
6775 0 : if (mChecked) {
6776 : //
6777 : // If it is checked, call "RadioSetChecked" to perform the selection/
6778 : // deselection ritual. This has the side effect of repainting the
6779 : // radio button, but as adding a checked radio button into the group
6780 : // should not be that common an occurrence, I think we can live with
6781 : // that.
6782 : //
6783 0 : RadioSetChecked(notify);
6784 : }
6785 :
6786 : //
6787 : // For integrity purposes, we have to ensure that "checkedChanged" is
6788 : // the same for this new element as for all the others in the group
6789 : //
6790 0 : bool checkedChanged = mCheckedChanged;
6791 :
6792 : nsCOMPtr<nsIRadioVisitor> visitor =
6793 0 : new nsRadioGetCheckedChangedVisitor(&checkedChanged, this);
6794 0 : VisitGroup(visitor, notify);
6795 :
6796 0 : SetCheckedChangedInternal(checkedChanged);
6797 :
6798 : //
6799 : // Add the radio to the radio group container.
6800 : //
6801 0 : nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
6802 0 : if (container) {
6803 0 : nsAutoString name;
6804 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
6805 0 : container->AddToRadioGroup(name, static_cast<nsIFormControl*>(this));
6806 :
6807 : // We initialize the validity of the element to the validity of the group
6808 : // because we assume UpdateValueMissingState() will be called after.
6809 0 : SetValidityState(VALIDITY_STATE_VALUE_MISSING,
6810 0 : container->GetValueMissingState(name));
6811 : }
6812 : }
6813 :
6814 : void
6815 0 : HTMLInputElement::WillRemoveFromRadioGroup()
6816 : {
6817 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
6818 0 : if (!container) {
6819 0 : return;
6820 : }
6821 :
6822 0 : nsAutoString name;
6823 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
6824 :
6825 : // If this button was checked, we need to notify the group that there is no
6826 : // longer a selected radio button
6827 0 : if (mChecked) {
6828 0 : container->SetCurrentRadioButton(name, nullptr);
6829 :
6830 0 : nsCOMPtr<nsIRadioVisitor> visitor = new nsRadioUpdateStateVisitor(this);
6831 0 : VisitGroup(visitor, true);
6832 : }
6833 :
6834 : // Remove this radio from its group in the container.
6835 : // We need to call UpdateValueMissingValidityStateForRadio before to make sure
6836 : // the group validity is updated (with this element being ignored).
6837 0 : UpdateValueMissingValidityStateForRadio(true);
6838 0 : container->RemoveFromRadioGroup(name, static_cast<nsIFormControl*>(this));
6839 : }
6840 :
6841 : bool
6842 0 : HTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable, int32_t* aTabIndex)
6843 : {
6844 0 : if (nsGenericHTMLFormElementWithState::IsHTMLFocusable(aWithMouse, aIsFocusable,
6845 : aTabIndex))
6846 : {
6847 0 : return true;
6848 : }
6849 :
6850 0 : if (IsDisabled()) {
6851 0 : *aIsFocusable = false;
6852 0 : return true;
6853 : }
6854 :
6855 0 : if (IsSingleLineTextControl(false) ||
6856 0 : mType == NS_FORM_INPUT_RANGE) {
6857 0 : *aIsFocusable = true;
6858 0 : return false;
6859 : }
6860 :
6861 : #ifdef XP_MACOSX
6862 : const bool defaultFocusable = !aWithMouse || nsFocusManager::sMouseFocusesFormControl;
6863 : #else
6864 0 : const bool defaultFocusable = true;
6865 : #endif
6866 :
6867 0 : if (mType == NS_FORM_INPUT_FILE ||
6868 0 : mType == NS_FORM_INPUT_NUMBER ||
6869 0 : mType == NS_FORM_INPUT_TIME ||
6870 0 : mType == NS_FORM_INPUT_DATE) {
6871 0 : if (aTabIndex) {
6872 : // We only want our native anonymous child to be tabable to, not ourself.
6873 0 : *aTabIndex = -1;
6874 : }
6875 0 : if (mType == NS_FORM_INPUT_NUMBER ||
6876 0 : mType == NS_FORM_INPUT_TIME ||
6877 0 : mType == NS_FORM_INPUT_DATE) {
6878 0 : *aIsFocusable = true;
6879 : } else {
6880 0 : *aIsFocusable = defaultFocusable;
6881 : }
6882 0 : return true;
6883 : }
6884 :
6885 0 : if (mType == NS_FORM_INPUT_HIDDEN) {
6886 0 : if (aTabIndex) {
6887 0 : *aTabIndex = -1;
6888 : }
6889 0 : *aIsFocusable = false;
6890 0 : return false;
6891 : }
6892 :
6893 0 : if (!aTabIndex) {
6894 : // The other controls are all focusable
6895 0 : *aIsFocusable = defaultFocusable;
6896 0 : return false;
6897 : }
6898 :
6899 0 : if (mType != NS_FORM_INPUT_RADIO) {
6900 0 : *aIsFocusable = defaultFocusable;
6901 0 : return false;
6902 : }
6903 :
6904 0 : if (mChecked) {
6905 : // Selected radio buttons are tabbable
6906 0 : *aIsFocusable = defaultFocusable;
6907 0 : return false;
6908 : }
6909 :
6910 : // Current radio button is not selected.
6911 : // But make it tabbable if nothing in group is selected.
6912 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
6913 0 : if (!container) {
6914 0 : *aIsFocusable = defaultFocusable;
6915 0 : return false;
6916 : }
6917 :
6918 0 : nsAutoString name;
6919 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
6920 :
6921 0 : if (container->GetCurrentRadioButton(name)) {
6922 0 : *aTabIndex = -1;
6923 : }
6924 0 : *aIsFocusable = defaultFocusable;
6925 0 : return false;
6926 : }
6927 :
6928 : nsresult
6929 0 : HTMLInputElement::VisitGroup(nsIRadioVisitor* aVisitor, bool aFlushContent)
6930 : {
6931 0 : nsIRadioGroupContainer* container = GetRadioGroupContainer();
6932 0 : if (container) {
6933 0 : nsAutoString name;
6934 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
6935 0 : return container->WalkRadioGroup(name, aVisitor, aFlushContent);
6936 : }
6937 :
6938 0 : aVisitor->Visit(this);
6939 0 : return NS_OK;
6940 : }
6941 :
6942 : HTMLInputElement::ValueModeType
6943 90 : HTMLInputElement::GetValueMode() const
6944 : {
6945 90 : switch (mType)
6946 : {
6947 : case NS_FORM_INPUT_HIDDEN:
6948 : case NS_FORM_INPUT_SUBMIT:
6949 : case NS_FORM_INPUT_BUTTON:
6950 : case NS_FORM_INPUT_RESET:
6951 : case NS_FORM_INPUT_IMAGE:
6952 0 : return VALUE_MODE_DEFAULT;
6953 : case NS_FORM_INPUT_CHECKBOX:
6954 : case NS_FORM_INPUT_RADIO:
6955 0 : return VALUE_MODE_DEFAULT_ON;
6956 : case NS_FORM_INPUT_FILE:
6957 0 : return VALUE_MODE_FILENAME;
6958 : #ifdef DEBUG
6959 : case NS_FORM_INPUT_TEXT:
6960 : case NS_FORM_INPUT_PASSWORD:
6961 : case NS_FORM_INPUT_SEARCH:
6962 : case NS_FORM_INPUT_TEL:
6963 : case NS_FORM_INPUT_EMAIL:
6964 : case NS_FORM_INPUT_URL:
6965 : case NS_FORM_INPUT_NUMBER:
6966 : case NS_FORM_INPUT_RANGE:
6967 : case NS_FORM_INPUT_DATE:
6968 : case NS_FORM_INPUT_TIME:
6969 : case NS_FORM_INPUT_COLOR:
6970 : case NS_FORM_INPUT_MONTH:
6971 : case NS_FORM_INPUT_WEEK:
6972 : case NS_FORM_INPUT_DATETIME_LOCAL:
6973 90 : return VALUE_MODE_VALUE;
6974 : default:
6975 0 : NS_NOTYETIMPLEMENTED("Unexpected input type in GetValueMode()");
6976 0 : return VALUE_MODE_VALUE;
6977 : #else // DEBUG
6978 : default:
6979 : return VALUE_MODE_VALUE;
6980 : #endif // DEBUG
6981 : }
6982 : }
6983 :
6984 : bool
6985 0 : HTMLInputElement::IsMutable() const
6986 : {
6987 0 : return !IsDisabled() &&
6988 0 : !(DoesReadOnlyApply() &&
6989 0 : HasAttr(kNameSpaceID_None, nsGkAtoms::readonly));
6990 : }
6991 :
6992 : bool
6993 0 : HTMLInputElement::DoesReadOnlyApply() const
6994 : {
6995 0 : switch (mType)
6996 : {
6997 : case NS_FORM_INPUT_HIDDEN:
6998 : case NS_FORM_INPUT_BUTTON:
6999 : case NS_FORM_INPUT_IMAGE:
7000 : case NS_FORM_INPUT_RESET:
7001 : case NS_FORM_INPUT_SUBMIT:
7002 : case NS_FORM_INPUT_RADIO:
7003 : case NS_FORM_INPUT_FILE:
7004 : case NS_FORM_INPUT_CHECKBOX:
7005 : case NS_FORM_INPUT_RANGE:
7006 : case NS_FORM_INPUT_COLOR:
7007 0 : return false;
7008 : #ifdef DEBUG
7009 : case NS_FORM_INPUT_TEXT:
7010 : case NS_FORM_INPUT_PASSWORD:
7011 : case NS_FORM_INPUT_SEARCH:
7012 : case NS_FORM_INPUT_TEL:
7013 : case NS_FORM_INPUT_EMAIL:
7014 : case NS_FORM_INPUT_URL:
7015 : case NS_FORM_INPUT_NUMBER:
7016 : case NS_FORM_INPUT_DATE:
7017 : case NS_FORM_INPUT_TIME:
7018 : case NS_FORM_INPUT_MONTH:
7019 : case NS_FORM_INPUT_WEEK:
7020 : case NS_FORM_INPUT_DATETIME_LOCAL:
7021 0 : return true;
7022 : default:
7023 0 : NS_NOTYETIMPLEMENTED("Unexpected input type in DoesReadOnlyApply()");
7024 0 : return true;
7025 : #else // DEBUG
7026 : default:
7027 : return true;
7028 : #endif // DEBUG
7029 : }
7030 : }
7031 :
7032 : bool
7033 60 : HTMLInputElement::DoesRequiredApply() const
7034 : {
7035 60 : switch (mType)
7036 : {
7037 : case NS_FORM_INPUT_HIDDEN:
7038 : case NS_FORM_INPUT_BUTTON:
7039 : case NS_FORM_INPUT_IMAGE:
7040 : case NS_FORM_INPUT_RESET:
7041 : case NS_FORM_INPUT_SUBMIT:
7042 : case NS_FORM_INPUT_RANGE:
7043 : case NS_FORM_INPUT_COLOR:
7044 0 : return false;
7045 : #ifdef DEBUG
7046 : case NS_FORM_INPUT_RADIO:
7047 : case NS_FORM_INPUT_CHECKBOX:
7048 : case NS_FORM_INPUT_FILE:
7049 : case NS_FORM_INPUT_TEXT:
7050 : case NS_FORM_INPUT_PASSWORD:
7051 : case NS_FORM_INPUT_SEARCH:
7052 : case NS_FORM_INPUT_TEL:
7053 : case NS_FORM_INPUT_EMAIL:
7054 : case NS_FORM_INPUT_URL:
7055 : case NS_FORM_INPUT_NUMBER:
7056 : case NS_FORM_INPUT_DATE:
7057 : case NS_FORM_INPUT_TIME:
7058 : case NS_FORM_INPUT_MONTH:
7059 : case NS_FORM_INPUT_WEEK:
7060 : case NS_FORM_INPUT_DATETIME_LOCAL:
7061 60 : return true;
7062 : default:
7063 0 : NS_NOTYETIMPLEMENTED("Unexpected input type in DoesRequiredApply()");
7064 0 : return true;
7065 : #else // DEBUG
7066 : default:
7067 : return true;
7068 : #endif // DEBUG
7069 : }
7070 : }
7071 :
7072 : bool
7073 70 : HTMLInputElement::PlaceholderApplies() const
7074 : {
7075 70 : if (IsDateTimeInputType(mType)) {
7076 0 : return false;
7077 : }
7078 :
7079 70 : return IsSingleLineTextControl(false);
7080 : }
7081 :
7082 : bool
7083 1 : HTMLInputElement::DoesMinMaxApply() const
7084 : {
7085 1 : switch (mType)
7086 : {
7087 : case NS_FORM_INPUT_NUMBER:
7088 : case NS_FORM_INPUT_DATE:
7089 : case NS_FORM_INPUT_TIME:
7090 : case NS_FORM_INPUT_RANGE:
7091 : case NS_FORM_INPUT_MONTH:
7092 : case NS_FORM_INPUT_WEEK:
7093 : case NS_FORM_INPUT_DATETIME_LOCAL:
7094 0 : return true;
7095 : #ifdef DEBUG
7096 : case NS_FORM_INPUT_RESET:
7097 : case NS_FORM_INPUT_SUBMIT:
7098 : case NS_FORM_INPUT_IMAGE:
7099 : case NS_FORM_INPUT_BUTTON:
7100 : case NS_FORM_INPUT_HIDDEN:
7101 : case NS_FORM_INPUT_RADIO:
7102 : case NS_FORM_INPUT_CHECKBOX:
7103 : case NS_FORM_INPUT_FILE:
7104 : case NS_FORM_INPUT_TEXT:
7105 : case NS_FORM_INPUT_PASSWORD:
7106 : case NS_FORM_INPUT_SEARCH:
7107 : case NS_FORM_INPUT_TEL:
7108 : case NS_FORM_INPUT_EMAIL:
7109 : case NS_FORM_INPUT_URL:
7110 : case NS_FORM_INPUT_COLOR:
7111 1 : return false;
7112 : default:
7113 0 : NS_NOTYETIMPLEMENTED("Unexpected input type in DoesRequiredApply()");
7114 0 : return false;
7115 : #else // DEBUG
7116 : default:
7117 : return false;
7118 : #endif // DEBUG
7119 : }
7120 : }
7121 :
7122 : bool
7123 0 : HTMLInputElement::DoesAutocompleteApply() const
7124 : {
7125 0 : switch (mType)
7126 : {
7127 : case NS_FORM_INPUT_HIDDEN:
7128 : case NS_FORM_INPUT_TEXT:
7129 : case NS_FORM_INPUT_SEARCH:
7130 : case NS_FORM_INPUT_URL:
7131 : case NS_FORM_INPUT_TEL:
7132 : case NS_FORM_INPUT_EMAIL:
7133 : case NS_FORM_INPUT_PASSWORD:
7134 : case NS_FORM_INPUT_DATE:
7135 : case NS_FORM_INPUT_TIME:
7136 : case NS_FORM_INPUT_NUMBER:
7137 : case NS_FORM_INPUT_RANGE:
7138 : case NS_FORM_INPUT_COLOR:
7139 : case NS_FORM_INPUT_MONTH:
7140 : case NS_FORM_INPUT_WEEK:
7141 : case NS_FORM_INPUT_DATETIME_LOCAL:
7142 0 : return true;
7143 : #ifdef DEBUG
7144 : case NS_FORM_INPUT_RESET:
7145 : case NS_FORM_INPUT_SUBMIT:
7146 : case NS_FORM_INPUT_IMAGE:
7147 : case NS_FORM_INPUT_BUTTON:
7148 : case NS_FORM_INPUT_RADIO:
7149 : case NS_FORM_INPUT_CHECKBOX:
7150 : case NS_FORM_INPUT_FILE:
7151 0 : return false;
7152 : default:
7153 0 : NS_NOTYETIMPLEMENTED("Unexpected input type in DoesAutocompleteApply()");
7154 0 : return false;
7155 : #else // DEBUG
7156 : default:
7157 : return false;
7158 : #endif // DEBUG
7159 : }
7160 : }
7161 :
7162 : Decimal
7163 0 : HTMLInputElement::GetStep() const
7164 : {
7165 0 : MOZ_ASSERT(DoesStepApply(), "GetStep() can only be called if @step applies");
7166 :
7167 0 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::step)) {
7168 0 : return GetDefaultStep() * GetStepScaleFactor();
7169 : }
7170 :
7171 0 : nsAutoString stepStr;
7172 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::step, stepStr);
7173 :
7174 0 : if (stepStr.LowerCaseEqualsLiteral("any")) {
7175 : // The element can't suffer from step mismatch if there is no step.
7176 0 : return kStepAny;
7177 : }
7178 :
7179 0 : Decimal step = StringToDecimal(stepStr);
7180 0 : if (!step.isFinite() || step <= Decimal(0)) {
7181 0 : step = GetDefaultStep();
7182 : }
7183 :
7184 : // For input type=date, we round the step value to have a rounded day.
7185 0 : if (mType == NS_FORM_INPUT_DATE || mType == NS_FORM_INPUT_MONTH ||
7186 0 : mType == NS_FORM_INPUT_WEEK) {
7187 0 : step = std::max(step.round(), Decimal(1));
7188 : }
7189 :
7190 0 : return step * GetStepScaleFactor();
7191 : }
7192 :
7193 : // nsIConstraintValidation
7194 :
7195 : NS_IMETHODIMP
7196 0 : HTMLInputElement::SetCustomValidity(const nsAString& aError)
7197 : {
7198 0 : nsIConstraintValidation::SetCustomValidity(aError);
7199 :
7200 0 : UpdateState(true);
7201 :
7202 0 : return NS_OK;
7203 : }
7204 :
7205 : bool
7206 7 : HTMLInputElement::IsTooLong()
7207 : {
7208 8 : if (!mValueChanged ||
7209 1 : !mLastValueChangeWasInteractive) {
7210 7 : return false;
7211 : }
7212 :
7213 0 : return mInputType->IsTooLong();
7214 : }
7215 :
7216 : bool
7217 7 : HTMLInputElement::IsTooShort()
7218 : {
7219 8 : if (!mValueChanged ||
7220 1 : !mLastValueChangeWasInteractive) {
7221 7 : return false;
7222 : }
7223 :
7224 0 : return mInputType->IsTooShort();
7225 : }
7226 :
7227 : bool
7228 30 : HTMLInputElement::IsValueMissing() const
7229 : {
7230 : // Should use UpdateValueMissingValidityStateForRadio() for type radio.
7231 30 : MOZ_ASSERT(mType != NS_FORM_INPUT_RADIO);
7232 :
7233 30 : return mInputType->IsValueMissing();
7234 : }
7235 :
7236 : bool
7237 7 : HTMLInputElement::HasTypeMismatch() const
7238 : {
7239 7 : return mInputType->HasTypeMismatch();
7240 : }
7241 :
7242 : bool
7243 7 : HTMLInputElement::HasPatternMismatch() const
7244 : {
7245 7 : return mInputType->HasPatternMismatch();
7246 : }
7247 :
7248 : bool
7249 7 : HTMLInputElement::IsRangeOverflow() const
7250 : {
7251 7 : return mInputType->IsRangeOverflow();
7252 : }
7253 :
7254 : bool
7255 7 : HTMLInputElement::IsRangeUnderflow() const
7256 : {
7257 7 : return mInputType->IsRangeUnderflow();
7258 : }
7259 :
7260 : bool
7261 7 : HTMLInputElement::HasStepMismatch(bool aUseZeroIfValueNaN) const
7262 : {
7263 7 : return mInputType->HasStepMismatch(aUseZeroIfValueNaN);
7264 : }
7265 :
7266 : bool
7267 7 : HTMLInputElement::HasBadInput() const
7268 : {
7269 7 : return mInputType->HasBadInput();
7270 : }
7271 :
7272 : void
7273 7 : HTMLInputElement::UpdateTooLongValidityState()
7274 : {
7275 7 : SetValidityState(VALIDITY_STATE_TOO_LONG, IsTooLong());
7276 7 : }
7277 :
7278 : void
7279 7 : HTMLInputElement::UpdateTooShortValidityState()
7280 : {
7281 7 : SetValidityState(VALIDITY_STATE_TOO_SHORT, IsTooShort());
7282 7 : }
7283 :
7284 : void
7285 0 : HTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
7286 : {
7287 0 : bool notify = mDoneCreating;
7288 0 : nsCOMPtr<nsIDOMHTMLInputElement> selection = GetSelectedRadioButton();
7289 :
7290 0 : aIgnoreSelf = aIgnoreSelf || !IsMutable();
7291 :
7292 : // If there is no selection, that might mean the radio is not in a group.
7293 : // In that case, we can look for the checked state of the radio.
7294 0 : bool selected = selection || (!aIgnoreSelf && mChecked);
7295 0 : bool required = !aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required);
7296 0 : bool valueMissing = false;
7297 :
7298 0 : nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
7299 :
7300 0 : if (!container) {
7301 0 : SetValidityState(VALIDITY_STATE_VALUE_MISSING,
7302 0 : IsMutable() && required && !selected);
7303 0 : return;
7304 : }
7305 :
7306 0 : nsAutoString name;
7307 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
7308 :
7309 : // If the current radio is required and not ignored, we can assume the entire
7310 : // group is required.
7311 0 : if (!required) {
7312 0 : required = (aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required))
7313 0 : ? container->GetRequiredRadioCount(name) - 1
7314 0 : : container->GetRequiredRadioCount(name);
7315 : }
7316 :
7317 0 : valueMissing = required && !selected;
7318 :
7319 0 : if (container->GetValueMissingState(name) != valueMissing) {
7320 0 : container->SetValueMissingState(name, valueMissing);
7321 :
7322 0 : SetValidityState(VALIDITY_STATE_VALUE_MISSING, valueMissing);
7323 :
7324 : // nsRadioSetValueMissingState will call ContentStateChanged while visiting.
7325 0 : nsAutoScriptBlocker scriptBlocker;
7326 : nsCOMPtr<nsIRadioVisitor> visitor =
7327 0 : new nsRadioSetValueMissingState(this, valueMissing, notify);
7328 0 : VisitGroup(visitor, notify);
7329 : }
7330 : }
7331 :
7332 : void
7333 30 : HTMLInputElement::UpdateValueMissingValidityState()
7334 : {
7335 30 : if (mType == NS_FORM_INPUT_RADIO) {
7336 0 : UpdateValueMissingValidityStateForRadio(false);
7337 0 : return;
7338 : }
7339 :
7340 30 : SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
7341 : }
7342 :
7343 : void
7344 7 : HTMLInputElement::UpdateTypeMismatchValidityState()
7345 : {
7346 7 : SetValidityState(VALIDITY_STATE_TYPE_MISMATCH, HasTypeMismatch());
7347 7 : }
7348 :
7349 : void
7350 7 : HTMLInputElement::UpdatePatternMismatchValidityState()
7351 : {
7352 7 : SetValidityState(VALIDITY_STATE_PATTERN_MISMATCH, HasPatternMismatch());
7353 7 : }
7354 :
7355 : void
7356 7 : HTMLInputElement::UpdateRangeOverflowValidityState()
7357 : {
7358 7 : SetValidityState(VALIDITY_STATE_RANGE_OVERFLOW, IsRangeOverflow());
7359 7 : }
7360 :
7361 : void
7362 7 : HTMLInputElement::UpdateRangeUnderflowValidityState()
7363 : {
7364 7 : SetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW, IsRangeUnderflow());
7365 7 : }
7366 :
7367 : void
7368 7 : HTMLInputElement::UpdateStepMismatchValidityState()
7369 : {
7370 7 : SetValidityState(VALIDITY_STATE_STEP_MISMATCH, HasStepMismatch());
7371 7 : }
7372 :
7373 : void
7374 7 : HTMLInputElement::UpdateBadInputValidityState()
7375 : {
7376 7 : SetValidityState(VALIDITY_STATE_BAD_INPUT, HasBadInput());
7377 7 : }
7378 :
7379 : void
7380 7 : HTMLInputElement::UpdateAllValidityStates(bool aNotify)
7381 : {
7382 7 : bool validBefore = IsValid();
7383 7 : UpdateTooLongValidityState();
7384 7 : UpdateTooShortValidityState();
7385 7 : UpdateValueMissingValidityState();
7386 7 : UpdateTypeMismatchValidityState();
7387 7 : UpdatePatternMismatchValidityState();
7388 7 : UpdateRangeOverflowValidityState();
7389 7 : UpdateRangeUnderflowValidityState();
7390 7 : UpdateStepMismatchValidityState();
7391 7 : UpdateBadInputValidityState();
7392 :
7393 7 : if (validBefore != IsValid()) {
7394 0 : UpdateState(aNotify);
7395 : }
7396 7 : }
7397 :
7398 : void
7399 24 : HTMLInputElement::UpdateBarredFromConstraintValidation()
7400 : {
7401 72 : SetBarredFromConstraintValidation(mType == NS_FORM_INPUT_HIDDEN ||
7402 48 : mType == NS_FORM_INPUT_BUTTON ||
7403 48 : mType == NS_FORM_INPUT_RESET ||
7404 72 : HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) ||
7405 48 : IsDisabled());
7406 24 : }
7407 :
7408 : void
7409 0 : HTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
7410 : ErrorResult& aRv)
7411 : {
7412 0 : aRv = GetValidationMessage(aValidationMessage);
7413 0 : }
7414 :
7415 : nsresult
7416 0 : HTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
7417 : ValidityStateType aType)
7418 : {
7419 0 : return mInputType->GetValidationMessage(aValidationMessage, aType);
7420 : }
7421 :
7422 : NS_IMETHODIMP_(bool)
7423 79 : HTMLInputElement::IsSingleLineTextControl() const
7424 : {
7425 79 : return IsSingleLineTextControl(false);
7426 : }
7427 :
7428 : NS_IMETHODIMP_(bool)
7429 20 : HTMLInputElement::IsTextArea() const
7430 : {
7431 20 : return false;
7432 : }
7433 :
7434 : NS_IMETHODIMP_(bool)
7435 6 : HTMLInputElement::IsPlainTextControl() const
7436 : {
7437 : // need to check our HTML attribute and/or CSS.
7438 6 : return true;
7439 : }
7440 :
7441 : NS_IMETHODIMP_(bool)
7442 4 : HTMLInputElement::IsPasswordTextControl() const
7443 : {
7444 4 : return mType == NS_FORM_INPUT_PASSWORD;
7445 : }
7446 :
7447 : NS_IMETHODIMP_(int32_t)
7448 20 : HTMLInputElement::GetCols()
7449 : {
7450 : // Else we know (assume) it is an input with size attr
7451 20 : const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::size);
7452 20 : if (attr && attr->Type() == nsAttrValue::eInteger) {
7453 0 : int32_t cols = attr->GetIntegerValue();
7454 0 : if (cols > 0) {
7455 0 : return cols;
7456 : }
7457 : }
7458 :
7459 20 : return DEFAULT_COLS;
7460 : }
7461 :
7462 : NS_IMETHODIMP_(int32_t)
7463 5 : HTMLInputElement::GetWrapCols()
7464 : {
7465 5 : return 0; // only textarea's can have wrap cols
7466 : }
7467 :
7468 : NS_IMETHODIMP_(int32_t)
7469 20 : HTMLInputElement::GetRows()
7470 : {
7471 20 : return DEFAULT_ROWS;
7472 : }
7473 :
7474 : NS_IMETHODIMP_(void)
7475 30 : HTMLInputElement::GetDefaultValueFromContent(nsAString& aValue)
7476 : {
7477 30 : nsTextEditorState *state = GetEditorState();
7478 30 : if (state) {
7479 30 : GetDefaultValue(aValue);
7480 : // This is called by the frame to show the value.
7481 : // We have to sanitize it when needed.
7482 30 : if (mDoneCreating) {
7483 30 : SanitizeValue(aValue);
7484 : }
7485 : }
7486 30 : }
7487 :
7488 : NS_IMETHODIMP_(bool)
7489 32 : HTMLInputElement::ValueChanged() const
7490 : {
7491 32 : return mValueChanged;
7492 : }
7493 :
7494 : NS_IMETHODIMP_(void)
7495 11 : HTMLInputElement::GetTextEditorValue(nsAString& aValue,
7496 : bool aIgnoreWrap) const
7497 : {
7498 11 : nsTextEditorState* state = GetEditorState();
7499 11 : if (state) {
7500 11 : state->GetValue(aValue, aIgnoreWrap);
7501 : }
7502 11 : }
7503 :
7504 : NS_IMETHODIMP_(void)
7505 4 : HTMLInputElement::InitializeKeyboardEventListeners()
7506 : {
7507 4 : nsTextEditorState* state = GetEditorState();
7508 4 : if (state) {
7509 4 : state->InitializeKeyboardEventListeners();
7510 : }
7511 4 : }
7512 :
7513 : NS_IMETHODIMP_(void)
7514 6 : HTMLInputElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange)
7515 : {
7516 6 : mLastValueChangeWasInteractive = aWasInteractiveUserChange;
7517 :
7518 6 : UpdateAllValidityStates(aNotify);
7519 :
7520 6 : if (HasDirAuto()) {
7521 0 : SetDirectionFromValue(aNotify);
7522 : }
7523 :
7524 : // :placeholder-shown pseudo-class may change when the value changes.
7525 : // However, we don't want to waste cycles if the state doesn't apply.
7526 12 : if (PlaceholderApplies() &&
7527 6 : HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
7528 3 : UpdateState(aNotify);
7529 : }
7530 6 : }
7531 :
7532 : NS_IMETHODIMP_(bool)
7533 4 : HTMLInputElement::HasCachedSelection()
7534 : {
7535 4 : bool isCached = false;
7536 4 : nsTextEditorState* state = GetEditorState();
7537 4 : if (state) {
7538 12 : isCached = state->IsSelectionCached() &&
7539 7 : state->HasNeverInitializedBefore() &&
7540 3 : state->GetSelectionProperties().GetStart() !=
7541 3 : state->GetSelectionProperties().GetEnd();
7542 4 : if (isCached) {
7543 0 : state->WillInitEagerly();
7544 : }
7545 : }
7546 4 : return isCached;
7547 : }
7548 :
7549 : void
7550 0 : HTMLInputElement::FieldSetDisabledChanged(bool aNotify)
7551 : {
7552 0 : UpdateValueMissingValidityState();
7553 0 : UpdateBarredFromConstraintValidation();
7554 :
7555 0 : nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
7556 0 : }
7557 :
7558 : void
7559 0 : HTMLInputElement::SetFilePickerFiltersFromAccept(nsIFilePicker* filePicker)
7560 : {
7561 : // We always add |filterAll|
7562 0 : filePicker->AppendFilters(nsIFilePicker::filterAll);
7563 :
7564 0 : NS_ASSERTION(HasAttr(kNameSpaceID_None, nsGkAtoms::accept),
7565 : "You should not call SetFilePickerFiltersFromAccept if the"
7566 : " element has no accept attribute!");
7567 :
7568 : // Services to retrieve image/*, audio/*, video/* filters
7569 : nsCOMPtr<nsIStringBundleService> stringService =
7570 0 : mozilla::services::GetStringBundleService();
7571 0 : if (!stringService) {
7572 0 : return;
7573 : }
7574 0 : nsCOMPtr<nsIStringBundle> filterBundle;
7575 0 : if (NS_FAILED(stringService->CreateBundle("chrome://global/content/filepicker.properties",
7576 : getter_AddRefs(filterBundle)))) {
7577 0 : return;
7578 : }
7579 :
7580 : // Service to retrieve mime type information for mime types filters
7581 0 : nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
7582 0 : if (!mimeService) {
7583 0 : return;
7584 : }
7585 :
7586 0 : nsAutoString accept;
7587 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::accept, accept);
7588 :
7589 0 : HTMLSplitOnSpacesTokenizer tokenizer(accept, ',');
7590 :
7591 0 : nsTArray<nsFilePickerFilter> filters;
7592 0 : nsString allExtensionsList;
7593 :
7594 0 : bool allMimeTypeFiltersAreValid = true;
7595 0 : bool atLeastOneFileExtensionFilter = false;
7596 :
7597 : // Retrieve all filters
7598 0 : while (tokenizer.hasMoreTokens()) {
7599 0 : const nsDependentSubstring& token = tokenizer.nextToken();
7600 :
7601 0 : if (token.IsEmpty()) {
7602 0 : continue;
7603 : }
7604 :
7605 0 : int32_t filterMask = 0;
7606 0 : nsString filterName;
7607 0 : nsString extensionListStr;
7608 :
7609 : // First, check for image/audio/video filters...
7610 0 : if (token.EqualsLiteral("image/*")) {
7611 0 : filterMask = nsIFilePicker::filterImages;
7612 0 : filterBundle->GetStringFromName(u"imageFilter",
7613 0 : getter_Copies(extensionListStr));
7614 0 : } else if (token.EqualsLiteral("audio/*")) {
7615 0 : filterMask = nsIFilePicker::filterAudio;
7616 0 : filterBundle->GetStringFromName(u"audioFilter",
7617 0 : getter_Copies(extensionListStr));
7618 0 : } else if (token.EqualsLiteral("video/*")) {
7619 0 : filterMask = nsIFilePicker::filterVideo;
7620 0 : filterBundle->GetStringFromName(u"videoFilter",
7621 0 : getter_Copies(extensionListStr));
7622 0 : } else if (token.First() == '.') {
7623 0 : if (token.Contains(';') || token.Contains('*')) {
7624 : // Ignore this filter as it contains reserved characters
7625 0 : continue;
7626 : }
7627 0 : extensionListStr = NS_LITERAL_STRING("*") + token;
7628 0 : filterName = extensionListStr;
7629 0 : atLeastOneFileExtensionFilter = true;
7630 : } else {
7631 : //... if no image/audio/video filter is found, check mime types filters
7632 0 : nsCOMPtr<nsIMIMEInfo> mimeInfo;
7633 0 : if (NS_FAILED(mimeService->GetFromTypeAndExtension(
7634 : NS_ConvertUTF16toUTF8(token),
7635 : EmptyCString(), // No extension
7636 0 : getter_AddRefs(mimeInfo))) ||
7637 0 : !mimeInfo) {
7638 0 : allMimeTypeFiltersAreValid = false;
7639 0 : continue;
7640 : }
7641 :
7642 : // Get a name for the filter: first try the description, then the mime type
7643 : // name if there is no description
7644 0 : mimeInfo->GetDescription(filterName);
7645 0 : if (filterName.IsEmpty()) {
7646 0 : nsCString mimeTypeName;
7647 0 : mimeInfo->GetType(mimeTypeName);
7648 0 : CopyUTF8toUTF16(mimeTypeName, filterName);
7649 : }
7650 :
7651 : // Get extension list
7652 0 : nsCOMPtr<nsIUTF8StringEnumerator> extensions;
7653 0 : mimeInfo->GetFileExtensions(getter_AddRefs(extensions));
7654 :
7655 : bool hasMore;
7656 0 : while (NS_SUCCEEDED(extensions->HasMore(&hasMore)) && hasMore) {
7657 0 : nsCString extension;
7658 0 : if (NS_FAILED(extensions->GetNext(extension))) {
7659 0 : continue;
7660 : }
7661 0 : if (!extensionListStr.IsEmpty()) {
7662 0 : extensionListStr.AppendLiteral("; ");
7663 : }
7664 0 : extensionListStr += NS_LITERAL_STRING("*.") +
7665 0 : NS_ConvertUTF8toUTF16(extension);
7666 : }
7667 : }
7668 :
7669 0 : if (!filterMask && (extensionListStr.IsEmpty() || filterName.IsEmpty())) {
7670 : // No valid filter found
7671 0 : allMimeTypeFiltersAreValid = false;
7672 0 : continue;
7673 : }
7674 :
7675 : // If we arrived here, that means we have a valid filter: let's create it
7676 : // and add it to our list, if no similar filter is already present
7677 0 : nsFilePickerFilter filter;
7678 0 : if (filterMask) {
7679 0 : filter = nsFilePickerFilter(filterMask);
7680 : } else {
7681 0 : filter = nsFilePickerFilter(filterName, extensionListStr);
7682 : }
7683 :
7684 0 : if (!filters.Contains(filter)) {
7685 0 : if (!allExtensionsList.IsEmpty()) {
7686 0 : allExtensionsList.AppendLiteral("; ");
7687 : }
7688 0 : allExtensionsList += extensionListStr;
7689 0 : filters.AppendElement(filter);
7690 : }
7691 : }
7692 :
7693 : // Remove similar filters
7694 : // Iterate over a copy, as we might modify the original filters list
7695 0 : nsTArray<nsFilePickerFilter> filtersCopy;
7696 0 : filtersCopy = filters;
7697 0 : for (uint32_t i = 0; i < filtersCopy.Length(); ++i) {
7698 0 : const nsFilePickerFilter& filterToCheck = filtersCopy[i];
7699 0 : if (filterToCheck.mFilterMask) {
7700 0 : continue;
7701 : }
7702 0 : for (uint32_t j = 0; j < filtersCopy.Length(); ++j) {
7703 0 : if (i == j) {
7704 0 : continue;
7705 : }
7706 : // Check if this filter's extension list is a substring of the other one.
7707 : // e.g. if filters are "*.jpeg" and "*.jpeg; *.jpg" the first one should
7708 : // be removed.
7709 : // Add an extra "; " to be sure the check will work and avoid cases like
7710 : // "*.xls" being a subtring of "*.xslx" while those are two differents
7711 : // filters and none should be removed.
7712 0 : if (FindInReadable(filterToCheck.mFilter + NS_LITERAL_STRING(";"),
7713 0 : filtersCopy[j].mFilter + NS_LITERAL_STRING(";"))) {
7714 : // We already have a similar, less restrictive filter (i.e.
7715 : // filterToCheck extensionList is just a subset of another filter
7716 : // extension list): remove this one
7717 0 : filters.RemoveElement(filterToCheck);
7718 : }
7719 : }
7720 : }
7721 :
7722 : // Add "All Supported Types" filter
7723 0 : if (filters.Length() > 1) {
7724 0 : nsXPIDLString title;
7725 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
7726 0 : "AllSupportedTypes", title);
7727 0 : filePicker->AppendFilter(title, allExtensionsList);
7728 : }
7729 :
7730 : // Add each filter
7731 0 : for (uint32_t i = 0; i < filters.Length(); ++i) {
7732 0 : const nsFilePickerFilter& filter = filters[i];
7733 0 : if (filter.mFilterMask) {
7734 0 : filePicker->AppendFilters(filter.mFilterMask);
7735 : } else {
7736 0 : filePicker->AppendFilter(filter.mTitle, filter.mFilter);
7737 : }
7738 : }
7739 :
7740 0 : if (filters.Length() >= 1 &&
7741 0 : (allMimeTypeFiltersAreValid || atLeastOneFileExtensionFilter)) {
7742 : // |filterAll| will always use index=0 so we need to set index=1 as the
7743 : // current filter.
7744 0 : filePicker->SetFilterIndex(1);
7745 : }
7746 : }
7747 :
7748 : Decimal
7749 0 : HTMLInputElement::GetStepScaleFactor() const
7750 : {
7751 0 : MOZ_ASSERT(DoesStepApply());
7752 :
7753 0 : switch (mType) {
7754 : case NS_FORM_INPUT_DATE:
7755 0 : return kStepScaleFactorDate;
7756 : case NS_FORM_INPUT_NUMBER:
7757 : case NS_FORM_INPUT_RANGE:
7758 0 : return kStepScaleFactorNumberRange;
7759 : case NS_FORM_INPUT_TIME:
7760 : case NS_FORM_INPUT_DATETIME_LOCAL:
7761 0 : return kStepScaleFactorTime;
7762 : case NS_FORM_INPUT_MONTH:
7763 0 : return kStepScaleFactorMonth;
7764 : case NS_FORM_INPUT_WEEK:
7765 0 : return kStepScaleFactorWeek;
7766 : default:
7767 0 : MOZ_ASSERT(false, "Unrecognized input type");
7768 : return Decimal::nan();
7769 : }
7770 : }
7771 :
7772 : Decimal
7773 0 : HTMLInputElement::GetDefaultStep() const
7774 : {
7775 0 : MOZ_ASSERT(DoesStepApply());
7776 :
7777 0 : switch (mType) {
7778 : case NS_FORM_INPUT_DATE:
7779 : case NS_FORM_INPUT_MONTH:
7780 : case NS_FORM_INPUT_WEEK:
7781 : case NS_FORM_INPUT_NUMBER:
7782 : case NS_FORM_INPUT_RANGE:
7783 0 : return kDefaultStep;
7784 : case NS_FORM_INPUT_TIME:
7785 : case NS_FORM_INPUT_DATETIME_LOCAL:
7786 0 : return kDefaultStepTime;
7787 : default:
7788 0 : MOZ_ASSERT(false, "Unrecognized input type");
7789 : return Decimal::nan();
7790 : }
7791 : }
7792 :
7793 : void
7794 0 : HTMLInputElement::UpdateValidityUIBits(bool aIsFocused)
7795 : {
7796 0 : if (aIsFocused) {
7797 : // If the invalid UI is shown, we should show it while focusing (and
7798 : // update). Otherwise, we should not.
7799 0 : mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
7800 :
7801 : // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
7802 : // UI while typing.
7803 0 : mCanShowValidUI = ShouldShowValidityUI();
7804 : } else {
7805 0 : mCanShowInvalidUI = true;
7806 0 : mCanShowValidUI = true;
7807 : }
7808 0 : }
7809 :
7810 : void
7811 1 : HTMLInputElement::UpdateHasRange()
7812 : {
7813 : /*
7814 : * There is a range if min/max applies for the type and if the element
7815 : * currently have a valid min or max.
7816 : */
7817 :
7818 1 : mHasRange = false;
7819 :
7820 1 : if (!DoesMinMaxApply()) {
7821 2 : return;
7822 : }
7823 :
7824 0 : Decimal minimum = GetMinimum();
7825 0 : if (!minimum.isNaN()) {
7826 0 : mHasRange = true;
7827 0 : return;
7828 : }
7829 :
7830 0 : Decimal maximum = GetMaximum();
7831 0 : if (!maximum.isNaN()) {
7832 0 : mHasRange = true;
7833 0 : return;
7834 : }
7835 : }
7836 :
7837 : void
7838 0 : HTMLInputElement::PickerClosed()
7839 : {
7840 0 : mPickerRunning = false;
7841 0 : }
7842 :
7843 : JSObject*
7844 2 : HTMLInputElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
7845 : {
7846 2 : return HTMLInputElementBinding::Wrap(aCx, this, aGivenProto);
7847 : }
7848 :
7849 : GetFilesHelper*
7850 0 : HTMLInputElement::GetOrCreateGetFilesHelper(bool aRecursiveFlag,
7851 : ErrorResult& aRv)
7852 : {
7853 0 : MOZ_ASSERT(mFileData);
7854 :
7855 0 : nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
7856 0 : MOZ_ASSERT(global);
7857 0 : if (!global) {
7858 0 : aRv.Throw(NS_ERROR_FAILURE);
7859 0 : return nullptr;
7860 : }
7861 :
7862 0 : if (aRecursiveFlag) {
7863 0 : if (!mFileData->mGetFilesRecursiveHelper) {
7864 0 : mFileData->mGetFilesRecursiveHelper =
7865 0 : GetFilesHelper::Create(global,
7866 : GetFilesOrDirectoriesInternal(),
7867 0 : aRecursiveFlag, aRv);
7868 0 : if (NS_WARN_IF(aRv.Failed())) {
7869 0 : return nullptr;
7870 : }
7871 : }
7872 :
7873 0 : return mFileData->mGetFilesRecursiveHelper;
7874 : }
7875 :
7876 0 : if (!mFileData->mGetFilesNonRecursiveHelper) {
7877 0 : mFileData->mGetFilesNonRecursiveHelper =
7878 0 : GetFilesHelper::Create(global,
7879 : GetFilesOrDirectoriesInternal(),
7880 0 : aRecursiveFlag, aRv);
7881 0 : if (NS_WARN_IF(aRv.Failed())) {
7882 0 : return nullptr;
7883 : }
7884 : }
7885 :
7886 0 : return mFileData->mGetFilesNonRecursiveHelper;
7887 : }
7888 :
7889 : void
7890 0 : HTMLInputElement::UpdateEntries(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
7891 : {
7892 0 : MOZ_ASSERT(mFileData && mFileData->mEntries.IsEmpty());
7893 :
7894 0 : nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
7895 0 : MOZ_ASSERT(global);
7896 :
7897 0 : RefPtr<FileSystem> fs = FileSystem::Create(global);
7898 0 : if (NS_WARN_IF(!fs)) {
7899 0 : return;
7900 : }
7901 :
7902 0 : Sequence<RefPtr<FileSystemEntry>> entries;
7903 0 : for (uint32_t i = 0; i < aFilesOrDirectories.Length(); ++i) {
7904 : RefPtr<FileSystemEntry> entry =
7905 0 : FileSystemEntry::Create(global, aFilesOrDirectories[i], fs);
7906 0 : MOZ_ASSERT(entry);
7907 :
7908 0 : if (!entries.AppendElement(entry, fallible)) {
7909 0 : return;
7910 : }
7911 : }
7912 :
7913 : // The root fileSystem is a DirectoryEntry object that contains only the
7914 : // dropped fileEntry and directoryEntry objects.
7915 0 : fs->CreateRoot(entries);
7916 :
7917 0 : mFileData->mEntries.SwapElements(entries);
7918 : }
7919 :
7920 : void
7921 0 : HTMLInputElement::GetWebkitEntries(nsTArray<RefPtr<FileSystemEntry>>& aSequence)
7922 : {
7923 0 : if (NS_WARN_IF(mType != NS_FORM_INPUT_FILE)) {
7924 0 : return;
7925 : }
7926 :
7927 0 : Telemetry::Accumulate(Telemetry::BLINK_FILESYSTEM_USED, true);
7928 0 : aSequence.AppendElements(mFileData->mEntries);
7929 : }
7930 :
7931 : already_AddRefed<nsINodeList>
7932 0 : HTMLInputElement::GetLabels()
7933 : {
7934 0 : if (!IsLabelable()) {
7935 0 : return nullptr;
7936 : }
7937 :
7938 0 : return nsGenericHTMLElement::Labels();
7939 : }
7940 :
7941 : } // namespace dom
7942 : } // namespace mozilla
7943 :
7944 : #undef NS_ORIGINAL_CHECKED_VALUE
|