Bug 275453 - Finish <upload> implementation. r=allan, r=aaronr. Not part of default build.

This commit is contained in:
pedemont%us.ibm.com 2005-10-23 04:24:23 +00:00
Родитель e01ea0902e
Коммит 2f5b9ff526
13 изменённых файлов: 747 добавлений и 258 удалений

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

@ -97,6 +97,7 @@ REQUIRES = \
intl \ intl \
pref \ pref \
htmlparser \ htmlparser \
exthandler \
$(NULL) $(NULL)
# XForms IDLs # XForms IDLs
@ -123,6 +124,8 @@ XPIDLSRCS = \
nsIXFormsItemElement.idl \ nsIXFormsItemElement.idl \
nsIXFormsLabelElement.idl \ nsIXFormsLabelElement.idl \
nsIXFormsItemSetUIElement.idl \ nsIXFormsItemSetUIElement.idl \
nsIXFormsUploadElement.idl \
nsIXFormsUploadUIElement.idl \
$(NULL) $(NULL)
# XForms source files # XForms source files

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

@ -0,0 +1,57 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla XForms support.
*
* The Initial Developer of the Original Code is
* IBM Corporation.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Javier Pedemonte <jhpedemonte@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
/**
* Interface implemented by the XTF part of the upload element to allow
* communication from XBL code.
*/
[scriptable, uuid(6dc00fd0-674c-41b3-8b0b-ba6c02aa769a)]
interface nsIXFormsUploadElement : nsISupports
{
/**
* Display file picker dialog so user can select a file.
*/
void pickFile();
/**
* Unset file from \<upload\> and instance data.
*/
void clearFile();
};

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

@ -0,0 +1,51 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla XForms support.
*
* The Initial Developer of the Original Code is
* IBM Corporation.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Javier Pedemonte <jhpedemonte@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
/**
* Interface implemented by the XBL part of the upload element.
*/
[scriptable, uuid(82c82c46-f111-416c-a3e6-ce9a0864d00f)]
interface nsIXFormsUploadUIElement : nsISupports
{
/**
* Set text value of \<upload\>'s text field.
*/
void setFieldText(in AString value);
};

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

