Bug 832923 - Implement <input type='file'> on B2G, r=mounir, r=fabrice

This commit is contained in:
Andrea Marchesini 2013-03-19 18:49:16 +01:00
Родитель 46ca3f76ad
Коммит ccfb81cfa0
12 изменённых файлов: 411 добавлений и 55 удалений

Просмотреть файл

@ -308,6 +308,7 @@ var shell = {
ppmm.addMessageListener("sms-handler", this);
ppmm.addMessageListener("mail-handler", this);
ppmm.addMessageListener("app-notification-send", AlertsHelper);
ppmm.addMessageListener("file-picker", this);
},
stop: function shell_stop() {
@ -532,19 +533,34 @@ var shell = {
},
receiveMessage: function shell_receiveMessage(message) {
var names = { 'content-handler': 'view',
'dial-handler' : 'dial',
'mail-handler' : 'new',
'sms-handler' : 'new' }
var activities = { 'content-handler': { name: 'view', response: null },
'dial-handler': { name: 'dial', response: null },
'mail-handler': { name: 'new', response: null },
'sms-handler': { name: 'new', response: null },
'file-picker': { name: 'pick', response: 'file-picked' } };
if (!(message.name in names))
if (!(message.name in activities))
return;
let data = message.data;
new MozActivity({
name: names[message.name],
let activity = activities[message.name];
let a = new MozActivity({
name: activity.name,
data: data
});
if (activity.response) {
a.onsuccess = function() {
let sender = message.target.QueryInterface(Ci.nsIMessageSender);
sender.sendAsyncMessage(activity.response, { success: true,
result: a.result });
}
a.onerror = function() {
let sender = message.target.QueryInterface(Ci.nsIMessageSender);
sender.sendAsyncMessage(activity.response, { success: false });
}
}
}
};

Просмотреть файл

@ -65,3 +65,7 @@ contract @mozilla.org/recovery-service;1 {b3caca5d-0bb0-48c6-912b-6be6cbf08832}
# B2GAboutRedirector
component {920400b1-cf8f-4760-a9c4-441417b15134} B2GAboutRedirector.js
contract @mozilla.org/network/protocol/about;1?what=certerror {920400b1-cf8f-4760-a9c4-441417b15134}
# FilePicker.js
component {436ff8f9-0acc-4b11-8ec7-e293efba3141} FilePicker.js
contract @mozilla.org/filepicker;1 {436ff8f9-0acc-4b11-8ec7-e293efba3141}

Просмотреть файл

@ -0,0 +1,184 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* No magic constructor behaviour, as is de rigeur for XPCOM.
* If you must perform some initialization, and it could possibly fail (even
* due to an out-of-memory condition), you should use an Init method, which
* can convey failure appropriately (thrown exception in JS,
* NS_FAILED(nsresult) return in C++).
*
* In JS, you can actually cheat, because a thrown exception will cause the
* CreateInstance call to fail in turn, but not all languages are so lucky.
* (Though ANSI C++ provides exceptions, they are verboten in Mozilla code
* for portability reasons -- and even when you're building completely
* platform-specific code, you can't throw across an XPCOM method boundary.)
*/
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
// FIXME: improve this list of filters.
const IMAGE_FILTERS = ['image/gif', 'image/jpeg', 'image/pjpeg',
'image/png', 'image/svg+xml', 'image/tiff',
'image/vnd.microsoft.icon'];
const VIDEO_FILTERS = ['video/mpeg', 'video/mp4', 'video/ogg',
'video/quicktime', 'video/webm', 'video/x-matroska',
'video/x-ms-wmv', 'video/x-flv'];
const AUDIO_FILTERS = ['audio/basic', 'audio/L24', 'audio/mp4',
'audio/mpeg', 'audio/ogg', 'audio/vorbis',
'audio/vnd.rn-realaudio', 'audio/vnd.wave',
'audio/webm'];
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyServiceGetter(this, 'cpmm',
'@mozilla.org/childprocessmessagemanager;1',
'nsIMessageSender');
function FilePicker() {
}
FilePicker.prototype = {
classID: Components.ID('{436ff8f9-0acc-4b11-8ec7-e293efba3141}'),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFilePicker]),
/* members */
mParent: undefined,
mFilterTypes: [],
mFileEnumerator: undefined,
mFilePickerShownCallback: undefined,
/* methods */
init: function(parent, title, mode) {
this.mParent = parent;
if (mode != Ci.nsIFilePicker.modeOpen &&
mode != Ci.nsIFilePicker.modeOpenMultiple) {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
}
},
/* readonly attribute nsILocalFile file - not implemented; */
/* readonly attribute nsISimpleEnumerator files - not implemented; */
/* readonly attribute nsIURI fileURL - not implemented; */
get domfiles() {
return this.mFilesEnumerator;
},
get domfile() {
return this.mFilesEnumerator ? this.mFilesEnumerator.mFiles[0] : null;
},
appendFilters: function(filterMask) {
this.mFilterTypes = null;
// Ci.nsIFilePicker.filterHTML is not supported
// Ci.nsIFilePicker.filterText is not supported
if (filterMask & Ci.nsIFilePicker.filterImages) {
this.mFilterTypes = IMAGE_FILTERS;
}
// Ci.nsIFilePicker.filterXML is not supported
// Ci.nsIFilePicker.filterXUL is not supported
// Ci.nsIFilePicker.filterApps is not supported
// Ci.nsIFilePicker.filterAllowURLs is not supported
if (filterMask & Ci.nsIFilePicker.filterVideo) {
this.mFilterTypes = VIDEO_FILTERS;
}
if (filterMask & Ci.nsIFilePicker.filterAudio) {
this.mFilterTypes = AUDIO_FILTERS;
}
// Ci.nsIFilePicker.filterAll is by default
},
appendFilter: function(title, extensions) {
// pick activity doesn't support extensions
},
open: function(aFilePickerShownCallback) {
this.mFilePickerShownCallback = aFilePickerShownCallback;
cpmm.addMessageListener('file-picked', this);
let detail = {};
if (this.mFilterTypes) {
detail.type = this.mFilterTypes;
}
cpmm.sendAsyncMessage('file-picker', detail);
},
fireSuccess: function(file) {
this.mFilesEnumerator = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]),
mFiles: [file],
mIndex: 0,
hasMoreElements: function() {
return (this.mIndex < this.mFiles.length);
},
getNext: function() {
if (this.mIndex >= this.mFiles.length) {
throw Components.results.NS_ERROR_FAILURE;
}
return this.mFiles[this.mIndex++];
}
};
if (this.mFilePickerShownCallback) {
this.mFilePickerShownCallback.done(Ci.nsIFilePicker.returnOK);
this.mFilePickerShownCallback = null;
}
},
fireError: function() {
if (this.mFilePickerShownCallback) {
this.mFilePickerShownCallback.done(Ci.nsIFilePicker.returnCancel);
this.mFilePickerShownCallback = null;
}
},
receiveMessage: function(message) {
if (message.name !== 'file-picked') {
return;
}
cpmm.removeMessageListener('file-picked', this);
let data = message.data;
if (!data.success || !data.result.blob) {
this.fireError();
return;
}
var mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
var mimeInfo = mimeSvc.getFromTypeAndExtension(data.result.blob.type, '');
var name = 'blob';
if (mimeInfo) {
name += '.' + mimeInfo.primaryExtension;
}
let file = new this.mParent.File(data.result.blob,
{ name: name,
type: data.result.blob.type });
if (file) {
this.fireSuccess(file);
} else {
this.fireError();
}
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FilePicker]);

