Source code

Revision control

Other Tools

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/BasePrincipal.h"
12
#include "mozilla/DebugOnly.h"
13
#include "mozilla/dom/Directory.h"
14
#include "mozilla/dom/DocumentOrShadowRoot.h"
15
#include "mozilla/dom/FileSystemUtils.h"
16
#include "mozilla/dom/GetFilesHelper.h"
17
#include "mozilla/dom/HTMLFormSubmission.h"
18
#include "mozilla/dom/InputType.h"
19
#include "mozilla/dom/UserActivation.h"
20
#include "mozilla/dom/WheelEventBinding.h"
21
#include "mozilla/PresShell.h"
22
#include "mozilla/StaticPrefs_dom.h"
23
#include "mozilla/TextUtils.h"
24
#include "nsAttrValueInlines.h"
25
#include "nsCRTGlue.h"
26
#include "nsQueryObject.h"
27
28
#include "nsIRadioVisitor.h"
29
30
#include "HTMLFormSubmissionConstants.h"
31
#include "mozilla/Telemetry.h"
32
#include "nsBaseCommandController.h"
33
#include "nsIStringBundle.h"
34
#include "nsFocusManager.h"
35
#include "nsColorControlFrame.h"
36
#include "nsNumberControlFrame.h"
37
#include "nsPIDOMWindow.h"
38
#include "nsRepeatService.h"
39
#include "nsContentCID.h"
40
#include "mozilla/dom/ProgressEvent.h"
41
#include "nsGkAtoms.h"
42
#include "nsStyleConsts.h"
43
#include "nsPresContext.h"
44
#include "nsMappedAttributes.h"
45
#include "nsIFormControl.h"
46
#include "mozilla/dom/Document.h"
47
#include "nsIFormControlFrame.h"
48
#include "nsITextControlFrame.h"
49
#include "nsIFrame.h"
50
#include "nsRangeFrame.h"
51
#include "nsError.h"
52
#include "nsIEditor.h"
53
#include "nsAttrValueOrString.h"
54
55
#include "mozilla/PresState.h"
56
#include "nsLinebreakConverter.h" //to strip out carriage returns
57
#include "nsReadableUtils.h"
58
#include "nsUnicharUtils.h"
59
#include "nsLayoutUtils.h"
60
#include "nsVariant.h"
61
62
#include "mozilla/ContentEvents.h"
63
#include "mozilla/EventDispatcher.h"
64
#include "mozilla/EventStates.h"
65
#include "mozilla/MappedDeclarations.h"
66
#include "mozilla/InternalMutationEvent.h"
67
#include "mozilla/TextControlState.h"
68
#include "mozilla/TextEditor.h"
69
#include "mozilla/TextEvents.h"
70
#include "mozilla/TouchEvents.h"
71
72
#include <algorithm>
73
74
// input type=radio
75
#include "nsIRadioGroupContainer.h"
76
77
// input type=file
78
#include "mozilla/dom/FileSystemEntry.h"
79
#include "mozilla/dom/FileSystem.h"
80
#include "mozilla/dom/File.h"
81
#include "mozilla/dom/FileList.h"
82
#include "nsIFile.h"
83
#include "nsDirectoryServiceDefs.h"
84
#include "nsIContentPrefService2.h"
85
#include "nsIMIMEService.h"
86
#include "nsIObserverService.h"
87
#include "nsGlobalWindow.h"
88
89
// input type=image
90
#include "nsImageLoadingContent.h"
91
#include "imgRequestProxy.h"
92
93
#include "mozAutoDocUpdate.h"
94
#include "nsContentCreatorFunctions.h"
95
#include "nsContentUtils.h"
96
#include "mozilla/dom/DirectionalityUtils.h"
97
#include "nsRadioVisitor.h"
98
99
#include "mozilla/LookAndFeel.h"
100
#include "mozilla/Preferences.h"
101
#include "mozilla/MathAlgorithms.h"
102
#include "mozilla/TextUtils.h"
103
104
#include <limits>
105
106
#include "nsIColorPicker.h"
107
#include "nsIStringEnumerator.h"
108
#include "HTMLSplitOnSpacesTokenizer.h"
109
#include "nsIMIMEInfo.h"
110
#include "nsFrameSelection.h"
111
#include "nsBaseCommandController.h"
112
#include "nsXULControllers.h"
113
114
// input type=date
115
#include "js/Date.h"
116
117
NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Input)
118
119
// XXX align=left, hspace, vspace, border? other nav4 attrs
120
121
namespace mozilla {
122
namespace dom {
123
124
// First bits are needed for the control type.
125
#define NS_OUTER_ACTIVATE_EVENT (1 << 9)
126
#define NS_ORIGINAL_CHECKED_VALUE (1 << 10)
127
// (1 << 11 is unused)
128
#define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12)
129
#define NS_PRE_HANDLE_BLUR_EVENT (1 << 13)
130
#define NS_IN_SUBMIT_CLICK (1 << 15)
131
#define NS_CONTROL_TYPE(bits) \
132
((bits) & ~(NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE | \
133
NS_ORIGINAL_INDETERMINATE_VALUE | NS_PRE_HANDLE_BLUR_EVENT | \
134
NS_IN_SUBMIT_CLICK))
135
136
// whether textfields should be selected once focused:
137
// -1: no, 1: yes, 0: uninitialized
138
static int32_t gSelectTextFieldOnFocus;
139
UploadLastDir* HTMLInputElement::gUploadLastDir;
140
141
static const nsAttrValue::EnumTable kInputTypeTable[] = {
142
{"button", NS_FORM_INPUT_BUTTON},
143
{"checkbox", NS_FORM_INPUT_CHECKBOX},
144
{"color", NS_FORM_INPUT_COLOR},
145
{"date", NS_FORM_INPUT_DATE},
146
{"datetime-local", NS_FORM_INPUT_DATETIME_LOCAL},
147
{"email", NS_FORM_INPUT_EMAIL},
148
{"file", NS_FORM_INPUT_FILE},
149
{"hidden", NS_FORM_INPUT_HIDDEN},
150
{"reset", NS_FORM_INPUT_RESET},
151
{"image", NS_FORM_INPUT_IMAGE},
152
{"month", NS_FORM_INPUT_MONTH},
153
{"number", NS_FORM_INPUT_NUMBER},
154
{"password", NS_FORM_INPUT_PASSWORD},
155
{"radio", NS_FORM_INPUT_RADIO},
156
{"range", NS_FORM_INPUT_RANGE},
157
{"search", NS_FORM_INPUT_SEARCH},
158
{"submit", NS_FORM_INPUT_SUBMIT},
159
{"tel", NS_FORM_INPUT_TEL},
160
{"time", NS_FORM_INPUT_TIME},
161
{"url", NS_FORM_INPUT_URL},
162
{"week", NS_FORM_INPUT_WEEK},
163
// "text" must be last for ParseAttribute to work right. If you add things
164
// before it, please update kInputDefaultType.
165
{"text", NS_FORM_INPUT_TEXT},
166
{nullptr, 0}};
167
168
// Default type is 'text'.
169
static const nsAttrValue::EnumTable* kInputDefaultType =
170
&kInputTypeTable[ArrayLength(kInputTypeTable) - 2];
171
172
static const uint8_t NS_INPUT_INPUTMODE_NONE = 1;
173
static const uint8_t NS_INPUT_INPUTMODE_TEXT = 2;
174
static const uint8_t NS_INPUT_INPUTMODE_TEL = 3;
175
static const uint8_t NS_INPUT_INPUTMODE_URL = 4;
176
static const uint8_t NS_INPUT_INPUTMODE_EMAIL = 5;
177
static const uint8_t NS_INPUT_INPUTMODE_NUMERIC = 6;
178
static const uint8_t NS_INPUT_INPUTMODE_DECIMAL = 7;
179
static const uint8_t NS_INPUT_INPUTMODE_SEARCH = 8;
180
181
static const nsAttrValue::EnumTable kInputInputmodeTable[] = {
182
{"none", NS_INPUT_INPUTMODE_NONE},
183
{"text", NS_INPUT_INPUTMODE_TEXT},
184
{"tel", NS_INPUT_INPUTMODE_TEL},
185
{"url", NS_INPUT_INPUTMODE_URL},
186
{"email", NS_INPUT_INPUTMODE_EMAIL},
187
{"numeric", NS_INPUT_INPUTMODE_NUMERIC},
188
{"decimal", NS_INPUT_INPUTMODE_DECIMAL},
189
{"search", NS_INPUT_INPUTMODE_SEARCH},
190
{nullptr, 0}};
191
192
static const nsAttrValue::EnumTable kCaptureTable[] = {
193
{"user", static_cast<int16_t>(nsIFilePicker::captureUser)},
194
{"environment", static_cast<int16_t>(nsIFilePicker::captureEnv)},
195
{"", static_cast<int16_t>(nsIFilePicker::captureDefault)},
196
{nullptr, static_cast<int16_t>(nsIFilePicker::captureNone)}};
197
198
static const nsAttrValue::EnumTable* kCaptureDefault = &kCaptureTable[2];
199
200
const Decimal HTMLInputElement::kStepScaleFactorDate = Decimal(86400000);
201
const Decimal HTMLInputElement::kStepScaleFactorNumberRange = Decimal(1);
202
const Decimal HTMLInputElement::kStepScaleFactorTime = Decimal(1000);
203
const Decimal HTMLInputElement::kStepScaleFactorMonth = Decimal(1);
204
const Decimal HTMLInputElement::kStepScaleFactorWeek = Decimal(7 * 86400000);
205
const Decimal HTMLInputElement::kDefaultStepBase = Decimal(0);
206
const Decimal HTMLInputElement::kDefaultStepBaseWeek = Decimal(-259200000);
207
const Decimal HTMLInputElement::kDefaultStep = Decimal(1);
208
const Decimal HTMLInputElement::kDefaultStepTime = Decimal(60);
209
const Decimal HTMLInputElement::kStepAny = Decimal(0);
210
211
const double HTMLInputElement::kMinimumYear = 1;
212
const double HTMLInputElement::kMaximumYear = 275760;
213
const double HTMLInputElement::kMaximumWeekInMaximumYear = 37;
214
const double HTMLInputElement::kMaximumDayInMaximumYear = 13;
215
const double HTMLInputElement::kMaximumMonthInMaximumYear = 9;
216
const double HTMLInputElement::kMaximumWeekInYear = 53;
217
const double HTMLInputElement::kMsPerDay = 24 * 60 * 60 * 1000;
218
219
#define NS_INPUT_ELEMENT_STATE_IID \
220
{ /* dc3b3d14-23e2-4479-b513-7b369343e3a0 */ \
221
0xdc3b3d14, 0x23e2, 0x4479, { \
222
0xb5, 0x13, 0x7b, 0x36, 0x93, 0x43, 0xe3, 0xa0 \
223
} \
224
}
225
226
// An helper class for the dispatching of the 'change' event.
227
// This class is used when the FilePicker finished its task (or when files and
228
// directories are set by some chrome/test only method).
229
// The task of this class is to postpone the dispatching of 'change' and 'input'
230
// events at the end of the exploration of the directories.
231
class DispatchChangeEventCallback final : public GetFilesCallback {
232
public:
233
explicit DispatchChangeEventCallback(HTMLInputElement* aInputElement)
234
: mInputElement(aInputElement) {
235
MOZ_ASSERT(aInputElement);
236
}
237
238
virtual void Callback(
239
nsresult aStatus,
240
const FallibleTArray<RefPtr<BlobImpl>>& aBlobImpls) override {
241
nsTArray<OwningFileOrDirectory> array;
242
for (uint32_t i = 0; i < aBlobImpls.Length(); ++i) {
243
OwningFileOrDirectory* element = array.AppendElement();
244
RefPtr<File> file =
245
File::Create(mInputElement->GetOwnerGlobal(), aBlobImpls[i]);
246
if (NS_WARN_IF(!file)) {
247
break;
248
}
249
250
element->SetAsFile() = file;
251
}
252
253
mInputElement->SetFilesOrDirectories(array, true);
254
Unused << NS_WARN_IF(NS_FAILED(DispatchEvents()));
255
}
256
257
MOZ_CAN_RUN_SCRIPT_BOUNDARY
258
nsresult DispatchEvents() {
259
RefPtr<HTMLInputElement> inputElement(mInputElement);
260
nsresult rv = nsContentUtils::DispatchInputEvent(inputElement);
261
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch input event");
262
263
rv = nsContentUtils::DispatchTrustedEvent(
264
mInputElement->OwnerDoc(), static_cast<Element*>(mInputElement.get()),
265
NS_LITERAL_STRING("change"), CanBubble::eYes, Cancelable::eNo);
266
267
return rv;
268
}
269
270
private:
271
RefPtr<HTMLInputElement> mInputElement;
272
};
273
274
struct HTMLInputElement::FileData {
275
/**
276
* The value of the input if it is a file input. This is the list of files or
277
* directories DOM objects used when uploading a file. It is vital that this
278
* is kept separate from mValue so that it won't be possible to 'leak' the
279
* value from a text-input to a file-input. Additionally, the logic for this
280
* value is kept as simple as possible to avoid accidental errors where the
281
* wrong filename is used. Therefor the list of filenames is always owned by
282
* this member, never by the frame. Whenever the frame wants to change the
283
* filename it has to call SetFilesOrDirectories to update this member.
284
*/
285
nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
286
287
RefPtr<GetFilesHelper> mGetFilesRecursiveHelper;
288
RefPtr<GetFilesHelper> mGetFilesNonRecursiveHelper;
289
290
/**
291
* Hack for bug 1086684: Stash the .value when we're a file picker.
292
*/
293
nsString mFirstFilePath;
294
295
RefPtr<FileList> mFileList;
296
Sequence<RefPtr<FileSystemEntry>> mEntries;
297
298
nsString mStaticDocFileList;
299
300
void ClearGetFilesHelpers() {
301
if (mGetFilesRecursiveHelper) {
302
mGetFilesRecursiveHelper->Unlink();
303
mGetFilesRecursiveHelper = nullptr;
304
}
305
306
if (mGetFilesNonRecursiveHelper) {
307
mGetFilesNonRecursiveHelper->Unlink();
308
mGetFilesNonRecursiveHelper = nullptr;
309
}
310
}
311
312
// Cycle Collection support.
313
void Traverse(nsCycleCollectionTraversalCallback& cb) {
314
FileData* tmp = this;
315
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesOrDirectories)
316
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
317
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntries)
318
if (mGetFilesRecursiveHelper) {
319
mGetFilesRecursiveHelper->Traverse(cb);
320
}
321
322
if (mGetFilesNonRecursiveHelper) {
323
mGetFilesNonRecursiveHelper->Traverse(cb);
324
}
325
}
326
327
void Unlink() {
328
FileData* tmp = this;
329
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories)
330
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
331
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntries)
332
ClearGetFilesHelpers();
333
}
334
};
335
336
HTMLInputElement::nsFilePickerShownCallback::nsFilePickerShownCallback(
337
HTMLInputElement* aInput, nsIFilePicker* aFilePicker)
338
: mFilePicker(aFilePicker), mInput(aInput) {}
339
340
NS_IMPL_ISUPPORTS(UploadLastDir::ContentPrefCallback, nsIContentPrefCallback2)
341
342
NS_IMETHODIMP
343
UploadLastDir::ContentPrefCallback::HandleCompletion(uint16_t aReason) {
344
nsCOMPtr<nsIFile> localFile;
345
nsAutoString prefStr;
346
347
if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR || !mResult) {
348
Preferences::GetString("dom.input.fallbackUploadDir", prefStr);
349
}
350
351
if (prefStr.IsEmpty() && mResult) {
352
nsCOMPtr<nsIVariant> pref;
353
mResult->GetValue(getter_AddRefs(pref));
354
pref->GetAsAString(prefStr);
355
}
356
357
if (!prefStr.IsEmpty()) {
358
localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
359
if (localFile && NS_WARN_IF(NS_FAILED(localFile->InitWithPath(prefStr)))) {
360
localFile = nullptr;
361
}
362
}
363
364
if (localFile) {
365
mFilePicker->SetDisplayDirectory(localFile);
366
} else {
367
// If no custom directory was set through the pref, default to
368
// "desktop" directory for each platform.
369
mFilePicker->SetDisplaySpecialDirectory(
370
NS_LITERAL_STRING(NS_OS_DESKTOP_DIR));
371
}
372
373
mFilePicker->Open(mFpCallback);
374
return NS_OK;
375
}
376
377
NS_IMETHODIMP
378
UploadLastDir::ContentPrefCallback::HandleResult(nsIContentPref* pref) {
379
mResult = pref;
380
return NS_OK;
381
}
382
383
NS_IMETHODIMP
384
UploadLastDir::ContentPrefCallback::HandleError(nsresult error) {
385
// HandleCompletion is always called (even with HandleError was called),
386
// so we don't need to do anything special here.
387
return NS_OK;
388
}
389
390
namespace {
391
392
/**
393
* This may return nullptr if the DOM File's implementation of
394
* File::mozFullPathInternal does not successfully return a non-empty
395
* string that is a valid path. This can happen on Firefox OS, for example,
396
* where the file picker can create Blobs.
397
*/
398
static already_AddRefed<nsIFile> LastUsedDirectory(
399
const OwningFileOrDirectory& aData) {
400
if (aData.IsFile()) {
401
nsAutoString path;
402
ErrorResult error;
403
aData.GetAsFile()->GetMozFullPathInternal(path, error);
404
if (error.Failed() || path.IsEmpty()) {
405
error.SuppressException();
406
return nullptr;
407
}
408
409
nsCOMPtr<nsIFile> localFile;
410
nsresult rv = NS_NewLocalFile(path, true, getter_AddRefs(localFile));
411
if (NS_WARN_IF(NS_FAILED(rv))) {
412
return nullptr;
413
}
414
415
nsCOMPtr<nsIFile> parentFile;
416
rv = localFile->GetParent(getter_AddRefs(parentFile));
417
if (NS_WARN_IF(NS_FAILED(rv))) {
418
return nullptr;
419
}
420
421
return parentFile.forget();
422
}
423
424
MOZ_ASSERT(aData.IsDirectory());
425
426
nsCOMPtr<nsIFile> localFile = aData.GetAsDirectory()->GetInternalNsIFile();
427
MOZ_ASSERT(localFile);
428
429
return localFile.forget();
430
}
431
432
void GetDOMFileOrDirectoryName(const OwningFileOrDirectory& aData,
433
nsAString& aName) {
434
if (aData.IsFile()) {
435
aData.GetAsFile()->GetName(aName);
436
} else {
437
MOZ_ASSERT(aData.IsDirectory());
438
ErrorResult rv;
439
aData.GetAsDirectory()->GetName(aName, rv);
440
if (NS_WARN_IF(rv.Failed())) {
441
rv.SuppressException();
442
}
443
}
444
}
445
446
void GetDOMFileOrDirectoryPath(const OwningFileOrDirectory& aData,
447
nsAString& aPath, ErrorResult& aRv) {
448
if (aData.IsFile()) {
449
aData.GetAsFile()->GetMozFullPathInternal(aPath, aRv);
450
} else {
451
MOZ_ASSERT(aData.IsDirectory());
452
aData.GetAsDirectory()->GetFullRealPath(aPath);
453
}
454
}
455
456
} // namespace
457
458
/* static */
459
bool HTMLInputElement::ValueAsDateEnabled(JSContext* cx, JSObject* obj) {
460
return IsExperimentalFormsEnabled() || StaticPrefs::dom_forms_datetime() ||
461
IsInputDateTimeOthersEnabled();
462
}
463
464
NS_IMETHODIMP
465
HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) {
466
mInput->PickerClosed();
467
468
if (aResult == nsIFilePicker::returnCancel) {
469
return NS_OK;
470
}
471
472
int16_t mode;
473
mFilePicker->GetMode(&mode);
474
475
// Collect new selected filenames
476
nsTArray<OwningFileOrDirectory> newFilesOrDirectories;
477
if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple)) {
478
nsCOMPtr<nsISimpleEnumerator> iter;
479
nsresult rv =
480
mFilePicker->GetDomFileOrDirectoryEnumerator(getter_AddRefs(iter));
481
NS_ENSURE_SUCCESS(rv, rv);
482
483
if (!iter) {
484
return NS_OK;
485
}
486
487
nsCOMPtr<nsISupports> tmp;
488
bool hasMore = true;
489
490
while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
491
iter->GetNext(getter_AddRefs(tmp));
492
RefPtr<Blob> domBlob = do_QueryObject(tmp);
493
MOZ_ASSERT(domBlob,
494
"Null file object from FilePicker's file enumerator?");
495
if (!domBlob) {
496
continue;
497
}
498
499
OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
500
element->SetAsFile() = domBlob->ToFile();
501
}
502
} else {
503
MOZ_ASSERT(mode == static_cast<int16_t>(nsIFilePicker::modeOpen) ||
504
mode == static_cast<int16_t>(nsIFilePicker::modeGetFolder));
505
nsCOMPtr<nsISupports> tmp;
506
nsresult rv = mFilePicker->GetDomFileOrDirectory(getter_AddRefs(tmp));
507
NS_ENSURE_SUCCESS(rv, rv);
508
509
RefPtr<Blob> blob = do_QueryObject(tmp);
510
if (blob) {
511
RefPtr<File> file = blob->ToFile();
512
MOZ_ASSERT(file);
513
514
OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
515
element->SetAsFile() = file;
516
} else if (tmp) {
517
RefPtr<Directory> directory = static_cast<Directory*>(tmp.get());
518
OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
519
element->SetAsDirectory() = directory;
520
}
521
}
522
523
if (newFilesOrDirectories.IsEmpty()) {
524
return NS_OK;
525
}
526
527
// Store the last used directory using the content pref service:
528
nsCOMPtr<nsIFile> lastUsedDir = LastUsedDirectory(newFilesOrDirectories[0]);
529
530
if (lastUsedDir) {
531
HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(mInput->OwnerDoc(),
532
lastUsedDir);
533
}
534
535
// The text control frame (if there is one) isn't going to send a change
536
// event because it will think this is done by a script.
537
// So, we can safely send one by ourself.
538
mInput->SetFilesOrDirectories(newFilesOrDirectories, true);
539
540
RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback =
541
new DispatchChangeEventCallback(mInput);
542
543
if (StaticPrefs::dom_webkitBlink_dirPicker_enabled() &&
544
mInput->HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)) {
545
ErrorResult error;
546
GetFilesHelper* helper = mInput->GetOrCreateGetFilesHelper(true, error);
547
if (NS_WARN_IF(error.Failed())) {
548
return error.StealNSResult();
549
}
550
551
helper->AddCallback(dispatchChangeEventCallback);
552
return NS_OK;
553
}
554
555
return dispatchChangeEventCallback->DispatchEvents();
556
}
557
558
NS_IMPL_ISUPPORTS(HTMLInputElement::nsFilePickerShownCallback,
559
nsIFilePickerShownCallback)
560
561
class nsColorPickerShownCallback final : public nsIColorPickerShownCallback {
562
~nsColorPickerShownCallback() = default;
563
564
public:
565
nsColorPickerShownCallback(HTMLInputElement* aInput,
566
nsIColorPicker* aColorPicker)
567
: mInput(aInput), mColorPicker(aColorPicker), mValueChanged(false) {}
568
569
NS_DECL_ISUPPORTS
570
571
MOZ_CAN_RUN_SCRIPT_BOUNDARY
572
NS_IMETHOD Update(const nsAString& aColor) override;
573
MOZ_CAN_RUN_SCRIPT_BOUNDARY
574
NS_IMETHOD Done(const nsAString& aColor) override;
575
576
private:
577
/**
578
* Updates the internals of the object using aColor as the new value.
579
* If aTrustedUpdate is true, it will consider that aColor is a new value.
580
* Otherwise, it will check that aColor is different from the current value.
581
*/
582
MOZ_CAN_RUN_SCRIPT
583
nsresult UpdateInternal(const nsAString& aColor, bool aTrustedUpdate);
584
585
RefPtr<HTMLInputElement> mInput;
586
nsCOMPtr<nsIColorPicker> mColorPicker;
587
bool mValueChanged;
588
};
589
590
nsresult nsColorPickerShownCallback::UpdateInternal(const nsAString& aColor,
591
bool aTrustedUpdate) {
592
bool valueChanged = false;
593
594
nsAutoString oldValue;
595
if (aTrustedUpdate) {
596
valueChanged = true;
597
} else {
598
mInput->GetValue(oldValue, CallerType::System);
599
}
600
601
mInput->SetValue(aColor, CallerType::System, IgnoreErrors());
602
603
if (!aTrustedUpdate) {
604
nsAutoString newValue;
605
mInput->GetValue(newValue, CallerType::System);
606
if (!oldValue.Equals(newValue)) {
607
valueChanged = true;
608
}
609
}
610
611
if (!valueChanged) {
612
return NS_OK;
613
}
614
615
mValueChanged = true;
616
RefPtr<HTMLInputElement> input(mInput);
617
DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(input);
618
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
619
"Failed to dispatch input event");
620
return NS_OK;
621
}
622
623
NS_IMETHODIMP
624
nsColorPickerShownCallback::Update(const nsAString& aColor) {
625
return UpdateInternal(aColor, true);
626
}
627
628
NS_IMETHODIMP
629
nsColorPickerShownCallback::Done(const nsAString& aColor) {
630
/**
631
* When Done() is called, we might be at the end of a serie of Update() calls
632
* in which case mValueChanged is set to true and a change event will have to
633
* be fired but we might also be in a one shot Done() call situation in which
634
* case we should fire a change event iif the value actually changed.
635
* UpdateInternal(bool) is taking care of that logic for us.
636
*/
637
nsresult rv = NS_OK;
638
639
mInput->PickerClosed();
640
641
if (!aColor.IsEmpty()) {
642
UpdateInternal(aColor, false);
643
}
644
645
if (mValueChanged) {
646
rv = nsContentUtils::DispatchTrustedEvent(
647
mInput->OwnerDoc(), static_cast<Element*>(mInput.get()),
648
NS_LITERAL_STRING("change"), CanBubble::eYes, Cancelable::eNo);
649
}
650
651
return rv;
652
}
653
654
NS_IMPL_ISUPPORTS(nsColorPickerShownCallback, nsIColorPickerShownCallback)
655
656
bool HTMLInputElement::IsPopupBlocked() const {
657
nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
658
MOZ_ASSERT(win, "window should not be null");
659
if (!win) {
660
return true;
661
}
662
663
// Check if page can open a popup without abuse regardless of allowed events
664
if (PopupBlocker::GetPopupControlState() <= PopupBlocker::openBlocked) {
665
return !PopupBlocker::TryUsePopupOpeningToken(OwnerDoc()->NodePrincipal());
666
}
667
668
return !PopupBlocker::CanShowPopupByPermission(OwnerDoc()->NodePrincipal());
669
}
670
671
nsresult HTMLInputElement::InitColorPicker() {
672
if (mPickerRunning) {
673
NS_WARNING("Just one nsIColorPicker is allowed");
674
return NS_ERROR_FAILURE;
675
}
676
677
nsCOMPtr<Document> doc = OwnerDoc();
678
679
nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
680
if (!win) {
681
return NS_ERROR_FAILURE;
682
}
683
684
if (IsPopupBlocked()) {
685
return NS_OK;
686
}
687
688
// Get Loc title
689
nsAutoString title;
690
nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
691
"ColorPicker", title);
692
693
nsCOMPtr<nsIColorPicker> colorPicker =
694
do_CreateInstance("@mozilla.org/colorpicker;1");
695
if (!colorPicker) {
696
return NS_ERROR_FAILURE;
697
}
698
699
nsAutoString initialValue;
700
GetNonFileValueInternal(initialValue);
701
nsresult rv = colorPicker->Init(win, title, initialValue);
702
NS_ENSURE_SUCCESS(rv, rv);
703
704
nsCOMPtr<nsIColorPickerShownCallback> callback =
705
new nsColorPickerShownCallback(this, colorPicker);
706
707
rv = colorPicker->Open(callback);
708
if (NS_SUCCEEDED(rv)) {
709
mPickerRunning = true;
710
}
711
712
return rv;
713
}
714
715
nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) {
716
if (mPickerRunning) {
717
NS_WARNING("Just one nsIFilePicker is allowed");
718
return NS_ERROR_FAILURE;
719
}
720
721
// Get parent nsPIDOMWindow object.
722
nsCOMPtr<Document> doc = OwnerDoc();
723
724
nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
725
if (!win) {
726
return NS_ERROR_FAILURE;
727
}
728
729
if (IsPopupBlocked()) {
730
return NS_OK;
731
}
732
733
// Get Loc title
734
nsAutoString title;
735
nsAutoString okButtonLabel;
736
if (aType == FILE_PICKER_DIRECTORY) {
737
nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
738
"DirectoryUpload", OwnerDoc(),
739
title);
740
741
nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
742
"DirectoryPickerOkButtonLabel",
743
OwnerDoc(), okButtonLabel);
744
} else {
745
nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
746
"FileUpload", OwnerDoc(), title);
747
}
748
749
nsCOMPtr<nsIFilePicker> filePicker =
750
do_CreateInstance("@mozilla.org/filepicker;1");
751
if (!filePicker) return NS_ERROR_FAILURE;
752
753
int16_t mode;
754
755
if (aType == FILE_PICKER_DIRECTORY) {
756
mode = static_cast<int16_t>(nsIFilePicker::modeGetFolder);
757
} else if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
758
mode = static_cast<int16_t>(nsIFilePicker::modeOpenMultiple);
759
} else {
760
mode = static_cast<int16_t>(nsIFilePicker::modeOpen);
761
}
762
763
nsresult rv = filePicker->Init(win, title, mode);
764
NS_ENSURE_SUCCESS(rv, rv);
765
766
if (!okButtonLabel.IsEmpty()) {
767
filePicker->SetOkButtonLabel(okButtonLabel);
768
}
769
770
// Native directory pickers ignore file type filters, so we don't spend
771
// cycles adding them for FILE_PICKER_DIRECTORY.
772
if (HasAttr(kNameSpaceID_None, nsGkAtoms::accept) &&
773
aType != FILE_PICKER_DIRECTORY) {
774
SetFilePickerFiltersFromAccept(filePicker);
775
776
if (StaticPrefs::dom_capture_enabled()) {
777
const nsAttrValue* captureVal =
778
GetParsedAttr(nsGkAtoms::capture, kNameSpaceID_None);
779
if (captureVal) {
780
filePicker->SetCapture(captureVal->GetEnumValue());
781
}
782
}
783
} else {
784
filePicker->AppendFilters(nsIFilePicker::filterAll);
785
}
786
787
// Set default directory and filename
788
nsAutoString defaultName;
789
790
const nsTArray<OwningFileOrDirectory>& oldFiles =
791
GetFilesOrDirectoriesInternal();
792
793
nsCOMPtr<nsIFilePickerShownCallback> callback =
794
new HTMLInputElement::nsFilePickerShownCallback(this, filePicker);
795
796
if (!oldFiles.IsEmpty() && aType != FILE_PICKER_DIRECTORY) {
797
nsAutoString path;
798
799
nsCOMPtr<nsIFile> parentFile = LastUsedDirectory(oldFiles[0]);
800
if (parentFile) {
801
filePicker->SetDisplayDirectory(parentFile);
802
}
803
804
// Unfortunately nsIFilePicker doesn't allow multiple files to be
805
// default-selected, so only select something by default if exactly
806
// one file was selected before.
807
if (oldFiles.Length() == 1) {
808
nsAutoString leafName;
809
GetDOMFileOrDirectoryName(oldFiles[0], leafName);
810
811
if (!leafName.IsEmpty()) {
812
filePicker->SetDefaultString(leafName);
813
}
814
}
815
816
rv = filePicker->Open(callback);
817
if (NS_SUCCEEDED(rv)) {
818
mPickerRunning = true;
819
}
820
821
return rv;
822
}
823
824
HTMLInputElement::gUploadLastDir->FetchDirectoryAndDisplayPicker(
825
doc, filePicker, callback);
826
mPickerRunning = true;
827
return NS_OK;
828
}
829
830
#define CPS_PREF_NAME NS_LITERAL_STRING("browser.upload.lastDir")
831
832
NS_IMPL_ISUPPORTS(UploadLastDir, nsIObserver, nsISupportsWeakReference)
833
834
void HTMLInputElement::InitUploadLastDir() {
835
gUploadLastDir = new UploadLastDir();
836
NS_ADDREF(gUploadLastDir);
837
838
nsCOMPtr<nsIObserverService> observerService =
839
mozilla::services::GetObserverService();
840
if (observerService && gUploadLastDir) {
841
observerService->AddObserver(gUploadLastDir,
842
"browser:purge-session-history", true);
843
}
844
}
845
846
void HTMLInputElement::DestroyUploadLastDir() { NS_IF_RELEASE(gUploadLastDir); }
847
848
nsresult UploadLastDir::FetchDirectoryAndDisplayPicker(
849
Document* aDoc, nsIFilePicker* aFilePicker,
850
nsIFilePickerShownCallback* aFpCallback) {
851
MOZ_ASSERT(aDoc, "aDoc is null");
852
MOZ_ASSERT(aFilePicker, "aFilePicker is null");
853
MOZ_ASSERT(aFpCallback, "aFpCallback is null");
854
855
nsIURI* docURI = aDoc->GetDocumentURI();
856
MOZ_ASSERT(docURI, "docURI is null");
857
858
nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
859
nsCOMPtr<nsIContentPrefCallback2> prefCallback =
860
new UploadLastDir::ContentPrefCallback(aFilePicker, aFpCallback);
861
862
// Attempt to get the CPS, if it's not present we'll fallback to use the
863
// Desktop folder
864
nsCOMPtr<nsIContentPrefService2> contentPrefService =
865
do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
866
if (!contentPrefService) {
867
prefCallback->HandleCompletion(nsIContentPrefCallback2::COMPLETE_ERROR);
868
return NS_OK;
869
}
870
871
nsAutoCString cstrSpec;
872
docURI->GetSpec(cstrSpec);
873
NS_ConvertUTF8toUTF16 spec(cstrSpec);
874
875
contentPrefService->GetByDomainAndName(spec, CPS_PREF_NAME, loadContext,
876
prefCallback);
877
return NS_OK;
878
}
879
880
nsresult UploadLastDir::StoreLastUsedDirectory(Document* aDoc, nsIFile* aDir) {
881
MOZ_ASSERT(aDoc, "aDoc is null");
882
if (!aDir) {
883
return NS_OK;
884
}
885
886
nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
887
MOZ_ASSERT(docURI, "docURI is null");
888
889
// Attempt to get the CPS, if it's not present we'll just return
890
nsCOMPtr<nsIContentPrefService2> contentPrefService =
891
do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
892
if (!contentPrefService) return NS_ERROR_NOT_AVAILABLE;
893
894
nsAutoCString cstrSpec;
895
docURI->GetSpec(cstrSpec);
896
NS_ConvertUTF8toUTF16 spec(cstrSpec);
897
898
// Find the parent of aFile, and store it
899
nsString unicodePath;
900
aDir->GetPath(unicodePath);
901
if (unicodePath.IsEmpty()) // nothing to do
902
return NS_OK;
903
RefPtr<nsVariantCC> prefValue = new nsVariantCC();
904
prefValue->SetAsAString(unicodePath);
905
906
// Use the document's current load context to ensure that the content pref
907
// service doesn't persistently store this directory for this domain if the
908
// user is using private browsing:
909
nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
910
return contentPrefService->Set(spec, CPS_PREF_NAME, prefValue, loadContext,
911
nullptr);
912
}
913
914
NS_IMETHODIMP
915
UploadLastDir::Observe(nsISupports* aSubject, char const* aTopic,
916
char16_t const* aData) {
917
if (strcmp(aTopic, "browser:purge-session-history") == 0) {
918
nsCOMPtr<nsIContentPrefService2> contentPrefService =
919
do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
920
if (contentPrefService)
921
contentPrefService->RemoveByName(CPS_PREF_NAME, nullptr, nullptr);
922
}
923
return NS_OK;
924
}
925
926
#ifdef ACCESSIBILITY
927
// Helper method
928
static nsresult FireEventForAccessibility(HTMLInputElement* aTarget,
929
EventMessage aEventMessage);
930
#endif
931
932
//
933
// construction, destruction
934
//
935
936
HTMLInputElement::HTMLInputElement(
937
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
938
FromParser aFromParser, FromClone aFromClone)
939
: TextControlElement(std::move(aNodeInfo), aFromParser,
940
kInputDefaultType->value),
941
mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown),
942
mAutocompleteInfoState(nsContentUtils::eAutocompleteAttrState_Unknown),
943
mDisabledChanged(false),
944
mValueChanged(false),
945
mLastValueChangeWasInteractive(false),
946
mCheckedChanged(false),
947
mChecked(false),
948
mHandlingSelectEvent(false),
949
mShouldInitChecked(false),
950
mDoneCreating(aFromParser == NOT_FROM_PARSER &&
951
aFromClone == FromClone::no),
952
mInInternalActivate(false),
953
mCheckedIsToggled(false),
954
mIndeterminate(false),
955
mInhibitRestoration(aFromParser & FROM_PARSER_FRAGMENT),
956
mCanShowValidUI(true),
957
mCanShowInvalidUI(true),
958
mHasRange(false),
959
mIsDraggingRange(false),
960
mNumberControlSpinnerIsSpinning(false),
961
mNumberControlSpinnerSpinsUp(false),
962
mPickerRunning(false),
963
mIsPreviewEnabled(false),
964
mHasBeenTypePassword(false),
965
mHasPatternAttribute(false) {
966
// If size is above 512, mozjemalloc allocates 1kB, see
967
// memory/build/mozjemalloc.cpp
968
static_assert(sizeof(HTMLInputElement) <= 512,
969
"Keep the size of HTMLInputElement under 512 to avoid "
970
"performance regression!");
971
972
// We are in a type=text so we now we currenty need a TextControlState.
973
mInputData.mState = TextControlState::Construct(this);
974
975
void* memory = mInputTypeMem;
976
mInputType = InputType::Create(this, mType, memory);
977
978
if (!gUploadLastDir) HTMLInputElement::InitUploadLastDir();
979
980
// Set up our default state. By default we're enabled (since we're
981
// a control type that can be disabled but not actually disabled
982
// right now), optional, and valid. We are NOT readwrite by default
983
// until someone calls UpdateEditableState on us, apparently! Also
984
// by default we don't have to show validity UI and so forth.
985
AddStatesSilently(NS_EVENT_STATE_ENABLED | NS_EVENT_STATE_OPTIONAL |
986
NS_EVENT_STATE_VALID);
987
UpdateApzAwareFlag();
988
}
989
990
HTMLInputElement::~HTMLInputElement() {
991
if (mNumberControlSpinnerIsSpinning) {
992
StopNumberControlSpinnerSpin(eDisallowDispatchingEvents);
993
}
994
DestroyImageLoadingContent();
995
FreeData();
996
}
997
998
void HTMLInputElement::FreeData() {
999
if (!IsSingleLineTextControl(false)) {
1000
free(mInputData.mValue);
1001
mInputData.mValue = nullptr;
1002
} else {
1003
UnbindFromFrame(nullptr);
1004
mInputData.mState->Destroy();
1005
mInputData.mState = nullptr;
1006
}
1007
1008
if (mInputType) {
1009
mInputType->DropReference();
1010
mInputType = nullptr;
1011
}
1012
}
1013
1014
TextControlState* HTMLInputElement::GetEditorState() const {
1015
if (!IsSingleLineTextControl(false)) {
1016
return nullptr;
1017
}
1018
1019
MOZ_ASSERT(mInputData.mState,
1020
"Single line text controls need to have a state"
1021
" associated with them");
1022
1023
return mInputData.mState;
1024
}
1025
1026
// nsISupports
1027
1028
NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLInputElement)
1029
1030
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement,
1031
TextControlElement)
1032
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
1033
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
1034
if (tmp->IsSingleLineTextControl(false)) {
1035
tmp->mInputData.mState->Traverse(cb);
1036
}
1037
1038
if (tmp->mFileData) {
1039
tmp->mFileData->Traverse(cb);
1040
}
1041
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1042
1043
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement,
1044
TextControlElement)
1045
NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
1046
NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
1047
if (tmp->IsSingleLineTextControl(false)) {
1048
tmp->mInputData.mState->Unlink();
1049
}
1050
1051
if (tmp->mFileData) {
1052
tmp->mFileData->Unlink();
1053
}
1054
// XXX should unlink more?
1055
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1056
1057
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLInputElement,
1058
TextControlElement,
1059
imgINotificationObserver,
1060
nsIImageLoadingContent,
1061
nsIConstraintValidation)
1062
1063
// nsINode
1064
1065
nsresult HTMLInputElement::Clone(dom::NodeInfo* aNodeInfo,
1066
nsINode** aResult) const {
1067
*aResult = nullptr;
1068
1069
RefPtr<HTMLInputElement> it = new (aNodeInfo->NodeInfoManager())
1070
HTMLInputElement(do_AddRef(aNodeInfo), NOT_FROM_PARSER, FromClone::yes);
1071
1072
nsresult rv = const_cast<HTMLInputElement*>(this)->CopyInnerTo(it);
1073
NS_ENSURE_SUCCESS(rv, rv);
1074
1075
switch (GetValueMode()) {
1076
case VALUE_MODE_VALUE:
1077
if (mValueChanged) {
1078
// We don't have our default value anymore. Set our value on
1079
// the clone.
1080
nsAutoString value;
1081
GetNonFileValueInternal(value);
1082
// SetValueInternal handles setting the VALUE_CHANGED bit for us
1083
rv = it->SetValueInternal(value, TextControlState::eSetValue_Notify);
1084
NS_ENSURE_SUCCESS(rv, rv);
1085
}
1086
break;
1087
case VALUE_MODE_FILENAME:
1088
if (it->OwnerDoc()->IsStaticDocument()) {
1089
// We're going to be used in print preview. Since the doc is static
1090
// we can just grab the pretty string and use it as wallpaper
1091
GetDisplayFileName(it->mFileData->mStaticDocFileList);
1092
} else {
1093
it->mFileData->ClearGetFilesHelpers();
1094
it->mFileData->mFilesOrDirectories.Clear();
1095
it->mFileData->mFilesOrDirectories.AppendElements(
1096
mFileData->mFilesOrDirectories);
1097
}
1098
break;
1099
case VALUE_MODE_DEFAULT_ON:
1100
if (mCheckedChanged) {
1101
// We no longer have our original checked state. Set our
1102
// checked state on the clone.
1103
it->DoSetChecked(mChecked, false, true);
1104
// Then tell DoneCreatingElement() not to overwrite:
1105
it->mShouldInitChecked = false;
1106
}
1107
break;
1108
case VALUE_MODE_DEFAULT:
1109
if (mType == NS_FORM_INPUT_IMAGE && it->OwnerDoc()->IsStaticDocument()) {
1110
CreateStaticImageClone(it);
1111
}
1112
break;
1113
}
1114
1115
it->DoneCreatingElement();
1116
1117
it->mLastValueChangeWasInteractive = mLastValueChangeWasInteractive;
1118
it.forget(aResult);
1119
return NS_OK;
1120
}
1121
1122
nsresult HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
1123
const nsAttrValueOrString* aValue,
1124
bool aNotify) {
1125
if (aNameSpaceID == kNameSpaceID_None) {
1126
//
1127
// When name or type changes, radio should be removed from radio group.
1128
// (type changes are handled in the form itself currently)
1129
// If we are not done creating the radio, we also should not do it.
1130
//
1131
if ((aName == nsGkAtoms::name || (aName == nsGkAtoms::type && !mForm)) &&
1132
mType == NS_FORM_INPUT_RADIO && (mForm || mDoneCreating)) {
1133
WillRemoveFromRadioGroup();
1134
} else if (aNotify && aName == nsGkAtoms::disabled) {
1135
mDisabledChanged = true;
1136
} else if (mType == NS_FORM_INPUT_RADIO && aName == nsGkAtoms::required) {
1137
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
1138
1139
if (container && ((aValue && !HasAttr(aNameSpaceID, aName)) ||
1140
(!aValue && HasAttr(aNameSpaceID, aName)))) {
1141
nsAutoString name;
1142
GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
1143
container->RadioRequiredWillChange(name, !!aValue);
1144
}
1145
}
1146
1147
if (aName == nsGkAtoms::webkitdirectory) {
1148
Telemetry::Accumulate(Telemetry::WEBKIT_DIRECTORY_USED, true);
1149
}
1150
}
1151
1152
return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
1153
aValue, aNotify);
1154
}
1155
1156
nsresult HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
1157
const nsAttrValue* aValue,
1158
const nsAttrValue* aOldValue,
1159
nsIPrincipal* aSubjectPrincipal,
1160
bool aNotify) {
1161
if (aNameSpaceID == kNameSpaceID_None) {
1162
//
1163
// When name or type changes, radio should be added to radio group.
1164
// (type changes are handled in the form itself currently)
1165
// If we are not done creating the radio, we also should not do it.
1166
//
1167
if ((aName == nsGkAtoms::name || (aName == nsGkAtoms::type && !mForm)) &&
1168
mType == NS_FORM_INPUT_RADIO && (mForm || mDoneCreating)) {
1169
AddedToRadioGroup();
1170
UpdateValueMissingValidityStateForRadio(false);
1171
}
1172
1173
if (aName == nsGkAtoms::src) {
1174
mSrcTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal(
1175
this, aValue ? aValue->GetStringValue() : EmptyString(),
1176
aSubjectPrincipal);
1177
if (aNotify && mType == NS_FORM_INPUT_IMAGE) {
1178
if (aValue) {
1179
// Mark channel as urgent-start before load image if the image load is
1180
// initiated by a user interaction.
1181
mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput();
1182
1183
LoadImage(aValue->GetStringValue(), true, aNotify,
1184
eImageLoadType_Normal, mSrcTriggeringPrincipal);
1185
} else {
1186
// Null value means the attr got unset; drop the image
1187
CancelImageRequests(aNotify);
1188
}
1189
}
1190
}
1191
1192
// If @value is changed and BF_VALUE_CHANGED is false, @value is the value
1193
// of the element so, if the value of the element is different than @value,
1194
// we have to re-set it. This is only the case when GetValueMode() returns
1195
// VALUE_MODE_VALUE.
1196
if (aName == nsGkAtoms::value) {
1197
if (!mValueChanged && GetValueMode() == VALUE_MODE_VALUE) {
1198
SetDefaultValueAsValue();
1199
}
1200
// GetStepBase() depends on the `value` attribute if `min` is not present,
1201
// even if the value doesn't change.
1202
UpdateStepMismatchValidityState();
1203
}
1204
1205
//
1206
// Checked must be set no matter what type of control it is, since
1207
// mChecked must reflect the new value
1208
if (aName == nsGkAtoms::checked && !mCheckedChanged) {
1209
// Delay setting checked if we are creating this element (wait
1210
// until everything is set)
1211
if (!mDoneCreating) {
1212
mShouldInitChecked = true;
1213
} else {
1214
DoSetChecked(DefaultChecked(), true, false);
1215
}
1216
}
1217
1218
if (aName == nsGkAtoms::type) {
1219
uint8_t newType;
1220
if (!aValue) {
1221
// We're now a text input.
1222
newType = kInputDefaultType->value;
1223
} else {
1224
newType = aValue->GetEnumValue();
1225
}
1226
if (newType != mType) {
1227
HandleTypeChange(newType, aNotify);
1228
}
1229
}
1230
1231
if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
1232
aName == nsGkAtoms::readonly) {
1233
if (aName == nsGkAtoms::disabled) {
1234
// This *has* to be called *before* validity state check because
1235
// UpdateBarredFromConstraintValidation and
1236
// UpdateValueMissingValidityState depend on our disabled state.
1237
UpdateDisabledState(aNotify);
1238
}
1239
1240
if (aName == nsGkAtoms::required && DoesRequiredApply()) {
1241
// This *has* to be called *before* UpdateValueMissingValidityState
1242
// because UpdateValueMissingValidityState depends on our required
1243
// state.
1244
UpdateRequiredState(!!aValue, aNotify);
1245
}
1246
1247
UpdateValueMissingValidityState();
1248
1249
// This *has* to be called *after* validity has changed.
1250
if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
1251
UpdateBarredFromConstraintValidation();
1252
}
1253
} else if (aName == nsGkAtoms::maxlength) {
1254
UpdateTooLongValidityState();
1255
} else if (aName == nsGkAtoms::minlength) {
1256
UpdateTooShortValidityState();
1257
} else if (aName == nsGkAtoms::pattern) {
1258
// Although pattern attribute only applies to single line text controls,
1259
// we set this flag for all input types to save having to check the type
1260
// here.
1261
mHasPatternAttribute = !!aValue;
1262
1263
if (mDoneCreating) {
1264
UpdatePatternMismatchValidityState();
1265
}
1266
} else if (aName == nsGkAtoms::multiple) {
1267
UpdateTypeMismatchValidityState();
1268
} else if (aName == nsGkAtoms::max) {
1269
UpdateHasRange();
1270
nsresult rv = mInputType->MinMaxStepAttrChanged();
1271
NS_ENSURE_SUCCESS(rv, rv);
1272
// Validity state must be updated *after* the UpdateValueDueToAttrChange
1273
// call above or else the following assert will not be valid.
1274
// We don't assert the state of underflow during creation since
1275
// DoneCreatingElement sanitizes.
1276
UpdateRangeOverflowValidityState();
1277
MOZ_ASSERT(!mDoneCreating || mType != NS_FORM_INPUT_RANGE ||
1278
!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
1279
"HTML5 spec does not allow underflow for type=range");
1280
} else if (aName == nsGkAtoms::min) {
1281
UpdateHasRange();
1282
nsresult rv = mInputType->MinMaxStepAttrChanged();
1283
NS_ENSURE_SUCCESS(rv, rv);
1284
// See corresponding @max comment
1285
UpdateRangeUnderflowValidityState();
1286
UpdateStepMismatchValidityState();
1287
MOZ_ASSERT(!mDoneCreating || mType != NS_FORM_INPUT_RANGE ||
1288
!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
1289
"HTML5 spec does not allow underflow for type=range");
1290
} else if (aName == nsGkAtoms::step) {
1291
nsresult rv = mInputType->MinMaxStepAttrChanged();
1292
NS_ENSURE_SUCCESS(rv, rv);
1293
// See corresponding @max comment
1294
UpdateStepMismatchValidityState();
1295
MOZ_ASSERT(!mDoneCreating || mType != NS_FORM_INPUT_RANGE ||
1296
!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
1297
"HTML5 spec does not allow underflow for type=range");
1298
} else if (aName == nsGkAtoms::dir && aValue &&
1299
aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
1300
SetDirectionFromValue(aNotify);
1301
} else if (aName == nsGkAtoms::lang) {
1302
// FIXME(emilio, bug 1605158): This doesn't account for lang changes on
1303
// ancestors.
1304
if (mType == NS_FORM_INPUT_NUMBER) {
1305
// The validity of our value may have changed based on the locale.
1306
UpdateValidityState();
1307
}
1308
} else if (aName == nsGkAtoms::autocomplete) {
1309
// Clear the cached @autocomplete attribute and autocompleteInfo state.
1310
mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown;
1311
mAutocompleteInfoState = nsContentUtils::eAutocompleteAttrState_Unknown;
1312
}
1313
1314
if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
1315
!IsExperimentalMobileType(mType)) {
1316
if (aName == nsGkAtoms::value || aName == nsGkAtoms::readonly ||
1317
aName == nsGkAtoms::tabindex || aName == nsGkAtoms::required ||
1318
aName == nsGkAtoms::disabled) {
1319
// If original target is this and not the inner text control, we should
1320
// pass the focus to the inner text control.
1321
if (Element* dateTimeBoxElement = GetDateTimeBoxElement()) {
1322
AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(
1323
dateTimeBoxElement,
1324
aName == nsGkAtoms::value
1325
? NS_LITERAL_STRING("MozDateTimeValueChanged")
1326
: NS_LITERAL_STRING("MozDateTimeAttributeChanged"),
1327
CanBubble::eNo, ChromeOnlyDispatch::eNo);
1328
dispatcher->RunDOMEventWhenSafe();
1329
}
1330
}
1331
}
1332
}
1333
1334
return nsGenericHTMLFormElementWithState::AfterSetAttr(
1335
aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
1336
}
1337
1338
void HTMLInputElement::BeforeSetForm(bool aBindToTree) {
1339
// No need to remove from radio group if we are just binding to tree.
1340
if (mType == NS_FORM_INPUT_RADIO && !aBindToTree) {
1341
WillRemoveFromRadioGroup();
1342
}
1343
}
1344
1345
void HTMLInputElement::AfterClearForm(bool aUnbindOrDelete) {
1346
MOZ_ASSERT(!mForm);
1347
1348
// Do not add back to radio group if we are releasing or unbinding from tree.
1349
if (mType == NS_FORM_INPUT_RADIO && !aUnbindOrDelete) {
1350
AddedToRadioGroup();
1351
UpdateValueMissingValidityStateForRadio(false);
1352
}
1353
}
1354
1355
void HTMLInputElement::GetAutocomplete(nsAString& aValue) {
1356
if (!DoesAutocompleteApply()) {
1357
return;
1358
}
1359
1360
aValue.Truncate();
1361
const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
1362
1363
mAutocompleteAttrState = nsContentUtils::SerializeAutocompleteAttribute(
1364
attributeVal, aValue, mAutocompleteAttrState);
1365
}
1366
1367
void HTMLInputElement::GetAutocompleteInfo(Nullable<AutocompleteInfo>& aInfo) {
1368
if (!DoesAutocompleteApply()) {
1369
aInfo.SetNull();
1370
return;
1371
}
1372
1373
const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
1374
mAutocompleteInfoState = nsContentUtils::SerializeAutocompleteAttribute(
1375
attributeVal, aInfo.SetValue(), mAutocompleteInfoState, true);
1376
}
1377
1378
void HTMLInputElement::GetCapture(nsAString& aValue) {
1379
GetEnumAttr(nsGkAtoms::capture, kCaptureDefault->tag, aValue);
1380
}
1381
1382
void HTMLInputElement::GetFormEnctype(nsAString& aValue) {
1383
GetEnumAttr(nsGkAtoms::formenctype, "", kFormDefaultEnctype->tag, aValue);
1384
}
1385
1386
void HTMLInputElement::GetFormMethod(nsAString& aValue) {
1387
GetEnumAttr(nsGkAtoms::formmethod, "", kFormDefaultMethod->tag, aValue);
1388
}
1389
1390
void HTMLInputElement::GetInputMode(nsAString& aValue) {
1391
GetEnumAttr(nsGkAtoms::inputmode, nullptr, aValue);
1392
}
1393
1394
void HTMLInputElement::GetType(nsAString& aValue) {
1395
GetEnumAttr(nsGkAtoms::type, kInputDefaultType->tag, aValue);
1396
}
1397
1398
int32_t HTMLInputElement::TabIndexDefault() { return 0; }
1399
1400
uint32_t HTMLInputElement::Height() {
1401
if (mType != NS_FORM_INPUT_IMAGE) {
1402
return 0;
1403
}
1404
RefPtr<imgRequestProxy> currentRequest(mCurrentRequest);
1405
return GetWidthHeightForImage(currentRequest).height;
1406
}
1407
1408
void HTMLInputElement::SetIndeterminateInternal(bool aValue,
1409
bool aShouldInvalidate) {
1410
mIndeterminate = aValue;
1411
1412
if (aShouldInvalidate) {
1413
// Repaint the frame
1414
nsIFrame* frame = GetPrimaryFrame();
1415
if (frame) frame->InvalidateFrameSubtree();
1416
}
1417
1418
UpdateState(true);
1419
}
1420
1421
void HTMLInputElement::SetIndeterminate(bool aValue) {
1422
SetIndeterminateInternal(aValue, true);
1423
}
1424
1425
uint32_t HTMLInputElement::Width() {
1426
if (mType != NS_FORM_INPUT_IMAGE) {
1427
return 0;
1428
}
1429
RefPtr<imgRequestProxy> currentRequest(mCurrentRequest);
1430
return GetWidthHeightForImage(currentRequest).width;
1431
}
1432
1433
bool HTMLInputElement::SanitizesOnValueGetter() const {
1434
// Don't return non-sanitized value for types that are experimental on mobile
1435
// or datetime types or number.
1436
return mType == NS_FORM_INPUT_NUMBER || IsExperimentalMobileType(mType) ||
1437
IsDateTimeInputType(mType);
1438
}
1439
1440
void HTMLInputElement::GetValue(nsAString& aValue, CallerType aCallerType) {
1441
GetValueInternal(aValue, aCallerType);
1442
1443
if (SanitizesOnValueGetter()) {
1444
SanitizeValue(aValue, ForValueGetter::Yes);
1445
}
1446
}
1447
1448
void HTMLInputElement::GetValueInternal(nsAString& aValue,
1449
CallerType aCallerType) const {
1450
if (mType != NS_FORM_INPUT_FILE) {
1451
GetNonFileValueInternal(aValue);
1452
return;
1453
}
1454
1455
if (aCallerType == CallerType::System) {
1456
aValue.Assign(mFileData->mFirstFilePath);
1457
return;
1458
}
1459
1460
if (mFileData->mFilesOrDirectories.IsEmpty()) {
1461
aValue.Truncate();
1462
return;
1463
}
1464
1465
nsAutoString file;
1466
GetDOMFileOrDirectoryName(mFileData->mFilesOrDirectories[0], file);
1467
if (file.IsEmpty()) {
1468
aValue.Truncate();
1469
return;
1470
}
1471
1472
aValue.AssignLiteral("C:\\fakepath\\");
1473
aValue.Append(file);
1474
}
1475
1476
void HTMLInputElement::GetNonFileValueInternal(nsAString& aValue) const {
1477
switch (GetValueMode()) {
1478
case VALUE_MODE_VALUE:
1479
if (IsSingleLineTextControl(false)) {
1480
mInputData.mState->GetValue(aValue, true);
1481
} else if (!aValue.Assign(mInputData.mValue, fallible)) {
1482
aValue.Truncate();
1483
}
1484
return;
1485
1486
case VALUE_MODE_FILENAME:
1487
MOZ_ASSERT_UNREACHABLE("Someone screwed up here");
1488
// We'll just return empty string if someone does screw up.
1489
aValue.Truncate();
1490
return;
1491
1492
case VALUE_MODE_DEFAULT:
1493
// Treat defaultValue as value.
1494
GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue);
1495
return;
1496
1497
case VALUE_MODE_DEFAULT_ON:
1498
// Treat default value as value and returns "on" if no value.
1499
if (!GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue)) {
1500
aValue.AssignLiteral("on");
1501
}
1502
return;
1503
}
1504
}
1505
1506
bool HTMLInputElement::IsValueEmpty() const {
1507
if (GetValueMode() == VALUE_MODE_VALUE && IsSingleLineTextControl(false)) {
1508
return !mInputData.mState->HasNonEmptyValue();
1509
}
1510
1511
nsAutoString value;
1512
GetNonFileValueInternal(value);
1513
1514
return value.IsEmpty();
1515
}
1516
1517
void HTMLInputElement::ClearFiles(bool aSetValueChanged) {
1518
nsTArray<OwningFileOrDirectory> data;
1519
SetFilesOrDirectories(data, aSetValueChanged);
1520
}
1521
1522
int32_t HTMLInputElement::MonthsSinceJan1970(uint32_t aYear,
1523
uint32_t aMonth) const {
1524
return (aYear - 1970) * 12 + aMonth - 1;
1525
}
1526
1527
/* static */
1528
Decimal HTMLInputElement::StringToDecimal(const nsAString& aValue) {
1529
if (!IsAscii(aValue)) {
1530
return Decimal::nan();
1531
}
1532
NS_LossyConvertUTF16toASCII asciiString(aValue);
1533
std::string stdString = asciiString.get();
1534
return Decimal::fromString(stdString);
1535
}
1536
1537
Decimal HTMLInputElement::GetValueAsDecimal() const {
1538
Decimal decimalValue;
1539
nsAutoString stringValue;
1540
1541
GetNonFileValueInternal(stringValue);
1542
1543
return !mInputType->ConvertStringToNumber(stringValue, decimalValue)
1544
? Decimal::nan()
1545
: decimalValue;
1546
}
1547
1548
void HTMLInputElement::SetValue(const nsAString& aValue, CallerType aCallerType,
1549
ErrorResult& aRv) {
1550
// check security. Note that setting the value to the empty string is always
1551
// OK and gives pages a way to clear a file input if necessary.
1552
if (mType == NS_FORM_INPUT_FILE) {
1553
if (!aValue.IsEmpty()) {
1554
if (aCallerType != CallerType::System) {
1555
// setting the value of a "FILE" input widget requires
1556
// chrome privilege
1557
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1558
return;
1559
}
1560
Sequence<nsString> list;
1561
if (!list.AppendElement(aValue, fallible)) {
1562
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
1563
return;
1564
}
1565
1566
MozSetFileNameArray(list, aRv);
1567
return;
1568
} else {
1569
ClearFiles(true);
1570
}
1571
} else {
1572
if (MayFireChangeOnBlur()) {
1573
// If the value has been set by a script, we basically want to keep the
1574
// current change event state. If the element is ready to fire a change
1575
// event, we should keep it that way. Otherwise, we should make sure the
1576
// element will not fire any event because of the script interaction.
1577
//
1578
// NOTE: this is currently quite expensive work (too much string
1579
// manipulation). We should probably optimize that.
1580
nsAutoString currentValue;
1581
GetValue(currentValue, aCallerType);
1582
1583
// Some types sanitize value, so GetValue doesn't return pure
1584
// previous value correctly.
1585
//
1586
// FIXME(emilio): Shouldn't above just use GetNonFileValueInternal() to
1587
// get the unsanitized value?
1588
nsresult rv = SetValueInternal(
1589
aValue, SanitizesOnValueGetter() ? nullptr : &currentValue,
1590
TextControlState::eSetValue_ByContent |
1591
TextControlState::eSetValue_Notify |
1592
TextControlState::eSetValue_MoveCursorToEndIfValueChanged);
1593
if (NS_FAILED(rv)) {
1594
aRv.Throw(rv);
1595
return;
1596
}
1597
1598
if (mFocusedValue.Equals(currentValue)) {
1599
GetValue(mFocusedValue, aCallerType);
1600
}
1601
} else {
1602
nsresult rv = SetValueInternal(
1603
aValue,
1604
TextControlState::eSetValue_ByContent |
1605
TextControlState::eSetValue_Notify |
1606
TextControlState::eSetValue_MoveCursorToEndIfValueChanged);
1607
if (NS_FAILED(rv)) {
1608
aRv.Throw(rv);
1609
return;
1610
}
1611
}
1612
}
1613
}
1614
1615
nsGenericHTMLElement* HTMLInputElement::GetList() const {
1616
nsAutoString dataListId;
1617
GetAttr(kNameSpaceID_None, nsGkAtoms::list_, dataListId);
1618
if (dataListId.IsEmpty()) {
1619
return nullptr;
1620
}
1621
1622
DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
1623
if (!docOrShadow) {
1624
return nullptr;
1625
}
1626
1627
Element* element = docOrShadow->GetElementById(dataListId);
1628
if (!element || !element->IsHTMLElement(nsGkAtoms::datalist)) {
1629
return nullptr;
1630
}
1631
1632
return static_cast<nsGenericHTMLElement*>(element);
1633
}
1634
1635
void HTMLInputElement::SetValue(Decimal aValue, CallerType aCallerType) {
1636
MOZ_ASSERT(!aValue.isInfinity(), "aValue must not be Infinity!");
1637
1638
if (aValue.isNaN()) {
1639
SetValue(EmptyString(), aCallerType, IgnoreErrors());
1640
return;
1641
}
1642
1643
nsAutoString value;
1644
mInputType->ConvertNumberToString(aValue, value);
1645
SetValue(value, aCallerType, IgnoreErrors());
1646
}
1647
1648
void HTMLInputElement::GetValueAsDate(JSContext* aCx,
1649
JS::MutableHandle<JSObject*> aObject,
1650
ErrorResult& aRv) {
1651
aObject.set(nullptr);
1652
if (!IsDateTimeInputType(mType)) {
1653
return;
1654
}
1655
1656
Maybe<JS::ClippedTime> time;
1657
1658
switch (mType) {
1659
case NS_FORM_INPUT_DATE: {
1660
uint32_t year, month, day;
1661
nsAutoString value;
1662
GetNonFileValueInternal(value);
1663
if (!ParseDate(value, &year, &month, &day)) {
1664
return;
1665
}
1666
1667
time.emplace(JS::TimeClip(JS::MakeDate(year, month - 1, day)));
1668
break;
1669
}
1670
case NS_FORM_INPUT_TIME: {
1671
uint32_t millisecond;
1672
nsAutoString value;
1673
GetNonFileValueInternal(value);
1674
if (!ParseTime(value, &millisecond)) {
1675
return;
1676
}
1677
1678
time.emplace(JS::TimeClip(millisecond));
1679
MOZ_ASSERT(time->toDouble() == millisecond,
1680
"HTML times are restricted to the day after the epoch and "
1681
"never clip");
1682
break;
1683
}
1684
case NS_FORM_INPUT_MONTH: {
1685
uint32_t year, month;
1686
nsAutoString value;
1687
GetNonFileValueInternal(value);
1688
if (!ParseMonth(value, &year, &month)) {
1689
return;
1690
}
1691
1692
time.emplace(JS::TimeClip(JS::MakeDate(year, month - 1, 1)));
1693
break;
1694
}
1695
case NS_FORM_INPUT_WEEK: {
1696
uint32_t year, week;
1697
nsAutoString value;
1698
GetNonFileValueInternal(value);
1699
if (!ParseWeek(value, &year, &week)) {
1700
return;
1701
}
1702
1703
double days = DaysSinceEpochFromWeek(year, week);
1704
time.emplace(JS::TimeClip(days * kMsPerDay));
1705
1706
break;
1707
}
1708
case NS_FORM_INPUT_DATETIME_LOCAL: {
1709
uint32_t year, month, day, timeInMs;
1710
nsAutoString value;
1711
GetNonFileValueInternal(value);
1712
if (!ParseDateTimeLocal(value, &year, &month, &day, &timeInMs)) {
1713
return;
1714
}
1715
1716
time.emplace(JS::TimeClip(JS::MakeDate(year, month - 1, day, timeInMs)));
1717
break;
1718
}
1719
}
1720
1721
if (time) {
1722
aObject.set(JS::NewDateObject(aCx, *time));
1723
if (!aObject) {
1724
aRv.NoteJSContextException(aCx);
1725
}
1726
return;
1727
}
1728
1729
MOZ_ASSERT(false, "Unrecognized input type");
1730
aRv.Throw(NS_ERROR_UNEXPECTED);
1731
}
1732
1733
void HTMLInputElement::SetValueAsDate(JSContext* aCx,
1734
JS::Handle<JSObject*> aObj,
1735
ErrorResult& aRv) {
1736
if (!IsDateTimeInputType(mType)) {
1737
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1738
return;
1739
}
1740
1741
if (aObj) {
1742
bool isDate;
1743
if (!JS::ObjectIsDate(aCx, aObj, &isDate)) {
1744
aRv.NoteJSContextException(aCx);
1745
return;
1746
}
1747
if (!isDate) {
1748
aRv.ThrowTypeError("Value being assigned is not a date.");
1749
return;
1750
}
1751
}
1752
1753
double milliseconds;
1754
if (aObj) {
1755
if (!js::DateGetMsecSinceEpoch(aCx, aObj, &milliseconds)) {
1756
aRv.NoteJSContextException(aCx);
1757
return;
1758
}
1759
} else {
1760
milliseconds = UnspecifiedNaN<double>();
1761
}
1762
1763
// At this point we know we're not a file input, so we can just pass "not
1764
// system" as the caller type, since the caller type only matters in the file
1765
// input case.
1766
if (IsNaN(milliseconds)) {
1767
SetValue(EmptyString(), CallerType::NonSystem, aRv);
1768
return;
1769
}
1770
1771
if (mType != NS_FORM_INPUT_MONTH) {
1772
SetValue(Decimal::fromDouble(milliseconds), CallerType::NonSystem);
1773
return;
1774
}
1775
1776
// type=month expects the value to be number of months.
1777
double year = JS::YearFromTime(milliseconds);
1778
double month = JS::MonthFromTime(milliseconds);
1779
1780
if (IsNaN(year) || IsNaN(month)) {
1781