@ -315,17 +315,6 @@ nsXFormsControlStubBase::ResetHelpAndHint(PRBool aInitialize)
} }
} }
PRBool
nsXFormsControlStubBase::GetReadOnlyState()
{
PRBool res = PR_FALSE;
nsCOMPtr<nsIContent> content(do_QueryInterface(mElement));
if (content && (content->IntrinsicState() & NS_EVENT_STATE_MOZ_READONLY)) {
res = PR_TRUE;
}
return res;
}
PRBool PRBool
nsXFormsControlStubBase::GetRelevantState() nsXFormsControlStubBase::GetRelevantState()
{ {

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

@ -178,9 +178,6 @@ protected:
*/ */
PRInt32 mBindAttrsCount; PRInt32 mBindAttrsCount;
/** Returns the read only state of the control (ie. mBoundNode) */
PRBool GetReadOnlyState();
/** Returns the relevant state of the control */ /** Returns the relevant state of the control */
PRBool GetRelevantState(); PRBool GetRelevantState();

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

@ -1289,8 +1289,8 @@ nsXFormsSubmissionElement::CopyChildren(nsIDOMNode *source, nsIDOMNode *dest,
nsCOMPtr<nsIContent> content = do_QueryInterface(currentNode); nsCOMPtr<nsIContent> content = do_QueryInterface(currentNode);
NS_ENSURE_STATE(content); NS_ENSURE_STATE(content);
nsILocalFile *file = nsIFile *file =
NS_STATIC_CAST(nsILocalFile *, NS_STATIC_CAST(nsIFile *,
content->GetProperty(nsXFormsAtoms::uploadFileProperty)); content->GetProperty(nsXFormsAtoms::uploadFileProperty));
// NOTE: this value may be null if a file hasn't been selected. // NOTE: this value may be null if a file hasn't been selected.
@ -1655,8 +1655,8 @@ nsXFormsSubmissionElement::AppendMultipartFormData(nsIDOMNode *data,
nsCOMPtr<nsIContent> content = do_QueryInterface(data); nsCOMPtr<nsIContent> content = do_QueryInterface(data);
NS_ENSURE_STATE(content); NS_ENSURE_STATE(content);
nsILocalFile *file = nsIFile *file =
NS_STATIC_CAST(nsILocalFile *, NS_STATIC_CAST(nsIFile *,
content->GetProperty(nsXFormsAtoms::uploadFileProperty)); content->GetProperty(nsXFormsAtoms::uploadFileProperty));
nsAutoString leafName; nsAutoString leafName;

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

@ -36,26 +36,142 @@
* *
* ***** END LICENSE BLOCK ***** */ * ***** END LICENSE BLOCK ***** */
#include "nsIDOMEventTarget.h" #include "nsIXFormsUploadElement.h"
#include "nsIDOM3Node.h" #include "nsIXFormsUploadUIElement.h"
#include "nsIDOMElement.h"
#include "nsMemory.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsIXTFXMLVisualWrapper.h"
#include "nsIDOMDocument.h"
#include "nsXFormsControlStub.h"
#include "nsISchema.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsXFormsAtoms.h"
#include "nsAutoPtr.h"
#include "nsIDOMXPathResult.h"
#include "nsIDOMFocusListener.h"
#include "nsXFormsUtils.h" #include "nsXFormsUtils.h"
#include "nsIModelElementPrivate.h"
#include "nsIContent.h" #include "nsIContent.h"
#include "nsIDOMXPathExpression.h"
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "nsXFormsDelegateStub.h"
#include "nsIMIMEService.h"
#include "nsCExternalHandlerService.h"
#include "plbase64.h"
#include "nsIFilePicker.h"
#include "nsIDOMDocument.h"
#include "nsIDOMDocumentView.h"
#include "nsIDOMAbstractView.h"
#include "nsIDOMWindowInternal.h"
#include "nsIStringBundle.h"
#include "nsIDOM3Node.h"
#include "nsAutoBuffer.h"
#include "nsIEventStateManager.h"
#include "prmem.h"
#define NS_HTMLFORM_BUNDLE_URL \
"chrome://global/locale/layout/HtmlForm.properties"
enum nsBoundType {
TYPE_DEFAULT,
TYPE_ANYURI,
TYPE_BASE64,
TYPE_HEX
};
/**
* Implementation of the \<upload\> element.
*/
class nsXFormsUploadElement : public nsXFormsDelegateStub,
public nsIXFormsUploadElement
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIXFORMSUPLOADELEMENT
NS_IMETHOD Refresh();
private:
/**
* Returns the type of the node to which this element is bound.
*/
nsBoundType GetBoundType();
/**
* Sets file path/contents into instance data. If aFile is nsnull,
* this clears the data.
*/
nsresult SetFile(nsILocalFile *aFile);
/**
* Sets "filename" & "mediatype" in the instance data, using the given file.
* If aFile == nsnull, then this function clears the values in the
* instance data.
*/
nsresult HandleChildElements(nsILocalFile *aFile, PRBool *aChanged);
/**
* Read the contents of the file and encode in Base64 or Hex. |aResult| must
* be freed by nsMemory::Free().
*/
nsresult EncodeFileContents(nsIFile *aFile, nsBoundType aType,
PRUnichar **aResult);
void BinaryToHex(const char *aBuffer, PRUint32 aCount,
PRUnichar **aHexString);
};
NS_IMPL_ISUPPORTS_INHERITED1(nsXFormsUploadElement,
nsXFormsDelegateStub,
nsIXFormsUploadElement)
NS_IMETHODIMP
nsXFormsUploadElement::Refresh()
{
// This is called when the 'bind', 'ref' or 'model' attribute has changed.
// So we need to make sure that the element is still bound to a node of
// type 'anyURI', 'base64Binary', or 'hexBinary'.
nsresult rv = nsXFormsDelegateStub::Refresh();
NS_ENSURE_SUCCESS(rv, rv);
if (!mBoundNode)
return NS_OK;
// If it is not bound to 'anyURI', 'base64Binary', or 'hexBinary', then
// mark as not relevant.
// XXX Bug 313313 - the 'relevant' state should be handled in XBL constructor.
nsCOMPtr<nsIXTFElementWrapper> xtfWrap(do_QueryInterface(mElement));
NS_ENSURE_STATE(xtfWrap);
if (GetBoundType() == TYPE_DEFAULT) {
xtfWrap->SetIntrinsicState(NS_EVENT_STATE_DISABLED);
nsXFormsUtils::ReportError(NS_LITERAL_STRING("uploadBoundTypeError"),
mElement);
} else {
xtfWrap->SetIntrinsicState(NS_EVENT_STATE_ENABLED);
}
return NS_OK;
}
nsBoundType
nsXFormsUploadElement::GetBoundType()
{
nsBoundType result = TYPE_DEFAULT;
// get type bound to node
nsAutoString type, prefix, nsuri;
nsresult rv = nsXFormsUtils::ParseTypeFromNode(mBoundNode, type, prefix);
if (NS_SUCCEEDED(rv)) {
// get the namespace url from the prefix
nsCOMPtr<nsIDOM3Node> domNode3 = do_QueryInterface(mElement, &rv);
if (NS_SUCCEEDED(rv)) {
rv = domNode3->LookupNamespaceURI(prefix, nsuri);
if (NS_SUCCEEDED(rv) && nsuri.EqualsLiteral(NS_NAMESPACE_XML_SCHEMA)) {
if (type.EqualsLiteral("anyURI")) {
result = TYPE_ANYURI;
} else if (type.EqualsLiteral("base64Binary")) {
result = TYPE_BASE64;
} else if (type.EqualsLiteral("hexBinary")) {
result = TYPE_HEX;
}
}
}
}
return result;
}
static void static void
ReleaseObject(void *aObject, ReleaseObject(void *aObject,
@ -66,215 +182,153 @@ ReleaseObject(void *aObject,
NS_STATIC_CAST(nsISupports *, aPropertyValue)->Release(); NS_STATIC_CAST(nsISupports *, aPropertyValue)->Release();
} }
/**
* Implementation of the \<upload\> element.
*/
class nsXFormsUploadElement : public nsIDOMFocusListener,
public nsXFormsControlStub
{
public:
NS_DECL_ISUPPORTS_INHERITED
// nsIXTFXMLVisual overrides
NS_IMETHOD OnCreated(nsIXTFXMLVisualWrapper *aWrapper);
// nsIXTFVisual overrides
NS_IMETHOD GetVisualContent(nsIDOMElement **aElement);
NS_IMETHOD GetInsertionPoint(nsIDOMElement **aPoint);
// nsIXTFElement overrides
NS_IMETHOD OnDestroyed();
NS_IMETHOD AttributeSet(nsIAtom *aName, const nsAString &aValue);
NS_IMETHOD AttributeRemoved(nsIAtom *aName);
// nsIXFormsControl
NS_IMETHOD Refresh();
NS_IMETHOD TryFocus(PRBool* aOK);
// nsIDOMEventListener
NS_IMETHOD HandleEvent(nsIDOMEvent *aEvent);
// nsIDOMFocusListener
NS_IMETHOD Focus(nsIDOMEvent *aEvent);
NS_IMETHOD Blur(nsIDOMEvent *aEvent);
private:
nsCOMPtr<nsIDOMElement> mLabel;
nsCOMPtr<nsIDOMHTMLInputElement> mInput;
};
NS_IMPL_ISUPPORTS_INHERITED2(nsXFormsUploadElement,
nsXFormsControlStub,
nsIDOMFocusListener,
nsIDOMEventListener)
// nsIXTFXMLVisual
NS_IMETHODIMP NS_IMETHODIMP
nsXFormsUploadElement::OnCreated(nsIXTFXMLVisualWrapper *aWrapper) nsXFormsUploadElement::PickFile()
{ {
nsresult rv = nsXFormsControlStub::OnCreated(aWrapper); if (!mElement)
return NS_OK;
nsresult rv;
// get localized file picker title text
nsCOMPtr<nsIStringBundleService> bundleService =
do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIStringBundle> bundle;
rv = bundleService->CreateBundle(NS_HTMLFORM_BUNDLE_URL,
getter_AddRefs(bundle));
NS_ENSURE_SUCCESS(rv, rv);
nsXPIDLString filepickerTitle;
rv = bundle->GetStringFromName(NS_LITERAL_STRING("FileUpload").get(),
getter_Copies(filepickerTitle));
if (NS_FAILED(rv)) {
// fall back to English text
filepickerTitle.AssignASCII("File Upload");
}
// get nsIDOMWindowInternal
nsCOMPtr<nsIDOMDocument> doc;
mElement->GetOwnerDocument(getter_AddRefs(doc));
nsCOMPtr<nsIDOMDocumentView> dview = do_QueryInterface(doc);
NS_ENSURE_STATE(dview);
nsCOMPtr<nsIDOMAbstractView> aview;
dview->GetDefaultView(getter_AddRefs(aview));
nsCOMPtr<nsIDOMWindowInternal> internal = do_QueryInterface(aview);
NS_ENSURE_STATE(internal);
// init filepicker
nsCOMPtr<nsIFilePicker> filePicker =
do_CreateInstance("@mozilla.org/filepicker;1");
if (!filePicker)
return NS_ERROR_FAILURE;
rv = filePicker->Init(internal, filepickerTitle, nsIFilePicker::modeOpen);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Our anonymous content structure will look like this: // set filter "All Files"
// // XXX implement "@mediatype" file filters
// <label> (mLabel) filePicker->AppendFilters(nsIFilePicker::filterAll);
// <span/> (insertion point)
// <input/> (mInput)
// </label>
nsCOMPtr<nsIDOMDocument> domDoc; // open dialog
mElement->GetOwnerDocument(getter_AddRefs(domDoc)); PRInt16 mode;
rv = filePicker->Show(&mode);
domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XHTML), NS_ENSURE_SUCCESS(rv, rv);
NS_LITERAL_STRING("label"), if (mode == nsIFilePicker::returnCancel)
getter_AddRefs(mLabel));
NS_ENSURE_STATE(mLabel);
nsCOMPtr<nsIDOMElement> element;
nsCOMPtr<nsIDOMNode> childReturn;
domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XHTML),
NS_LITERAL_STRING("span"),
getter_AddRefs(element));
NS_ENSURE_STATE(element);
mLabel->AppendChild(element, getter_AddRefs(childReturn));
domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XHTML),
NS_LITERAL_STRING("input"),
getter_AddRefs(element));
mInput = do_QueryInterface(element);
NS_ENSURE_STATE(mInput);
mInput->SetType(NS_LITERAL_STRING("file"));
mLabel->AppendChild(mInput, getter_AddRefs(childReturn));
// We can't use xtf handleDefault here because editor stops blur events from
// bubbling, and we can't use a system event group handler because blur
// events don't go to the system event group (bug 263240). So, just install
// a bubbling listener in the normal event group. This works out because
// content can't get at the anonymous content to install an event handler,
// and the event doesn't bubble up.
nsCOMPtr<nsIDOMEventTarget> targ = do_QueryInterface(mInput);
NS_ASSERTION(targ, "input must be an event target!");
targ->AddEventListener(NS_LITERAL_STRING("blur"), this, PR_FALSE);
return NS_OK;
}
NS_IMETHODIMP
nsXFormsUploadElement::AttributeSet(nsIAtom *aName, const nsAString &aValue)
{
if (aName == nsXFormsAtoms::accesskey) {
// accesskey
mInput->SetAttribute(NS_LITERAL_STRING("accesskey"), aValue);
}
return NS_OK;
}
NS_IMETHODIMP
nsXFormsUploadElement::AttributeRemoved(nsIAtom *aName)
{
if (aName == nsXFormsAtoms::accesskey) {
// accesskey
mInput->RemoveAttribute(NS_LITERAL_STRING("accesskey"));
}
return NS_OK;
}
// nsIXTFVisual
NS_IMETHODIMP
nsXFormsUploadElement::GetVisualContent(nsIDOMElement **aElement)
{
NS_ADDREF(*aElement = mLabel);
return NS_OK;
}
NS_IMETHODIMP
nsXFormsUploadElement::GetInsertionPoint(nsIDOMElement **aPoint)
{
nsCOMPtr<nsIDOMNode> childNode;
mLabel->GetFirstChild(getter_AddRefs(childNode));
return CallQueryInterface(childNode, aPoint);
}
// nsIXTFElement
NS_IMETHODIMP
nsXFormsUploadElement::OnDestroyed()
{
nsCOMPtr<nsIDOMEventTarget> targ = do_QueryInterface(mInput);
if (NS_LIKELY(targ != nsnull)) {
targ->RemoveEventListener(NS_LITERAL_STRING("blur"), this, PR_FALSE);
}
return nsXFormsControlStub::OnDestroyed();
}
// nsIDOMEventListener
NS_IMETHODIMP
nsXFormsUploadElement::HandleEvent(nsIDOMEvent *aEvent)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsUploadElement::Focus(nsIDOMEvent *aEvent)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsUploadElement::Blur(nsIDOMEvent *aEvent)
{
if (!mInput || !mBoundNode || !mModel ||
!nsXFormsUtils::EventHandlingAllowed(aEvent, mElement))
return NS_OK; return NS_OK;
nsAutoString value; // file was selected
mInput->GetValue(value); nsCOMPtr<nsILocalFile> localFile;
rv = filePicker->GetFile(getter_AddRefs(localFile));
if (localFile) {
// set path value in \<upload\>'s text field.
nsCOMPtr<nsIXFormsUploadUIElement> uiUpload = do_QueryInterface(mElement);
if (uiUpload) {
nsCAutoString spec;
NS_GetURLSpecFromFile(localFile, spec);
uiUpload->SetFieldText(NS_ConvertUTF8toUTF16(spec));
}
// store the file as a property on the selected content node. the submission // set file into instance data
// code will read this value. return SetFile(localFile);
}
return rv;
}
NS_IMETHODIMP
nsXFormsUploadElement::ClearFile()
{
// clear path value in \<upload\>'s text field.
nsCOMPtr<nsIXFormsUploadUIElement> uiUpload = do_QueryInterface(mElement);
if (uiUpload) {
uiUpload->SetFieldText(EmptyString());
}
// clear file from instance data
return SetFile(nsnull);
}
nsresult
nsXFormsUploadElement::SetFile(nsILocalFile *aFile)
{
if (!mBoundNode || !mModel)
return NS_OK;
nsresult rv;
nsCOMPtr<nsIContent> content = do_QueryInterface(mBoundNode); nsCOMPtr<nsIContent> content = do_QueryInterface(mBoundNode);
NS_ENSURE_STATE(content); NS_ENSURE_STATE(content);
nsILocalFile *file = nsnull; PRBool dataChanged = PR_FALSE;
nsCAutoString spec; if (!aFile) {
// clear instance data
// We need to special case an empty value. If the input field is blank,
// then I assume that the intention is that all bound nodes will become
// (or stay) empty and that there shouldn't be any file kept in the property.
if (!value.IsEmpty()) {
NS_NewLocalFile(value, PR_FALSE, &file);
NS_ENSURE_STATE(file);
NS_GetURLSpecFromFile(file, spec);
content->SetProperty(nsXFormsAtoms::uploadFileProperty, file,
ReleaseObject);
} else {
content->DeleteProperty(nsXFormsAtoms::uploadFileProperty); content->DeleteProperty(nsXFormsAtoms::uploadFileProperty);
rv = mModel->SetNodeValue(mBoundNode, EmptyString(), &dataChanged);
} else {
// set file into instance data
nsBoundType type = GetBoundType();
if (type == TYPE_ANYURI) {
// set fully qualified path as value in instance data node
nsCAutoString spec;
NS_GetURLSpecFromFile(aFile, spec);
rv = mModel->SetNodeValue(mBoundNode, NS_ConvertUTF8toUTF16(spec),
&dataChanged);
} else if (type == TYPE_BASE64 || type == TYPE_HEX) {
// encode file contents in base64/hex and set into instance data node
PRUnichar *fileData;
rv = EncodeFileContents(aFile, type, &fileData);
if (NS_SUCCEEDED(rv)) {
rv = mModel->SetNodeValue(mBoundNode, nsDependentString(fileData),
&dataChanged);
nsMemory::Free(fileData);
}
} else {
rv = NS_ERROR_FAILURE;
} }
// XXX need to handle derived types
// update the instance data node. this value is never used by the submission // Set nsIFile object as property on instance data node, so submission
// code. instead, it exists so that the instance data will appear to be in- // code can use it.
// sync with what is actually submitted. if (NS_SUCCEEDED(rv)) {
nsIFile *fileCopy = nsnull;
PRBool changed; rv = aFile->Clone(&fileCopy);
nsresult rv = mModel->SetNodeValue(mBoundNode, NS_ConvertUTF8toUTF16(spec),
&changed);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
if (changed) { rv = content->SetProperty(nsXFormsAtoms::uploadFileProperty, fileCopy,
ReleaseObject);
}
}
NS_ENSURE_SUCCESS(rv, rv);
// Handle <filename> and <mediatype> children
PRBool childrenChanged;
rv = HandleChildElements(aFile, &childrenChanged);
NS_ENSURE_SUCCESS(rv, rv);
if (dataChanged || childrenChanged) {
nsCOMPtr<nsIDOMNode> model = do_QueryInterface(mModel); nsCOMPtr<nsIDOMNode> model = do_QueryInterface(mModel);
if (model) { if (model) {
rv = nsXFormsUtils::DispatchEvent(model, eEvent_Recalculate); rv = nsXFormsUtils::DispatchEvent(model, eEvent_Recalculate);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
@ -284,42 +338,199 @@ nsXFormsUploadElement::Blur(nsIDOMEvent *aEvent)
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
} }
return NS_OK; return NS_OK;
} }
// nsIXFormsControl nsresult
nsXFormsUploadElement::HandleChildElements(nsILocalFile *aFile,
NS_IMETHODIMP PRBool *aChanged)
nsXFormsUploadElement::Refresh()
{ {
if (!mInput || !mBoundNode) if (!aChanged) {
return NS_OK; return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> content = do_QueryInterface(mBoundNode);
NS_ENSURE_STATE(content);
nsILocalFile *file =
NS_STATIC_CAST(nsILocalFile *,
content->GetProperty(nsXFormsAtoms::uploadFileProperty));
// get the text value for the control
nsAutoString value;
if (file)
file->GetPath(value);
mInput->SetValue(value);
mInput->SetReadOnly(GetReadOnlyState());
return NS_OK;
} }
*aChanged = PR_FALSE;
NS_IMETHODIMP // return immediately if we have no children
nsXFormsUploadElement::TryFocus(PRBool* aOK) PRBool hasNodes;
mElement->HasChildNodes(&hasNodes);
if (!hasNodes)
return NS_OK;
nsresult rv = NS_OK;
// look for the \<filename\> & \<mediatype\> elements in the
// \<upload\> children
nsCOMPtr<nsIDOMNode> filenameNode, mediatypeNode;
nsCOMPtr<nsIDOMNode> child, temp;
mElement->GetFirstChild(getter_AddRefs(child));
while (child && (!filenameNode || !mediatypeNode)) {
if (!filenameNode &&
nsXFormsUtils::IsXFormsElement(child,
NS_LITERAL_STRING("filename")))
{ {
*aOK = GetRelevantState() && nsXFormsUtils::FocusControl(mInput); filenameNode = child;
return NS_OK;
} }
if (!mediatypeNode &&
nsXFormsUtils::IsXFormsElement(child,
NS_LITERAL_STRING("mediatype")))
{
mediatypeNode = child;
}
temp.swap(child);
temp->GetNextSibling(getter_AddRefs(child));
}
// handle "filename"
PRBool filenameChanged = PR_FALSE;
if (filenameNode) {
nsCOMPtr<nsIDOMElement> filenameElem = do_QueryInterface(filenameNode);
if (aFile) {
nsAutoString filename;
rv = aFile->GetLeafName(filename);
if (!filename.IsEmpty()) {
rv = nsXFormsUtils::SetSingleNodeBindingValue(filenameElem, filename,
&filenameChanged);
}
} else {
rv = nsXFormsUtils::SetSingleNodeBindingValue(filenameElem, EmptyString(),
&filenameChanged);
}
NS_ENSURE_SUCCESS(rv, rv);
}
// handle "mediatype"
PRBool mediatypechanged = PR_FALSE;
if (mediatypeNode) {
nsCOMPtr<nsIDOMElement> mediatypeElem = do_QueryInterface(mediatypeNode);
if (aFile) {
nsCOMPtr<nsIMIMEService> mimeService =
do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsCAutoString contentType;
rv = mimeService->GetTypeFromFile(aFile, contentType);
if (NS_FAILED(rv)) {
contentType.AssignLiteral("application/octet-stream");
}
rv = nsXFormsUtils::SetSingleNodeBindingValue(mediatypeElem,
NS_ConvertUTF8toUTF16(contentType), &mediatypechanged);
}
} else {
rv = nsXFormsUtils::SetSingleNodeBindingValue(mediatypeElem,
EmptyString(), &mediatypechanged);
}
}
*aChanged = filenameChanged || mediatypechanged;
return rv;
}
typedef nsAutoBuffer<char, 256> nsAutoCharBuffer;
static void
ReportEncodingMemoryError(nsIDOMElement* aElement, nsIFile *aFile,
PRUint32 aFailedSize)
{
nsAutoString filename;
if (NS_FAILED(aFile->GetLeafName(filename))) {
return;
}
nsAutoString size;
size.AppendInt((PRInt64)aFailedSize);
const PRUnichar *strings[] = { filename.get(), size.get() };
nsXFormsUtils::ReportError(NS_LITERAL_STRING("encodingMemoryError"),
strings, 2, aElement, aElement);
}
nsresult
nsXFormsUploadElement::EncodeFileContents(nsIFile *aFile, nsBoundType aType,
PRUnichar **aResult)
{
nsresult rv;
// get file contents
nsCOMPtr<nsIInputStream> fileStream;
rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), aFile,
PR_RDONLY, -1 /* no mode bits */,
nsIFileInputStream::CLOSE_ON_EOF);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 size;
rv = fileStream->Available(&size);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCharBuffer fileData;
if (!fileData.EnsureElemCapacity(size + 1)) {
ReportEncodingMemoryError(mElement, aFile, size + 1);
return NS_ERROR_OUT_OF_MEMORY;
}
PRUint32 bytesRead;
rv = fileStream->Read(fileData.get(), size, &bytesRead);
NS_ASSERTION(NS_SUCCEEDED(rv) && bytesRead == size,
"fileStream->Read failed");
if (NS_SUCCEEDED(rv)) {
if (aType == TYPE_BASE64) {
// encode file contents
*aResult = nsnull;
char *buffer = PL_Base64Encode(fileData.get(), bytesRead, nsnull);
if (buffer) {
*aResult = ToNewUnicode(nsDependentCString(buffer));
PR_Free(buffer);
}
if (!*aResult) {
PRUint32 failedSize = buffer ? strlen(buffer) * sizeof(PRUnichar)
: ((bytesRead + 2) / 3) * 4 + 1;
ReportEncodingMemoryError(mElement, aFile, failedSize);
rv = NS_ERROR_OUT_OF_MEMORY;
}
} else if (aType == TYPE_HEX) {
// create buffer for hex encoded data
PRUint32 length = bytesRead * 2 + 1;
PRUnichar *fileDataHex =
NS_STATIC_CAST(PRUnichar*, nsMemory::Alloc(length * sizeof(PRUnichar)));
if (!fileDataHex) {
ReportEncodingMemoryError(mElement, aFile, length * sizeof(PRUnichar));
rv = NS_ERROR_OUT_OF_MEMORY;
} else {
// encode file contents
BinaryToHex(fileData.get(), bytesRead, &fileDataHex);
fileDataHex[bytesRead * 2] = 0;
*aResult = fileDataHex;
}
} else {
NS_ERROR("Unknown encoding type for <upload> element");
rv = NS_ERROR_INVALID_ARG;
}
}
return rv;
}
static inline PRUnichar
ToHexChar(PRInt16 aValue)
{
if (aValue < 10)
return (PRUnichar) aValue + '0';
else
return (PRUnichar) aValue - 10 + 'A';
}
void
nsXFormsUploadElement::BinaryToHex(const char *aBuffer, PRUint32 aCount,
PRUnichar **aHexString)
{
for (PRUint32 index = 0; index < aCount; index++) {
(*aHexString)[index * 2] = ToHexChar((aBuffer[index] >> 4) & 0xf);
(*aHexString)[index * 2 + 1] = ToHexChar(aBuffer[index] & 0xf);
}
}
NS_HIDDEN_(nsresult) NS_HIDDEN_(nsresult)
NS_NewXFormsUploadElement(nsIXTFElement **aResult) NS_NewXFormsUploadElement(nsIXTFElement **aResult)
{ {

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

@ -754,11 +754,10 @@ nsXFormsUtils::SetNodeValue(nsIDOMNode* aDataNode, const nsString& aNodeValue)
} }
} }
///
/// @todo Use this consistently, or delete? (XXX)
/* static */ PRBool /* static */ PRBool
nsXFormsUtils::GetSingleNodeBindingValue(nsIDOMElement* aElement, nsXFormsUtils::GetSingleNodeBinding(nsIDOMElement* aElement,
nsString& aValue) nsIDOMNode** aNode,
nsIModelElementPrivate** aModel)
{ {
if (!aElement) if (!aElement)
return PR_FALSE; return PR_FALSE;
@ -781,10 +780,44 @@ nsXFormsUtils::GetSingleNodeBindingValue(nsIDOMElement* aElement,
if (!singleNode) if (!singleNode)
return PR_FALSE; return PR_FALSE;
nsXFormsUtils::GetNodeValue(singleNode, aValue); singleNode.swap(*aNode);
if (aModel)
model.swap(*aModel); // transfers ref
return PR_TRUE; return PR_TRUE;
} }
///
/// @todo Use this consistently, or delete? (XXX)
/* static */ PRBool
nsXFormsUtils::GetSingleNodeBindingValue(nsIDOMElement* aElement,
nsString& aValue)
{
nsCOMPtr<nsIDOMNode> node;
if (GetSingleNodeBinding(aElement, getter_AddRefs(node), nsnull)) {
nsXFormsUtils::GetNodeValue(node, aValue);
return PR_TRUE;
}
return PR_FALSE;
}
/* static */ PRBool
nsXFormsUtils::SetSingleNodeBindingValue(nsIDOMElement *aElement,
const nsAString &aValue,
PRBool *aChanged)
{
*aChanged = PR_FALSE;
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsIModelElementPrivate> model;
if (GetSingleNodeBinding(aElement, getter_AddRefs(node),
getter_AddRefs(model)))
{
nsresult rv = model->SetNodeValue(node, aValue, aChanged);
if (NS_SUCCEEDED(rv))
return PR_TRUE;
}
return PR_FALSE;
}
/* static */ nsresult /* static */ nsresult
nsXFormsUtils::DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent, nsXFormsUtils::DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent,
PRBool *aDefaultActionEnabled) PRBool *aDefaultActionEnabled)

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

