зеркало из https://github.com/mozilla/gecko-dev.git
Bug 907767 - Make HTMLInputElement::OpenDirectoryPicker dispatch progress events. r=smaug
This commit is contained in:
Родитель
5dbe9b1217
Коммит
ca081831f5
|
@ -24,6 +24,7 @@
|
|||
#include "nsContentCID.h"
|
||||
#include "nsIComponentManager.h"
|
||||
#include "nsIDOMHTMLFormElement.h"
|
||||
#include "nsIDOMProgressEvent.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsPresContext.h"
|
||||
|
@ -214,6 +215,9 @@ const Decimal HTMLInputElement::kStepAny = 0;
|
|||
{0xb5, 0x13, 0x7b, 0x36, 0x93, 0x43, 0xe3, 0xa0} \
|
||||
}
|
||||
|
||||
#define PROGRESS_STR "progress"
|
||||
static const uint32_t kProgressEventInterval = 50; // ms
|
||||
|
||||
class HTMLInputElementState MOZ_FINAL : public nsISupports
|
||||
{
|
||||
public:
|
||||
|
@ -483,6 +487,7 @@ public:
|
|||
nsCOMPtr<nsIDOMFile> domFile = do_QueryInterface(tmp);
|
||||
MOZ_ASSERT(domFile);
|
||||
mFileList.AppendElement(domFile);
|
||||
mInput->SetFileListProgress(mFileList.Length());
|
||||
}
|
||||
return NS_DispatchToMainThread(this);
|
||||
}
|
||||
|
@ -495,6 +500,7 @@ public:
|
|||
// event because it will think this is done by a script.
|
||||
// So, we can safely send one by ourself.
|
||||
mInput->SetFiles(mFileList, true);
|
||||
mInput->MaybeDispatchProgressEvent(true); // last progress event
|
||||
nsresult rv =
|
||||
nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
|
||||
|
@ -567,6 +573,9 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
|
|||
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||
NS_ASSERTION(target, "Must have stream transport service");
|
||||
|
||||
mInput->ResetProgressCounters();
|
||||
mInput->StartProgressEventTimer();
|
||||
|
||||
// DirPickerBuildFileListTask takes care of calling SetFiles() and
|
||||
// dispatching the "change" event.
|
||||
nsRefPtr<DirPickerBuildFileListTask> event =
|
||||
|
@ -992,6 +1001,8 @@ static nsresult FireEventForAccessibility(nsIDOMHTMLInputElement* aTarget,
|
|||
HTMLInputElement::HTMLInputElement(already_AddRefed<nsINodeInfo> aNodeInfo,
|
||||
FromParser aFromParser)
|
||||
: nsGenericHTMLFormElementWithState(aNodeInfo)
|
||||
, mFileListProgress(0)
|
||||
, mLastFileListProgress(0)
|
||||
, mType(kInputDefaultType->value)
|
||||
, mDisabledChanged(false)
|
||||
, mValueChanged(false)
|
||||
|
@ -1008,6 +1019,7 @@ HTMLInputElement::HTMLInputElement(already_AddRefed<nsINodeInfo> aNodeInfo,
|
|||
, mCanShowInvalidUI(true)
|
||||
, mHasRange(false)
|
||||
, mIsDraggingRange(false)
|
||||
, mProgressTimerIsActive(false)
|
||||
{
|
||||
// We are in a type=text so we now we currenty need a nsTextEditorState.
|
||||
mInputData.mState = new nsTextEditorState(this);
|
||||
|
@ -1096,7 +1108,7 @@ NS_IMPL_RELEASE_INHERITED(HTMLInputElement, Element)
|
|||
|
||||
// QueryInterface implementation for HTMLInputElement
|
||||
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLInputElement)
|
||||
NS_INTERFACE_TABLE_INHERITED8(HTMLInputElement,
|
||||
NS_INTERFACE_TABLE_INHERITED9(HTMLInputElement,
|
||||
nsIDOMHTMLInputElement,
|
||||
nsITextControlElement,
|
||||
nsIPhonetic,
|
||||
|
@ -1104,6 +1116,7 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLInputElement)
|
|||
nsIImageLoadingContent,
|
||||
imgIOnloadBlocker,
|
||||
nsIDOMNSEditableElement,
|
||||
nsITimerCallback,
|
||||
nsIConstraintValidation)
|
||||
NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLFormElementWithState)
|
||||
|
||||
|
@ -2431,6 +2444,93 @@ HTMLInputElement::OpenDirectoryPicker(ErrorResult& aRv)
|
|||
InitFilePicker(FILE_PICKER_DIRECTORY);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::StartProgressEventTimer()
|
||||
{
|
||||
if (!mProgressTimer) {
|
||||
mProgressTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
}
|
||||
if (mProgressTimer) {
|
||||
mProgressTimerIsActive = true;
|
||||
mProgressTimer->Cancel();
|
||||
mProgressTimer->InitWithCallback(this, kProgressEventInterval,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
}
|
||||
|
||||
// nsITimerCallback's only method
|
||||
NS_IMETHODIMP
|
||||
HTMLInputElement::Notify(nsITimer* aTimer)
|
||||
{
|
||||
if (mProgressTimer == aTimer) {
|
||||
mProgressTimerIsActive = false;
|
||||
MaybeDispatchProgressEvent(false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Just in case some JS user wants to QI to nsITimerCallback and play with us...
|
||||
NS_WARNING("Unexpected timer!");
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::MaybeDispatchProgressEvent(bool aFinalProgress)
|
||||
{
|
||||
nsRefPtr<HTMLInputElement> kungFuDeathGrip;
|
||||
|
||||
if (aFinalProgress && mProgressTimerIsActive) {
|
||||
// mProgressTimer may hold the last reference to us, so take another strong
|
||||
// ref to make sure we don't die under Cancel() and leave this method
|
||||
// running on deleted memory.
|
||||
kungFuDeathGrip = this;
|
||||
|
||||
mProgressTimerIsActive = false;
|
||||
mProgressTimer->Cancel();
|
||||
}
|
||||
|
||||
if (mProgressTimerIsActive ||
|
||||
mFileListProgress == mLastFileListProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aFinalProgress) {
|
||||
StartProgressEventTimer();
|
||||
}
|
||||
|
||||
mLastFileListProgress = mFileListProgress;
|
||||
|
||||
DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR),
|
||||
false, mLastFileListProgress,
|
||||
0);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::DispatchProgressEvent(const nsAString& aType,
|
||||
bool aLengthComputable,
|
||||
uint64_t aLoaded, uint64_t aTotal)
|
||||
{
|
||||
NS_ASSERTION(!aType.IsEmpty(), "missing event type");
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
nsresult rv = NS_NewDOMProgressEvent(getter_AddRefs(event), this,
|
||||
nullptr, nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
|
||||
if (!progress) {
|
||||
return;
|
||||
}
|
||||
|
||||
progress->InitProgressEvent(aType, false, false, aLengthComputable,
|
||||
aLoaded, (aTotal == UINT64_MAX) ? 0 : aTotal);
|
||||
|
||||
event->SetTrusted(true);
|
||||
|
||||
DispatchDOMEvent(nullptr, event, nullptr, nullptr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLInputElement::UpdateFileList()
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "nsImageLoadingContent.h"
|
||||
#include "nsIDOMHTMLInputElement.h"
|
||||
#include "nsITextControlElement.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIPhonetic.h"
|
||||
#include "nsIDOMNSEditableElement.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
@ -82,6 +83,7 @@ class HTMLInputElement MOZ_FINAL : public nsGenericHTMLFormElementWithState,
|
|||
public nsITextControlElement,
|
||||
public nsIPhonetic,
|
||||
public nsIDOMNSEditableElement,
|
||||
public nsITimerCallback,
|
||||
public nsIConstraintValidation
|
||||
{
|
||||
public:
|
||||
|
@ -225,6 +227,14 @@ public:
|
|||
|
||||
void MaybeLoadImage();
|
||||
|
||||
// nsITimerCallback
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
// Avoid warning about the implementation of nsITimerCallback::Notify hiding
|
||||
// our nsImageLoadingContent base class' implementation of
|
||||
// imgINotificationObserver::Notify:
|
||||
using nsImageLoadingContent::Notify;
|
||||
|
||||
// nsIConstraintValidation
|
||||
bool IsTooLong();
|
||||
bool IsValueMissing() const;
|
||||
|
@ -392,6 +402,17 @@ public:
|
|||
|
||||
void OpenDirectoryPicker(ErrorResult& aRv);
|
||||
|
||||
void ResetProgressCounters()
|
||||
{
|
||||
mFileListProgress = 0;
|
||||
mLastFileListProgress = 0;
|
||||
}
|
||||
void StartProgressEventTimer();
|
||||
void MaybeDispatchProgressEvent(bool aFinalProgress);
|
||||
void DispatchProgressEvent(const nsAString& aType,
|
||||
bool aLengthComputable,
|
||||
uint64_t aLoaded, uint64_t aTotal);
|
||||
|
||||
// XPCOM GetFormAction() is OK
|
||||
void SetFormAction(const nsAString& aValue, ErrorResult& aRv)
|
||||
{
|
||||
|
@ -648,6 +669,13 @@ public:
|
|||
|
||||
// XPCOM GetPhonetic() is OK
|
||||
|
||||
void SetFileListProgress(uint32_t mFileCount)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(),
|
||||
"Why are we calling this on the main thread?");
|
||||
mFileListProgress = mFileCount;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual JSObject* WrapNode(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
@ -1144,6 +1172,13 @@ protected:
|
|||
*/
|
||||
Decimal mRangeThumbDragStartValue;
|
||||
|
||||
/**
|
||||
* Timer that is used when mType == NS_FORM_INPUT_FILE and the user selects a
|
||||
* directory. It is used to fire progress events while the list of files
|
||||
* under that directory tree is built.
|
||||
*/
|
||||
nsCOMPtr<nsITimer> mProgressTimer;
|
||||
|
||||
// Step scale factor values, for input types that have one.
|
||||
static const Decimal kStepScaleFactorDate;
|
||||
static const Decimal kStepScaleFactorNumberRange;
|
||||
|
@ -1159,6 +1194,19 @@ protected:
|
|||
// Float value returned by GetStep() when the step attribute is set to 'any'.
|
||||
static const Decimal kStepAny;
|
||||
|
||||
/**
|
||||
* The number of files added to the FileList being built off-main-thread when
|
||||
* mType == NS_FORM_INPUT_FILE and the user selects a directory. This is set
|
||||
* off the main thread, read on main thread.
|
||||
*/
|
||||
mozilla::Atomic<uint32_t> mFileListProgress;
|
||||
|
||||
/**
|
||||
* The number of files added to the FileList at the time the last progress
|
||||
* event was fired.
|
||||
*/
|
||||
uint32_t mLastFileListProgress;
|
||||
|
||||
/**
|
||||
* The type of this input (<input type=...>) as an integer.
|
||||
* @see nsIFormControl.h (specifically NS_FORM_INPUT_*)
|
||||
|
@ -1179,6 +1227,7 @@ protected:
|
|||
bool mCanShowInvalidUI : 1;
|
||||
bool mHasRange : 1;
|
||||
bool mIsDraggingRange : 1;
|
||||
bool mProgressTimerIsActive : 1;
|
||||
|
||||
private:
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче