Coverage Report

Created: 2018-09-25 14:53

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