@ -252,6 +252,15 @@ public:
static NS_HIDDEN_(void) SetNodeValue(nsIDOMNode *aDataNode, static NS_HIDDEN_(void) SetNodeValue(nsIDOMNode *aDataNode,
const nsString &aNodeValue); const nsString &aNodeValue);
/**
* Convenience method for doing XPath evaluations to get bound node
* for an element. Also returns the associated model if aModel != null.
* Returns PR_TRUE if the evaluation succeeds.
*/
static NS_HIDDEN_(PRBool)
GetSingleNodeBinding(nsIDOMElement* aElement, nsIDOMNode** aNode,
nsIModelElementPrivate** aModel);
/** /**
* Convenience method for doing XPath evaluations to get string value * Convenience method for doing XPath evaluations to get string value
* for an element. * for an element.
@ -260,6 +269,14 @@ public:
static NS_HIDDEN_(PRBool) static NS_HIDDEN_(PRBool)
GetSingleNodeBindingValue(nsIDOMElement* aElement, nsString& aValue); GetSingleNodeBindingValue(nsIDOMElement* aElement, nsString& aValue);
/**
* Convenience method for doing XPath evaluations to set string value
* for an element.
* Returns PR_TRUE if the evaluation succeeds.
*/
static NS_HIDDEN_(PRBool)
SetSingleNodeBindingValue(nsIDOMElement *aElement, const nsAString &aValue,
PRBool *aChanged);
/** /**
* Dispatch an XForms event. aDefaultActionEnabled is returned indicating * Dispatch an XForms event. aDefaultActionEnabled is returned indicating
* if the default action of the dispatched event was enabled. * if the default action of the dispatched event was enabled.

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

@ -255,4 +255,19 @@ select html|div.select-choice-content {
padding-left: 10px; padding-left: 10px;
} }
upload {
-moz-binding: url('chrome://xforms/content/xforms.xml#xformswidget-upload-disabled');
}
upload[mozType|type="http://www.w3.org/2001/XMLSchema#anyURI"] {
-moz-binding: url('chrome://xforms/content/xforms.xml#xformswidget-upload');
}
upload[mozType|type="http://www.w3.org/2001/XMLSchema#base64Binary"] {
-moz-binding: url('chrome://xforms/content/xforms.xml#xformswidget-upload');
}
upload[mozType|type="http://www.w3.org/2001/XMLSchema#hexBinary"] {
-moz-binding: url('chrome://xforms/content/xforms.xml#xformswidget-upload');
}

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

@ -38,6 +38,11 @@
- -
- ***** END LICENSE BLOCK ***** --> - ***** END LICENSE BLOCK ***** -->
<!DOCTYPE bindings [
<!ENTITY % xformsDTD SYSTEM "chrome://xforms/locale/xforms.dtd">
%xformsDTD;
]>
<bindings id="xformsBindings" <bindings id="xformsBindings"
xmlns="http://www.mozilla.org/xbl" xmlns="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml"
@ -443,4 +448,111 @@
</method> </method>
</implementation> </implementation>
</binding> </binding>
<!-- UPLOAD: <DEFAULT> -->
<binding id="xformswidget-upload"
extends="chrome://xforms/content/xforms.xml#xformswidget-base">
<content>
<children includes="label"/>
<html:input anonid="upload_text_control"
readonly="true"
xbl:inherits="accesskey"/>
<html:button anonid="upload_browse_button"
type="button"
onclick="this.parentNode.uploadElem.pickFile();"
onkeypress="if (event.keyCode == event.DOM_VK_RETURN) this.parentNode.dispatchDOMActivate();"
xbl:inherits="accesskey"> &xforms.upload.browsetext; </html:button>
<html:button anonid="upload_clear_button"
type="button"
onclick="this.parentNode.uploadElem.clearFile();"
onkeypress="if (event.keyCode == event.DOM_VK_RETURN) this.parentNode.dispatchDOMActivate();"
xbl:inherits="accesskey"> &xforms.upload.cleartext; </html:button>
<children/>
</content>
<implementation implements="nsIXFormsUIWidget, nsIXFormsUploadUIElement">
<destructor>
this._uploadElem = null;
this._textControl = null;
this._browseButton = null;
this._clearButton = null;
</destructor>
<field name="_uploadElem">null</field>
<property name="uploadElem" readonly="true">
<getter>
if (!this._uploadElem) {
this._uploadElem =
this.QueryInterface(Components.interfaces.nsIXFormsUploadElement);
}
return this._uploadElem;
</getter>
</property>
<field name="_textControl">null</field>
<property name="textControl" readonly="true">
<getter>
if (!this._textControl) {
this._textControl =
document.getAnonymousElementByAttribute(this, "anonid",
"upload_text_control");
}
return this._textControl;
</getter>
</property>
<field name="_browseButton">null</field>
<property name="browseButton" readonly="true">
<getter>
if (!this._browseButton) {
this._browseButton =
document.getAnonymousElementByAttribute(this, "anonid",
"upload_browse_button");
}
return this._browseButton;
</getter>
</property>
<field name="_clearButton">null</field>
<property name="clearButton" readonly="true">
<getter>
if (!this._clearButton) {
this._clearButton =
document.getAnonymousElementByAttribute(this, "anonid",
"upload_clear_button");
}
return this._clearButton;
</getter>
</property>
<method name="setFieldText">
<parameter name="aString"/>
<body>
this.textControl.value = aString;
</body>
</method>
</implementation>
</binding>
<!-- UPLOAD: DISABLED -->
<binding id="xformswidget-upload-disabled"
extends="chrome://xforms/content/xforms.xml#xformswidget-base">
<content>
<children includes="label"/>
<html:input readonly="true"
xbl:inherits="accesskey"/>
<html:button type="button"
disabled="true"
xbl:inherits="accesskey"> &xforms.upload.browsetext; </html:button>
<html:button type="button"
disabled="true"
xbl:inherits="accesskey"> &xforms.upload.cleartext; </html:button>
<children/>
</content>
</binding>
</bindings> </bindings>

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

@ -43,3 +43,6 @@
<!ENTITY xforms.bindingdialog.title "XForms Error"> <!ENTITY xforms.bindingdialog.title "XForms Error">
<!ENTITY xforms.bindingdialog.description1 "The form on this page contains an error and will not work properly"> <!ENTITY xforms.bindingdialog.description1 "The form on this page contains an error and will not work properly">
<!ENTITY xforms.bindingdialog.description2 "See the JavaScript Console for details"> <!ENTITY xforms.bindingdialog.description2 "See the JavaScript Console for details">
<!ENTITY xforms.upload.browsetext "Browse...">
<!ENTITY xforms.upload.cleartext "Clear">

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

@ -58,6 +58,8 @@ labelLink2Error = XForms Error (19): Failed to load Label element from exte
invalidSeparator = XForms Error (20): Submission separator may only be either "&" or ";", but found "%S". invalidSeparator = XForms Error (20): Submission separator may only be either "&" or ";", but found "%S".
instanceBindError = XForms Error (21): Submission failed trying to replace instance document '%S'. Instance document doesn't exist in same model as submission element. instanceBindError = XForms Error (21): Submission failed trying to replace instance document '%S'. Instance document doesn't exist in same model as submission element.
instanceInstanceLoad = XForms Error (22): Instance document not allowed to load external instance: %S instanceInstanceLoad = XForms Error (22): Instance document not allowed to load external instance: %S
encodingMemoryError = XForms Error (23): Not enough available memory to encode file %S, size = %S.
uploadBoundTypeError = XForms Error (24): Upload element not bound to valid datatype. Must be bound to datatype 'xsd:anyURI', 'xsd:base64Binary', or 'xsd:hexBinary'.
# Warning Messages: # Warning Messages:
warnSOAP = XForms Warning (1): You are using the SOAP post feature, which is an experimental feature! Beware that the functionality might change, and forms may stop working at any time. warnSOAP = XForms Warning (1): You are using the SOAP post feature, which is an experimental feature! Beware that the functionality might change, and forms may stop working at any time.
@ -65,4 +67,3 @@ warnSOAP = XForms Warning (1): You are using the SOAP post feature,
# XForms Permission Messages: # XForms Permission Messages:
xformsXDPermissionDialogTitle = Allowed Sites - XForms Cross Domain Access xformsXDPermissionDialogTitle = Allowed Sites - XForms Cross Domain Access
xformsXDPermissionDialogIntro = You can specify which web sites containing XForms may submit data to other domains. Type the exact address of the site you want to allow and then press Allow. xformsXDPermissionDialogIntro = You can specify which web sites containing XForms may submit data to other domains. Type the exact address of the site you want to allow and then press Allow.