author | David Zbarsky <dzbarsky@gmail.com> |
Fri, 27 Aug 2010 11:49:06 -0400 | |
changeset 51580 | a5a97bafc953463ead28733d769132e0a37ec31d |
parent 51579 | 1c724924c92d1d132d2b00c0a224a827e0122c1e |
child 51581 | af7d35e5d336396844d00cc1c09463be9fb76668 |
push id | unknown |
push user | unknown |
push date | unknown |
reviewers | sicking, blocking |
bugs | 36619 |
milestone | 2.0b5pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -88,24 +88,33 @@ #include "nsIDOMMutationEvent.h" #include "nsIDOMEventTarget.h" #include "nsMutationEvent.h" #include "nsIEventListenerManager.h" #include "nsRuleData.h" -// input type=radio -#include "nsIRadioGroupContainer.h" +// input type=radio +#include "nsIRadioGroupContainer.h" // input type=file #include "nsIFile.h" #include "nsILocalFile.h" #include "nsNetUtil.h" #include "nsDOMFile.h" +#include "nsFileControlFrame.h" +#include "nsTextControlFrame.h" +#include "nsIFilePicker.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIPrivateBrowsingService.h" +#include "nsIContentURIGrouper.h" +#include "nsIContentPrefService.h" +#include "nsIObserverService.h" +#include "nsIPopupWindowManager.h" // input type=image #include "nsImageLoadingContent.h" #include "nsIDOMWindowInternal.h" #include "mozAutoDocUpdate.h" #include "nsHTMLFormElement.h" #include "nsContentCreatorFunctions.h" @@ -134,16 +143,17 @@ static NS_DEFINE_CID(kLookAndFeelCID, NS #define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12) #define NS_CONTROL_TYPE(bits) ((bits) & ~( \ NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE | NS_NO_CONTENT_DISPATCH | \ NS_ORIGINAL_INDETERMINATE_VALUE)) // whether textfields should be selected once focused: // -1: no, 1: yes, 0: uninitialized static PRInt32 gSelectTextFieldOnFocus; +UploadLastDir* nsHTMLInputElement::gUploadLastDir; static const nsAttrValue::EnumTable kInputTypeTable[] = { { "button", NS_FORM_INPUT_BUTTON }, { "checkbox", NS_FORM_INPUT_CHECKBOX }, { "email", NS_FORM_INPUT_EMAIL }, { "file", NS_FORM_INPUT_FILE }, { "hidden", NS_FORM_INPUT_HIDDEN }, { "reset", NS_FORM_INPUT_RESET }, @@ -215,16 +225,371 @@ class nsHTMLInputElementState : public n nsTArray<nsString> mFilenames; PRPackedBool mChecked; PRPackedBool mCheckedSet; }; NS_IMPL_ISUPPORTS1(nsHTMLInputElementState, nsHTMLInputElementState) NS_DEFINE_STATIC_IID_ACCESSOR(nsHTMLInputElementState, NS_INPUT_ELEMENT_STATE_IID) +class AsyncClickHandler : public nsRunnable { +public: + AsyncClickHandler(nsHTMLInputElement* aInput) + : mInput(aInput) { + + nsIDocument* doc = aInput->GetOwnerDoc(); + if (doc) { + nsPIDOMWindow* win = doc->GetWindow(); + if (win) + mPopupControlState = win->GetPopupControlState(); + } + }; + + NS_IMETHOD Run(); + +protected: + nsRefPtr<nsHTMLInputElement> mInput; + PopupControlState mPopupControlState; +}; + +NS_IMETHODIMP +AsyncClickHandler::Run() +{ + nsresult rv; + + // Get parent nsIDOMWindowInternal object. + nsCOMPtr<nsIDocument> doc = mInput->GetOwnerDoc(); + if (!doc) + return NS_ERROR_FAILURE; + + nsPIDOMWindow* win = doc->GetWindow(); + if (!win) { + return NS_ERROR_FAILURE; + } + + // Check if page is allowed to open the popup + if (mPopupControlState != openAllowed) { + nsCOMPtr<nsIPopupWindowManager> pm = + do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID); + + if (!pm) { + return NS_OK; + } + + PRUint32 permission; + pm->TestPermission(doc->GetDocumentURI(), &permission); + if (permission == nsIPopupWindowManager::DENY_POPUP) + return NS_OK; + } + + // Get Loc title + nsXPIDLString title; + nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, + "FileUpload", title); + + nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1"); + if (!filePicker) + return NS_ERROR_FAILURE; + + nsFileControlFrame* frame = static_cast<nsFileControlFrame*>(mInput->GetPrimaryFrame()); + nsTextControlFrame* textFrame = nsnull; + if (frame) + textFrame = static_cast<nsTextControlFrame*>(frame->GetTextFrame()); + + PRBool multi; + rv = mInput->GetMultiple(&multi); + NS_ENSURE_SUCCESS(rv, rv); + + rv = filePicker->Init(win, title, multi ? + (PRInt16)nsIFilePicker::modeOpenMultiple : + (PRInt16)nsIFilePicker::modeOpen); + NS_ENSURE_SUCCESS(rv, rv); + + // We want to get the file filter from the accept attribute and we add the + // |filterAll| filter to be sure the user has a valid fallback. + PRUint32 filter = 0; + if (frame) + filter = frame->GetFileFilterFromAccept(); + filePicker->AppendFilters(filter | nsIFilePicker::filterAll); + + // If the accept attribute asks for a filter, it has to be the default one. + if (filter) { + // We have two filters: |filterAll| and another one. |filterAll| is + // always the first one (index=0) so we can assume the one we want to be + // the default is at index 1. + filePicker->SetFilterIndex(1); + } + + // Set default directry and filename + nsAutoString defaultName; + + nsCOMArray<nsIFile> oldFiles; + mInput->GetFileArray(oldFiles); + + if (oldFiles.Count()) { + // set directory + nsCOMPtr<nsIFile> parentFile; + oldFiles[0]->GetParent(getter_AddRefs(parentFile)); + if (parentFile) { + nsCOMPtr<nsILocalFile> parentLocalFile = do_QueryInterface(parentFile, &rv); + if (parentLocalFile) { + filePicker->SetDisplayDirectory(parentLocalFile); + } + } + + // Unfortunately nsIFilePicker doesn't allow multiple files to be + // default-selected, so only select something by default if exactly + // one file was selected before. + if (oldFiles.Count() == 1) { + nsAutoString leafName; + oldFiles[0]->GetLeafName(leafName); + if (!leafName.IsEmpty()) { + filePicker->SetDefaultString(leafName); + } + } + } else { + // Attempt to retrieve the last used directory from the content pref service + nsCOMPtr<nsILocalFile> localFile; + nsHTMLInputElement::gUploadLastDir->FetchLastUsedDirectory(doc->GetDocumentURI(), + getter_AddRefs(localFile)); + if (!localFile) { + // Default to "desktop" directory for each platform + nsCOMPtr<nsIFile> homeDir; + NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(homeDir)); + localFile = do_QueryInterface(homeDir); + } + filePicker->SetDisplayDirectory(localFile); + } + + // Tell our textframe to remember the currently focused value + if (textFrame) + textFrame->InitFocusedValue(); + + // Open dialog + PRInt16 mode; + rv = filePicker->Show(&mode); + NS_ENSURE_SUCCESS(rv, rv); + if (mode == nsIFilePicker::returnCancel) + return NS_OK; + + // Collect new selected filenames + nsTArray<nsString> newFileNames; + if (multi) { + nsCOMPtr<nsISimpleEnumerator> iter; + rv = filePicker->GetFiles(getter_AddRefs(iter)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsISupports> tmp; + PRBool prefSaved = PR_FALSE; + while (NS_SUCCEEDED(iter->GetNext(getter_AddRefs(tmp)))) { + nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(tmp); + if (localFile) { + nsString unicodePath; + rv = localFile->GetPath(unicodePath); + if (!unicodePath.IsEmpty()) { + newFileNames.AppendElement(unicodePath); + } + if (!prefSaved) { + // Store the last used directory using the content pref service + rv = nsHTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(doc->GetDocumentURI(), + localFile); + NS_ENSURE_SUCCESS(rv, rv); + prefSaved = PR_TRUE; + } + } + } + } + else { + nsCOMPtr<nsILocalFile> localFile; + rv = filePicker->GetFile(getter_AddRefs(localFile)); + if (localFile) { + nsString unicodePath; + rv = localFile->GetPath(unicodePath); + if (!unicodePath.IsEmpty()) { + newFileNames.AppendElement(unicodePath); + } + // Store the last used directory using the content pref service + rv = nsHTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(doc->GetDocumentURI(), + localFile); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + // Set new selected files + if (!newFileNames.IsEmpty()) { + // Tell mTextFrame that this update of the value is a user initiated + // change. Otherwise it'll think that the value is being set by a script + // and not fire onchange when it should. + PRBool oldState; + if (textFrame) { + oldState = textFrame->GetFireChangeEventState(); + textFrame->SetFireChangeEventState(PR_TRUE); + } + + mInput->SetFileNames(newFileNames); + if (textFrame) { + textFrame->SetFireChangeEventState(oldState); + // May need to fire an onchange here + textFrame->CheckFireOnChange(); + } + } + + return NS_OK; +} + +#define CPS_PREF_NAME NS_LITERAL_STRING("browser.upload.lastDir") + +NS_IMPL_ISUPPORTS2(UploadLastDir, nsIObserver, nsISupportsWeakReference) + +void +nsHTMLInputElement::InitUploadLastDir() { + gUploadLastDir = new UploadLastDir(); + NS_ADDREF(gUploadLastDir); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (observerService && gUploadLastDir) { + observerService->AddObserver(gUploadLastDir, NS_PRIVATE_BROWSING_SWITCH_TOPIC, PR_TRUE); + observerService->AddObserver(gUploadLastDir, "browser:purge-session-history", PR_TRUE); + } +} + +void +nsHTMLInputElement::DestroyUploadLastDir() { + NS_IF_RELEASE(gUploadLastDir); +} + +UploadLastDir::UploadLastDir(): + mInPrivateBrowsing(PR_FALSE) +{ + nsCOMPtr<nsIPrivateBrowsingService> pbService = + do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID); + if (pbService) { + pbService->GetPrivateBrowsingEnabled(&mInPrivateBrowsing); + } + + mUploadLastDirStore.Init(); +} + +nsresult +UploadLastDir::FetchLastUsedDirectory(nsIURI* aURI, nsILocalFile** aFile) +{ + NS_PRECONDITION(aURI, "aURI is null"); + NS_PRECONDITION(aFile, "aFile is null"); + // Retrieve the data from memory if it's present during private browsing mode, + // otherwise fall through to check the CPS + if (mInPrivateBrowsing) { + nsCOMPtr<nsIContentURIGrouper> hostnameGrouperService = + do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID); + if (!hostnameGrouperService) + return NS_ERROR_NOT_AVAILABLE; + nsString group; + hostnameGrouperService->Group(aURI, group); + + if (mUploadLastDirStore.Get(group, aFile)) { + return NS_OK; + } + } + + // Attempt to get the CPS, if it's not present we'll just return + nsCOMPtr<nsIContentPrefService> contentPrefService = + do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID); + if (!contentPrefService) + return NS_ERROR_NOT_AVAILABLE; + nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID); + if (!uri) + return NS_ERROR_OUT_OF_MEMORY; + uri->SetAsISupports(aURI); + + // Get the last used directory, if it is stored + PRBool hasPref; + if (NS_SUCCEEDED(contentPrefService->HasPref(uri, CPS_PREF_NAME, &hasPref)) && hasPref) { + nsCOMPtr<nsIVariant> pref; + contentPrefService->GetPref(uri, CPS_PREF_NAME, nsnull, getter_AddRefs(pref)); + nsString prefStr; + pref->GetAsAString(prefStr); + + nsCOMPtr<nsILocalFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); + if (!localFile) + return NS_ERROR_OUT_OF_MEMORY; + localFile->InitWithPath(prefStr); + + *aFile = localFile; + NS_ADDREF(*aFile); + } + return NS_OK; +} + +nsresult +UploadLastDir::StoreLastUsedDirectory(nsIURI* aURI, nsILocalFile* aFile) +{ + NS_PRECONDITION(aURI, "aURI is null"); + NS_PRECONDITION(aFile, "aFile is null"); + nsCOMPtr<nsIFile> parentFile; + aFile->GetParent(getter_AddRefs(parentFile)); + nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(parentFile); + + // Store the data in memory instead of the CPS during private browsing mode + if (mInPrivateBrowsing) { + nsCOMPtr<nsIContentURIGrouper> hostnameGrouperService = + do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID); + if (!hostnameGrouperService) + return NS_ERROR_NOT_AVAILABLE; + nsString group; + hostnameGrouperService->Group(aURI, group); + + return mUploadLastDirStore.Put(group, localFile); + } + + // Attempt to get the CPS, if it's not present we'll just return + nsCOMPtr<nsIContentPrefService> contentPrefService = + do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID); + if (!contentPrefService) + return NS_ERROR_NOT_AVAILABLE; + nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID); + if (!uri) + return NS_ERROR_OUT_OF_MEMORY; + uri->SetAsISupports(aURI); + + // Find the parent of aFile, and store it + nsString unicodePath; + parentFile->GetPath(unicodePath); + if (unicodePath.IsEmpty()) // nothing to do + return NS_OK; + nsCOMPtr<nsIWritableVariant> prefValue = do_CreateInstance(NS_VARIANT_CONTRACTID); + if (!prefValue) + return NS_ERROR_OUT_OF_MEMORY; + prefValue->SetAsAString(unicodePath); + return contentPrefService->SetPref(uri, CPS_PREF_NAME, prefValue); +} + +NS_IMETHODIMP +UploadLastDir::Observe(nsISupports *aSubject, char const *aTopic, PRUnichar const *aData) +{ + if (strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) { + if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData)) { + mInPrivateBrowsing = PR_TRUE; + } else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) { + mInPrivateBrowsing = PR_FALSE; + if (mUploadLastDirStore.IsInitialized()) { + mUploadLastDirStore.Clear(); + } + } + } else if (strcmp(aTopic, "browser:purge-session-history") == 0) { + if (mUploadLastDirStore.IsInitialized()) { + mUploadLastDirStore.Clear(); + } + nsCOMPtr<nsIContentPrefService> contentPrefService = + do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID); + if (contentPrefService) + contentPrefService->RemovePrefsByName(CPS_PREF_NAME); + } + return NS_OK; +} + #ifdef ACCESSIBILITY //Helper method static nsresult FireEventForAccessibility(nsIDOMHTMLInputElement* aTarget, nsPresContext* aPresContext, const nsAString& aEventType); #endif // @@ -239,16 +604,19 @@ nsHTMLInputElement::nsHTMLInputElement(a mType(kInputDefaultType->value), mBitField(0) { SET_BOOLBIT(mBitField, BF_PARSER_CREATING, aFromParser); SET_BOOLBIT(mBitField, BF_INHIBIT_RESTORATION, aFromParser & NS_FROM_PARSER_FRAGMENT); mInputData.mState = new nsTextEditorState(this); NS_ADDREF(mInputData.mState); + + if (!gUploadLastDir) + nsHTMLInputElement::InitUploadLastDir(); } nsHTMLInputElement::~nsHTMLInputElement() { DestroyImageLoadingContent(); FreeData(); } @@ -1409,17 +1777,18 @@ nsHTMLInputElement::Click() // see what type of input we are. Only click button, checkbox, radio, // reset, submit, & image if (mType == NS_FORM_INPUT_BUTTON || mType == NS_FORM_INPUT_CHECKBOX || mType == NS_FORM_INPUT_RADIO || mType == NS_FORM_INPUT_RESET || mType == NS_FORM_INPUT_SUBMIT || - mType == NS_FORM_INPUT_IMAGE) { + mType == NS_FORM_INPUT_IMAGE || + mType == NS_FORM_INPUT_FILE) { // Strong in case the event kills it nsCOMPtr<nsIDocument> doc = GetCurrentDoc(); if (!doc) { return rv; } nsCOMPtr<nsIPresShell> shell = doc->GetShell(); @@ -1441,27 +1810,36 @@ nsHTMLInputElement::Click() // called from chrome JS. Mark this event trusted if Click() // is called from chrome code. nsMouseEvent event(nsContentUtils::IsCallerChrome(), NS_MOUSE_CLICK, nsnull, nsMouseEvent::eReal); event.inputSource = nsIDOMNSMouseEvent::MOZ_SOURCE_UNKNOWN; nsEventStatus status = nsEventStatus_eIgnore; SET_BOOLBIT(mBitField, BF_HANDLING_CLICK, PR_TRUE); - + if (mType == NS_FORM_INPUT_FILE){ + FireAsyncClickHandler(); + } nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this), context, &event, nsnull, &status); SET_BOOLBIT(mBitField, BF_HANDLING_CLICK, PR_FALSE); } } return NS_OK; } +NS_IMETHODIMP +nsHTMLInputElement::FireAsyncClickHandler() +{ + nsCOMPtr<nsIRunnable> event = new AsyncClickHandler(this); + return NS_DispatchToMainThread(event); +} + PRBool nsHTMLInputElement::NeedToInitializeEditorForEvent(nsEventChainPreVisitor& aVisitor) const { // We only need to initialize the editor for single line input controls because they // are lazily initialized. We don't need to initialize the control for // certain types of events, because we know that those events are safe to be // handled without the editor being initialized. These events include: // mousein/move/out, and DOM mutation events.
--- a/content/html/content/src/nsHTMLInputElement.h +++ b/content/html/content/src/nsHTMLInputElement.h @@ -70,16 +70,49 @@ #define GET_BOOLBIT(bitfield, field) (((bitfield) & (0x01 << (field))) \ ? PR_TRUE : PR_FALSE) #define SET_BOOLBIT(bitfield, field, b) ((b) \ ? ((bitfield) |= (0x01 << (field))) \ : ((bitfield) &= ~(0x01 << (field)))) class nsDOMFileList; +class UploadLastDir : public nsIObserver, public nsSupportsWeakReference { +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + UploadLastDir(); + + /** + * Fetch the last used directory for this location from the content + * pref service, if it is available. + * + * @param aURI URI of the current page + * @param aFile path to the last used directory + */ + nsresult FetchLastUsedDirectory(nsIURI* aURI, nsILocalFile** aFile); + + /** + * Store the last used directory for this location using the + * content pref service, if it is available + * @param aURI URI of the current page + * @param aFile file chosen by the user - the path to the parent of this + * file will be stored + */ + nsresult StoreLastUsedDirectory(nsIURI* aURI, nsILocalFile* aFile); +private: + // Directories are stored here during private browsing mode + nsInterfaceHashtable<nsStringHashKey, nsILocalFile> mUploadLastDirStore; + PRBool mInPrivateBrowsing; +}; + +class nsIRadioGroupContainer; +class nsIRadioVisitor; + class nsHTMLInputElement : public nsGenericHTMLFormElement, public nsImageLoadingContent, public nsIDOMHTMLInputElement, public nsITextControlElement, public nsIPhonetic, public nsIDOMNSEditableElement, public nsIFileControlElement, public nsIConstraintValidation @@ -194,24 +227,32 @@ public: * button in the group. * * @return the selected button (or null). */ already_AddRefed<nsIDOMHTMLInputElement> GetSelectedRadioButton(); virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; + NS_IMETHOD FireAsyncClickHandler(); + virtual void UpdateEditableState() { return UpdateEditableFormControlState(); } NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLInputElement, nsGenericHTMLFormElement) + static UploadLastDir* gUploadLastDir; + // create and destroy the static UploadLastDir object for remembering + // which directory was last used on a site-by-site basis + static void InitUploadLastDir(); + static void DestroyUploadLastDir(); + void MaybeLoadImage(); virtual nsXPCClassInfo* GetClassInfo(); // nsIConstraintValidation PRBool IsTooLong(); PRBool IsValueMissing(); PRBool HasTypeMismatch();
--- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -83,17 +83,17 @@ #include "nsWebSocket.h" #include "nsDOMThreadService.h" #include "nsHTMLDNSPrefetch.h" #include "nsHtml5Module.h" #include "nsCrossSiteListenerProxy.h" #include "nsFocusManager.h" #include "nsFrameList.h" #include "nsListControlFrame.h" -#include "nsFileControlFrame.h" +#include "nsHTMLInputElement.h" #ifdef MOZ_SVG #include "nsSVGUtils.h" #endif #ifdef MOZ_XUL #include "nsXULPopupManager.h" #include "nsXULContentUtils.h" #include "nsXULElement.h" @@ -378,10 +378,10 @@ nsLayoutStatics::Shutdown() nsHtml5Module::ReleaseStatics(); nsRegion::ShutdownStatic(); NS_ShutdownChainItemPool(); nsFrameList::Shutdown(); - nsFileControlFrame::DestroyUploadLastDir(); + nsHTMLInputElement::DestroyUploadLastDir(); }
--- a/layout/forms/nsFileControlFrame.cpp +++ b/layout/forms/nsFileControlFrame.cpp @@ -76,69 +76,30 @@ #include "nsIDOMHTMLInputElement.h" #ifdef ACCESSIBILITY #include "nsIAccessibilityService.h" #endif #include "nsInterfaceHashtable.h" #include "nsURIHashKey.h" #include "nsILocalFile.h" -#include "nsIPrivateBrowsingService.h" #include "nsNetCID.h" -#include "nsIObserver.h" -#include "nsIObserverService.h" #include "nsWeakReference.h" #include "nsIVariant.h" -#include "nsIContentPrefService.h" -#include "nsIContentURIGrouper.h" #include "mozilla/Services.h" #include "nsDirectoryServiceDefs.h" #include "nsCharSeparatedTokenizer.h" +#include "nsHTMLInputElement.h" #include "nsICapturePicker.h" #include "nsIFileURL.h" #define SYNC_TEXT 0x1 #define SYNC_BUTTON 0x2 #define SYNC_BOTH 0x3 -#define CPS_PREF_NAME NS_LITERAL_STRING("browser.upload.lastDir") - -class UploadLastDir : public nsIObserver, public nsSupportsWeakReference { -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - - UploadLastDir(); - - /** - * Fetch the last used directory for this location from the content - * pref service, if it is available. - * - * @param aURI URI of the current page - * @param aFile path to the last used directory - */ - nsresult FetchLastUsedDirectory(nsIURI* aURI, nsILocalFile** aFile); - - /** - * Store the last used directory for this location using the - * content pref service, if it is available - * @param aURI URI of the current page - * @param aFile file chosen by the user - the path to the parent of this - * file will be stored - */ - nsresult StoreLastUsedDirectory(nsIURI* aURI, nsILocalFile* aFile); -private: - // Directories are stored here during private browsing mode - nsInterfaceHashtable<nsStringHashKey, nsILocalFile> mUploadLastDirStore; - PRBool mInPrivateBrowsing; -}; - -NS_IMPL_ISUPPORTS2(UploadLastDir, nsIObserver, nsISupportsWeakReference) -UploadLastDir* gUploadLastDir = nsnull; - nsIFrame* NS_NewFileControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsFileControlFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsFileControlFrame) @@ -158,19 +119,16 @@ nsFileControlFrame::Init(nsIContent* aCo nsresult rv = nsBlockFrame::Init(aContent, aParent, aPrevInFlow); NS_ENSURE_SUCCESS(rv, rv); mMouseListener = new BrowseMouseListener(this); NS_ENSURE_TRUE(mMouseListener, NS_ERROR_OUT_OF_MEMORY); mCaptureMouseListener = new CaptureMouseListener(this); NS_ENSURE_TRUE(mCaptureMouseListener, NS_ERROR_OUT_OF_MEMORY); - if (!gUploadLastDir) - nsFileControlFrame::InitUploadLastDir(); - return rv; } void nsFileControlFrame::DestroyFrom(nsIFrame* aDestructRoot) { mTextFrame = nsnull; ENSURE_TRUE(mContent); @@ -524,329 +482,24 @@ nsFileControlFrame::CaptureMouseListener } /** * This is called when our browse button is clicked */ NS_IMETHODIMP nsFileControlFrame::BrowseMouseListener::MouseClick(nsIDOMEvent* aMouseEvent) { - nsresult rv; - NS_ASSERTION(mFrame, "We should have been unregistered"); if (!ShouldProcessMouseClick(aMouseEvent)) return NS_OK; - - // Get parent nsIDOMWindowInternal object. + nsIContent* content = mFrame->GetContent(); - nsCOMPtr<nsIDOMHTMLInputElement> inputElem = do_QueryInterface(content); - nsCOMPtr<nsIFileControlElement> fileControl = do_QueryInterface(content); - if (!content || !inputElem || !fileControl) - return NS_ERROR_FAILURE; - - nsCOMPtr<nsIDocument> doc = content->GetDocument(); - if (!doc) - return NS_ERROR_FAILURE; - - // Get Loc title - nsXPIDLString title; - nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, - "FileUpload", title); - - nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1"); - if (!filePicker) - return NS_ERROR_FAILURE; - - nsPIDOMWindow* win = doc->GetWindow(); - if (!win) { - return NS_ERROR_FAILURE; - } - - PRBool multi; - rv = inputElem->GetMultiple(&multi); - NS_ENSURE_SUCCESS(rv, rv); - - rv = filePicker->Init(win, title, multi ? - (PRInt16)nsIFilePicker::modeOpenMultiple : - (PRInt16)nsIFilePicker::modeOpen); - NS_ENSURE_SUCCESS(rv, rv); - - // We want to get the file filter from the accept attribute and we add the - // |filterAll| filter to be sure the user has a valid fallback. - PRUint32 filter = mFrame->GetFileFilterFromAccept(); - filePicker->AppendFilters(filter | nsIFilePicker::filterAll); - - // If the accept attribute asks for a filter, it has to be the default one. - if (filter) { - // We have two filters: |filterAll| and another one. |filterAll| is - // always the first one (index=0) so we can assume the one we want to be - // the default is at index 1. - filePicker->SetFilterIndex(1); - } - - // Set default directry and filename - nsAutoString defaultName; - - nsCOMArray<nsIFile> oldFiles; - fileControl->GetFileArray(oldFiles); - - if (oldFiles.Count()) { - // set directory - nsCOMPtr<nsIFile> parentFile; - oldFiles[0]->GetParent(getter_AddRefs(parentFile)); - if (parentFile) { - nsCOMPtr<nsILocalFile> parentLocalFile = do_QueryInterface(parentFile, &rv); - if (parentLocalFile) { - filePicker->SetDisplayDirectory(parentLocalFile); - } - } - - // Unfortunately nsIFilePicker doesn't allow multiple files to be - // default-selected, so only select something by default if exactly - // one file was selected before. - if (oldFiles.Count() == 1) { - nsAutoString leafName; - oldFiles[0]->GetLeafName(leafName); - if (!leafName.IsEmpty()) { - filePicker->SetDefaultString(leafName); - } - } - } else { - // Attempt to retrieve the last used directory from the content pref service - nsCOMPtr<nsILocalFile> localFile; - gUploadLastDir->FetchLastUsedDirectory(doc->GetDocumentURI(), - getter_AddRefs(localFile)); - if (!localFile) { - // Default to "desktop" directory for each platform - nsCOMPtr<nsIFile> homeDir; - NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(homeDir)); - localFile = do_QueryInterface(homeDir); - } - filePicker->SetDisplayDirectory(localFile); - } - - // Tell our textframe to remember the currently focused value - mFrame->mTextFrame->InitFocusedValue(); - - // Open dialog - PRInt16 mode; - rv = filePicker->Show(&mode); - NS_ENSURE_SUCCESS(rv, rv); - if (mode == nsIFilePicker::returnCancel) - return NS_OK; - - if (!mFrame) { - // The frame got destroyed while the filepicker was up. Don't do - // anything here. - // (This listener itself can't be destroyed because the event listener - // manager holds a strong reference to us while it fires the event.) - return NS_OK; - } - - // Collect new selected filenames - nsTArray<nsString> newFileNames; - if (multi) { - nsCOMPtr<nsISimpleEnumerator> iter; - rv = filePicker->GetFiles(getter_AddRefs(iter)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsISupports> tmp; - PRBool prefSaved = PR_FALSE; - while (NS_SUCCEEDED(iter->GetNext(getter_AddRefs(tmp)))) { - nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(tmp); - if (localFile) { - nsString unicodePath; - rv = localFile->GetPath(unicodePath); - if (!unicodePath.IsEmpty()) { - newFileNames.AppendElement(unicodePath); - } - if (!prefSaved) { - // Store the last used directory using the content pref service - rv = gUploadLastDir->StoreLastUsedDirectory(doc->GetDocumentURI(), - localFile); - NS_ENSURE_SUCCESS(rv, rv); - prefSaved = PR_TRUE; - } - } - } - } - else { - nsCOMPtr<nsILocalFile> localFile; - rv = filePicker->GetFile(getter_AddRefs(localFile)); - if (localFile) { - nsString unicodePath; - rv = localFile->GetPath(unicodePath); - if (!unicodePath.IsEmpty()) { - newFileNames.AppendElement(unicodePath); - } - // Store the last used directory using the content pref service - rv = gUploadLastDir->StoreLastUsedDirectory(doc->GetDocumentURI(), - localFile); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - // Set new selected files - if (!newFileNames.IsEmpty()) { - // Tell mTextFrame that this update of the value is a user initiated - // change. Otherwise it'll think that the value is being set by a script - // and not fire onchange when it should. - PRBool oldState = mFrame->mTextFrame->GetFireChangeEventState(); - mFrame->mTextFrame->SetFireChangeEventState(PR_TRUE); - fileControl->SetFileNames(newFileNames); - - mFrame->mTextFrame->SetFireChangeEventState(oldState); - // May need to fire an onchange here - mFrame->mTextFrame->CheckFireOnChange(); - } - - return NS_OK; -} - -void nsFileControlFrame::InitUploadLastDir() { - gUploadLastDir = new UploadLastDir(); - NS_IF_ADDREF(gUploadLastDir); - - nsCOMPtr<nsIObserverService> observerService = - mozilla::services::GetObserverService(); - if (observerService && gUploadLastDir) { - observerService->AddObserver(gUploadLastDir, NS_PRIVATE_BROWSING_SWITCH_TOPIC, PR_TRUE); - observerService->AddObserver(gUploadLastDir, "browser:purge-session-history", PR_TRUE); - } -} - -void nsFileControlFrame::DestroyUploadLastDir() { - if (gUploadLastDir) - NS_RELEASE(gUploadLastDir); -} - -UploadLastDir::UploadLastDir(): - mInPrivateBrowsing(PR_FALSE) -{ - nsCOMPtr<nsIPrivateBrowsingService> pbService = - do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID); - if (pbService) { - pbService->GetPrivateBrowsingEnabled(&mInPrivateBrowsing); - } - - mUploadLastDirStore.Init(); -} - -nsresult -UploadLastDir::FetchLastUsedDirectory(nsIURI* aURI, nsILocalFile** aFile) -{ - NS_PRECONDITION(aURI, "aURI is null"); - NS_PRECONDITION(aFile, "aFile is null"); - // Retrieve the data from memory if it's present during private browsing mode, - // otherwise fall through to check the CPS - if (mInPrivateBrowsing) { - nsCOMPtr<nsIContentURIGrouper> hostnameGrouperService = - do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID); - if (!hostnameGrouperService) - return NS_ERROR_NOT_AVAILABLE; - nsString group; - hostnameGrouperService->Group(aURI, group); - - if (mUploadLastDirStore.Get(group, aFile)) { - return NS_OK; - } - } - - // Attempt to get the CPS, if it's not present we'll just return - nsCOMPtr<nsIContentPrefService> contentPrefService = - do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID); - if (!contentPrefService) - return NS_ERROR_NOT_AVAILABLE; - nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID); - if (!uri) - return NS_ERROR_OUT_OF_MEMORY; - uri->SetAsISupports(aURI); - - // Get the last used directory, if it is stored - PRBool hasPref; - if (NS_SUCCEEDED(contentPrefService->HasPref(uri, CPS_PREF_NAME, &hasPref)) && hasPref) { - nsCOMPtr<nsIVariant> pref; - contentPrefService->GetPref(uri, CPS_PREF_NAME, nsnull, getter_AddRefs(pref)); - nsString prefStr; - pref->GetAsAString(prefStr); - - nsCOMPtr<nsILocalFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); - if (!localFile) - return NS_ERROR_OUT_OF_MEMORY; - localFile->InitWithPath(prefStr); - - *aFile = localFile; - NS_ADDREF(*aFile); - } - return NS_OK; -} - -nsresult -UploadLastDir::StoreLastUsedDirectory(nsIURI* aURI, nsILocalFile* aFile) -{ - NS_PRECONDITION(aURI, "aURI is null"); - NS_PRECONDITION(aFile, "aFile is null"); - nsCOMPtr<nsIFile> parentFile; - aFile->GetParent(getter_AddRefs(parentFile)); - nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(parentFile); - - // Store the data in memory instead of the CPS during private browsing mode - if (mInPrivateBrowsing) { - nsCOMPtr<nsIContentURIGrouper> hostnameGrouperService = - do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID); - if (!hostnameGrouperService) - return NS_ERROR_NOT_AVAILABLE; - nsString group; - hostnameGrouperService->Group(aURI, group); - - return mUploadLastDirStore.Put(group, localFile); - } - - // Attempt to get the CPS, if it's not present we'll just return - nsCOMPtr<nsIContentPrefService> contentPrefService = - do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID); - if (!contentPrefService) - return NS_ERROR_NOT_AVAILABLE; - nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID); - if (!uri) - return NS_ERROR_OUT_OF_MEMORY; - uri->SetAsISupports(aURI); - - // Find the parent of aFile, and store it - nsString unicodePath; - parentFile->GetPath(unicodePath); - if (unicodePath.IsEmpty()) // nothing to do - return NS_OK; - nsCOMPtr<nsIWritableVariant> prefValue = do_CreateInstance(NS_VARIANT_CONTRACTID); - if (!prefValue) - return NS_ERROR_OUT_OF_MEMORY; - prefValue->SetAsAString(unicodePath); - return contentPrefService->SetPref(uri, CPS_PREF_NAME, prefValue); -} - -NS_IMETHODIMP -UploadLastDir::Observe(nsISupports *aSubject, char const *aTopic, PRUnichar const *aData) -{ - if (strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) { - if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData)) { - mInPrivateBrowsing = PR_TRUE; - } else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) { - mInPrivateBrowsing = PR_FALSE; - if (mUploadLastDirStore.IsInitialized()) { - mUploadLastDirStore.Clear(); - } - } - } else if (strcmp(aTopic, "browser:purge-session-history") == 0) { - if (mUploadLastDirStore.IsInitialized()) { - mUploadLastDirStore.Clear(); - } - nsCOMPtr<nsIContentPrefService> contentPrefService = - do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID); - if (contentPrefService) - contentPrefService->RemovePrefsByName(CPS_PREF_NAME); + if (content->IsHTML() && content->Tag() == nsGkAtoms::input) { + nsHTMLInputElement* input = static_cast<nsHTMLInputElement*>(content); + return input->FireAsyncClickHandler(); } return NS_OK; } nscoord nsFileControlFrame::GetMinWidth(nsIRenderingContext *aRenderingContext) { nscoord result;
--- a/layout/forms/nsFileControlFrame.h +++ b/layout/forms/nsFileControlFrame.h @@ -94,37 +94,35 @@ public: // nsIAnonymousContentCreator virtual nsresult CreateAnonymousContent(nsTArray<nsIContent*>& aElements); virtual void AppendAnonymousContentTo(nsBaseContentList& aElements); #ifdef ACCESSIBILITY virtual already_AddRefed<nsAccessible> CreateAccessible(); #endif - // create and destroy the static UploadLastDir object for remembering - // which directory was last used on a site-by-site basis - static void InitUploadLastDir(); - static void DestroyUploadLastDir(); - /** * This methods return the file filter mask requested by the HTML5 accept * attribute. If the accept attribute isn't present or the value isn't valid, * the returned value will be 0. * * See: * http://dev.w3.org/html5/spec/forms.html#attr-input-accept * * @return the file picker filter mask or 0 if there is no filter. */ PRInt32 GetFileFilterFromAccept() const; typedef PRBool (*AcceptAttrCallback)(const nsAString&, void*); void ParseAcceptAttribute(AcceptAttrCallback aCallback, void* aClosure) const; + nsIFrame* GetTextFrame() { return mTextFrame; } + protected: + class MouseListener; friend class MouseListener; class MouseListener : public nsIDOMMouseListener { public: NS_DECL_ISUPPORTS MouseListener(nsFileControlFrame* aFrame) : mFrame(aFrame) @@ -156,17 +154,17 @@ protected: PRUint32 mMode; }; class BrowseMouseListener: public MouseListener { public: BrowseMouseListener(nsFileControlFrame* aFrame) : MouseListener(aFrame) {}; NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent); }; - + virtual PRBool IsFrameOfType(PRUint32 aFlags) const { return nsBlockFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock)); } virtual PRIntn GetSkipSides() const;