Files
Mozilla/mozilla/extensions/xforms/nsXFormsSubmissionElement.cpp
darin%meer.net f6e0e35988 hide printfs
git-svn-id: svn://10.0.0.236/branches/FORMS_20040722_BRANCH@163268 18797224-902f-48f8-a5cc-f745e15eee43
2004-10-05 23:04:26 +00:00

544 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Darin Fisher <darin@meer.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#ifdef DEBUG_darinf
#include <stdio.h>
#endif
#include "nsXFormsSubmissionElement.h"
#include "nsXFormsAtoms.h"
#include "nsIXTFGenericElementWrapper.h"
#include "nsIDOMElement.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMEventTarget.h"
#include "nsIDOMEvent.h"
#include "nsIDOMDocumentEvent.h"
#include "nsIDOM3Node.h"
#include "nsIDOMXPathResult.h"
#include "nsIDOMXPathEvaluator.h"
#include "nsIDOMXPathNSResolver.h"
#include "nsIDOMXPathExpression.h"
#include "nsIWebNavigation.h"
#include "nsIInputStream.h"
#include "nsINameSpaceManager.h"
#include "nsIDocument.h"
#include "nsIContent.h"
#include "nsString.h"
#include "nsMemory.h"
#include "nsCOMPtr.h"
static const nsIID sScriptingIIDs[] = {
NS_IDOMELEMENT_IID,
NS_IDOMEVENTTARGET_IID,
NS_IDOM3NODE_IID
};
// nsISupports
NS_IMPL_ISUPPORTS3(nsXFormsSubmissionElement,
nsIXTFElement,
nsIXTFGenericElement,
nsIDOMEventListener)
// nsIXTFElement
NS_IMETHODIMP
nsXFormsSubmissionElement::OnDestroyed()
{
mContent = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::GetElementType(PRUint32 *aElementType)
{
*aElementType = nsIXTFElement::ELEMENT_TYPE_GENERIC_ELEMENT;
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::GetIsAttributeHandler(PRBool *aIsAttributeHandler)
{
*aIsAttributeHandler = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::GetScriptingInterfaces(PRUint32 *aCount, nsIID ***aArray)
{
return CloneScriptingInterfaces(sScriptingIIDs,
NS_ARRAY_LENGTH(sScriptingIIDs),
aCount, aArray);
}
NS_IMETHODIMP
nsXFormsSubmissionElement::GetNotificationMask(PRUint32 *aNotificationMask)
{
*aNotificationMask = 0;
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::WillChangeDocument(nsISupports *aNewDocument)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::DocumentChanged(nsISupports *aNewDocument)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::WillChangeParent(nsISupports *aNewParent)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::ParentChanged(nsISupports *aNewParent)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::WillInsertChild(nsISupports *aChild, PRUint32 aIndex)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::ChildInserted(nsISupports *aChild, PRUint32 aIndex)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::WillAppendChild(nsISupports *aChild)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::ChildAppended(nsISupports *aChild)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::WillRemoveChild(PRUint32 aIndex)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::ChildRemoved(PRUint32 aIndex)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::WillSetAttribute(nsIAtom *aName,
const nsAString &aNewValue)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::AttributeSet(nsIAtom *aName, const nsAString &aNewValue)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::WillUnsetAttribute(nsIAtom *aName)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::AttributeUnset(nsIAtom *aName)
{
return NS_OK;
}
NS_IMETHODIMP
nsXFormsSubmissionElement::DoneAddingChildren()
{
return NS_OK;
}
// nsIXTFGenericElement
NS_IMETHODIMP
nsXFormsSubmissionElement::OnCreated(nsIXTFGenericElementWrapper *aWrapper)
{
nsCOMPtr<nsIDOMElement> node;
aWrapper->GetElementNode(getter_AddRefs(node));
// It's ok to keep a weak pointer to mContent. mContent will have an
// owning reference to this object, so as long as we null out mContent in
// OnDestroyed, it will always be valid.
nsCOMPtr<nsIContent> content = do_QueryInterface(node);
mContent = content;
NS_ASSERTION(mContent, "Wrapper is not an nsIContent, we'll crash soon");
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mContent);
NS_ASSERTION(target, "Wrapper is not a DOM event target");
nsresult rv = target->AddEventListener(NS_LITERAL_STRING("xforms-submit"),
this, PR_FALSE);
#ifdef DEBUG_darinf
if (NS_FAILED(rv))
printf("+++ AddEventListener failed [rv=%x]\n", rv);
#endif
return NS_OK;
}
// nsIDOMEventListener
NS_IMETHODIMP
nsXFormsSubmissionElement::HandleEvent(nsIDOMEvent *aEvent)
{
nsAutoString type;
aEvent->GetType(type);
if (type.EqualsLiteral("xforms-submit"))
Submit();
return NS_OK;
}
// private methods
void
nsXFormsSubmissionElement::Submit()
{
#ifdef DEBUG_darinf
printf("+++ nsXFormsSubmissionElement::Submit\n");
#endif
// 1. ensure that we are not currently processing a xforms-submit on our model
// 2. get selected node from the instance data (use xpath, gives us node iterator)
nsCOMPtr<nsIDOMNode> data;
GetSelectedInstanceData(getter_AddRefs(data));
if (!data)
{
NS_WARNING("could not get selected instance data");
return;
}
// 3. revalidate selected instance data (only for namespaces considered for
// serialization)
// XXX call nsISchemaValidator::validate on each node
// 4. serialize instance data
nsCOMPtr<nsIInputStream> stream;
nsAutoString uri;
if (NS_FAILED(SerializeData(data, uri, getter_AddRefs(stream))))
{
NS_WARNING("failed to serialize data");
return;
}
// 5. dispatch network request
if (NS_FAILED(SendData(uri, stream)))
{
NS_WARNING("failed to send data");
return;
}
}
nsresult
nsXFormsSubmissionElement::SubmitEnd(PRBool succeeded)
{
nsCOMPtr<nsIDOMDocumentEvent> doc = do_QueryInterface(mContent->GetDocument());
nsCOMPtr<nsIDOMEvent> event;
doc->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
event->InitEvent(succeeded ? NS_LITERAL_STRING("xforms-submit-done")
: NS_LITERAL_STRING("xforms-submit-error"),
PR_TRUE, PR_FALSE);
nsCOMPtr<nsIDOMEventTarget> target;
if (succeeded)
target = do_QueryInterface(mContent);
//else
// target = GetModel();
PRBool cancelled;
return target->DispatchEvent(event, &cancelled);
}
void
nsXFormsSubmissionElement::GetSelectedInstanceData(nsIDOMNode **result)
{
// XXX need to support 'instance(id)' xpath function. for now, we assume
// that any xpath expression is relative to the first <instance> element.
nsCOMPtr<nsIDOMNode> instance;
GetDefaultInstanceData(getter_AddRefs(instance));
if (!instance)
{
NS_WARNING("model has no instance data!");
return;
}
nsAutoString value;
mContent->GetAttr(kNameSpaceID_None, nsXFormsAtoms::bind, value);
if (value.IsEmpty())
{
// inspect 'ref' attribute
mContent->GetAttr(kNameSpaceID_None, nsXFormsAtoms::ref, value);
if (value.IsEmpty())
{
// select first <instance> element
NS_ADDREF(*result = instance);
return;
}
}
else
{
// XXX get bind element, and inspect the 'ref' attribute
// XXX or we should be able to
}
}
void
nsXFormsSubmissionElement::GetDefaultInstanceData(nsIDOMNode **result)
{
*result = nsnull;
// default <instance> element is the first <instance> child node of
// our parent, which should be a <model> element.
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(mContent);
nsCOMPtr<nsIDOMNode> parent;
node->GetParentNode(getter_AddRefs(parent));
if (!parent)
{
NS_WARNING("no parent node!");
return;
}
parent->GetFirstChild(getter_AddRefs(node));
while (node)
{
PRUint16 nodeType;
node->GetNodeType(&nodeType);
if (nodeType == nsIDOMNode::ELEMENT_NODE)
{
nsAutoString name;
node->GetLocalName(name);
if (name.EqualsLiteral("instance"))
{
NS_ADDREF(*result = node);
return;
}
}
nsIDOMNode *temp = nsnull;
node->GetNextSibling(&temp);
node.swap(temp);
}
}
nsresult
nsXFormsSubmissionElement::SerializeData(nsIDOMNode *data, nsString &uri,
nsIInputStream **stream)
{
uri.Truncate();
*stream = nsnull;
nsAutoString action;
mContent->GetAttr(kNameSpaceID_None, nsXFormsAtoms::action, action);
nsAutoString method;
mContent->GetAttr(kNameSpaceID_None, nsXFormsAtoms::method, method);
// XXX case sensistive?
if (!method.EqualsLiteral("get"))
{
NS_NOTREACHED("method not implemented");
return NS_ERROR_NOT_IMPLEMENTED;
}
nsAutoString separator;
mContent->GetAttr(kNameSpaceID_None, nsXFormsAtoms::separator, separator);
if (separator.IsEmpty())
separator.AssignLiteral(";");
// 1. Each element node is visited in document order. Each element that has
// one text node child is selected for inclusion.
// 2. Element nodes selected for inclusion are encoded as EltName=value{sep},
// where = is a literal character, {sep} is the separator character from the
// separator attribute on submission, EltName represents the element local
// name, and value represents the contents of the text node.
// NOTE:
// The encoding of EltName and value are as follows: space characters are
// replaced by +, and then non-ASCII and reserved characters (as defined
// by [RFC 2396] as amended by subsequent documents in the IETF track) are
// escaped by replacing the character with one or more octets of the UTF-8
// representation of the character, with each octet in turn replaced by
// %HH, where HH represents the uppercase hexadecimal notation for the
// octet value and % is a literal character. Line breaks are represented
// as "CR LF" pairs (i.e., %0D%0A).
// 'get' method:
// The URI is constructed as follows:
// o The submit URI from the action attribute is examined. If it does not
// already contain a ? (question mark) character, one is appended. If it
// does already contain a question mark character, then a separator
// character from the attribute separator is appended.
// o The serialized form data is appended to the URI.
if (action.FindChar(PRUnichar('?')) == kNotFound)
action.Append(PRUnichar('?'));
else
action.Append(separator);
// recursively build uri
AppendDataToURI(data, action, separator);
uri = action;
return NS_OK;
}
void
nsXFormsSubmissionElement::AppendDataToURI(nsIDOMNode *data, nsString &uri,
const nsString &separator)
{
nsCOMPtr<nsIDOMNode> firstChild;
data->GetFirstChild(getter_AddRefs(firstChild));
if (!firstChild)
return;
nsCOMPtr<nsIDOMNode> node;
data->GetLastChild(getter_AddRefs(node));
if (firstChild != node)
{
// call AppendDataToURI on each child node
do
{
AppendDataToURI(firstChild, uri, separator);
firstChild->GetNextSibling(getter_AddRefs(node));
firstChild.swap(node);
}
while (firstChild);
return;
}
PRUint16 nodeType;
firstChild->GetNodeType(&nodeType);
if (nodeType != nsIDOMNode::TEXT_NODE)
{
AppendDataToURI(firstChild, uri, separator);
return;
}
nsAutoString localName;
data->GetLocalName(localName);
nsAutoString value;
firstChild->GetNodeValue(value);
value.ReplaceChar(PRUnichar(' '), PRUnichar('+'));
uri.Append(localName + NS_LITERAL_STRING("=") + value + separator);
}
nsresult
nsXFormsSubmissionElement::SendData(nsString &uri, nsIInputStream *stream)
{
#ifdef DEBUG_darinf
printf("+++ sending to uri=%s [stream=%p]\n",
NS_ConvertUTF16toUTF8(uri).get(), (void*) stream);
#endif
// XXX need to properly support the various 'replace' modes and trigger
// xforms-submit-done or xforms-submit-error when appropriate.
nsAutoString replace;
mContent->GetAttr(kNameSpaceID_None, nsXFormsAtoms::replace, replace);
if (!replace.IsEmpty() && !replace.EqualsLiteral("all"))
{
NS_WARNING("replace != 'all' not implemented");
return NS_ERROR_NOT_IMPLEMENTED;
}
nsIDocument *doc = mContent->GetDocument();
nsCOMPtr<nsISupports> container = doc->GetContainer();
nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(container);
// XXX this is wrong since we need to handle load errors ourselves.
// XXX we need an API for handing off our channel to the URI loader,
// once we decide to load its content into the browser, so that
// the URI loader can run its DispatchContent algorithm.
// see bug 263084.
return webNav->LoadURI(uri.get(), nsIWebNavigation::LOAD_FLAGS_NONE,
doc->GetDocumentURI(), nsnull, nsnull);
}
// factory constructor
nsresult
NS_NewXFormsSubmissionElement(nsIXTFElement **aResult)
{
*aResult = new nsXFormsSubmissionElement();
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult);
return NS_OK;
}