Просмотреть файл

@ -19,6 +19,7 @@ EXTRA_PP_COMPONENTS = \
ContentHandler.js \
ContentPermissionPrompt.js \
DirectoryProvider.js \
FilePicker.js \
MailtoProtocolHandler.js \
MozKeyboard.js \
ProcessGlobal.js \

Просмотреть файл

@ -379,10 +379,6 @@
@BINPATH@/components/crypto-SDR.js
@BINPATH@/components/jsconsole-clhandler.manifest
@BINPATH@/components/jsconsole-clhandler.js
#ifdef MOZ_GTK2
@BINPATH@/components/nsFilePicker.manifest
@BINPATH@/components/nsFilePicker.js
#endif
@BINPATH@/components/nsHelperAppDlg.manifest
@BINPATH@/components/nsHelperAppDlg.js
@BINPATH@/components/nsDownloadManagerUI.manifest
@ -739,6 +735,7 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/SmsProtocolHandler.js
@BINPATH@/components/TelProtocolHandler.js
@BINPATH@/components/B2GAboutRedirector.js
@BINPATH@/components/FilePicker.js
#ifdef XP_MACOSX
@BINPATH@/@DLL_PREFIX@plugin_child_interpose@DLL_SUFFIX@

Просмотреть файл

@ -267,49 +267,38 @@ nsHTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
nsCOMArray<nsIDOMFile> newFiles;
if (mMulti) {
nsCOMPtr<nsISimpleEnumerator> iter;
nsresult rv = mFilePicker->GetFiles(getter_AddRefs(iter));
nsresult rv = mFilePicker->GetDomfiles(getter_AddRefs(iter));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupports> tmp;
bool prefSaved = false;
bool loop = true;
while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) {
iter->GetNext(getter_AddRefs(tmp));
nsCOMPtr<nsIFile> localFile = do_QueryInterface(tmp);
if (!localFile) {
continue;
}
nsString path;
localFile->GetPath(path);
if (path.IsEmpty()) {
continue;
}
nsCOMPtr<nsIDOMFile> domFile =
do_QueryObject(new nsDOMFileFile(localFile));
nsCOMPtr<nsIDOMFile> domFile = do_QueryInterface(tmp);
MOZ_ASSERT(domFile);
newFiles.AppendObject(domFile);
if (!prefSaved) {
// Store the last used directory using the content pref service
nsHTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(
mInput->OwnerDoc(), localFile);
mInput->OwnerDoc(), domFile);
prefSaved = true;
}
}
}
else {
nsCOMPtr<nsIFile> localFile;
nsresult rv = mFilePicker->GetFile(getter_AddRefs(localFile));
nsCOMPtr<nsIDOMFile> domFile;
nsresult rv = mFilePicker->GetDomfile(getter_AddRefs(domFile));
NS_ENSURE_SUCCESS(rv, rv);
if (localFile) {
nsString path;
rv = localFile->GetPath(path);
if (!path.IsEmpty()) {
nsCOMPtr<nsIDOMFile> domFile=
do_QueryObject(new nsDOMFileFile(localFile));
newFiles.AppendObject(domFile);
// Store the last used directory using the content pref service
nsHTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(
mInput->OwnerDoc(), localFile);
}
if (domFile) {
newFiles.AppendObject(domFile);
// Store the last used directory using the content pref service
nsHTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(
mInput->OwnerDoc(), domFile);
}
}
@ -502,16 +491,27 @@ UploadLastDir::FetchLastUsedDirectory(nsIDocument* aDoc, nsIFile** aFile)
}
nsresult
UploadLastDir::StoreLastUsedDirectory(nsIDocument* aDoc, nsIFile* aFile)
UploadLastDir::StoreLastUsedDirectory(nsIDocument* aDoc, nsIDOMFile* aDomFile)
{
NS_PRECONDITION(aDoc, "aDoc is null");
NS_PRECONDITION(aFile, "aFile is null");
NS_PRECONDITION(aDomFile, "aDomFile is null");
nsString path;
nsresult rv = aDomFile->GetMozFullPathInternal(path);
if (NS_FAILED(rv) || path.IsEmpty()) {
return NS_OK;
}
nsCOMPtr<nsIFile> localFile;
rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
getter_AddRefs(localFile));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
NS_PRECONDITION(docURI, "docURI is null");
nsCOMPtr<nsIFile> parentFile;
aFile->GetParent(getter_AddRefs(parentFile));
localFile->GetParent(getter_AddRefs(parentFile));
if (!parentFile) {
return NS_OK;
}
@ -1882,6 +1882,9 @@ nsHTMLInputElement::GetDisplayFileName(nsAString& aValue) const
for (int32_t i = 0; i < mFiles.Count(); ++i) {
nsString str;
mFiles[i]->GetMozFullPathInternal(str);
if (str.IsEmpty()) {
mFiles[i]->GetName(str);
}
if (i == 0) {
aValue.Append(str);
}

Просмотреть файл

@ -44,10 +44,10 @@ public:
* 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
* @param aDomFile file chosen by the user - the path to the parent of this
* file will be stored
*/
nsresult StoreLastUsedDirectory(nsIDocument* aDoc, nsIFile* aFile);
nsresult StoreLastUsedDirectory(nsIDocument* aDoc, nsIDOMFile* aDomFile);
};
class nsHTMLInputElement : public nsGenericHTMLFormElement,

Просмотреть файл

@ -97,6 +97,7 @@ MockFilePickerInstance.prototype = {
init: function(aParent, aTitle, aMode) {
MockFilePicker.mode = aMode;
this.filterIndex = MockFilePicker.filterIndex;
this.parent = aParent;
},
appendFilter: function(aTitle, aFilter) {
if (typeof MockFilePicker.appendFilterCallback == "function")
@ -108,6 +109,7 @@ MockFilePickerInstance.prototype = {
},
defaultString: "",
defaultExtension: "",
parent: null,
filterIndex: 0,
displayDirectory: null,
get file() {
@ -115,6 +117,14 @@ MockFilePickerInstance.prototype = {
return MockFilePicker.returnFiles[0];
return null;
},
get domfile() {
if (MockFilePicker.returnFiles.length >= 1) {
let utils = this.parent.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return utils.wrapDOMFile(MockFilePicker.returnFiles[0]);
}
return null;
},
get fileURL() {
if (MockFilePicker.returnFiles.length >= 1)
return Services.io.newFileURI(MockFilePicker.returnFiles[0]);
@ -132,6 +142,20 @@ MockFilePickerInstance.prototype = {
}
};
},
get domfiles() {
let utils = this.parent.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return {
index: 0,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]),
hasMoreElements: function() {
return this.index < MockFilePicker.returnFiles.length;
},
getNext: function() {
return utils.wrapDOMFile(MockFilePicker.returnFiles[this.index++]);
}
};
},
show: function() {
MockFilePicker.displayDirectory = this.displayDirectory;
MockFilePicker.shown = true;

Просмотреть файл

@ -88,15 +88,53 @@ nsFilePicker.prototype = {
},
/* readonly attribute nsILocalFile file; */
set file(a) { throw "readonly property"; },
get file() { return this.mFilesEnumerator.mFiles[0]; },
/* readonly attribute nsISimpleEnumerator files; */
set files(a) { throw "readonly property"; },
get files() { return this.mFilesEnumerator; },
/* readonly attribute nsIDOMFile domfile; */
get domfile() {
let enumerator = this.domfiles;
return enumerator ? enumerator.mFiles[0] : null;
},
/* readonly attribute nsISimpleEnumerator domfiles; */
get domfiles() {
if (!this.mFilesEnumerator) {
return null;
}
if (!this.mDOMFilesEnumerator) {
this.mDOMFilesEnumerator {
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISimpleEnumerator]),
mFiles: [],
mIndex: 0,
hasMoreElements: function() {
return (this.mIndex < this.mFiles.length);
},
getNext: function() {
if (this.mIndex >= this.mFiles.length) {
throw Components.results.NS_ERROR_FAILURE;
}
return this.mFiles[this.mIndex++];
}
};
for (var i = 0, i < this.mFilesEnumerator.mFiles.length; ++i) {
var file = this.mParentWindow.wrapDOMFile(
this.mFilesEnumerator.mFiles[i]);
this.mDOMFilesEnumerator.mFiles.push(file);
}
}
return this.mDOMFilesEnumerator;
},
/* readonly attribute nsIURI fileURL; */
set fileURL(a) { throw "readonly property"; },
get fileURL() {
if (this.mFileURL)
return this.mFileURL;
@ -128,6 +166,7 @@ nsFilePicker.prototype = {
/* members */
mFilesEnumerator: undefined,
mDOMFilesEnumerator: undefined,
mParentWindow: null,
/* methods */

Просмотреть файл

@ -8,6 +8,7 @@
interface nsIFile;
interface nsIURI;
interface nsIDOMFile;
interface nsIDOMWindow;
interface nsISimpleEnumerator;
@ -23,7 +24,7 @@ interface nsIFilePickerShownCallback : nsISupports
void done(in short aResult);
};
[scriptable, uuid(60e2dfb6-3fc7-4A2C-8137-16bef44536fc)]
[scriptable, uuid(a6a24df3-d20a-4b6a-96d4-4736b10a51b7)]
interface nsIFilePicker : nsISupports
{
const short modeOpen = 0; // Load a file or directory
@ -141,6 +142,21 @@ interface nsIFilePicker : nsISupports
*/
readonly attribute nsISimpleEnumerator files;
/**
* Get the nsIDOMFile for the file.
*
* @return Returns the file currently selected as DOMFile
*/
readonly attribute nsIDOMFile domfile;
/**
* Get the enumerator for the selected files
* only works in the modeOpenMultiple mode
*
* @return Returns the files currently selected as DOMFiles
*/
readonly attribute nsISimpleEnumerator domfiles;
/**
* Controls whether the chosen file(s) should be added to the system's recent
* documents list. This attribute will be ignored if the system has no "Recent

Просмотреть файл

@ -16,6 +16,7 @@
#include "nsIServiceManager.h"
#include "nsCOMArray.h"
#include "nsIFile.h"
#include "nsDOMFile.h"
#include "nsEnumeratorUtils.h"
#include "mozilla/Services.h"
#include "WidgetUtils.h"
@ -67,6 +68,51 @@ private:
nsRefPtr<nsIFilePickerShownCallback> mCallback;
};
class nsBaseFilePickerEnumerator : public nsISimpleEnumerator
{
public:
NS_DECL_ISUPPORTS
nsBaseFilePickerEnumerator(nsISimpleEnumerator* iterator)
: mIterator(iterator)
{}
virtual ~nsBaseFilePickerEnumerator()
{}
NS_IMETHOD
GetNext(nsISupports** aResult)
{
nsCOMPtr<nsISupports> tmp;
nsresult rv = mIterator->GetNext(getter_AddRefs(tmp));
NS_ENSURE_SUCCESS(rv, rv);
if (!tmp) {
return NS_OK;
}
nsCOMPtr<nsIFile> localFile = do_QueryInterface(tmp);
if (!localFile) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMFile> domFile = new nsDOMFileFile(localFile);
domFile.forget(aResult);
return NS_OK;
}
NS_IMETHOD
HasMoreElements(bool* aResult)
{
return mIterator->HasMoreElements(aResult);
}
private:
nsCOMPtr<nsISimpleEnumerator> mIterator;
};
NS_IMPL_ISUPPORTS1(nsBaseFilePickerEnumerator, nsISimpleEnumerator)
nsBaseFilePicker::nsBaseFilePicker() :
mAddToRecentDocs(true)
{
@ -201,8 +247,6 @@ NS_IMETHODIMP nsBaseFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
return NS_NewArrayEnumerator(aFiles, files);
}
#ifdef BASEFILEPICKER_HAS_DISPLAYDIRECTORY
// Set the display directory
NS_IMETHODIMP nsBaseFilePicker::SetDisplayDirectory(nsIFile *aDirectory)
{
@ -230,7 +274,6 @@ NS_IMETHODIMP nsBaseFilePicker::GetDisplayDirectory(nsIFile **aDirectory)
return rv;
return CallQueryInterface(directory, aDirectory);
}
#endif
NS_IMETHODIMP
nsBaseFilePicker::GetAddToRecentDocs(bool *aFlag)
@ -245,3 +288,35 @@ nsBaseFilePicker::SetAddToRecentDocs(bool aFlag)
mAddToRecentDocs = aFlag;
return NS_OK;
}
NS_IMETHODIMP
nsBaseFilePicker::GetDomfile(nsIDOMFile** aDomfile)
{
nsCOMPtr<nsIFile> localFile;
nsresult rv = GetFile(getter_AddRefs(localFile));
NS_ENSURE_SUCCESS(rv, rv);
if (!localFile) {
*aDomfile = nullptr;
return NS_OK;
}
nsRefPtr<nsDOMFileFile> domFile = new nsDOMFileFile(localFile);
domFile.forget(aDomfile);
return NS_OK;
}
NS_IMETHODIMP
nsBaseFilePicker::GetDomfiles(nsISimpleEnumerator** aDomfiles)
{
nsCOMPtr<nsISimpleEnumerator> iter;
nsresult rv = GetFiles(getter_AddRefs(iter));
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<nsBaseFilePickerEnumerator> retIter =
new nsBaseFilePickerEnumerator(iter);
retIter.forget(aDomfiles);
return NS_OK;
}

Просмотреть файл

@ -15,8 +15,6 @@
class nsIWidget;
#define BASEFILEPICKER_HAS_DISPLAYDIRECTORY 1
class nsBaseFilePicker : public nsIFilePicker
{
public:
@ -32,22 +30,21 @@ public:
NS_IMETHOD GetFilterIndex(int32_t *aFilterIndex);
NS_IMETHOD SetFilterIndex(int32_t aFilterIndex);
NS_IMETHOD GetFiles(nsISimpleEnumerator **aFiles);
#ifdef BASEFILEPICKER_HAS_DISPLAYDIRECTORY
NS_IMETHOD GetDisplayDirectory(nsIFile * *aDisplayDirectory);
NS_IMETHOD SetDisplayDirectory(nsIFile * aDisplayDirectory);
#endif
NS_IMETHOD GetAddToRecentDocs(bool *aFlag);
NS_IMETHOD SetAddToRecentDocs(bool aFlag);
NS_IMETHOD GetDomfile(nsIDOMFile** aDomfile);
NS_IMETHOD GetDomfiles(nsISimpleEnumerator** aDomfiles);
protected:
virtual void InitNative(nsIWidget *aParent, const nsAString& aTitle,
int16_t aMode) = 0;
bool mAddToRecentDocs;
#ifdef BASEFILEPICKER_HAS_DISPLAYDIRECTORY
nsCOMPtr<nsIFile> mDisplayDirectory;
#endif
};
#endif // nsBaseFilePicker_h__