diff --git a/extensions/xforms/.cvsignore b/extensions/xforms/.cvsignore new file mode 100644 index 00000000000..f3c7a7c5da6 --- /dev/null +++ b/extensions/xforms/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/extensions/xforms/Makefile.in b/extensions/xforms/Makefile.in new file mode 100644 index 00000000000..8f1667e4225 --- /dev/null +++ b/extensions/xforms/Makefile.in @@ -0,0 +1,88 @@ +# +# ***** 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.org code. +# +# 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): +# Brian Ryner +# +# 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 ***** + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = xforms +LIBRARY_NAME = xforms +IS_COMPONENT = 1 +EXPORT_LIBRARY = 1 +MODULE_NAME = xforms + +REQUIRES = \ + xpcom \ + string \ + content \ + dom \ + widget \ + necko \ + websrvcs \ + xmlextras \ + docshell \ + mimetype \ + $(NULL) + +CPPSRCS = \ + nsXFormsElementFactory.cpp \ + nsXFormsElement.cpp \ + nsXFormsControl.cpp \ + nsXFormsModelElement.cpp \ + nsXFormsInputElement.cpp \ + nsXFormsSubmissionElement.cpp \ + nsXFormsStubElement.cpp \ + nsXFormsInstanceElement.cpp \ + nsXFormsAtoms.cpp \ + nsXFormsModule.cpp \ + nsXFormsMDG.cpp \ + nsXFormsMDGEngine.cpp \ + nsXFormsMDGSet.cpp \ + nsXFormsXPathAnalyzer.cpp \ + nsXFormsXPathNode.cpp \ + nsXFormsXPathParser.cpp \ + nsXFormsXPathScanner.cpp \ + nsXFormsXPathXMLUtil.cpp \ + $(NULL) + +EXTRA_DSO_LDOPTS = $(MOZ_COMPONENT_LIBS) + +include $(topsrcdir)/config/rules.mk diff --git a/extensions/xforms/nsXFormsAtoms.cpp b/extensions/xforms/nsXFormsAtoms.cpp new file mode 100644 index 00000000000..22f50475bdf --- /dev/null +++ b/extensions/xforms/nsXFormsAtoms.cpp @@ -0,0 +1,72 @@ +/* -*- 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): + * Brian Ryner + * + * 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 ***** */ + +#include "nsXFormsAtoms.h" +#include "nsMemory.h" + +nsIAtom* nsXFormsAtoms::src; +nsIAtom* nsXFormsAtoms::bind; +nsIAtom* nsXFormsAtoms::type; +nsIAtom* nsXFormsAtoms::readonly; +nsIAtom* nsXFormsAtoms::required; +nsIAtom* nsXFormsAtoms::relevant; +nsIAtom* nsXFormsAtoms::calculate; +nsIAtom* nsXFormsAtoms::constraint; +nsIAtom* nsXFormsAtoms::p3ptype; +nsIAtom* nsXFormsAtoms::modelListProperty; +nsIAtom *nsXFormsAtoms::ref; + +const nsStaticAtom nsXFormsAtoms::Atoms_info[] = { + { "src", &nsXFormsAtoms::src }, + { "bind", &nsXFormsAtoms::bind }, + { "type", &nsXFormsAtoms::type }, + { "readonly", &nsXFormsAtoms::readonly }, + { "required", &nsXFormsAtoms::required }, + { "relevant", &nsXFormsAtoms::relevant }, + { "calculate", &nsXFormsAtoms::calculate }, + { "constraint", &nsXFormsAtoms::constraint }, + { "p3ptype", &nsXFormsAtoms::p3ptype }, + { "ModelListProperty", &nsXFormsAtoms::modelListProperty }, + { "ref", &nsXFormsAtoms::ref }, +}; + +void +nsXFormsAtoms::InitAtoms() +{ + NS_RegisterStaticAtoms(Atoms_info, NS_ARRAY_LENGTH(Atoms_info)); +} diff --git a/extensions/xforms/nsXFormsAtoms.h b/extensions/xforms/nsXFormsAtoms.h new file mode 100644 index 00000000000..5eb7d372364 --- /dev/null +++ b/extensions/xforms/nsXFormsAtoms.h @@ -0,0 +1,60 @@ +/* -*- 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): + * Brian Ryner + * + * 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 ***** */ + +#include "nsStaticAtom.h" + +class nsXFormsAtoms +{ + public: + static NS_HIDDEN_(nsIAtom *) src; + static NS_HIDDEN_(nsIAtom *) bind; + static NS_HIDDEN_(nsIAtom *) type; + static NS_HIDDEN_(nsIAtom *) readonly; + static NS_HIDDEN_(nsIAtom *) required; + static NS_HIDDEN_(nsIAtom *) relevant; + static NS_HIDDEN_(nsIAtom *) calculate; + static NS_HIDDEN_(nsIAtom *) constraint; + static NS_HIDDEN_(nsIAtom *) p3ptype; + static NS_HIDDEN_(nsIAtom *) modelListProperty; + static NS_HIDDEN_(nsIAtom *) ref; + + static NS_HIDDEN_(void) InitAtoms(); + + private: + static NS_HIDDEN_(const nsStaticAtom) Atoms_info[]; +}; diff --git a/extensions/xforms/nsXFormsControl.cpp b/extensions/xforms/nsXFormsControl.cpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/xforms/nsXFormsControl.h b/extensions/xforms/nsXFormsControl.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/xforms/nsXFormsElement.cpp b/extensions/xforms/nsXFormsElement.cpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/xforms/nsXFormsElement.h b/extensions/xforms/nsXFormsElement.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/xforms/nsXFormsElementFactory.cpp b/extensions/xforms/nsXFormsElementFactory.cpp new file mode 100644 index 00000000000..fa1165081ed --- /dev/null +++ b/extensions/xforms/nsXFormsElementFactory.cpp @@ -0,0 +1,67 @@ +/* -*- 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): + * Brian Ryner + * + * 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 ***** */ + +#include "nsXFormsElementFactory.h" +#include "nsXFormsModelElement.h" +#include "nsXFormsInstanceElement.h" +#include "nsXFormsSubmissionElement.h" +#include "nsXFormsStubElement.h" +#include "nsString.h" + +NS_HIDDEN_(nsresult) NS_NewXFormsInputElement(nsIXTFElement **aElement); + +NS_IMPL_ISUPPORTS1(nsXFormsElementFactory, nsIXTFElementFactory) + +NS_IMETHODIMP +nsXFormsElementFactory::CreateElement(const nsAString& aTagName, + nsIXTFElement **aElement) +{ + if (aTagName.EqualsLiteral("model")) + return NS_NewXFormsModelElement(aElement); + if (aTagName.EqualsLiteral("instance")) + return NS_NewXFormsInstanceElement(aElement); + if (aTagName.EqualsLiteral("bind")) + return NS_NewXFormsStubElement(aElement); + if (aTagName.EqualsLiteral("input")) + return NS_NewXFormsInputElement(aElement); + if (aTagName.EqualsLiteral("submission")) + return NS_NewXFormsSubmissionElement(aElement); + + *aElement = nsnull; + return NS_ERROR_FAILURE; +} diff --git a/extensions/xforms/nsXFormsElementFactory.h b/extensions/xforms/nsXFormsElementFactory.h new file mode 100644 index 00000000000..b4229e56c76 --- /dev/null +++ b/extensions/xforms/nsXFormsElementFactory.h @@ -0,0 +1,49 @@ +/* -*- 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): + * Brian Ryner + * + * 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 ***** */ + +#include "nsIXTFElementFactory.h" + +#define NS_XFORMSELEMENTFACTORY_CID \ +{0xc068f682, 0x03b5, 0x4e96, {0x81, 0xe1, 0x60, 0x13, 0xf9, 0x12, 0x88, 0xb2}} + +class nsXFormsElementFactory : public nsIXTFElementFactory +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIXTFELEMENTFACTORY +}; diff --git a/extensions/xforms/nsXFormsInputElement.cpp b/extensions/xforms/nsXFormsInputElement.cpp new file mode 100644 index 00000000000..f5e042dba68 --- /dev/null +++ b/extensions/xforms/nsXFormsInputElement.cpp @@ -0,0 +1,401 @@ +/* -*- 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): + * Brian Ryner + * + * 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 ***** */ + +#include "nsIXTFXMLVisual.h" +#include "nsIDOMEventTarget.h" +#include "nsIDOM3Node.h" +#include "nsIDOMElement.h" +#include "nsMemory.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsIXTFXMLVisualWrapper.h" +#include "nsIDOMDocument.h" +#include "nsXFormsControl.h" +#include "nsISchema.h" +#include "nsXFormsModelElement.h" +#include "nsIDOMHTMLInputElement.h" +#include "nsXFormsAtoms.h" +#include "nsAutoPtr.h" +#include "nsIDOMXPathResult.h" +#include "nsIDOMFocusListener.h" +#include "nsIDOM3EventTarget.h" +#include "nsIDOMEventReceiver.h" +#include "nsIDOMEventGroup.h" + +static const nsIID sScriptingIIDs[] = { + NS_IDOMELEMENT_IID, + NS_IDOMEVENTTARGET_IID, + NS_IDOM3NODE_IID +}; + +class nsXFormsInputElement : public nsXFormsControl, + public nsIXTFXMLVisual, + public nsIDOMFocusListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIXTFXMLVISUAL + NS_DECL_NSIXTFVISUAL + NS_DECL_NSIXTFELEMENT + + // nsIDOMEventListener + NS_IMETHOD HandleEvent(nsIDOMEvent *aEvent); + + // nsIDOMFocusListener + NS_IMETHOD Focus(nsIDOMEvent *aEvent); + NS_IMETHOD Blur(nsIDOMEvent *aEvent); + + // nsXFormsControl + virtual NS_HIDDEN_(void) Refresh(); + +private: + nsCOMPtr mInput; +}; + +NS_IMPL_ADDREF(nsXFormsInputElement) +NS_IMPL_RELEASE(nsXFormsInputElement) + +NS_INTERFACE_MAP_BEGIN(nsXFormsInputElement) + NS_INTERFACE_MAP_ENTRY(nsIXTFXMLVisual) + NS_INTERFACE_MAP_ENTRY(nsIXTFElement) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXTFXMLVisual) +NS_INTERFACE_MAP_END + +// nsIXTFXMLVisual + +NS_IMETHODIMP +nsXFormsInputElement::OnCreated(nsIXTFXMLVisualWrapper *aWrapper) +{ + aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_WILL_SET_ATTRIBUTE | + nsIXTFElement::NOTIFY_ATTRIBUTE_SET); + + mWrapper = aWrapper; + + nsCOMPtr node; + mWrapper->GetElementNode(getter_AddRefs(node)); + nsCOMPtr domDoc; + node->GetOwnerDocument(getter_AddRefs(domDoc)); + + nsCOMPtr inputElement; + domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XHTML), + NS_LITERAL_STRING("input"), + getter_AddRefs(inputElement)); + + mInput = do_QueryInterface(inputElement); + NS_ENSURE_TRUE(mInput, NS_ERROR_FAILURE); + + nsCOMPtr receiver = do_QueryInterface(mInput); + nsCOMPtr group; + receiver->GetSystemEventGroup(getter_AddRefs(group)); + + nsCOMPtr targ = do_QueryInterface(mInput); + targ->AddGroupedEventListener(NS_LITERAL_STRING("blur"), this, + PR_FALSE, group); + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::GetVisualContent(nsIDOMElement **aElement) +{ + NS_ADDREF(*aElement = mInput); + return NS_OK; +} + +// nsIXTFElement + +NS_IMETHODIMP +nsXFormsInputElement::OnDestroyed() +{ + if (!mInput) + return NS_OK; + + nsCOMPtr receiver = do_QueryInterface(mInput); + nsCOMPtr group; + receiver->GetSystemEventGroup(getter_AddRefs(group)); + + nsCOMPtr targ = do_QueryInterface(mInput); + targ->RemoveGroupedEventListener(NS_LITERAL_STRING("blur"), this, + PR_FALSE, group); + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::GetElementType(PRUint32 *aType) +{ + *aType = ELEMENT_TYPE_XML_VISUAL; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::GetIsAttributeHandler(PRBool *aIsHandler) +{ + *aIsHandler = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::GetScriptingInterfaces(PRUint32 *aCount, nsIID ***aArray) +{ + return CloneScriptingInterfaces(sScriptingIIDs, + NS_ARRAY_LENGTH(sScriptingIIDs), + aCount, aArray); +} + +NS_IMETHODIMP +nsXFormsInputElement::WillChangeDocument(nsIDOMDocument *aNewDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::DocumentChanged(nsIDOMDocument *aNewDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::WillChangeParent(nsIDOMElement *aNewParent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::ParentChanged(nsIDOMElement *aNewParent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::WillInsertChild(nsIDOMNode *aChild, PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::WillAppendChild(nsIDOMNode *aChild) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::ChildAppended(nsIDOMNode *aChild) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::WillRemoveChild(PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::ChildRemoved(PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::WillSetAttribute(nsIAtom *aName, const nsAString &aValue) +{ + if (aName == nsXFormsAtoms::bind || aName == nsXFormsAtoms::ref) { + nsCOMPtr bindElement; + nsXFormsModelElement *model = GetModelAndBind(getter_AddRefs(bindElement)); + if (model) + model->RemoveFormControl(this); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::AttributeSet(nsIAtom *aName, const nsAString &aValue) +{ + if (aName == nsXFormsAtoms::bind || aName == nsXFormsAtoms::ref) { + Refresh(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::WillRemoveAttribute(nsIAtom *aName) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::AttributeRemoved(nsIAtom *aName) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::DoneAddingChildren() +{ + return NS_OK; +} + +// nsIDOMEventListener + +NS_IMETHODIMP +nsXFormsInputElement::HandleEvent(nsIDOMEvent *aEvent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::Focus(nsIDOMEvent *aEvent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInputElement::Blur(nsIDOMEvent *aEvent) +{ + if (!mInput) + return NS_OK; + + nsRefPtr model; + nsCOMPtr bindElement; + nsCOMPtr result = + EvaluateBinding(nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE, + getter_AddRefs(model), getter_AddRefs(bindElement)); + + if (!result) + return NS_OK; + + nsCOMPtr singleNode; + result->GetSingleNodeValue(getter_AddRefs(singleNode)); + + if (!singleNode) + return NS_OK; + + nsAutoString value, type; + mInput->GetType(type); + if (type.EqualsLiteral("checkbox")) { + PRBool checked; + mInput->GetChecked(&checked); + value.AssignASCII(checked ? "1" : "0", 1); + } else { + mInput->GetValue(value); + } + + PRUint16 nodeType = 0; + singleNode->GetNodeType(&nodeType); + + switch (nodeType) { + case nsIDOMNode::ATTRIBUTE_NODE: + case nsIDOMNode::TEXT_NODE: + singleNode->SetNodeValue(value); + break; + case nsIDOMNode::ELEMENT_NODE: + { + nsCOMPtr node = do_QueryInterface(singleNode); + NS_ASSERTION(node, "DOM Nodes must support DOM3 interfaces"); + + node->SetTextContent(value); + break; + } + } + + return NS_OK; +} + +// other methods + +void +nsXFormsInputElement::Refresh() +{ + if (!mInput) + return; + + nsRefPtr model; + nsCOMPtr bindElement; + nsCOMPtr result = + EvaluateBinding(nsIDOMXPathResult::STRING_TYPE, + getter_AddRefs(model), getter_AddRefs(bindElement)); + + if (model) { + model->AddFormControl(this); + + if (result) { + nsAutoString nodeValue; + result->GetStringValue(nodeValue); + + nsCOMPtr type = model->GetTypeForControl(this); + nsCOMPtr biType = do_QueryInterface(type); + PRUint16 typeValue = nsISchemaBuiltinType::BUILTIN_TYPE_STRING; + + if (biType) + biType->GetBuiltinType(&typeValue); + + if (typeValue == nsISchemaBuiltinType::BUILTIN_TYPE_BOOLEAN) { + mInput->SetAttribute(NS_LITERAL_STRING("type"), + NS_LITERAL_STRING("checkbox")); + + mInput->SetChecked(nodeValue.EqualsLiteral("true") || + nodeValue.EqualsLiteral("1")); + } else { + mInput->RemoveAttribute(NS_LITERAL_STRING("type")); + mInput->SetValue(nodeValue); + } + } + } +} + +NS_HIDDEN_(nsresult) +NS_NewXFormsInputElement(nsIXTFElement **aResult) +{ + *aResult = new nsXFormsInputElement(); + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} diff --git a/extensions/xforms/nsXFormsInstanceElement.cpp b/extensions/xforms/nsXFormsInstanceElement.cpp new file mode 100644 index 00000000000..56f693d9228 --- /dev/null +++ b/extensions/xforms/nsXFormsInstanceElement.cpp @@ -0,0 +1,431 @@ +/* -*- 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): + * Brian Ryner + * + * 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 ***** */ + +#include "nsXFormsInstanceElement.h" +#include "nsIDOMElement.h" +#include "nsIDOMEventTarget.h" +#include "nsIDOM3Node.h" +#include "nsMemory.h" +#include "nsXFormsAtoms.h" +#include "nsString.h" +#include "nsIDOMEventReceiver.h" +#include "nsIDOMXMLDocument.h" +#include "nsIDOMDOMImplementation.h" + +static const nsIID sScriptingIIDs[] = { + NS_IDOMELEMENT_IID, + NS_IDOMEVENTTARGET_IID, + NS_IDOM3NODE_IID +}; + +NS_IMPL_ADDREF(nsXFormsInstanceElement) +NS_IMPL_RELEASE(nsXFormsInstanceElement) + +NS_INTERFACE_MAP_BEGIN(nsXFormsInstanceElement) + NS_INTERFACE_MAP_ENTRY(nsIXTFGenericElement) + NS_INTERFACE_MAP_ENTRY(nsIXTFElement) + NS_INTERFACE_MAP_ENTRY(nsIXTFPrivate) + NS_INTERFACE_MAP_ENTRY(nsIDOMLoadListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXTFGenericElement) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +nsXFormsInstanceElement::OnDestroyed() +{ + nsCOMPtr rec = do_QueryInterface(mDocument); + if (rec) + rec->RemoveEventListenerByIID(this, NS_GET_IID(nsIDOMLoadListener)); + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::GetElementType(PRUint32 *aElementType) +{ + *aElementType = nsIXTFElement::ELEMENT_TYPE_GENERIC_ELEMENT; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::GetIsAttributeHandler(PRBool *aIsAttributeHandler) +{ + *aIsAttributeHandler = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::GetScriptingInterfaces(PRUint32 *aCount, + nsIID ***aArray) +{ + return CloneScriptingInterfaces(sScriptingIIDs, + NS_ARRAY_LENGTH(sScriptingIIDs), + aCount, aArray); +} + +NS_IMETHODIMP +nsXFormsInstanceElement::WillChangeDocument(nsIDOMDocument *aNewDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::DocumentChanged(nsIDOMDocument *aNewDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::WillChangeParent(nsIDOMElement *aNewParent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::ParentChanged(nsIDOMElement *aNewParent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::WillInsertChild(nsIDOMNode *aChild, PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::WillAppendChild(nsIDOMNode *aChild) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::ChildAppended(nsIDOMNode *aChild) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::WillRemoveChild(PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::ChildRemoved(PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::WillSetAttribute(nsIAtom *aName, + const nsAString &aNewValue) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::AttributeSet(nsIAtom *aName, + const nsAString &aNewValue) +{ + if (aName == nsXFormsAtoms::src) { + // Note that this will fail if encountered during document construction, + // because we won't be in the document yet, so CreateInstanceDocument + // won't find a document to work with. That's ok, we'll fix things after + // our children are appended and we're in the document (DoneAddingChildren) + + LoadExternalInstance(aNewValue); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::WillRemoveAttribute(nsIAtom *aName) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::AttributeRemoved(nsIAtom *aName) +{ + if (aName == nsXFormsAtoms::src) { + // We no longer have an external instance to use. + // Reset our instance document to whatever inline content we have. + return CloneInlineInstance(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::DoneAddingChildren() +{ + // By the time this is called, we should be inserted in the document + // and have all of our child elements, so this is our first opportunity + // to create the instance document. + + nsCOMPtr element; + mWrapper->GetElementNode(getter_AddRefs(element)); + NS_ASSERTION(element, "no wrapper element"); + + nsAutoString src; + element->GetAttribute(NS_LITERAL_STRING("src"), src); + + if (src.IsEmpty()) { + // If we don't have a linked external instance, use our inline data. + CloneInlineInstance(); + } else { + LoadExternalInstance(src); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::OnCreated(nsIXTFGenericElementWrapper *aWrapper) +{ + aWrapper->SetNotificationMask (nsIXTFElement::NOTIFY_PARENT_CHANGED | + nsIXTFElement::NOTIFY_ATTRIBUTE_SET | + nsIXTFElement::NOTIFY_ATTRIBUTE_REMOVED | + nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN); + + mWrapper = aWrapper; + return NS_OK; +} + +// nsIDOMLoadListener + +NS_IMETHODIMP +nsXFormsInstanceElement::Load(nsIDOMEvent *aEvent) +{ + nsXFormsModelElement *model = GetModel(); + if (model) + model->RemovePendingInstance(); + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::BeforeUnload(nsIDOMEvent *aEvent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::Unload(nsIDOMEvent *aEvent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::Abort(nsIDOMEvent *aEvent) +{ + nsXFormsModelElement *model = GetModel(); + if (model) { + model->RemovePendingInstance(); + model->DispatchEvent(eEvent_LinkException); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsInstanceElement::Error(nsIDOMEvent *aEvent) +{ + nsXFormsModelElement *model = GetModel(); + if (model) { + model->RemovePendingInstance(); + model->DispatchEvent(eEvent_LinkException); + } + + return NS_OK; +} + +// nsIDOMEventListener + +NS_IMETHODIMP +nsXFormsInstanceElement::HandleEvent(nsIDOMEvent *aEvent) +{ + return NS_OK; +} + +// nsIXTFPrivate + +NS_IMETHODIMP +nsXFormsInstanceElement::GetInner(nsISupports **aInner) +{ + NS_ENSURE_ARG_POINTER(aInner); + + NS_ADDREF(*aInner = NS_STATIC_CAST(nsIXTFGenericElement*, this)); + return NS_OK; +} + + +// private methods + +nsresult +nsXFormsInstanceElement::CloneInlineInstance() +{ + // Clear out our existing instance data + nsresult rv = CreateInstanceDocument(); + if (NS_FAILED(rv)) + return rv; // don't warn, we might just not be in the document yet + + // look for our first child element (skip over text nodes, etc.) + nsCOMPtr element; + mWrapper->GetElementNode(getter_AddRefs(element)); + nsCOMPtr child, temp; + + element->GetFirstChild(getter_AddRefs(child)); + while (child) { + PRUint16 nodeType; + child->GetNodeType(&nodeType); + + if (nodeType == nsIDOMNode::ELEMENT_NODE) + break; + + temp.swap(child); + temp->GetNextSibling(getter_AddRefs(child)); + } + + if (child) { + nsCOMPtr newNode; + rv = mDocument->ImportNode(child, PR_TRUE, getter_AddRefs(newNode)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr nodeReturn; + rv = mDocument->AppendChild(newNode, getter_AddRefs(nodeReturn)); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to append root instance node"); + } + + return rv; +} + +void +nsXFormsInstanceElement::LoadExternalInstance(const nsAString &aSrc) +{ + // Clear out our existing instance data + if (NS_FAILED(CreateInstanceDocument())) + return; + + nsCOMPtr rec = do_QueryInterface(mDocument); + rec->AddEventListenerByIID(this, NS_GET_IID(nsIDOMLoadListener)); + + nsCOMPtr xmlDoc = do_QueryInterface(mDocument); + NS_ASSERTION(xmlDoc, "we created a document but it's not an XMLDocument?"); + + nsCOMPtr element; + mWrapper->GetElementNode(getter_AddRefs(element)); + + PRBool success; + xmlDoc->Load(aSrc, &success); + + nsXFormsModelElement *model = GetModel(); + if (model) { + if (success) + model->AddPendingInstance(); + else + model->DispatchEvent(eEvent_LinkException); + } +} + +nsresult +nsXFormsInstanceElement::CreateInstanceDocument() +{ + nsCOMPtr element; + mWrapper->GetElementNode(getter_AddRefs(element)); + NS_ASSERTION(element, "no wrapper element"); + + nsCOMPtr doc; + nsresult rv = element->GetOwnerDocument(getter_AddRefs(doc)); + NS_ENSURE_SUCCESS(rv, rv); + + if (!doc) // could be we just aren't inserted yet, so don't warn + return NS_ERROR_FAILURE; + + nsCOMPtr domImpl; + rv = doc->GetImplementation(getter_AddRefs(domImpl)); + NS_ENSURE_SUCCESS(rv, rv); + + return domImpl->CreateDocument(EmptyString(), EmptyString(), nsnull, + getter_AddRefs(mDocument)); +} + +nsXFormsModelElement* +nsXFormsInstanceElement::GetModel() +{ + nsCOMPtr element; + mWrapper->GetElementNode(getter_AddRefs(element)); + NS_ASSERTION(element, "no wrapper element"); + + nsCOMPtr parentNode; + element->GetParentNode(getter_AddRefs(parentNode)); + + nsCOMPtr modelElt = do_QueryInterface(parentNode); + if (!modelElt) + return nsnull; + + nsCOMPtr xtfPriv = do_QueryInterface(modelElt); + NS_ENSURE_TRUE(xtfPriv, nsnull); + + nsCOMPtr modelInner; + xtfPriv->GetInner(getter_AddRefs(modelInner)); + NS_ENSURE_TRUE(modelInner, nsnull); + + nsISupports *isupp = NS_STATIC_CAST(nsISupports*, modelInner.get()); + return NS_STATIC_CAST(nsXFormsModelElement*, + NS_STATIC_CAST(nsIXFormsModelElement*, isupp)); +} + +nsresult +NS_NewXFormsInstanceElement(nsIXTFElement **aResult) +{ + *aResult = new nsXFormsInstanceElement(); + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} diff --git a/extensions/xforms/nsXFormsInstanceElement.h b/extensions/xforms/nsXFormsInstanceElement.h new file mode 100644 index 00000000000..18c250769a2 --- /dev/null +++ b/extensions/xforms/nsXFormsInstanceElement.h @@ -0,0 +1,93 @@ +/* -*- 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): + * Brian Ryner + * + * 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 ***** */ + +/** + * nsXFormsInstanceElement implements the xforms element. + * It creates an instance document by either cloning the inline instance data + * or loading an external xml document given by the src attribute. + */ + +#ifndef nsXFormsInstanceElement_h_ +#define nsXFormsInstanceElement_h_ + +#include "nsIXTFGenericElement.h" +#include "nsIXTFPrivate.h" +#include "nsXFormsElement.h" +#include "nsIDOMDocument.h" +#include "nsCOMPtr.h" +#include "nsIXTFGenericElementWrapper.h" +#include "nsIDOMLoadListener.h" +#include "nsXFormsModelElement.h" + +class nsXFormsInstanceElement : public nsXFormsElement, + public nsIXTFGenericElement, + public nsIXTFPrivate, + public nsIDOMLoadListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIXTFELEMENT + NS_DECL_NSIXTFGENERICELEMENT + NS_DECL_NSIXTFPRIVATE + + // nsIDOMEventListener + NS_IMETHOD HandleEvent(nsIDOMEvent *aEvent); + + // nsIDOMLoadListener + NS_IMETHOD Load(nsIDOMEvent *aEvent); + NS_IMETHOD BeforeUnload(nsIDOMEvent *aEvent); + NS_IMETHOD Unload(nsIDOMEvent *aEvent); + NS_IMETHOD Abort(nsIDOMEvent *aEvent); + NS_IMETHOD Error(nsIDOMEvent *aEvent); + + NS_HIDDEN_(nsIDOMDocument*) GetDocument() { return mDocument; } + +private: + NS_HIDDEN_(nsresult) CloneInlineInstance(); + NS_HIDDEN_(void) LoadExternalInstance(const nsAString &aSrc); + NS_HIDDEN_(nsresult) CreateInstanceDocument(); + NS_HIDDEN_(nsXFormsModelElement*) GetModel(); + + nsCOMPtr mDocument; + nsCOMPtr mWrapper; +}; + +NS_HIDDEN_(nsresult) +NS_NewXFormsInstanceElement(nsIXTFElement **aResult); + +#endif diff --git a/extensions/xforms/nsXFormsMDG.cpp b/extensions/xforms/nsXFormsMDG.cpp new file mode 100644 index 00000000000..3400d0cca0d --- /dev/null +++ b/extensions/xforms/nsXFormsMDG.cpp @@ -0,0 +1,329 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#include "nsXFormsMDG.h" + +#include "nsVoidArray.h" +#include "nsIDOMDocument.h" +#include "nsIDOMText.h" +#include "nsXFormsXPathNode.h" + +MOZ_DECL_CTOR_COUNTER(nsXFormsMDG) + +nsXFormsMDG::nsXFormsMDG() + : mEngine(this) +{ + MOZ_COUNT_CTOR(nsXFormsMDG); +} + +nsXFormsMDG::~nsXFormsMDG() +{ + MOZ_COUNT_DTOR(nsXFormsMDG); +} + +nsresult +nsXFormsMDG::Init() +{ + return mEngine.Init(); +} + +nsresult +nsXFormsMDG::CreateNewChild(nsIDOMNode* aContextNode, const nsAString& aNodeValue, + nsIDOMNode* aBeforeNode) +{ + nsresult rv; + + nsCOMPtr document; + rv = aContextNode->GetOwnerDocument(getter_AddRefs(document)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr textNode; + rv = document->CreateTextNode(aNodeValue, getter_AddRefs(textNode)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr newNode; + if (aBeforeNode) { + rv = aContextNode->InsertBefore(textNode, aBeforeNode, getter_AddRefs(newNode)); + } else { + rv = aContextNode->AppendChild(textNode, getter_AddRefs(newNode)); + } + + return rv; +} + +nsresult +nsXFormsMDG::AddMIP(ModelItemPropName aType, nsIDOMXPathExpression* aExpression, + nsXFormsMDGSet* aDeps, PRBool aDynFunc, nsIDOMNode* aContextNode, + PRInt32 aContextPos, PRInt32 aContextSize) +{ + NS_ENSURE_ARG(aExpression); + NS_ENSURE_ARG(aContextNode); + + return mEngine.Insert(aContextNode, aExpression, aDeps, aDynFunc, aContextPos, aContextSize, aType); +} + +nsresult +nsXFormsMDG::MarkNodeAsChanged(nsIDOMNode* aContextNode) +{ + return mEngine.MarkNode(aContextNode); +} + +nsresult +nsXFormsMDG::Recalculate(nsXFormsMDGSet& aChangedNodes) +{ + return mEngine.Calculate(aChangedNodes); +} + +nsresult +nsXFormsMDG::Rebuild() +{ + return mEngine.Rebuild(); +} + +void +nsXFormsMDG::ClearDispatchFlags() +{ + mEngine.ClearDispatchFlags(); +} + +void +nsXFormsMDG::Clear() { + mEngine.Clear(); +} + +nsresult +nsXFormsMDG::GetNodeValue(nsIDOMNode* aContextNode, nsAString& aNodeValue) +{ + nsresult rv; + nsCOMPtr childNode; + + PRUint16 nodeType; + rv = aContextNode->GetNodeType(&nodeType); + NS_ENSURE_SUCCESS(rv, rv); + + switch(nodeType) { + case nsIDOMNode::ATTRIBUTE_NODE: + case nsIDOMNode::TEXT_NODE: + case nsIDOMNode::CDATA_SECTION_NODE: + case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: + case nsIDOMNode::COMMENT_NODE: + rv = aContextNode->GetNodeValue(aNodeValue); + NS_ENSURE_SUCCESS(rv, rv); + break; + + case nsIDOMNode::ELEMENT_NODE: + rv = aContextNode->GetFirstChild(getter_AddRefs(childNode)); + if (NS_FAILED(rv) || !childNode) { + // No child + aNodeValue.Truncate(0); + } else { + PRUint16 childType; + rv = childNode->GetNodeType(&childType); + NS_ENSURE_SUCCESS(rv, rv); + + if ( childType == nsIDOMNode::TEXT_NODE + || childType == nsIDOMNode::CDATA_SECTION_NODE) { + rv = childNode->GetNodeValue(aNodeValue); + NS_ENSURE_SUCCESS(rv, rv); + } else { + // Not a text child + aNodeValue.Truncate(0); + } + } + break; + + default: + // Asked for a node which cannot have a text child + // TODO: Should return more specific error? + return NS_ERROR_ILLEGAL_VALUE; + break; + } + + return NS_OK; +} + +nsresult +nsXFormsMDG::SetNodeValue(nsIDOMNode* aContextNode, nsAString& aNodeValue, PRBool aMarkNode) +{ + if (IsReadonly(aContextNode)) { + // TODO: Better feedback for readonly nodes? + return NS_OK; + } + + nsresult rv; + nsCOMPtr childNode; + PRUint16 nodeType; + rv = aContextNode->GetNodeType(&nodeType); + NS_ENSURE_SUCCESS(rv, rv); + + switch(nodeType) { + case nsIDOMNode::ATTRIBUTE_NODE: + case nsIDOMNode::TEXT_NODE: + case nsIDOMNode::CDATA_SECTION_NODE: + case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: + case nsIDOMNode::COMMENT_NODE: + // TODO: Check existing value, and ignore if same?? + rv = aContextNode->SetNodeValue(aNodeValue); + NS_ENSURE_SUCCESS(rv, rv); + + break; + + case nsIDOMNode::ELEMENT_NODE: + rv = aContextNode->GetFirstChild(getter_AddRefs(childNode)); + NS_ENSURE_SUCCESS(rv, rv); + + if (!childNode) { + rv = CreateNewChild(aContextNode, aNodeValue); + NS_ENSURE_SUCCESS(rv, rv); + } else { + PRUint16 childType; + rv = childNode->GetNodeType(&childType); + NS_ENSURE_SUCCESS(rv, rv); + + if ( childType == nsIDOMNode::TEXT_NODE + || childType == nsIDOMNode::CDATA_SECTION_NODE) { + rv = childNode->SetNodeValue(aNodeValue); + NS_ENSURE_SUCCESS(rv, rv); + } else { + // Not a text child, create a new one + rv = CreateNewChild(aContextNode, aNodeValue, childNode); + NS_ENSURE_SUCCESS(rv, rv); + } + } + break; + + default: + // Unsupported nodeType + // TODO: Should return more specific error? + return NS_ERROR_ILLEGAL_VALUE; + break; + } + + // NB: Never reached for Readonly nodes. + if (aMarkNode) { + MarkNodeAsChanged(aContextNode); + } + + return NS_OK; +} + + +/**********************************************/ +/* Bit test functions */ +/**********************************************/ +PRBool +nsXFormsMDG::IsValid(nsIDOMNode* aContextNode) +{ + + PRBool valid = mEngine.Test(aContextNode, MDG_FLAG_CONSTRAINT); + if (valid) { + // TODO: needs Schema support + // valid = mSchemaHandler->ValidateNode(aContextNode); + } + return valid; +} + +PRBool +nsXFormsMDG::IsConstraint(nsIDOMNode* aContextNode) +{ + return mEngine.Test(aContextNode, MDG_FLAG_CONSTRAINT); +} + +PRBool +nsXFormsMDG::ShouldDispatchValid(nsIDOMNode* aContextNode) +{ + return mEngine.Test(aContextNode, MDG_FLAG_DISPATCH_VALID_CHANGED); +} + +PRBool +nsXFormsMDG::IsReadonly(nsIDOMNode* aContextNode) +{ + PRBool valid; + if ( mEngine.Test(aContextNode, MDG_FLAG_READONLY) + || mEngine.Test(aContextNode, MDG_FLAG_INHERITED_READONLY)) { + valid = PR_TRUE; + } else { + valid = PR_FALSE; + } + return valid; +} + +PRBool +nsXFormsMDG::ShouldDispatchReadonly(nsIDOMNode* aContextNode) +{ + return mEngine.Test(aContextNode, MDG_FLAG_DISPATCH_READONLY_CHANGED); +} + + +PRBool +nsXFormsMDG::IsRelevant(nsIDOMNode* aContextNode) +{ + PRBool valid; + if ( mEngine.Test(aContextNode, MDG_FLAG_RELEVANT) + && mEngine.Test(aContextNode, MDG_FLAG_INHERITED_RELEVANT)) { + valid = PR_TRUE; + } else { + valid = PR_FALSE; + } + return valid; +} + +PRBool +nsXFormsMDG::ShouldDispatchRelevant(nsIDOMNode* aContextNode) +{ + return mEngine.Test(aContextNode, MDG_FLAG_DISPATCH_RELEVANT_CHANGED); +} + +PRBool +nsXFormsMDG::IsRequired(nsIDOMNode* aContextNode) +{ + return mEngine.Test(aContextNode, MDG_FLAG_REQUIRED); +} + +PRBool +nsXFormsMDG::ShouldDispatchRequired(nsIDOMNode* aContextNode) +{ + return mEngine.Test(aContextNode, MDG_FLAG_DISPATCH_REQUIRED_CHANGED); +} + +PRBool +nsXFormsMDG::ShouldDispatchValueChanged(nsIDOMNode* aContextNode) +{ + return mEngine.Test(aContextNode, MDG_FLAG_DISPATCH_VALUE_CHANGED); +} diff --git a/extensions/xforms/nsXFormsMDG.h b/extensions/xforms/nsXFormsMDG.h new file mode 100644 index 00000000000..c04410ae52f --- /dev/null +++ b/extensions/xforms/nsXFormsMDG.h @@ -0,0 +1,155 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#include "nsString.h" +#include "nsIDOMNode.h" +#include "nsIDOMXPathExpression.h" +#include "nsXFormsMDGEngine.h" + +/** + * Class nsXFormsMDG. + * + * This a wrapper of the more low-level nsXFormsMDGEngine. It handles all the Multi + * Dependency Graph (MDG) functions for the Mozilla XForms implementation. + */ +class nsXFormsMDG { +protected: + /** + * The MDG engine used. + */ + nsXFormsMDGEngine mEngine; + + /** + * Inserts a new text child for aContextNode. + * + * @param aContextNode The node to create a child for + * @param aNodeValue The value of the new node + * @param aBeforeNode If non-null, insert new node before this node + */ + nsresult CreateNewChild(nsIDOMNode* aContextNode, const nsAString& aNodeValue, nsIDOMNode* aBeforeNode = nsnull); + +public: + /** + * Constructor. Be sure to call Init() before using the object. + */ + nsXFormsMDG(); + + /** + * Destructor. + */ + ~nsXFormsMDG(); + + /** + * Initializes internal objects. If it fails, the object is unusable. + */ + nsresult Init(); + + /** + * Insert new MIP (Model Item Property) into graph. + * + * @param aType The type of MIP + * @param aExpression The XPath expression + * @param aDeps Set of nodes expression depends on + * @param aDynFunc True if expression uses dynamic functions + * @param aContextNode The context node for aExpression + * @param aContextPos The context positions of aExpression + * @param aContextSize The context size for aExpression + */ + nsresult AddMIP(ModelItemPropName aType, nsIDOMXPathExpression* aExpression, + nsXFormsMDGSet* aDeps, PRBool aDynFunc, + nsIDOMNode* aContextNode, + PRInt32 aContextPos, PRInt32 aContextSize); + + /** + * Recalculate the MDG. + * + * @param aChangedNodes Returns the nodes that was changed during recalculation. + */ + nsresult Recalculate(nsXFormsMDGSet& aChangedNodes); + + /** + * Rebuilds the MDG. + */ + nsresult Rebuild(); + + /** + * Clears all information in the MDG. + */ + void Clear(); + + /** + * Clears all Dispatch flags. + */ + void ClearDispatchFlags(); + + /** + * Mark a node as changed. + * + * @param aContextNode The node to be marked. + */ + nsresult MarkNodeAsChanged(nsIDOMNode* aContextNode); + + /** + * Set the value of a node. (used by nsXFormsMDG) + + * @param aContextNode The node to set the value for + * @param aNodeValue The value + * @param aMarkNode Mark node as changed? + */ + nsresult SetNodeValue(nsIDOMNode* aContextNode, nsAString& aNodeValue, PRBool aMarkNode = PR_FALSE); + + /** + * Get the value of a node. (used by nsXFormsMDG) + + * @param aContextNode The node to get the value for + * @param aNodeValue The value of the node + */ + nsresult GetNodeValue(nsIDOMNode* aContextNode, nsAString& aNodeValue); + + PRBool IsConstraint(nsIDOMNode* aContextNode); + PRBool IsValid(nsIDOMNode* aContextNode); + PRBool ShouldDispatchValid(nsIDOMNode* aContextNode); + PRBool IsReadonly(nsIDOMNode* aContextNode); + PRBool ShouldDispatchReadonly(nsIDOMNode* aContextNode); + PRBool IsRelevant(nsIDOMNode* aContextNode); + PRBool ShouldDispatchRelevant(nsIDOMNode* aContextNode); + PRBool IsRequired(nsIDOMNode* aContextNode); + PRBool ShouldDispatchRequired(nsIDOMNode* aContextNode); + PRBool ShouldDispatchValueChanged(nsIDOMNode* aContextNode); +}; diff --git a/extensions/xforms/nsXFormsMDGEngine.cpp b/extensions/xforms/nsXFormsMDGEngine.cpp new file mode 100644 index 00000000000..102c1b3a671 --- /dev/null +++ b/extensions/xforms/nsXFormsMDGEngine.cpp @@ -0,0 +1,781 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#include "nsXFormsMDGEngine.h" +#include "nsXFormsMDG.h" + +#include "nsIDOMNodeList.h" +#include "nsIDOMXPathExpression.h" +#include "nsIDOMXPathResult.h" +#include "nsDeque.h" + +#ifdef DEBUG +// #define DEBUG_XF_MDG +#endif + +/* ------------------------------------ */ +/* --------- nsXFormsMDGNode ---------- */ +/* ------------------------------------ */ +MOZ_DECL_CTOR_COUNTER(nsXFormsMDGNode) + +nsXFormsMDGNode::nsXFormsMDGNode(nsIDOMNode* aNode, const ModelItemPropName aType) + : mDirty (PR_TRUE), mHasExpr(PR_FALSE), mContextNode(aNode), mCount(0), mType(aType), + mContextSize(0), mContextPosition(0), mDynFunc(PR_FALSE), mNext(nsnull) +{ + MOZ_COUNT_CTOR(nsXFormsMDGNode); +} + +nsXFormsMDGNode::~nsXFormsMDGNode() +{ + MOZ_COUNT_DTOR(nsXFormsMDGNode); +} + +void +nsXFormsMDGNode::SetExpression(nsIDOMXPathExpression* aExpression, PRBool aDynFunc, + PRInt32 aContextPosition, PRInt32 aContextSize) +{ + mHasExpr = PR_TRUE; + mDynFunc = aDynFunc; + mExpression = aExpression; + mContextPosition = aContextPosition; + mContextSize = aContextSize; +} + +PRBool +nsXFormsMDGNode::HasExpr() const +{ + return mHasExpr; +} + +PRBool +nsXFormsMDGNode::IsDirty() const +{ + // A node is always dirty, if it depends on a dynamic function. + return mDirty || mDynFunc; +} + +void +nsXFormsMDGNode::MarkDirty() +{ + mDirty = PR_TRUE; +} + +void +nsXFormsMDGNode::MarkClean() +{ + mDirty = PR_FALSE; +} + +/* ------------------------------------ */ +/* -------- nsXFormsMDGEngine --------- */ +/* ------------------------------------ */ +MOZ_DECL_CTOR_COUNTER(nsXFormsMDGEngine) + +nsXFormsMDGEngine::nsXFormsMDGEngine(nsXFormsMDG* aOwner) +: mNodesInGraph(0), mOwner(aOwner) +{ + MOZ_COUNT_CTOR(nsXFormsMDGEngine); +} + +nsXFormsMDGEngine::~nsXFormsMDGEngine() +{ + mNodeToMDG.Enumerate(DeleteLinkedNodes, nsnull); + + MOZ_COUNT_DTOR(nsXFormsMDGEngine); +} + +PLDHashOperator +nsXFormsMDGEngine::DeleteLinkedNodes(nsISupports *aKey, nsAutoPtr& aNode, void* aArg) +{ + if (!aNode) { + NS_WARNING("nsXFormsMDGEngine::DeleteLinkedNodes() called with aNode == nsnull!"); + return PL_DHASH_STOP; + } + nsXFormsMDGNode* temp; + nsXFormsMDGNode* next = aNode->mNext; + + while (next) { + temp = next; + next = next->mNext; + delete temp; + } + + return PL_DHASH_NEXT; +} + +nsresult +nsXFormsMDGEngine::Init() +{ + nsresult rv = NS_ERROR_FAILURE; + if (mNodeToFlag.Init() && mNodeToMDG.Init()) { + rv = NS_OK; + } + + return rv; +} + +nsXFormsMDGNode* +nsXFormsMDGEngine::GetNode(nsIDOMNode* aDomNode, ModelItemPropName aType, PRBool aCreate) +{ + nsIDOMNode* nodeKey = aDomNode; + nsXFormsMDGNode* nd = nsnull; + +#ifdef DEBUG_XF_MDG + printf("nsXFormsMDGEngine::GetNode(aDomNode=%p, aType=%d, aCreate=%d)\n", (void*) nodeKey, aType, aCreate); +#endif + + + // Find correct type + if (mNodeToMDG.Get(nodeKey, &nd)) { + while (nd && nd->mType != aType) { + nd = nd->mNext; + } + } + + // Eventually create node + if (!nd && aCreate){ + nd = new nsXFormsMDGNode(nodeKey, aType); + if (!nd) { + NS_ERROR("Could not allocate room for new nsXFormsMDGNode"); + return nsnull; + } + +#ifdef DEBUG_XF_MDG + printf("\tNode not found, create new MDGNode '%p'\n", (void*) nd); +#endif + // Link to existing node + nsXFormsMDGNode* nd_exists; + if (mNodeToMDG.Get(nodeKey, &nd_exists)) { + while (nd_exists->mNext) { + nd_exists = nd_exists->mNext; + } +#ifdef DEBUG_XF_MDG + printf("\tLinking to existing MDGNode '%p'\n", (void*) nd_exists); +#endif + nd_exists->mNext = nd; + } else { + nodeKey->AddRef(); + if (!mNodeToMDG.Put(nodeKey, nd)) { + delete nd; + NS_ERROR("Could not insert new node in HashTable!"); + return nsnull; + } + } + + mNodesInGraph++; + } + return nd; +} + +nsresult +nsXFormsMDGEngine::Insert(nsIDOMNode* aContextNode, nsIDOMXPathExpression* aExpression, + const nsXFormsMDGSet* aDependencies, PRBool aDynFunc, + PRInt32 aContextPos, PRInt32 aContextSize, + ModelItemPropName aType, ModelItemPropName aDepType) +{ + NS_ENSURE_ARG(aContextNode); + NS_ENSURE_ARG(aExpression); + +#ifdef DEBUG_XF_MDG + nsAutoString nodename; + aContextNode->GetNodeName(nodename); + printf("nsXFormsMDGEngine::Insert(aContextNode=%s, aExpression=n/a, aDependencies=|%d|,\n", + NS_ConvertUCS2toUTF8(nodename).get(), + aDependencies->Count()); + printf(" aContextPos=%d, aContextSize=%d, aType=%d, aDepType=%d,\n", + aContextPos, aContextSize, aType, aDepType); + printf(" aDynFunc=%d)\n", + aDynFunc); +#endif + nsXFormsMDGNode* newnode = GetNode(aContextNode, aType); + + if (!newnode) { + return NS_ERROR_OUT_OF_MEMORY; + } + + if (newnode->HasExpr()) { + // MIP already in the graph. That is. there is already a MIP of the same + // type for this node, which is illegal. + // Example: + return NS_ERROR_ABORT; + } + + newnode->SetExpression(aExpression, aDynFunc, aContextPos, aContextSize); + + // Add dependencies + if (aDependencies) { + nsCOMPtr dep_domnode; + nsXFormsMDGNode* dep_gnode; + for (PRInt32 i = 0; i < aDependencies->Count(); ++i) { + dep_domnode = aDependencies->GetNode(i); + if (!dep_domnode) { + return NS_ERROR_NULL_POINTER; + } + + dep_gnode = GetNode(dep_domnode, aDepType); + if (!dep_gnode) { + return NS_ERROR_OUT_OF_MEMORY; + } + + if (aType == aDepType && dep_gnode->mContextNode == aContextNode) { + // Reference to itself, ignore + continue; + } + + dep_gnode->mSuc.AppendElement(newnode); + newnode->mCount++; + } + } + + return NS_OK; +} + +PLDHashOperator +nsXFormsMDGEngine::AddStartNodes(nsISupports *aKey, nsXFormsMDGNode* aNode, void* aDeque) +{ +#ifdef DEBUG_XF_MDG + printf("nsXFormsMDGEngine::AddStartNodes(aKey=n/a, aNode=%p, aDeque=%p)\n", (void*) aNode, aDeque); +#endif + + nsDeque* deque = NS_STATIC_CAST(nsDeque*, aDeque); + if (!deque) { + NS_ERROR("nsXFormsMDGEngine::AddStartNodes called with NULL aDeque"); + return PL_DHASH_STOP; + } + + while (aNode) { + if (aNode->mCount == 0) { + // Is it not possible to check error condition? + deque->Push(aNode); + } + aNode = aNode->mNext; + } + + return PL_DHASH_NEXT; +} + +nsresult +nsXFormsMDGEngine::Rebuild() +{ +#ifdef DEBUG_XF_MDG + printf("nsXFormsMDGEngine::Rebuild()\n"); +#endif + nsresult rv = NS_OK; + mJustRebuilt = PR_TRUE; + mFirstCalculate = PR_FALSE; + + mGraph.Clear(); + mNodeToFlag.Clear(); + + nsDeque sortedNodes(nsnull); + +#ifdef DEBUG_XF_MDG + printf("\tmNodesInGraph: %d\n", mNodesInGraph); + printf("\tmNodeToMDG: %d\n", mNodeToMDG.Count()); + printf("\tmNodeToFlag: %d\n", mNodeToFlag.Count()); +#endif + + // Initial scan for nsXFormsMDGNodes with no dependencies (count == 0) + PRUint32 entries = mNodeToMDG.EnumerateRead(AddStartNodes, &sortedNodes); + if (entries != mNodeToMDG.Count()) { + return NS_ERROR_OUT_OF_MEMORY; + } + +#ifdef DEBUG_XF_MDG + printf("\tStartNodes: %d\n", sortedNodes.GetSize()); +#endif + + + nsXFormsMDGNode* node; + while ((node = NS_STATIC_CAST(nsXFormsMDGNode*, sortedNodes.Pop()))) { + for (PRInt32 i = 0; i < node->mSuc.Count(); ++i) { + nsXFormsMDGNode* sucNode = NS_STATIC_CAST(nsXFormsMDGNode*, node->mSuc[i]); + NS_ASSERTION(sucNode, "XForms: NULL successor node"); + + sucNode->mCount--; + if (sucNode->mCount == 0) { + sortedNodes.Push(sucNode); + } + } + + node->MarkDirty(); + + if (!mGraph.AppendElement(node)) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + + if (mGraph.Count() != mNodesInGraph) { + NS_WARNING("XForms: There are loops in the MDG\n"); + rv = NS_ERROR_ABORT; + } + + mNodesInGraph = 0; + + return rv; +} + +void +nsXFormsMDGEngine::Clear() { +#ifdef DEBUG_XF_MDG + printf("nsXFormsMDGEngine::Clear()\n"); +#endif + + mNodeToMDG.Enumerate(DeleteLinkedNodes, nsnull); + mNodeToMDG.Clear(); + + mNodeToFlag.Clear(); + + mGraph.Clear(); + + mNodesInGraph = 0; +} + +PLDHashOperator +nsXFormsMDGEngine::AndFlag(nsISupports *aKey, PRUint16& aFlag, void* aMask) +{ + PRUint16* andMask = NS_STATIC_CAST(PRUint16*, aMask); + if (!andMask) { + return PL_DHASH_STOP; + } + aFlag &= *andMask; + return PL_DHASH_NEXT; +} + +PRBool +nsXFormsMDGEngine::AndFlags(PRUint16 aAndMask) +{ + PRUint32 entries = mNodeToFlag.Enumerate(AndFlag, &aAndMask); + return (entries == mNodeToFlag.Count()) ? PR_TRUE : PR_FALSE; +} + +PRUint16 +nsXFormsMDGEngine::GetFlag(nsIDOMNode* aDomNode) +{ + PRUint16 flag = MDG_FLAG_DEFAULT | (mJustRebuilt && mFirstCalculate ? MDG_FLAG_INITIAL_DISPATCH : 0); + + // If node is found, flag is modified, if not flag is untouched + mNodeToFlag.Get(aDomNode, &flag); + + return flag; +} + +void +nsXFormsMDGEngine::SetFlag(nsIDOMNode* aDomNode, PRUint16 aFlag) +{ + nsIDOMNode *nodeKey = aDomNode; + nodeKey->AddRef(); + mNodeToFlag.Put(nodeKey, aFlag); +} + +void +nsXFormsMDGEngine::OrFlag(nsIDOMNode* aDomNode, PRInt16 aFlag) +{ + PRUint16 fl = GetFlag(aDomNode); + fl |= aFlag; + SetFlag(aDomNode, fl); +} + +void +nsXFormsMDGEngine::SetFlagBits(nsIDOMNode* aDomNode, PRUint16 aBits, PRBool aOn) +{ + PRUint32 fl = GetFlag(aDomNode); + if (aOn) { + fl |= aBits; + } else { + fl &= ~aBits; + } + SetFlag(aDomNode, fl); +} + + +nsresult +nsXFormsMDGEngine::BooleanExpression(nsXFormsMDGNode* aNode, PRBool& state) +{ + NS_ENSURE_ARG_POINTER(aNode); + + // TODO: Use aNode->contextPosition and aNode->contextSize! + nsISupports* retval; + nsresult rv; + + rv = aNode->mExpression->Evaluate(aNode->mContextNode, nsIDOMXPathResult::BOOLEAN_TYPE, nsnull, &retval); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr xpath_res = do_QueryInterface(retval); + NS_ENSURE_TRUE(xpath_res, NS_ERROR_FAILURE); + + rv = xpath_res->GetBooleanValue(&state); + + return rv; +} + +nsresult +nsXFormsMDGEngine::ComputeMIP(PRUint16 aStateFlag, PRUint16 aDispatchFlag, nsXFormsMDGNode* aNode, PRBool& aDidChange) +{ + NS_ENSURE_ARG_POINTER(aNode); + + PRUint32 word = GetFlag(aNode->mContextNode); + PRBool state; + nsresult rv = BooleanExpression(aNode, state); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool cstate = ((word & aStateFlag) != 0) ? PR_TRUE : PR_FALSE; + + if (state) { + word |= aStateFlag; + } else { + word &= ~aStateFlag; + } + + aDidChange = (state != cstate) ? PR_TRUE : PR_FALSE; + if (aDidChange) { + word |= aDispatchFlag; + } + + SetFlag(aNode->mContextNode, word); + + return NS_OK; +} + +nsresult +nsXFormsMDGEngine::ComputeMIPWithInheritance(PRUint16 aStateFlag, PRUint16 aDispatchFlag, PRUint16 aInheritanceFlag, nsXFormsMDGNode* aNode, nsXFormsMDGSet& aSet) +{ + nsresult rv; + PRBool didChange; + rv = ComputeMIP(aStateFlag, aDispatchFlag, aNode, didChange); + NS_ENSURE_SUCCESS(rv, rv); + + if (didChange) { + PRUint16 flag = GetFlag(aNode->mContextNode); + PRBool state = (flag & aStateFlag != 0) ? PR_TRUE : PR_FALSE; + if ( (aStateFlag == MDG_FLAG_READONLY && (flag & aInheritanceFlag) == 0) + || (aStateFlag == MDG_FLAG_RELEVANT && (flag & aInheritanceFlag) > 0) ) + { + NS_ENSURE_TRUE(aSet.AddNode(aNode->mContextNode), NS_ERROR_FAILURE); + rv = AttachInheritance(aSet, aNode->mContextNode, state, aStateFlag); + } + } + + return NS_OK; +} + +nsresult +nsXFormsMDGEngine::AttachInheritance(nsXFormsMDGSet& aSet, nsIDOMNode* aSrc, PRBool aState, PRUint16 aStateFlag) +{ + NS_ENSURE_ARG(aSrc); + + nsCOMPtr node; + PRUint32 flag; + PRBool cstate; + nsresult rv; + PRBool updateNode = PR_FALSE; + + nsCOMPtr childList; + rv = aSrc->GetChildNodes(getter_AddRefs(childList)); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 childCount; + rv = childList->GetLength(&childCount); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRUint32 i = 0; i < childCount; i++) { + rv = childList->Item(i, getter_AddRefs(node)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); + + flag = GetFlag(node); + + cstate = ((flag & aStateFlag) != 0) ? PR_TRUE : PR_FALSE; + + if (aStateFlag == MDG_FLAG_RELEVANT) { + if (aState == PR_FALSE) { // The nodes are getting irrelevant + if ((flag & MDG_FLAG_INHERITED_RELEVANT) != 0 && cstate) { + flag &= ~MDG_FLAG_INHERITED_RELEVANT; + flag |= MDG_FLAG_DISPATCH_RELEVANT_CHANGED; + updateNode = PR_TRUE; + } + } else { // The nodes are becoming relevant + if (cstate) { + flag |= MDG_FLAG_DISPATCH_RELEVANT_CHANGED; // Relevant has changed from inheritance + flag |= MDG_FLAG_INHERITED_RELEVANT; // Clear the flag for inheritance + updateNode = PR_TRUE; + } + } + } else if (aStateFlag == MDG_FLAG_READONLY) { + if (aState) { // The nodes are getting readonly + if ((flag & MDG_FLAG_INHERITED_READONLY) == 0 && cstate == PR_FALSE) { + flag |= MDG_FLAG_INHERITED_READONLY | MDG_FLAG_DISPATCH_READONLY_CHANGED; + updateNode = PR_TRUE; + } + } else { // The nodes are getting readwrite + if (cstate) { + flag |= MDG_FLAG_DISPATCH_READONLY_CHANGED; + flag &= ~MDG_FLAG_INHERITED_READONLY; + updateNode = PR_TRUE; + } + } + } + + if (updateNode) { + SetFlag(node, flag); + rv = AttachInheritance(aSet, node, aState, aStateFlag); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(aSet.AddNode(node), NS_ERROR_FAILURE); + updateNode = PR_FALSE; + } + } + + return NS_OK; +} + +nsresult +nsXFormsMDGEngine::Calculate(nsXFormsMDGSet& aValueChanged) +{ +#ifdef DEBUG_XF_MDG + printf("nsXFormsMDGEngine::Calculcate(aValueChanged=|%d|)\n", aValueChanged.Count()); +#endif + + NS_ENSURE_TRUE(aValueChanged.AddSet(mMarkedNodes), NS_ERROR_OUT_OF_MEMORY); + + mMarkedNodes.Clear(); + + PRBool res = PR_TRUE; + + mFirstCalculate = mJustRebuilt; + +#ifdef DEBUG_XF_MDG + printf("\taValueChanged: %d\n", aValueChanged.Count()); + printf("\tmNodeToMDG: %d\n", mNodeToFlag.Count()); + printf("\tmNodeToFlag: %d\n", mNodeToFlag.Count()); + printf("\tGraph nodes: %d\n", mGraph.Count()); +#endif + + // Go through all dirty nodes in the graph + nsresult rv; + nsXFormsMDGNode* g; + for (PRInt32 i = 0; i < mGraph.Count(); ++i) { + g = NS_STATIC_CAST(nsXFormsMDGNode*, mGraph[i]); + + if (!g) { + NS_WARNING("nsXFormsMDGEngine::Calculcate(): Empty node in graph!!!"); + continue; + } + +#ifdef DEBUG_XF_MDG + nsAutoString domNodeName; + g->mContextNode->GetNodeName(domNodeName); + + printf("\tNode #%d: This=%p, Dirty=%d, DynFunc=%d, Type=%d, Count=%d, Suc=%d, CSize=%d, CPos=%d, Next=%p, domnode=%s\n", + i, (void*) g, g->IsDirty(), g->mDynFunc, g->mType, g->mCount, g->mSuc.Count(), + g->mContextSize, g->mContextPosition, (void*) g->mNext, NS_ConvertUCS2toUTF8(domNodeName).get()); +#endif + + // Ignore node if it is not dirty + if (!g->IsDirty()) { + continue; + } + + PRBool constraint = PR_TRUE; + // Find MIP-type and handle it accordingly + switch (g->mType) { + case eModel_calculate: + if (g->HasExpr()) { + nsISupports* retval; + rv = g->mExpression->Evaluate(g->mContextNode, nsIDOMXPathResult::STRING_TYPE, nsnull, &retval); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr xpath_res = do_QueryInterface(retval); + NS_ENSURE_TRUE(xpath_res, NS_ERROR_OUT_OF_MEMORY); + + nsAutoString nodeval; + rv = xpath_res->GetStringValue(nodeval); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mOwner->SetNodeValue(g->mContextNode, nodeval); + if (NS_SUCCEEDED(rv)) { + NS_ENSURE_TRUE(aValueChanged.AddNode(g->mContextNode), NS_ERROR_FAILURE); + } + } + + OrFlag(g->mContextNode, MDG_FLAG_DISPATCH_VALUE_CHANGED);// | MDG_FLAG_DISPATCH_READONLY_CHANGED | MDG_FLAG_DISPATCH_VALID_CHANGED | MDG_FLAG_DISPATCH_RELEVANT_CHANGED); + break; + + case eModel_constraint: + if (g->HasExpr()) { + rv = BooleanExpression(g, constraint); + NS_ENSURE_SUCCESS(rv, rv); + } + // TODO: Schema validity should be checked here + + if ((GetFlag(g->mContextNode) & MDG_FLAG_CONSTRAINT) > 0 != constraint) { + SetFlagBits(g->mContextNode, MDG_FLAG_CONSTRAINT, constraint); + SetFlagBits(g->mContextNode, MDG_FLAG_DISPATCH_VALID_CHANGED, PR_TRUE); + NS_ENSURE_TRUE(aValueChanged.AddNode(g->mContextNode), NS_ERROR_FAILURE); + } + break; + + case eModel_readonly: + if (g->HasExpr()) { + rv = ComputeMIPWithInheritance(MDG_FLAG_READONLY, MDG_FLAG_DISPATCH_READONLY_CHANGED, MDG_FLAG_INHERITED_READONLY, g, aValueChanged); + NS_ENSURE_SUCCESS(rv, rv); + } + break; + + case eModel_relevant: + if (g->HasExpr()) { + rv = ComputeMIPWithInheritance(MDG_FLAG_RELEVANT, MDG_FLAG_DISPATCH_RELEVANT_CHANGED, MDG_FLAG_INHERITED_RELEVANT, g, aValueChanged); + NS_ENSURE_SUCCESS(rv, rv); + } + break; + + case eModel_required: + PRBool didChange; + rv = ComputeMIP(MDG_FLAG_REQUIRED, MDG_FLAG_DISPATCH_REQUIRED_CHANGED, g, didChange); + NS_ENSURE_SUCCESS(rv, rv); + + if (g->HasExpr() && didChange) { + NS_ENSURE_TRUE(aValueChanged.AddNode(g->mContextNode), NS_ERROR_FAILURE); + } + break; + + default: + NS_ERROR("There was no expression which matched\n"); + res = PR_FALSE; + break; + } + + // Mark successors dirty + nsXFormsMDGNode* sucnode; + for (PRInt32 j = 0; j < g->mSuc.Count(); ++j) { + sucnode = NS_STATIC_CAST(nsXFormsMDGNode*, g->mSuc[j]); + if (!sucnode) { + NS_ERROR("nsXFormsMDGEngine::Calculate(): Node has NULL successor!"); + return NS_ERROR_FAILURE; + } + sucnode->MarkDirty(); + } + + g->MarkClean(); + } + aValueChanged.MakeUnique(); + +#ifdef DEBUG_XF_MDG + printf("\taValueChanged: %d\n", aValueChanged.Count()); + printf("\tmNodeToMDG: %d\n", mNodeToFlag.Count()); + printf("\tmNodeToFlag: %d\n", mNodeToFlag.Count()); + printf("\tGraph nodes: %d\n", mGraph.Count()); +#endif + + return res; +} + +// TODO: This function needs to be called +nsresult +nsXFormsMDGEngine::MarkNode(nsIDOMNode* aDomNode) +{ + SetFlagBits(aDomNode, MDG_FLAG_ALL_DISPATCH, PR_TRUE); + + nsXFormsMDGNode* n = GetNode(aDomNode, eModel_calculate); + if (n) { + n->MarkDirty(); + } + + // Add constraint to trigger validation of node + n = GetNode(aDomNode, eModel_constraint, PR_FALSE); + if (!n) { + n = GetNode(aDomNode, eModel_constraint, PR_TRUE); + if (!n) { + return NS_ERROR_OUT_OF_MEMORY; + } + NS_ENSURE_TRUE(mGraph.AppendElement(n), NS_ERROR_OUT_OF_MEMORY); + } + + n->MarkDirty(); + + NS_ENSURE_TRUE(mMarkedNodes.AddNode(aDomNode), NS_ERROR_FAILURE); + + return NS_OK; +} + +PRBool +nsXFormsMDGEngine::ClearDispatchFlags() +{ + mJustRebuilt = PR_FALSE; + return AndFlags(MDG_FLAGMASK_NOT_DISPATCH); +} + +nsresult +nsXFormsMDGEngine::Invalidate() +{ + nsXFormsMDGNode* g; + for (PRInt32 i = 0; i < mGraph.Count(); ++i) { + g = NS_STATIC_CAST(nsXFormsMDGNode*, mGraph[i]); + NS_ENSURE_TRUE(g, NS_ERROR_FAILURE); + g->MarkDirty(); + } + return NS_OK; +} + +PRBool +nsXFormsMDGEngine::TestAndClear(nsIDOMNode* aDomNode, PRUint16 aFlag) +{ + PRUint16 fl = GetFlag(aDomNode); + PRUint16 test = fl & aFlag; + fl &= ~aFlag; + SetFlag(aDomNode, fl); + return (test != 0) ? PR_TRUE : PR_FALSE; +} + +PRBool +nsXFormsMDGEngine::TestAndSet(nsIDOMNode* aDomNode, PRUint16 aFlag) +{ + PRUint16 fl = GetFlag(aDomNode); + PRUint16 test = fl & aFlag; + fl |= aFlag; + SetFlag(aDomNode, fl); + return (test != 0) ? PR_TRUE : PR_FALSE; +} + +PRBool +nsXFormsMDGEngine::Test(nsIDOMNode* aDomNode, PRUint16 aFlag) +{ + PRUint16 fl = GetFlag(aDomNode); + PRUint16 test = fl & aFlag; + return (test != 0) ? PR_TRUE : PR_FALSE; +} diff --git a/extensions/xforms/nsXFormsMDGEngine.h b/extensions/xforms/nsXFormsMDGEngine.h new file mode 100644 index 00000000000..bbf24b7659b --- /dev/null +++ b/extensions/xforms/nsXFormsMDGEngine.h @@ -0,0 +1,430 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#ifndef __NSXFORMSMDGENGINE__ +#define __NSXFORMSMDGENGINE__ + +#include "nscore.h" +#include "nsCOMPtr.h" +#include "nsVoidArray.h" +#include "nsClassHashtable.h" +#include "nsDataHashtable.h" +#include "nsIDOMNode.h" +#include "nsXFormsTypes.h" +#include "nsXFormsMDGSet.h" + +class nsIDOMXPathExpression; +class nsXFormsMDG; + +/** + * Flags, etc. + * + * TODO: Convert to enum? + */ +const PRUint16 MDG_FLAG_READONLY = 1 << 1; +const PRUint16 MDG_FLAG_CONSTRAINT = 1 << 2; +const PRUint16 MDG_FLAG_RELEVANT = 1 << 3; +const PRUint16 MDG_FLAG_REQUIRED = 1 << 4; +const PRUint16 MDG_FLAG_SCHEMA_VALID = 1 << 5; +const PRUint16 MDG_FLAG_VALID = 1 << 6; +const PRUint16 MDG_FLAG_INHERITED_RELEVANT = 1 << 7; +const PRUint16 MDG_FLAG_INHERITED_READONLY = 1 << 8; +const PRUint16 MDG_FLAG_DISPATCH_VALUE_CHANGED = 1 << 9; +const PRUint16 MDG_FLAG_DISPATCH_READONLY_CHANGED = 1 << 10; +const PRUint16 MDG_FLAG_DISPATCH_VALID_CHANGED = 1 << 11; +const PRUint16 MDG_FLAG_DISPATCH_RELEVANT_CHANGED = 1 << 12; +const PRUint16 MDG_FLAG_DISPATCH_REQUIRED_CHANGED = 1 << 13; +const PRUint16 MDG_FLAG_DISPATCH_CONSTRAINT_CHANGED = 1 << 14; + +const PRUint16 MDG_FLAGMASK_NOT_DISPATCH = ~(MDG_FLAG_DISPATCH_VALUE_CHANGED | \ + MDG_FLAG_DISPATCH_READONLY_CHANGED |\ + MDG_FLAG_DISPATCH_VALID_CHANGED |\ + MDG_FLAG_DISPATCH_RELEVANT_CHANGED |\ + MDG_FLAG_DISPATCH_REQUIRED_CHANGED |\ + MDG_FLAG_DISPATCH_CONSTRAINT_CHANGED); + +const PRUint16 MDG_FLAG_DEFAULT = MDG_FLAG_CONSTRAINT | \ + MDG_FLAG_RELEVANT |\ + MDG_FLAG_INHERITED_RELEVANT; + +const PRUint16 MDG_FLAG_INITIAL_DISPATCH = MDG_FLAG_DISPATCH_READONLY_CHANGED |\ + MDG_FLAG_DISPATCH_VALID_CHANGED |\ + MDG_FLAG_DISPATCH_RELEVANT_CHANGED |\ + MDG_FLAG_DISPATCH_REQUIRED_CHANGED; + +const PRUint16 MDG_FLAG_ALL_DISPATCH = MDG_FLAG_DISPATCH_READONLY_CHANGED |\ + MDG_FLAG_DISPATCH_VALID_CHANGED |\ + MDG_FLAG_DISPATCH_RELEVANT_CHANGED |\ + MDG_FLAG_DISPATCH_REQUIRED_CHANGED |\ + MDG_FLAG_DISPATCH_VALUE_CHANGED |\ + MDG_FLAG_DISPATCH_CONSTRAINT_CHANGED; + + + +/** + * Data structure for nodes in the graph. + * + * There is one node per type (calculate, readonly, etc) for each nsIDOMNode. + * All nsXFormsMDGNodes for same nsIDOMNode are linked together via 'next'. + */ +class nsXFormsMDGNode { +private: + /** Dirty flag */ + PRBool mDirty; + + /** Does this node have an XPath expression attached to it */ + PRBool mHasExpr; + +public: + /** Pointer to the nsIDOMNode */ + nsCOMPtr mContextNode; + + /** The XPath expression for this node */ + nsCOMPtr mExpression; + + /** List of nodes that depend on this node */ + nsVoidArray mSuc; + + /** Number of nodes that this node depends on */ + unsigned int mCount; + + /** The type */ + ModelItemPropName mType; + + /** (XPath) Context size for this node */ + int mContextSize; + + /** (XPath) Position for this node */ + int mContextPosition; + + /** Does expression use dynamic functions */ + PRBool mDynFunc; + + /** Pointer to next nsXFormsMDGNode with same nsIDOMNode, but different type */ + nsXFormsMDGNode* mNext; + + /** + * Constructor. + * + * @param aNode The context node + * @param aType The type of node (calculate, readonly, etc.) + */ + nsXFormsMDGNode(nsIDOMNode* aNode, const ModelItemPropName aType); + + /** Destructor */ + ~nsXFormsMDGNode(); + + /** + * Sets the XPath expression for the node. + * + * @param aExpression The XPath expression + * @param aDynFunc Whether expression uses dynamic functions + * @param aContextPosition The context position for the expression + * @param aContextSize The context size for the expression + */ + void SetExpression(nsIDOMXPathExpression* aExpression, PRBool aDynFunc, + PRInt32 aContextPosition, PRInt32 aContextSize); + + /** Does node have an expression? */ + PRBool HasExpr() const; + + /** Is node dirty? */ + PRBool IsDirty() const; + + /* Mark node clean */ + void MarkClean(); + + /* Mark node dirty */ + void MarkDirty(); +}; + +/** + * The Multi Dependency Graph (MDG) Engine. + * + * This class handles all the necessary logic to create and maintain the MDG, + * and update the instance data accordingly. + * + * A bit about the graph: + * As specified in the spec., one node (nsXFormsMDGNode) is created in the graph for + * each of the attributes (readonly, calculate, etc.) for a nsIDOMNode. These graph + * nodes are owned by the mNodeToMDG hash table, which maps from a nsIDOMNode to + * the first node in a single-linked list (mNext) of nodes for the same nsIDOMNode. + */ +class nsXFormsMDGEngine { +protected: + /** + * Maps from nsIDOMNode to nsXFormsMDGNode(s) + */ + nsClassHashtable mNodeToMDG; + + /** + * Maps from nsIDOMNode to flag (MDG_FLAG_*) + */ + nsDataHashtable mNodeToFlag; + + /** + * True when Rebuild() has been run, but not ClearDispatchFlags() + */ + PRBool mJustRebuilt; + + /** + * True when last Calculate() was run when mJustRebuilded was true. + */ + PRBool mFirstCalculate; + + /** The actual MDG */ + nsVoidArray mGraph; + + /** Set of nodes that are marked as changed, and should be included in recalculation */ + nsXFormsMDGSet mMarkedNodes; + + /** Number of nodes in the graph */ + PRInt32 mNodesInGraph; + + /** Hidden copy constructor */ + nsXFormsMDGEngine(nsXFormsMDGEngine&) {} + + /** + * Used by Clear() and ~ to delete the linked nodes in mNodeToMDG, the hash table + * itself handles the main nodes. + */ + static PLDHashOperator PR_CALLBACK DeleteLinkedNodes(nsISupports *aKey, nsAutoPtr& aNode, void* aArg); + + /** + * Used by Rebuild() to find the start nodes for mGraph, that is nodes + * where mCount == 0. + */ + static PLDHashOperator PR_CALLBACK AddStartNodes(nsISupports *aKey, nsXFormsMDGNode* aNode, void* aDeque); + + /** + * Used by AndFlags() to boolean AND all flags with aMask. + */ + static PLDHashOperator PR_CALLBACK AndFlag(nsISupports *aKey, PRUint16& aFlag, void* aMask); + + /** + * Retrieve a node from the graph. + * + * @param aDomNode The DOM node to retrieve + * @param aType The type to retrieve (readonly, calculate, etc) + * @param aCreate Create the node and insert it into the graph if it does not exist? + * @return The node, nsnull if not found and aCreate != PR_TRUE + */ + nsXFormsMDGNode* GetNode(nsIDOMNode* aDomNode, ModelItemPropName aType, PRBool aCreate = PR_TRUE); + + /** + * Sets the flag for a node. + * + * @param aDomNode The node + * @param aFlag The flag + */ + void SetFlag(nsIDOMNode* aDomNode, PRUint16 aFlag); + + /** + * Boolean OR the existing flag on the node with a new flag + * + * @param aDomNode The node + * @param aFlag The new flag + */ + void OrFlag(nsIDOMNode* aDomNode, PRInt16 aFlag); + + /** + * Retrieve the flag for a node. + * + * @param aDomNode The node + * @return The flag + */ + PRUint16 GetFlag(nsIDOMNode* aDomNode); + + /** + * Sets a bit in the flag for a node + * + * @param aDomNode The node + * @param aBit The bit position to set + * @param aOn The bit value + */ + void SetFlagBits(nsIDOMNode* aDomNode, PRUint16 aBit, PRBool aOn); + + /** + * Boolean AND all flags with a mask. + * + * @param aAndMask The mask + * @return Did operation succeed? + */ + PRBool AndFlags(PRUint16 aAndMask); + + /** + * Evaluates the expression for the given node and returns the boolean result. + * + * @param aNode The node to evaluate + * @param res The result of the evaluation + */ + nsresult BooleanExpression(nsXFormsMDGNode* aNode, PRBool& res); + + /** + * Compute MIP value for node types with boolean result (all except calculate) + * + * @param aStateFlag The flag for the type of MIP + * @param aDispatchFlag The dispatch flag + * @param aNode The context node + * @param aDidChange Was the node changed? + */ + nsresult ComputeMIP(PRUint16 aStateFlag, PRUint16 aDispatchFlag, nsXFormsMDGNode* aNode, PRBool& aDidChange); + + /** + * Same as ComputeMIP(), but also handles any inheritance of attributes. + * + * @param aStateFlag The flag for the type of MIP + * @param aDispatchFlag The dispatch flag + * @param aInheritanceFlag The inheritance flag for the type of MIP + * @param aNode The context node + * @param aSet Set of the nodes influenced by operation + */ + nsresult ComputeMIPWithInheritance(PRUint16 aStateFlag, PRUint16 aDispatchFlag, PRUint16 aInheritanceFlag, nsXFormsMDGNode* aNode, nsXFormsMDGSet& aSet); + + /** + * Attaches inheritance to all children of a given node + * + * @param aSet Set of the nodes influenced by operation + * @param aSrc The node + * @param aState The state of the flag + * @param aStateFlag The flag + */ + nsresult AttachInheritance(nsXFormsMDGSet& aSet, nsIDOMNode* aSrc, PRBool aState, PRUint16 aStateFlag); + + /** A pointer to the owner, used to access Get- and SetNodeValue() */ + nsXFormsMDG* mOwner; + +public: + /** + * Constructor + * + * @param aOwner Pointer to the owner + */ + nsXFormsMDGEngine(nsXFormsMDG* aOwner); + + /** + * Destructor + */ + ~nsXFormsMDGEngine(); + + /** + * Initializes the hash tables. Needs to be called before class is used! + */ + nsresult Init(); + + /** + * Inserts a MIP into the graph + */ + nsresult Insert(nsIDOMNode* aContextNode, nsIDOMXPathExpression* aExpression, + const nsXFormsMDGSet* aDependencies, + PRBool aDynFunc, + PRInt32 aContextPos, PRInt32 aContextSize, + ModelItemPropName aType = eModel_calculate, + ModelItemPropName aDeptype = eModel_calculate); + + /** + * Rebuilds the MDG. + * + * Does this by doing a topological sort of all nodes in mNodeToMDG, and storing + * the result in mGraph. + * + * @note This has to be called before Calculate(). + */ + nsresult Rebuild(); + + /** + * Removes all node information from engine + */ + void Clear(); + + /** + * Perform a calculation on all dirty nodes + * + * @param aValueChanged The nodes which were changed by operation. + */ + nsresult Calculate(nsXFormsMDGSet& aValueChanged); + + /** + * Invalidate the information, ie. mark all nodes as dirty. + */ + nsresult Invalidate(); + + /** + * Get flag value for node and clear flag. + * + * @param aDomNode The node + * @param aFlag The flag + * @return The flag value + */ + PRBool TestAndClear(nsIDOMNode* aDomNode, PRUint16 aFlag); + + /** + * Get flag value for node and set flag. + * + * @param aDomNode The node + * @param aFlag The flag + * @return The flag value + */ + PRBool TestAndSet(nsIDOMNode* aDomNode, PRUint16 aFlag); + + /** + * Get flag value for node + * + * @param aDomNode The node + * @param aFlag The flag + * @return The flag value + */ + PRBool Test(nsIDOMNode* aDomNode, PRUint16 aFlag); + + /** + * Clear dispatch flags for all nodes + * + * @return Did operation succeed? + */ + PRBool ClearDispatchFlags(); + + /** + * Make a node as changed, ie. include in next Calculate + * + * @param aDomNode The node + */ + nsresult MarkNode(nsIDOMNode* aDomNode); +}; + +#endif diff --git a/extensions/xforms/nsXFormsMDGSet.cpp b/extensions/xforms/nsXFormsMDGSet.cpp new file mode 100644 index 00000000000..53456bcbde7 --- /dev/null +++ b/extensions/xforms/nsXFormsMDGSet.cpp @@ -0,0 +1,97 @@ +#include "nsXFormsMDGSet.h" + +MOZ_DECL_CTOR_COUNTER(nsXFormsMDGSet) + +nsXFormsMDGSet::nsXFormsMDGSet() +{ + MOZ_COUNT_CTOR(nsXFormsMDGSet); +} + +nsXFormsMDGSet::~nsXFormsMDGSet() { + Clear(); + + MOZ_COUNT_DTOR(nsXFormsMDGSet); +} + +int +nsXFormsMDGSet::sortFunc(const void* aElement1, const void* aElement2, void* aData) +{ + int res = 0; + if (aElement1 < aElement2) + res = -1; + if (aElement1 > aElement2) + res = 1; + + return res; +} + + +void +nsXFormsMDGSet::Clear() +{ + // Remove reference counters + nsISupports* node; + for (PRInt32 i = 0; i < array.Count(); ++i) { + node = NS_STATIC_CAST(nsISupports*, array[i]); + node->Release(); + } + // Clear array + array.Clear(); +} + +void +nsXFormsMDGSet::MakeUnique() +{ + array.Sort(sortFunc, nsnull); + + // TODO: Assuming that moving element x, will delete x, and move list "one to the left", + // ie. x = x+1, x+1=x+2, ... etc. + // Is that correct? + PRInt32 pos = 0; + nsIDOMNode* node; + while (pos + 1 < array.Count()) { + if (array[pos] == array[pos + 1]) { + node = NS_STATIC_CAST(nsIDOMNode*, array[pos + 1]); + node->Release(); + array.RemoveElementAt(pos + 1); + } else { + ++pos; + } + } +} + +PRInt32 +nsXFormsMDGSet::Count() const +{ + return array.Count(); +} + +PRBool +nsXFormsMDGSet::AddNode(nsIDOMNode* aDomNode) +{ + if (!aDomNode) { + return PR_FALSE; + } + + aDomNode->AddRef(); + return array.AppendElement(aDomNode); +} + +PRBool +nsXFormsMDGSet::AddSet(nsXFormsMDGSet& aSet) +{ + nsIDOMNode* node; + for (PRInt32 i = 0; i < aSet.array.Count(); ++i) { + node = NS_STATIC_CAST(nsIDOMNode*, aSet.array[i]); + if (!AddNode(node)) { + return PR_FALSE; + } + } + + return PR_TRUE; +} + +nsIDOMNode* +nsXFormsMDGSet::GetNode(const PRInt32 aIndex) const { + return NS_STATIC_CAST(nsIDOMNode*, array[aIndex]); +} diff --git a/extensions/xforms/nsXFormsMDGSet.h b/extensions/xforms/nsXFormsMDGSet.h new file mode 100644 index 00000000000..e4d3abf58e9 --- /dev/null +++ b/extensions/xforms/nsXFormsMDGSet.h @@ -0,0 +1,110 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#ifndef __NSXFORMSMDGSET_H__ +#define __NSXFORMSMDGSET_H__ + +#include "nsVoidArray.h" +#include "nsIDOMNode.h" + +/** + * A simple "set" implementation. For now it is just a wrapper for nsVoidArray, + * but nsVoidArray can be changed if a more suitable structure is found. + * + * It is not a set, in that no two same nodes can exist. You need to call + * MakeUnique() manually for that to be true. + * + * The class owns the nsIDOMNodes that it stores, and manages the reference + * counters automatically. + */ +class nsXFormsMDGSet { +private: + /** The data structure */ + nsVoidArray array; + + /** The sorting function used by MakeUnique() */ + static int sortFunc(const void* aElement1, const void* aElement2, void* aData); + +public: + /** Constructor */ + nsXFormsMDGSet(); + + /** Destructor */ + ~nsXFormsMDGSet(); + + /** Clears the struture (removes all stored data) */ + void Clear(); + + /** + * Delete any duplicates (ie. make the set a set...). + * + * As a side effect, it also sorts the data structure by the data pointer in + * ascending order. + */ + void MakeUnique(); + + /** Returns the number of members */ + PRInt32 Count() const; + + /** + * Adds a node to the set. + * + * @param aDomNode The node to add. + * @return Did the storage succeed? + */ + PRBool AddNode(nsIDOMNode* aDomNode); + + /** + * Adds all nodes from another set to this set + * + * @param aSet The set to add nodes from + * @return Did the operation succeed? + */ + PRBool AddSet(nsXFormsMDGSet& aSet); + + /** + * Get a specific node from the set + * + * @param aIndex The position of the node to get + * @return The node + */ + nsIDOMNode* GetNode(const PRInt32 aIndex) const; +}; + +#endif diff --git a/extensions/xforms/nsXFormsModelElement.cpp b/extensions/xforms/nsXFormsModelElement.cpp new file mode 100644 index 00000000000..0294483ed7e --- /dev/null +++ b/extensions/xforms/nsXFormsModelElement.cpp @@ -0,0 +1,944 @@ +/* -*- 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): + * Brian Ryner + * Allan Beaufour + * + * 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 ***** */ + +#include "nsXFormsModelElement.h" +#include "nsIXTFGenericElementWrapper.h" +#include "nsMemory.h" +#include "nsIDOMDocumentEvent.h" +#include "nsIDOMEvent.h" +#include "nsIDOMEventTarget.h" +#include "nsIDOMElement.h" +#include "nsIDOM3Node.h" +#include "nsIDOMNodeList.h" +#include "nsString.h" +#include "nsIDocument.h" +#include "nsXFormsAtoms.h" +#include "nsINameSpaceManager.h" +#include "nsIServiceManager.h" +#include "nsINodeInfo.h" +#include "nsIDOMDOMImplementation.h" +#include "nsIDOMXMLDocument.h" +#include "nsIDOMEventReceiver.h" +#include "nsIDOMXPathResult.h" +#include "nsIDOMXPathEvaluator.h" +#include "nsIDOMXPathNSResolver.h" +#include "nsIDOMXPathExpression.h" +#include "nsIDOM3EventTarget.h" +#include "nsIDOMEventGroup.h" +#include "nsIDOMNSUIEvent.h" +#include "nsIScriptGlobalObject.h" +#include "nsIContent.h" +#include "nsNetUtil.h" +#include "nsXFormsControl.h" +#include "nsXFormsTypes.h" +#include "nsXFormsXPathParser.h" +#include "nsXFormsXPathAnalyzer.h" +#include "nsXFormsInstanceElement.h" + +#include "nsISchemaLoader.h" +#include "nsAutoPtr.h" + +#ifdef DEBUG_beaufour +#include "nsIDOMSerializer.h" +#endif + +static const nsIID sScriptingIIDs[] = { + NS_IDOMELEMENT_IID, + NS_IDOMEVENTTARGET_IID, + NS_IDOM3NODE_IID, + NS_IXFORMSMODELELEMENT_IID +}; + +struct EventData +{ + const char *name; + PRBool canCancel; + PRBool canBubble; +}; + +static const EventData sModelEvents[] = { + { "xforms-model-construct", PR_FALSE, PR_TRUE }, + { "xforms-model-construct-done", PR_FALSE, PR_TRUE }, + { "xforms-ready", PR_FALSE, PR_TRUE }, + { "xforms-model-destruct", PR_FALSE, PR_TRUE }, + { "xforms-rebuild", PR_TRUE, PR_TRUE }, + { "xforms-refresh", PR_TRUE, PR_TRUE }, + { "xforms-revalidate", PR_TRUE, PR_TRUE }, + { "xforms-recalculate", PR_TRUE, PR_TRUE }, + { "xforms-reset", PR_TRUE, PR_TRUE }, + { "xforms-binding-exception", PR_FALSE, PR_TRUE }, + { "xforms-link-exception", PR_FALSE, PR_TRUE }, + { "xforms-link-error", PR_FALSE, PR_TRUE }, + { "xforms-compute-exception", PR_FALSE, PR_TRUE } +}; + +static nsIAtom* sModelPropsList[eModel__count]; + +struct nsXFormsModelElement::ModelItemProperties +{ + nsCOMPtr properties[eModel__count]; +}; + +nsXFormsModelElement::nsXFormsModelElement() + : mElement(nsnull), + mSchemaCount(0) +{ +} + +NS_IMPL_ADDREF(nsXFormsModelElement) +NS_IMPL_RELEASE(nsXFormsModelElement) + +NS_INTERFACE_MAP_BEGIN(nsXFormsModelElement) + NS_INTERFACE_MAP_ENTRY(nsIXTFElement) + NS_INTERFACE_MAP_ENTRY(nsIXTFGenericElement) + NS_INTERFACE_MAP_ENTRY(nsIXTFPrivate) + NS_INTERFACE_MAP_ENTRY(nsIXFormsModelElement) + NS_INTERFACE_MAP_ENTRY(nsISchemaLoadListener) + NS_INTERFACE_MAP_ENTRY(nsIWebServiceErrorHandler) + NS_INTERFACE_MAP_ENTRY(nsIDOMLoadListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXTFElement) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +nsXFormsModelElement::OnDestroyed() +{ + nsCOMPtr receiver = do_QueryInterface(mElement); + NS_ASSERTION(receiver, "xml elements must be event receivers"); + + nsCOMPtr systemGroup; + receiver->GetSystemEventGroup(getter_AddRefs(systemGroup)); + NS_ASSERTION(systemGroup, "system event group must exist"); + + nsCOMPtr targ = do_QueryInterface(mElement); + for (unsigned int i = 0; i < NS_ARRAY_LENGTH(sModelEvents); ++i) { + targ->RemoveGroupedEventListener(NS_ConvertUTF8toUTF16(sModelEvents[i].name), + this, PR_FALSE, systemGroup); + } + + RemoveModelFromDocument(); + + mElement = nsnull; + return NS_OK; +} + +void +nsXFormsModelElement::RemoveModelFromDocument() +{ + // Find out if we are handling the model-construct-done for this document. + nsCOMPtr domDoc; + mElement->GetOwnerDocument(getter_AddRefs(domDoc)); + + nsCOMPtr doc = do_QueryInterface(domDoc); + nsIScriptGlobalObject *window = nsnull; + if (doc) + window = doc->GetScriptGlobalObject(); + nsCOMPtr targ = do_QueryInterface(window); + if (targ) { + nsVoidArray *models = NS_STATIC_CAST(nsVoidArray*, + doc->GetProperty(nsXFormsAtoms::modelListProperty)); + + if (models) { + if (models->SafeElementAt(0) == this) { + nsXFormsModelElement *next = + NS_STATIC_CAST(nsXFormsModelElement*, models->SafeElementAt(1)); + if (next) { + targ->AddEventListener(NS_LITERAL_STRING("load"), next, PR_TRUE); + } + + targ->RemoveEventListener(NS_LITERAL_STRING("load"), this, PR_TRUE); + } + + models->RemoveElement(this); + } + } +} + +NS_IMETHODIMP +nsXFormsModelElement::GetElementType(PRUint32 *aType) +{ + *aType = ELEMENT_TYPE_GENERIC_ELEMENT; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::GetIsAttributeHandler(PRBool *aIsHandler) +{ + *aIsHandler = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::GetScriptingInterfaces(PRUint32 *aCount, nsIID ***aArray) +{ + return CloneScriptingInterfaces(sScriptingIIDs, + NS_ARRAY_LENGTH(sScriptingIIDs), + aCount, aArray); +} + +NS_IMETHODIMP +nsXFormsModelElement::WillChangeDocument(nsIDOMDocument* aNewDocument) +{ + RemoveModelFromDocument(); + return NS_OK; +} + +static void +DeleteVoidArray(void *aObject, + nsIAtom *aPropertyName, + void *aPropertyValue, + void *aData) +{ + delete NS_STATIC_CAST(nsVoidArray*, aPropertyValue); +} + +NS_IMETHODIMP +nsXFormsModelElement::DocumentChanged(nsIDOMDocument* aNewDocument) +{ + // Add this model to the document's model list. If this is the first + // model to be created, register an onload handler so that we can + // do model-construct-done notifications. + + if (!aNewDocument) + return NS_OK; + + nsCOMPtr doc = do_QueryInterface(aNewDocument); + + nsVoidArray *models = NS_STATIC_CAST(nsVoidArray*, + doc->GetProperty(nsXFormsAtoms::modelListProperty)); + + if (!models) { + models = new nsVoidArray(16); + doc->SetProperty(nsXFormsAtoms::modelListProperty, + models, DeleteVoidArray); + + nsIScriptGlobalObject *window = doc->GetScriptGlobalObject(); + + nsCOMPtr targ = do_QueryInterface(window); + targ->AddEventListener(NS_LITERAL_STRING("load"), this, PR_TRUE); + } + + models->AppendElement(this); + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::WillChangeParent(nsIDOMElement* aNewParent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::ParentChanged(nsIDOMElement* aNewParent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::WillInsertChild(nsIDOMNode* aChild, PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::ChildInserted(nsIDOMNode* aChild, PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::WillAppendChild(nsIDOMNode* aChild) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::ChildAppended(nsIDOMNode* aChild) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::WillRemoveChild(PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::ChildRemoved(PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::WillSetAttribute(nsIAtom *aName, + const nsAString &aNewValue) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::AttributeSet(nsIAtom *aName, const nsAString &aNewValue) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::WillRemoveAttribute(nsIAtom *aName) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::AttributeRemoved(nsIAtom *aName) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::DoneAddingChildren() +{ + // We wait until all children are added to dispatch xforms-model-construct, + // since the model may have an action handler for this event. + + nsresult rv = DispatchEvent(eEvent_ModelConstruct); + NS_ENSURE_SUCCESS(rv, rv); + + // xforms-model-construct is not cancellable, so always proceed. + // We continue here rather than doing this in HandleEvent since we know + // it only makes sense to perform this default action once. + + // (XForms 4.2.1) + // 1. load xml schemas + + nsAutoString schemaList; + mElement->GetAttribute(NS_LITERAL_STRING("schema"), schemaList); + if (!schemaList.IsEmpty()) { + nsCOMPtr loader = do_GetService(NS_SCHEMALOADER_CONTRACTID); + NS_ENSURE_TRUE(loader, NS_ERROR_FAILURE); + + // Parse the space-separated list. + PRUint32 offset = 0; + nsCOMPtr content = do_QueryInterface(mElement); + nsRefPtr baseURI = content->GetBaseURI(); + + while (1) { + ++mSchemaCount; + PRInt32 index = schemaList.FindChar(PRUnichar(' '), offset); + + nsCOMPtr newURI; + NS_NewURI(getter_AddRefs(newURI), + Substring(schemaList, offset, index - offset), + nsnull, baseURI); + + if (!newURI) { + DispatchEvent(eEvent_LinkException); // this is a fatal error + return NS_OK; + } + + nsCAutoString uriSpec; + newURI->GetSpec(uriSpec); + + rv = loader->LoadAsync(NS_ConvertUTF8toUTF16(uriSpec), this); + if (NS_FAILED(rv)) { + DispatchEvent(eEvent_LinkException); // this is a fatal error + return NS_OK; + } + if (index == -1) + break; + + offset = index + 1; + } + } + + // 2. construct an XPath data model from inline or external initial instance + // data. This is done by our child instance elements as they are inserted + // into the document, and all of the instances will be processed by this + // point. + + // XXX schema and external instance data loads should delay document onload + + if (IsComplete()) { + return FinishConstruction(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::OnCreated(nsIXTFGenericElementWrapper *aWrapper) +{ + aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_WILL_CHANGE_DOCUMENT | + nsIXTFElement::NOTIFY_DOCUMENT_CHANGED | + nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN); + + nsCOMPtr node; + aWrapper->GetElementNode(getter_AddRefs(node)); + + // It's ok to keep a weak pointer to mElement. mElement will have an + // owning reference to this object, so as long as we null out mElement in + // OnDestroyed, it will always be valid. + + mElement = node; + NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon"); + + nsresult rv = mMDG.Init(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +// nsIXTFPrivate +NS_IMETHODIMP +nsXFormsModelElement::GetInner(nsISupports **aInner) +{ + NS_ENSURE_ARG_POINTER(aInner); + NS_ADDREF(*aInner = NS_STATIC_CAST(nsIXFormsModelElement*, this)); + return NS_OK; +} + +// nsIXFormsModelElement + +NS_IMETHODIMP +nsXFormsModelElement::GetInstanceDocument(const nsAString& aInstanceID, + nsIDOMDocument **aDocument) +{ + NS_ENSURE_ARG_POINTER(aDocument); + + NS_IF_ADDREF(*aDocument = FindInstanceDocument(aInstanceID)); + return *aDocument ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsXFormsModelElement::Rebuild() +{ +#ifdef DEBUG + printf("nsXFormsModelElement::Rebuild()\n"); +#endif + + // TODO: Clear graph and re-attach elements + + // 1 . Clear graph + // mMDG.Clear(); + + // 2. Re-attach all elements + + // 3. Rebuild graph + return mMDG.Rebuild(); +} + +NS_IMETHODIMP +nsXFormsModelElement::Recalculate() +{ +#ifdef DEBUG + printf("nsXFormsModelElement::Recalculate()\n"); +#endif + + nsXFormsMDGSet changedNodes; + // TODO: Handle changed nodes. That is, dispatch events, etc. + + return mMDG.Recalculate(changedNodes); +} + +NS_IMETHODIMP +nsXFormsModelElement::Revalidate() +{ +#ifdef DEBUG + printf("nsXFormsModelElement::Revalidate()\n"); +#endif + +#ifdef DEBUG_beaufour + // Dump instance document to stdout + nsresult rv; + nsCOMPtr serializer(do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + // TODO: Should use SerializeToStream and write directly to stdout... + nsAutoString instanceString; + rv = serializer->SerializeToString(mInstanceDocument, instanceString); + NS_ENSURE_SUCCESS(rv, rv); + + printf("Instance data:\n%s\n", NS_ConvertUCS2toUTF8(instanceString).get()); +#endif + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::Refresh() +{ +#ifdef DEBUG + printf("nsXFormsModelElement::Refresh()\n"); +#endif + + return NS_OK; +} + +// nsISchemaLoadListener + +NS_IMETHODIMP +nsXFormsModelElement::OnLoad(nsISchema* aSchema) +{ + mSchemas.AppendObject(aSchema); + if (IsComplete()) { + nsresult rv = FinishConstruction(); + NS_ENSURE_SUCCESS(rv, rv); + + DispatchEvent(eEvent_Refresh); + } + + return NS_OK; +} + +// nsIWebServiceErrorHandler + +NS_IMETHODIMP +nsXFormsModelElement::OnError(nsresult aStatus, + const nsAString &aStatusMessage) +{ + DispatchEvent(eEvent_LinkException); + return NS_OK; +} + +// nsIDOMEventListener + +NS_IMETHODIMP +nsXFormsModelElement::HandleEvent(nsIDOMEvent* aEvent) +{ + nsCOMPtr evt = do_QueryInterface(aEvent); + NS_ASSERTION(evt, "event should implement nsIDOMNSUIEvent"); + + PRBool defaultPrevented; + evt->GetPreventDefault(&defaultPrevented); + if (defaultPrevented) + return NS_OK; + + nsAutoString type; + aEvent->GetType(type); + + if (type.EqualsLiteral("xforms-refresh")) { + // refresh all of our form controls + PRInt32 controlCount = mFormControls.Count(); + for (PRInt32 i = 0; i < controlCount; ++i) { + NS_STATIC_CAST(nsXFormsControl*, mFormControls[i])->Refresh(); + } + } else if (type.EqualsLiteral("xforms-revalidate")) { + Revalidate(); + } else if (type.EqualsLiteral("xforms-recalculate")) { + Recalculate(); + } else if (type.EqualsLiteral("xforms-rebuild")) { + Rebuild(); + } else if (type.EqualsLiteral("xforms-reset")) { +#ifdef DEBUG + printf("nsXFormsModelElement::Reset()\n"); +#endif + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::Load(nsIDOMEvent* aEvent) +{ + nsCOMPtr target; + aEvent->GetTarget(getter_AddRefs(target)); + + nsCOMPtr document = do_QueryInterface(target); + if (document) { + // The document has finished loading; that means that all of the models + // in it are initialized. Fire the model-construct-done event to each + // model. + + nsVoidArray *models = NS_STATIC_CAST(nsVoidArray*, + document->GetProperty(nsXFormsAtoms::modelListProperty)); + + NS_ASSERTION(models, "models list is empty!"); + for (PRInt32 i = 0; i < models->Count(); ++i) { + NS_STATIC_CAST(nsXFormsModelElement*, models->ElementAt(i)) + ->DispatchEvent(eEvent_ModelConstructDone); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::BeforeUnload(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::Unload(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::Abort(nsIDOMEvent* aEvent) +{ + DispatchEvent(eEvent_LinkException); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsModelElement::Error(nsIDOMEvent* aEvent) +{ + DispatchEvent(eEvent_LinkException); + return NS_OK; +} + +// internal methods + +nsIDOMDocument* +nsXFormsModelElement::FindInstanceDocument(const nsAString &aID) +{ + nsCOMPtr children; + mElement->GetChildNodes(getter_AddRefs(children)); + + if (!children) + return nsnull; + + PRUint32 childCount = 0; + children->GetLength(&childCount); + + nsCOMPtr node; + nsCOMPtr element; + nsAutoString id; + + for (PRUint32 i = 0; i < childCount; ++i) { + children->Item(i, getter_AddRefs(node)); + NS_ASSERTION(node, "incorrect NodeList length?"); + + element = do_QueryInterface(node); + if (!element) + continue; + + element->GetAttribute(NS_LITERAL_STRING("id"), id); + if (aID.IsEmpty() || aID.Equals(id)) { + // make sure this is an xforms instance element + nsAutoString namespaceURI, localName; + element->GetNamespaceURI(namespaceURI); + element->GetLocalName(localName); + + if (localName.EqualsLiteral("instance") && + namespaceURI.EqualsLiteral(NS_NAMESPACE_XFORMS)) { + + // This is the requested instance, so get its document. + nsCOMPtr xtfPriv = do_QueryInterface(element); + NS_ENSURE_TRUE(xtfPriv, nsnull); + + nsCOMPtr instanceInner; + xtfPriv->GetInner(getter_AddRefs(instanceInner)); + NS_ENSURE_TRUE(instanceInner, nsnull); + + nsISupports *isupp = NS_STATIC_CAST(nsISupports*, instanceInner.get()); + nsXFormsInstanceElement *instance = + NS_STATIC_CAST(nsXFormsInstanceElement*, + NS_STATIC_CAST(nsIXTFGenericElement*, isupp)); + + return instance->GetDocument(); + } + } + } + + return nsnull; +} + +nsresult +nsXFormsModelElement::FinishConstruction() +{ + // 3. if applicable, initialize P3P + + // 4. construct instance data from initial instance data. apply all + // elements in document order. + + // we get the instance data from our instance child nodes + + nsIDOMDocument *firstInstanceDoc = FindInstanceDocument(EmptyString()); + if (!firstInstanceDoc) + return NS_OK; + + nsCOMPtr firstInstanceRoot; + firstInstanceDoc->GetDocumentElement(getter_AddRefs(firstInstanceRoot)); + + nsCOMPtr xpath = do_QueryInterface(firstInstanceDoc); + + nsCOMPtr children; + mElement->GetChildNodes(getter_AddRefs(children)); + + PRUint32 childCount = 0; + if (children) + children->GetLength(&childCount); + + nsAutoString namespaceURI, localName; + + for (PRUint32 i = 0; i < childCount; ++i) { + nsCOMPtr child; + children->Item(i, getter_AddRefs(child)); + NS_ASSERTION(child, "there can't be null items in the NodeList!"); + + child->GetLocalName(localName); + if (localName.EqualsLiteral("bind")) { + child->GetNamespaceURI(namespaceURI); + if (namespaceURI.EqualsLiteral(NS_NAMESPACE_XFORMS)) { + if (!ProcessBind(xpath, firstInstanceRoot, + nsCOMPtr(do_QueryInterface(child)))) { + DispatchEvent(eEvent_BindingException); + return NS_OK; + } + } + } + } + + // 5. dispatch xforms-rebuild, xforms-recalculate, xforms-revalidate + + // First hook up our event listener so we invoke the default action for + // these events. We listen on the system event group so that we can check + // whether preventDefault() was called by any content listeners. + + nsCOMPtr receiver = do_QueryInterface(mElement); + NS_ASSERTION(receiver, "xml elements must be event receivers"); + + nsCOMPtr systemGroup; + receiver->GetSystemEventGroup(getter_AddRefs(systemGroup)); + NS_ASSERTION(systemGroup, "system event group must exist"); + + nsCOMPtr targ = do_QueryInterface(mElement); + for (unsigned int j = 0; j < NS_ARRAY_LENGTH(sModelEvents); ++j) { + targ->AddGroupedEventListener(NS_ConvertUTF8toUTF16(sModelEvents[j].name), + this, PR_FALSE, systemGroup); + } + + DispatchEvent(eEvent_Rebuild); + DispatchEvent(eEvent_Recalculate); + DispatchEvent(eEvent_Revalidate); + + // We're done initializing this model. + + return NS_OK; +} + +static void +ReleaseExpr(void *aElement, + nsIAtom *aPropertyName, + void *aPropertyValue, + void *aData) +{ + nsIDOMXPathExpression *expr = NS_STATIC_CAST(nsIDOMXPathExpression*, + aPropertyValue); + + NS_RELEASE(expr); +} + +PRBool +nsXFormsModelElement::ProcessBind(nsIDOMXPathEvaluator *aEvaluator, + nsIDOMNode *aContextNode, + nsIDOMElement *aBindElement) +{ + // Get the expression for the nodes that this applies to. + nsAutoString expr; + aBindElement->GetAttribute(NS_LITERAL_STRING("nodeset"), expr); + if (expr.IsEmpty()) + return PR_TRUE; + + nsCOMPtr resolver; + aEvaluator->CreateNSResolver(aBindElement, getter_AddRefs(resolver)); + + // Get the model item properties specified by this . + nsCOMPtr props[eModel__count]; + nsAutoString exprStrings[eModel__count]; + PRInt32 propCount = 0; + nsresult rv = NS_OK; + nsAutoString attrStr; + + for (int i = 0; i < eModel__count; ++i) { + sModelPropsList[i]->ToString(attrStr); + + aBindElement->GetAttribute(attrStr, exprStrings[i]); + if (!exprStrings[i].IsEmpty()) { + rv = aEvaluator->CreateExpression(exprStrings[i], resolver, + getter_AddRefs(props[i])); + if (NS_FAILED(rv)) + return PR_FALSE; + + ++propCount; + } + } + + if (propCount == 0) + return PR_TRUE; // successful, but nothing to do + + nsCOMPtr result; + rv = aEvaluator->Evaluate(expr, aContextNode, resolver, + nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, + nsnull, getter_AddRefs(result)); + if (NS_FAILED(rv)) + return PR_FALSE; + + PRUint32 snapLen; + rv = result->GetSnapshotLength(&snapLen); + NS_ENSURE_SUCCESS(rv, rv); + + nsXFormsMDGSet set; + nsCOMPtr node; + PRInt32 contextPosition = 1; + for (PRUint32 snapItem = 0; snapItem < snapLen; ++snapItem) { + rv = result->SnapshotItem(snapItem, getter_AddRefs(node)); + NS_ENSURE_SUCCESS(rv, rv); + + if (!node) { + NS_WARNING("nsXFormsModelElement::ProcessBind(): Empty node in result set."); + continue; + } + + nsXFormsXPathParser parser; + nsXFormsXPathAnalyzer analyzer(aEvaluator, resolver); + + // We must check whether the properties already exist on the node. + for (int j = 0; j < eModel__count; ++j) { + if (props[j]) { + nsCOMPtr content = do_QueryInterface(node, &rv); + + if (NS_FAILED(rv)) { + NS_WARNING("nsXFormsModelElement::ProcessBind(): Node is not IContent!\n"); + continue; + } + + nsIDOMXPathExpression *expr = props[j]; + NS_ADDREF(expr); + + // Set property + rv = content->SetProperty(sModelPropsList[j], expr, ReleaseExpr); + if (rv == NS_PROPTABLE_PROP_OVERWRITTEN) { + return PR_FALSE; + } + + // Get node dependencies + nsAutoPtr xNode(parser.Parse(exprStrings[j])); + set.Clear(); + rv = analyzer.Analyze(node, xNode, expr, &exprStrings[j], &set); + NS_ENSURE_SUCCESS(rv, rv); + + // Insert into MDG + rv = mMDG.AddMIP((ModelItemPropName) j, expr, &set, parser.UsesDynamicFunc(), + node, contextPosition++, snapLen); + NS_ENSURE_SUCCESS(rv, rv); + } + } + } + + + return PR_TRUE; +} + +nsresult +nsXFormsModelElement::DispatchEvent(nsXFormsModelEvent aEvent) +{ + nsCOMPtr domDoc; + mElement->GetOwnerDocument(getter_AddRefs(domDoc)); + + nsCOMPtr doc = do_QueryInterface(domDoc); + + nsCOMPtr event; + doc->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event)); + NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY); + + const EventData *data = &sModelEvents[aEvent]; + event->InitEvent(NS_ConvertUTF8toUTF16(data->name), + data->canBubble, data->canCancel); + + nsCOMPtr target = do_QueryInterface(mElement); + PRBool cancelled; + return target->DispatchEvent(event, &cancelled); +} + +already_AddRefed +nsXFormsModelElement::GetTypeForControl(nsXFormsControl *aControl) +{ + return nsnull; +} + +void +nsXFormsModelElement::AddFormControl(nsXFormsControl *aControl) +{ + if (mFormControls.IndexOf(aControl) == -1) + mFormControls.AppendElement(aControl); +} + +void +nsXFormsModelElement::RemoveFormControl(nsXFormsControl *aControl) +{ + mFormControls.RemoveElement(aControl); +} + +void +nsXFormsModelElement::RemovePendingInstance() +{ + --mPendingInstanceCount; + if (IsComplete()) { + nsresult rv = FinishConstruction(); + if (NS_SUCCEEDED(rv)) + DispatchEvent(eEvent_Refresh); + } +} + +/* static */ void +nsXFormsModelElement::Startup() +{ + sModelPropsList[eModel_type] = nsXFormsAtoms::type; + sModelPropsList[eModel_readonly] = nsXFormsAtoms::readonly; + sModelPropsList[eModel_required] = nsXFormsAtoms::required; + sModelPropsList[eModel_relevant] = nsXFormsAtoms::relevant; + sModelPropsList[eModel_calculate] = nsXFormsAtoms::calculate; + sModelPropsList[eModel_constraint] = nsXFormsAtoms::constraint; + sModelPropsList[eModel_p3ptype] = nsXFormsAtoms::p3ptype; +} + +nsresult +NS_NewXFormsModelElement(nsIXTFElement **aResult) +{ + *aResult = new nsXFormsModelElement(); + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} diff --git a/extensions/xforms/nsXFormsModelElement.h b/extensions/xforms/nsXFormsModelElement.h new file mode 100644 index 00000000000..e42772e0f4e --- /dev/null +++ b/extensions/xforms/nsXFormsModelElement.h @@ -0,0 +1,146 @@ +/* -*- 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): + * Brian Ryner + * + * 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 ***** */ + +#ifndef nsXFormsModelElement_h_ +#define nsXFormsModelElement_h_ + +#include "nsIXTFGenericElement.h" +#include "nsIXTFPrivate.h" +#include "nsIXFormsModelElement.h" +#include "nsISchema.h" +#include "nsCOMArray.h" +#include "nsVoidArray.h" +#include "nsCOMPtr.h" +#include "nsIDOMLoadListener.h" +#include "nsIDOMDocument.h" +#include "nsXFormsMDG.h" +#include "nsXFormsElement.h" + +#include "nsISchemaLoader.h" +#include "nsISchema.h" + +class nsIDOMElement; +class nsIDOMNode; +class nsIDOMXPathEvaluator; +class nsXFormsControl; +class nsXFormsInstanceElement; + +enum nsXFormsModelEvent { + eEvent_ModelConstruct, + eEvent_ModelConstructDone, + eEvent_Ready, + eEvent_ModelDestruct, + eEvent_Rebuild, + eEvent_Refresh, + eEvent_Revalidate, + eEvent_Recalculate, + eEvent_Reset, + eEvent_BindingException, + eEvent_LinkException, + eEvent_LinkError, + eEvent_ComputeExeception +}; + +class nsXFormsModelElement : public nsXFormsElement, + public nsIXTFGenericElement, + public nsIXTFPrivate, + public nsIXFormsModelElement, + public nsISchemaLoadListener, + public nsIDOMLoadListener +{ +public: + nsXFormsModelElement() NS_HIDDEN; + + NS_DECL_ISUPPORTS + NS_DECL_NSIXTFELEMENT + NS_DECL_NSIXTFGENERICELEMENT + NS_DECL_NSIXTFPRIVATE + NS_DECL_NSIXFORMSMODELELEMENT + NS_DECL_NSISCHEMALOADLISTENER + NS_DECL_NSIWEBSERVICEERRORHANDLER + NS_DECL_NSIDOMEVENTLISTENER + + // nsIDOMLoadListener + NS_IMETHOD Load(nsIDOMEvent* aEvent); + NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent); + NS_IMETHOD Unload(nsIDOMEvent* aEvent); + NS_IMETHOD Abort(nsIDOMEvent* aEvent); + NS_IMETHOD Error(nsIDOMEvent* aEvent); + + NS_HIDDEN_(nsresult) DispatchEvent(nsXFormsModelEvent aEvent); + + NS_HIDDEN_(already_AddRefed) + GetTypeForControl(nsXFormsControl *aControl); + + NS_HIDDEN_(void) AddFormControl(nsXFormsControl *aControl); + NS_HIDDEN_(void) RemoveFormControl(nsXFormsControl *aControl); + + NS_HIDDEN_(void) AddPendingInstance() { ++mPendingInstanceCount; } + NS_HIDDEN_(void) RemovePendingInstance(); + + NS_HIDDEN_(nsIDOMDocument*) FindInstanceDocument(const nsAString &aID); + + // Called after nsXFormsAtoms is registered + static NS_HIDDEN_(void) Startup(); + +private: + class ModelItemProperties; + + NS_HIDDEN_(nsresult) FinishConstruction(); + NS_HIDDEN_(PRBool) ProcessBind(nsIDOMXPathEvaluator *aEvaluator, + nsIDOMNode *aContextNode, + nsIDOMElement *aBindElement); + + NS_HIDDEN_(void) RemoveModelFromDocument(); + + PRBool IsComplete() const { return (mSchemas.Count() == mSchemaCount + && mPendingInstanceCount == 0); } + + nsIDOMElement *mElement; + nsCOMArray mSchemas; + nsVoidArray mFormControls; + + PRInt32 mSchemaCount; + PRInt32 mPendingInstanceCount; + + nsXFormsMDG mMDG; +}; + +NS_HIDDEN_(nsresult) NS_NewXFormsModelElement(nsIXTFElement **aResult); + +#endif diff --git a/extensions/xforms/nsXFormsModule.cpp b/extensions/xforms/nsXFormsModule.cpp new file mode 100644 index 00000000000..d297a184e6b --- /dev/null +++ b/extensions/xforms/nsXFormsModule.cpp @@ -0,0 +1,61 @@ +/* -*- 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): + * Brian Ryner + * + * 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 ***** */ + +#include "nsIGenericFactory.h" +#include "nsXFormsElementFactory.h" +#include "nsXFormsAtoms.h" +#include "nsXFormsModelElement.h" + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsXFormsElementFactory) + +static const nsModuleComponentInfo components[] = { + { "XForms element factory", + NS_XFORMSELEMENTFACTORY_CID, + NS_XTF_ELEMENT_FACTORY_CONTRACTID_PREFIX NS_NAMESPACE_XFORMS, + nsXFormsElementFactoryConstructor } +}; + +PR_STATIC_CALLBACK(nsresult) +XFormsModuleCtor(nsIModule* aSelf) +{ + nsXFormsAtoms::InitAtoms(); + nsXFormsModelElement::Startup(); + return NS_OK; +} + +NS_IMPL_NSGETMODULE_WITH_CTOR(xforms, components, XFormsModuleCtor) diff --git a/extensions/xforms/nsXFormsStubElement.cpp b/extensions/xforms/nsXFormsStubElement.cpp new file mode 100644 index 00000000000..5fa6695d2e3 --- /dev/null +++ b/extensions/xforms/nsXFormsStubElement.cpp @@ -0,0 +1,187 @@ +/* -*- 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): + * Brian Ryner + * + * 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 ***** */ + +#include "nsXFormsStubElement.h" +#include "nsIDOMElement.h" +#include "nsIDOMEventTarget.h" +#include "nsIDOM3Node.h" +#include "nsMemory.h" + +static const nsIID sScriptingIIDs[] = { + NS_IDOMELEMENT_IID, + NS_IDOMEVENTTARGET_IID, + NS_IDOM3NODE_IID +}; + +NS_IMPL_ISUPPORTS2(nsXFormsStubElement, nsIXTFElement, nsIXTFGenericElement) + +NS_IMETHODIMP +nsXFormsStubElement::OnDestroyed() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::GetElementType(PRUint32 *aElementType) +{ + *aElementType = nsIXTFElement::ELEMENT_TYPE_GENERIC_ELEMENT; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::GetIsAttributeHandler(PRBool *aIsAttributeHandler) +{ + *aIsAttributeHandler = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::GetScriptingInterfaces(PRUint32 *aCount, nsIID ***aArray) +{ + return CloneScriptingInterfaces(sScriptingIIDs, + NS_ARRAY_LENGTH(sScriptingIIDs), + aCount, aArray); +} + +NS_IMETHODIMP +nsXFormsStubElement::WillChangeDocument(nsIDOMDocument *aNewDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::DocumentChanged(nsIDOMDocument *aNewDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::WillChangeParent(nsIDOMElement *aNewParent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::ParentChanged(nsIDOMElement *aNewParent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::WillInsertChild(nsIDOMNode *aChild, PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::WillAppendChild(nsIDOMNode *aChild) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::ChildAppended(nsIDOMNode *aChild) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::WillRemoveChild(PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::ChildRemoved(PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::WillSetAttribute(nsIAtom *aName, + const nsAString &aNewValue) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::AttributeSet(nsIAtom *aName, const nsAString &aNewValue) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::WillRemoveAttribute(nsIAtom *aName) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::AttributeRemoved(nsIAtom *aName) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::DoneAddingChildren() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsStubElement::OnCreated(nsIXTFGenericElementWrapper *aWrapper) +{ + return NS_OK; +} + +nsresult +NS_NewXFormsStubElement(nsIXTFElement **aResult) +{ + *aResult = new nsXFormsStubElement(); + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} diff --git a/extensions/xforms/nsXFormsStubElement.h b/extensions/xforms/nsXFormsStubElement.h new file mode 100644 index 00000000000..c5bf5fbe514 --- /dev/null +++ b/extensions/xforms/nsXFormsStubElement.h @@ -0,0 +1,56 @@ +/* -*- 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): + * Brian Ryner + * + * 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 ***** */ + +#ifndef nsXFormsStubElement_h_ +#define nsXFormsStubElement_h_ + +#include "nsIXTFGenericElement.h" +#include "nsXFormsElement.h" + +class nsXFormsStubElement : public nsXFormsElement, + public nsIXTFGenericElement +{ + NS_DECL_ISUPPORTS + NS_DECL_NSIXTFELEMENT + NS_DECL_NSIXTFGENERICELEMENT +}; + +NS_HIDDEN_(nsresult) +NS_NewXFormsStubElement(nsIXTFElement **aResult); + +#endif diff --git a/extensions/xforms/nsXFormsSubmissionElement.cpp b/extensions/xforms/nsXFormsSubmissionElement.cpp new file mode 100644 index 00000000000..e52baaa2595 --- /dev/null +++ b/extensions/xforms/nsXFormsSubmissionElement.cpp @@ -0,0 +1,1371 @@ +/* -*- 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 + * + * 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 +#define LOG(args) printf args +#else +#define LOG(args) +#endif + +#include + +#include "nsXFormsSubmissionElement.h" +#include "nsXFormsAtoms.h" +#include "nsIXFormsModelElement.h" +#include "nsIXTFGenericElementWrapper.h" +#include "nsIDOMDocument.h" +#include "nsIDOMElement.h" +#include "nsIDOMText.h" +#include "nsIDOMEventListener.h" +#include "nsIDOMEventGroup.h" +#include "nsIDOMEventReceiver.h" +#include "nsIDOMEvent.h" +#include "nsIDOMDocumentEvent.h" +#include "nsIDOM3EventTarget.h" +#include "nsIDOM3Node.h" +#include "nsIDOMNSUIEvent.h" +#include "nsIDOMXPathResult.h" +#include "nsIDOMXPathEvaluator.h" +#include "nsIDOMXPathNSResolver.h" +#include "nsIDOMXPathExpression.h" +#include "nsIDOMSerializer.h" +#include "nsIDOMDOMImplementation.h" +#include "nsIDOMProcessingInstruction.h" +#include "nsComponentManagerUtils.h" +#include "nsIWebNavigation.h" +#include "nsIStringStream.h" +#include "nsIInputStream.h" +#include "nsIStorageStream.h" +#include "nsIMultiplexInputStream.h" +#include "nsIMIMEInputStream.h" +#include "nsINameSpaceManager.h" +#include "nsIDocument.h" +#include "nsIFileURL.h" +#include "nsIMIMEService.h" +#include "nsLinebreakConverter.h" +#include "nsEscape.h" +#include "nsString.h" +#include "nsMemory.h" +#include "nsCOMPtr.h" +#include "nsNetUtil.h" + +// namespace literals +#define NAMESPACE_XML_SCHEMA \ + NS_LITERAL_STRING("http://www.w3.org/2001/XMLSchema") +#define NAMESPACE_XML_SCHEMA_INSTANCE \ + NS_LITERAL_STRING("http://www.w3.org/2001/XMLSchema-instance") + +static const nsIID sScriptingIIDs[] = { + NS_IDOMELEMENT_IID, + NS_IDOMEVENTTARGET_IID, + NS_IDOM3NODE_IID +}; + +// submission methods +#define METHOD_GET 0x01 +#define METHOD_POST 0x02 +#define METHOD_PUT 0x04 + +// submission encodings +#define ENCODING_XML 0x10 // application/xml +#define ENCODING_URL 0x20 // application/x-www-form-urlencoded +#define ENCODING_MULTIPART_RELATED 0x40 // multipart/related +#define ENCODING_MULTIPART_FORM_DATA 0x80 // multipart/form-data + +struct SubmissionFormat +{ + const char *method; + PRUint32 format; +}; + +static const SubmissionFormat sSubmissionFormats[] = { + { "post", ENCODING_XML | METHOD_POST }, + { "get", ENCODING_URL | METHOD_GET }, + { "put", ENCODING_XML | METHOD_PUT }, + { "multipart-post", ENCODING_MULTIPART_RELATED | METHOD_POST }, + { "form-data-post", ENCODING_MULTIPART_FORM_DATA | METHOD_POST }, + { "urlencoded-post", ENCODING_URL | METHOD_POST } +}; + +static PRUint32 +GetSubmissionFormat(nsIDOMElement *aElement) +{ + nsAutoString method; + aElement->GetAttribute(NS_LITERAL_STRING("method"), method); + + NS_ConvertUTF16toUTF8 utf8method(method); + for (PRUint32 i=0; i mime = do_GetService("@mozilla.org/mime;1"); + if (mime) + mime->GetTypeFromFile(file, result); + if (result.IsEmpty()) + result.Assign("application/octet-stream"); +} + +// structure used to store information needed to generate attachments +// for multipart/related submission. +struct SubmissionAttachment +{ + nsString uri; + nsCString cid; +}; + +// an array of SubmissionAttachment objects +class SubmissionAttachmentArray : nsVoidArray +{ +public: + SubmissionAttachmentArray() {} + ~SubmissionAttachmentArray() + { + for (PRUint32 i=0; iuri = uri; + a->cid = cid; + AppendElement(a); + return NS_OK; + } + PRUint32 Count() const + { + return (PRUint32) nsVoidArray::Count(); + } + SubmissionAttachment *Item(PRUint32 index) + { + return (SubmissionAttachment *) ElementAt(index); + } +}; + +// nsISupports + +NS_IMPL_ISUPPORTS3(nsXFormsSubmissionElement, + nsIXTFElement, + nsIXTFGenericElement, + nsIDOMEventListener) + +// nsIXTFElement + +NS_IMETHODIMP +nsXFormsSubmissionElement::OnDestroyed() +{ + mElement = 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::WillChangeDocument(nsIDOMDocument *aNewDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSubmissionElement::DocumentChanged(nsIDOMDocument *aNewDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSubmissionElement::WillChangeParent(nsIDOMElement *aNewParent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSubmissionElement::ParentChanged(nsIDOMElement *aNewParent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSubmissionElement::WillInsertChild(nsIDOMNode *aChild, PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSubmissionElement::ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSubmissionElement::WillAppendChild(nsIDOMNode *aChild) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSubmissionElement::ChildAppended(nsIDOMNode *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::WillRemoveAttribute(nsIAtom *aName) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSubmissionElement::AttributeRemoved(nsIAtom *aName) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSubmissionElement::DoneAddingChildren() +{ + return NS_OK; +} + +// nsIXTFGenericElement + +NS_IMETHODIMP +nsXFormsSubmissionElement::OnCreated(nsIXTFGenericElementWrapper *aWrapper) +{ + nsCOMPtr node; + aWrapper->GetElementNode(getter_AddRefs(node)); + + // It's ok to keep a weak pointer to mElement. mElement will have an + // owning reference to this object, so as long as we null out mElement in + // OnDestroyed, it will always be valid. + + mElement = node; + NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon"); + + // We listen on the system event group so that we can check + // whether preventDefault() was called by any content listeners. + + nsCOMPtr receiver = do_QueryInterface(mElement); + NS_ASSERTION(receiver, "xml elements must be event receivers"); + + nsCOMPtr systemGroup; + receiver->GetSystemEventGroup(getter_AddRefs(systemGroup)); + NS_ASSERTION(systemGroup, "system event group must exist"); + + nsCOMPtr target = do_QueryInterface(mElement); + + target->AddGroupedEventListener(NS_LITERAL_STRING("xforms-submit"), + this, PR_FALSE, systemGroup); + return NS_OK; +} + +// nsIDOMEventListener + +NS_IMETHODIMP +nsXFormsSubmissionElement::HandleEvent(nsIDOMEvent *aEvent) +{ + nsCOMPtr evt = do_QueryInterface(aEvent); + + PRBool defaultPrevented; + evt->GetPreventDefault(&defaultPrevented); + if (defaultPrevented) + return NS_OK; + + nsAutoString type; + aEvent->GetType(type); + if (type.EqualsLiteral("xforms-submit")) + Submit(); + return NS_OK; +} + +// private methods + +void +nsXFormsSubmissionElement::Submit() +{ + LOG(("+++ nsXFormsSubmissionElement::Submit\n")); + + // 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 data; + if (NS_FAILED(GetSelectedInstanceData(getter_AddRefs(data))) || !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 + + PRUint32 format = GetSubmissionFormat(mElement); + if (format == 0) + { + NS_WARNING("unknown submission format"); + return; + } + + nsCOMPtr stream; + nsCAutoString uri, contentType; + if (NS_FAILED(SerializeData(data, format, uri, getter_AddRefs(stream), + contentType))) + { + NS_WARNING("failed to serialize data"); + return; + } + + + // 5. dispatch network request + + if (NS_FAILED(SendData(format, uri, stream, contentType))) + { + NS_WARNING("failed to send data"); + return; + } +} + +nsresult +nsXFormsSubmissionElement::SubmitEnd(PRBool succeeded) +{ + nsCOMPtr domDoc; + mElement->GetOwnerDocument(getter_AddRefs(domDoc)); + nsCOMPtr doc = do_QueryInterface(domDoc); + + nsCOMPtr 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 target; + if (succeeded) + target = do_QueryInterface(mElement); + //else + // target = GetModel(); + + PRBool cancelled; + return target->DispatchEvent(event, &cancelled); +} + +nsresult +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 element. + + nsCOMPtr instance; + GetDefaultInstanceData(getter_AddRefs(instance)); + NS_ENSURE_TRUE(instance, NS_ERROR_UNEXPECTED); + + nsAutoString value; + mElement->GetAttribute(NS_LITERAL_STRING("bind"), value); + if (value.IsEmpty()) + { + // inspect 'ref' attribute + mElement->GetAttribute(NS_LITERAL_STRING("ref"), value); + } + else + { + // ok, value contains the 'ID' of a element. + nsCOMPtr doc; + mElement->GetOwnerDocument(getter_AddRefs(doc)); + + nsCOMPtr bindElement; + doc->GetElementById(value, getter_AddRefs(bindElement)); + NS_ENSURE_TRUE(bindElement, NS_ERROR_UNEXPECTED); + + bindElement->GetAttribute(NS_LITERAL_STRING("nodeset"), value); + } + + if (value.IsEmpty()) + { + // select first element + // instance->GetFirstChild(result); + NS_ADDREF(*result = instance); + return NS_OK; + } + + // evaluate 'value' as an xpath expression + + nsCOMPtr domDoc; + mElement->GetOwnerDocument(getter_AddRefs(domDoc)); + nsCOMPtr xpath = do_QueryInterface(domDoc); + NS_ENSURE_TRUE(xpath, NS_ERROR_UNEXPECTED); + + nsCOMPtr resolver; + xpath->CreateNSResolver(instance, getter_AddRefs(resolver)); + NS_ENSURE_TRUE(resolver, NS_ERROR_UNEXPECTED); + + nsCOMPtr xpathResult; + xpath->Evaluate(value, instance, resolver, + nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE, nsnull, + getter_AddRefs(xpathResult)); + nsCOMPtr nodeset = do_QueryInterface(xpathResult); + NS_ENSURE_TRUE(nodeset, NS_ERROR_UNEXPECTED); + + return nodeset->GetSingleNodeValue(result); +} + +PRBool +nsXFormsSubmissionElement::GetBooleanAttr(const nsAString &name, + PRBool defaultVal) +{ + nsAutoString value; + mElement->GetAttribute(name, value); + + // use defaultVal when value does not match a legal literal + + if (!value.IsEmpty()) + { + if (value.EqualsLiteral("true") || value.EqualsLiteral("1")) + return PR_TRUE; + if (value.EqualsLiteral("false") || value.EqualsLiteral("0")) + return PR_FALSE; + } + + return defaultVal; +} + +void +nsXFormsSubmissionElement::GetDefaultInstanceData(nsIDOMNode **result) +{ + *result = nsnull; + + // default element is the first child node of + // our parent, which should be a element. + + nsCOMPtr parent; + mElement->GetParentNode(getter_AddRefs(parent)); + if (!parent) + { + NS_WARNING("no parent node!"); + return; + } + + nsCOMPtr model = do_QueryInterface(parent); + if (!model) + { + NS_WARNING("parent node is not a model"); + return; + } + + nsCOMPtr instanceDoc; + model->GetInstanceDocument(EmptyString(), getter_AddRefs(instanceDoc)); + + nsCOMPtr instanceDocElem; + instanceDoc->GetDocumentElement(getter_AddRefs(instanceDocElem)); + + NS_ADDREF(*result = instanceDocElem); +} + +nsresult +nsXFormsSubmissionElement::SerializeData(nsIDOMNode *data, + PRUint32 format, + nsCString &uri, + nsIInputStream **stream, + nsCString &contentType) +{ + // initialize uri to the given action + nsAutoString action; + mElement->GetAttribute(NS_LITERAL_STRING("action"), action); + CopyUTF16toUTF8(action, uri); + + // '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 (format & ENCODING_XML) + return SerializeDataXML(data, format, stream, contentType, nsnull); + + if (format & ENCODING_URL) + return SerializeDataURLEncoded(data, format, uri, stream, contentType); + + if (format & ENCODING_MULTIPART_RELATED) + return SerializeDataMultipartRelated(data, format, stream, contentType); + + if (format & ENCODING_MULTIPART_FORM_DATA) + return SerializeDataMultipartFormData(data, format, stream, contentType); + + NS_WARNING("unsupported submission encoding"); + return NS_ERROR_UNEXPECTED; +} + +nsresult +nsXFormsSubmissionElement::SerializeDataXML(nsIDOMNode *data, + PRUint32 format, + nsIInputStream **stream, + nsCString &contentType, + SubmissionAttachmentArray *attachments) +{ + nsAutoString mediaType; + mElement->GetAttribute(NS_LITERAL_STRING("mediaType"), mediaType); + if (mediaType.IsEmpty()) + contentType.AssignLiteral("application/xml"); + else + CopyUTF16toUTF8(mediaType, contentType); + + nsAutoString encoding; + mElement->GetAttribute(NS_LITERAL_STRING("encoding"), encoding); + if (encoding.IsEmpty()) + encoding.AssignLiteral("UTF-8"); + + nsCOMPtr storage; + NS_NewStorageStream(4096, PR_UINT32_MAX, getter_AddRefs(storage)); + NS_ENSURE_TRUE(storage, NS_ERROR_OUT_OF_MEMORY); + + nsCOMPtr sink; + storage->GetOutputStream(0, getter_AddRefs(sink)); + NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY); + + nsCOMPtr serializer = + do_GetService("@mozilla.org/xmlextras/xmlserializer;1"); + NS_ENSURE_TRUE(serializer, NS_ERROR_UNEXPECTED); + + nsCOMPtr doc; + data->GetOwnerDocument(getter_AddRefs(doc)); + NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); + + // clone and possibly modify the document for submission + nsCOMPtr newDoc; + CreateSubmissionDoc(doc, encoding, attachments, getter_AddRefs(newDoc)); + NS_ENSURE_TRUE(newDoc, NS_ERROR_UNEXPECTED); + + nsresult rv = + serializer->SerializeToStream(newDoc, sink, + NS_LossyConvertUTF16toASCII(encoding)); + NS_ENSURE_SUCCESS(rv, rv); + + // close the output stream, so that the input stream will not return + // NS_BASE_STREAM_WOULD_BLOCK when it reaches end-of-stream. + sink->Close(); + + return storage->NewInputStream(0, stream); +} + +nsresult +nsXFormsSubmissionElement::CreateSubmissionDoc(nsIDOMDocument *source, + const nsString &encoding, + SubmissionAttachmentArray *attachments, + nsIDOMDocument **result) +{ + PRBool indent = GetBooleanAttr(NS_LITERAL_STRING("indent"), PR_FALSE); + PRBool omit_xml_declaration + = GetBooleanAttr(NS_LITERAL_STRING("omit-xml-declaration"), PR_FALSE); + + nsAutoString cdataElements; + mElement->GetAttribute(NS_LITERAL_STRING("cdata-section-elements"), + cdataElements); + + // XXX cdataElements contains space delimited QNames. these may have + // namespace prefixes relative to our document. we need to translate + // them to the corresponding namespace prefix in the source document. + + nsCOMPtr impl; + source->GetImplementation(getter_AddRefs(impl)); + NS_ENSURE_TRUE(impl, NS_ERROR_UNEXPECTED); + + nsCOMPtr doc; + impl->CreateDocument(EmptyString(), EmptyString(), nsnull, + getter_AddRefs(doc)); + NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); + + if (!omit_xml_declaration) + { + nsAutoString buf + = NS_LITERAL_STRING("version=\"1.0\" encoding=\"") + encoding + + NS_LITERAL_STRING("\""); + + nsCOMPtr pi; + doc->CreateProcessingInstruction(NS_LITERAL_STRING("xml"), buf, + getter_AddRefs(pi)); + + nsCOMPtr newChild; + doc->AppendChild(pi, getter_AddRefs(newChild)); + } + + // recursively walk the source document, copying nodes as appropriate + + CopyChildren(source, doc, doc, attachments, cdataElements, indent, 0); + + NS_ADDREF(*result = doc); + return NS_OK; +} + +nsresult +nsXFormsSubmissionElement::CopyChildren(nsIDOMNode *source, nsIDOMNode *dest, + nsIDOMDocument *destDoc, + SubmissionAttachmentArray *attachments, + const nsString &cdataElements, + PRBool indent, PRUint32 depth) +{ + nsCOMPtr sourceChild, node, destChild; + source->GetFirstChild(getter_AddRefs(sourceChild)); + while (sourceChild) + { + // if not indenting, then strip all unnecessary whitespace + + // XXX importing the entire node is not quite right here... we also have + // to iterate over the attributes since the attributes could somehow + // (remains to be determined) reference external entities. + + destDoc->ImportNode(sourceChild, PR_FALSE, getter_AddRefs(destChild)); + NS_ENSURE_TRUE(destChild, NS_ERROR_UNEXPECTED); + + PRUint16 type; + destChild->GetNodeType(&type); + if (type == nsIDOMNode::PROCESSING_INSTRUCTION_NODE) + { + nsCOMPtr pi = do_QueryInterface(destChild); + NS_ENSURE_TRUE(pi, NS_ERROR_UNEXPECTED); + + // ignore "" since we would have already inserted this. + + nsAutoString target; + pi->GetTarget(target); + if (!target.EqualsLiteral("xml")) + dest->AppendChild(destChild, getter_AddRefs(node)); + } + else if (type == nsIDOMNode::TEXT_NODE) + { + // XXX honor cdata-section-elements (see xslt spec section 16.1) + dest->AppendChild(destChild, getter_AddRefs(node)); + } + else + { + // if |destChild| is an element node of type 'xsd:anyURI', and if we have + // an attachments array, then we need to perform multipart/related + // processing (i.e., generate a ContentID for the child of this element, + // and append a new attachment to the attachments array). + + PRUint32 encType; + if (attachments && + NS_SUCCEEDED(GetElementEncodingType(destChild, &encType)) && + encType == ELEMENT_ENCTYPE_URI) + { + // ok, looks like we have a URI to process + sourceChild->GetFirstChild(getter_AddRefs(node)); + NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED); + + node->GetNodeType(&type); + NS_ENSURE_TRUE(type == nsIDOMNode::TEXT_NODE, NS_ERROR_UNEXPECTED); + + nsString value; + node->GetNodeValue(value); + + nsCString cid; + MakeMultipartContentID(cid); + + nsAutoString cidURI; + cidURI.AssignLiteral("cid:"); + AppendASCIItoUTF16(cid, cidURI); + + nsCOMPtr text; + destDoc->CreateTextNode(cidURI, getter_AddRefs(text)); + NS_ENSURE_TRUE(text, NS_ERROR_UNEXPECTED); + + destChild->AppendChild(text, getter_AddRefs(node)); + dest->AppendChild(destChild, getter_AddRefs(node)); + + attachments->Append(value, cid); + } + else + { + dest->AppendChild(destChild, getter_AddRefs(node)); + + // recurse + nsresult rv = CopyChildren(sourceChild, destChild, destDoc, attachments, + cdataElements, indent, depth + 1); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + sourceChild->GetNextSibling(getter_AddRefs(node)); + sourceChild.swap(node); + } + return NS_OK; +} + +nsresult +nsXFormsSubmissionElement::SerializeDataURLEncoded(nsIDOMNode *data, + PRUint32 format, + nsCString &uri, + nsIInputStream **stream, + nsCString &contentType) +{ + nsCAutoString separator; + { + nsAutoString temp; + mElement->GetAttribute(NS_LITERAL_STRING("separator"), temp); + if (temp.IsEmpty()) + { + separator.AssignLiteral(";"); + } + else + { + // XXX validate input? take only the first character? + CopyUTF16toUTF8(temp, separator); + } + } + + if (format & METHOD_GET) + { + if (uri.FindChar('?') == kNotFound) + uri.Append('?'); + else + uri.Append(separator); + AppendURLEncodedData(data, separator, uri); + + *stream = nsnull; + contentType.Truncate(); + } + else if (format & METHOD_POST) + { + nsCAutoString buf; + AppendURLEncodedData(data, separator, buf); + + // make new stream + NS_NewCStringInputStream(stream, buf); + NS_ENSURE_TRUE(*stream, NS_ERROR_UNEXPECTED); + + contentType.AssignLiteral("application/x-www-form-urlencoded"); + } + else + { + NS_WARNING("unexpected submission format"); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +void +nsXFormsSubmissionElement::AppendURLEncodedData(nsIDOMNode *data, + const nsCString &separator, + nsCString &buf) +{ + // 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). + +#ifdef DEBUG_darinf + nsAutoString nodeName; + data->GetNodeName(nodeName); + LOG(("+++ AppendURLEncodedData: inspecting <%s>\n", + NS_ConvertUTF16toUTF8(nodeName).get())); +#endif + + nsCOMPtr child; + data->GetFirstChild(getter_AddRefs(child)); + if (!child) + return; + + PRUint16 childType; + child->GetNodeType(&childType); + + nsCOMPtr sibling; + child->GetNextSibling(getter_AddRefs(sibling)); + + if (!sibling && childType == nsIDOMNode::TEXT_NODE) + { + nsAutoString localName; + data->GetLocalName(localName); + + nsAutoString value; + child->GetNodeValue(value); + + LOG((" appending data for <%s>\n", NS_ConvertUTF16toUTF8(localName).get())); + + nsCString encLocalName, encValue; + URLEncode(localName, encLocalName); + URLEncode(value, encValue); + + buf.Append(encLocalName + NS_LITERAL_CSTRING("=") + encValue + separator); + } + else + { + // call AppendURLEncodedData on each child node + do + { + AppendURLEncodedData(child, separator, buf); + child->GetNextSibling(getter_AddRefs(sibling)); + child.swap(sibling); + } + while (child); + } +} + +nsresult +nsXFormsSubmissionElement::SerializeDataMultipartRelated(nsIDOMNode *data, + PRUint32 format, + nsIInputStream **stream, + nsCString &contentType) +{ + NS_ASSERTION(format & METHOD_POST, "unexpected submission method"); + + nsCAutoString boundary; + MakeMultipartBoundary(boundary); + + nsCOMPtr multiStream = + do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); + NS_ENSURE_TRUE(multiStream, NS_ERROR_UNEXPECTED); + + nsCAutoString type, start; + + MakeMultipartContentID(start); + + // XXX we need to extend SerializeDataXML so that it has a mode in which it + // generates ContentIDs for elements of type xsd:anyURI, and returns a + // list of ContentID <-> URI mappings. + + nsCOMPtr xml; + SubmissionAttachmentArray attachments; + SerializeDataXML(data, ENCODING_XML | METHOD_POST, getter_AddRefs(xml), type, + &attachments); + + // XXX we should output a 'charset=' with the 'Content-Type' header + + nsCString postDataChunk; + postDataChunk += NS_LITERAL_CSTRING("--") + boundary + + NS_LITERAL_CSTRING("\r\nContent-Type: ") + type + + NS_LITERAL_CSTRING("\r\nContent-ID: <") + start + + NS_LITERAL_CSTRING(">\r\n"); + nsresult rv = AppendPostDataChunk(postDataChunk, multiStream); + NS_ENSURE_SUCCESS(rv, rv); + + multiStream->AppendStream(xml); + + for (PRUint32 i=0; i file; + nsCOMPtr fileStream; + rv = CreateFileStream(a->uri, + getter_AddRefs(file), + getter_AddRefs(fileStream)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCAutoString type; + GetMimeTypeFromFile(file, type); + + postDataChunk += NS_LITERAL_CSTRING("\r\n--") + boundary + + NS_LITERAL_CSTRING("\r\nContent-Type: ") + type + + NS_LITERAL_CSTRING("\r\nContent-Transfer-Encoding: binary") + + NS_LITERAL_CSTRING("\r\nContent-ID: <") + a->cid + + NS_LITERAL_CSTRING(">\r\n"); + rv = AppendPostDataChunk(postDataChunk, multiStream); + NS_ENSURE_SUCCESS(rv, rv); + + multiStream->AppendStream(fileStream); + } + + // final boundary + postDataChunk += NS_LITERAL_CSTRING("\r\n--") + boundary + + NS_LITERAL_CSTRING("--\r\n"); + rv = AppendPostDataChunk(postDataChunk, multiStream); + NS_ENSURE_SUCCESS(rv, rv); + + contentType = + NS_LITERAL_CSTRING("multipart/related; boundary=") + boundary + + NS_LITERAL_CSTRING("; type=") + type + + NS_LITERAL_CSTRING("; start=\"<") + start + + NS_LITERAL_CSTRING(">\""); + + NS_ADDREF(*stream = multiStream); + return NS_OK; +} + +nsresult +nsXFormsSubmissionElement::SerializeDataMultipartFormData(nsIDOMNode *data, + PRUint32 format, + nsIInputStream **stream, + nsCString &contentType) +{ + NS_ASSERTION(format & METHOD_POST, "unexpected submission method"); + + // This format follows the rules for multipart/form-data MIME data streams in + // [RFC 2388], with specific requirements of this serialization listed below: + // o Each element node is visited in document order. + // o Each element that has exactly one text node child is selected for + // inclusion. + // o Element nodes selected for inclusion are as encoded as + // Content-Disposition: form-data MIME parts as defined in [RFC 2387], with + // the name parameter being the element local name. + // o Element nodes of any datatype populated by upload are serialized as the + // specified content and additionally have a Content-Disposition filename + // parameter, if available. + // o The Content-Type must be text/plain except for xsd:base64Binary, + // xsd:hexBinary, and derived types, in which case the header represents the + // media type of the attachment if known, otherwise + // application/octet-stream. If a character set is applicable, the + // Content-Type may have a charset parameter. + + nsCAutoString boundary; + MakeMultipartBoundary(boundary); + + nsCOMPtr multiStream = + do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); + NS_ENSURE_TRUE(multiStream, NS_ERROR_UNEXPECTED); + + nsCString postDataChunk; + nsresult rv = AppendMultipartFormData(data, boundary, postDataChunk, multiStream); + NS_ENSURE_SUCCESS(rv, rv); + + postDataChunk += NS_LITERAL_CSTRING("--") + boundary + + NS_LITERAL_CSTRING("--\r\n"); + rv = AppendPostDataChunk(postDataChunk, multiStream); + NS_ENSURE_SUCCESS(rv, rv); + + contentType = NS_LITERAL_CSTRING("multipart/form-data; boundary=") + boundary; + + NS_ADDREF(*stream = multiStream); + return NS_OK; +} + +nsresult +nsXFormsSubmissionElement::AppendMultipartFormData(nsIDOMNode *data, + const nsCString &boundary, + nsCString &postDataChunk, + nsIMultiplexInputStream *multiStream) +{ +#ifdef DEBUG_darinf + nsAutoString nodeName; + data->GetNodeName(nodeName); + LOG(("+++ AppendMultipartFormData: inspecting <%s>\n", + NS_ConvertUTF16toUTF8(nodeName).get())); +#endif + + nsresult rv; + + nsCOMPtr child; + data->GetFirstChild(getter_AddRefs(child)); + if (!child) + return NS_OK; + + PRUint16 childType; + child->GetNodeType(&childType); + + nsCOMPtr sibling; + child->GetNextSibling(getter_AddRefs(sibling)); + + if (!sibling && childType == nsIDOMNode::TEXT_NODE) + { + nsAutoString localName; + data->GetLocalName(localName); + + nsAutoString value; + child->GetNodeValue(value); + + LOG((" appending data for <%s>\n", NS_ConvertUTF16toUTF8(localName).get())); + + PRUint32 encType; + rv = GetElementEncodingType(data, &encType); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ConvertUTF16toUTF8 encName(localName); + encName.Adopt(nsLinebreakConverter::ConvertLineBreaks(encName.get(), + nsLinebreakConverter::eLinebreakAny, + nsLinebreakConverter::eLinebreakNet)); + + postDataChunk += NS_LITERAL_CSTRING("--") + boundary + + NS_LITERAL_CSTRING("\r\nContent-Disposition: form-data; name=\"") + + encName + NS_LITERAL_CSTRING("\""); + + nsCAutoString contentType; + nsCOMPtr fileStream; + if (encType == ELEMENT_ENCTYPE_URI) + { + // 'value' contains an absolute URI reference + nsCOMPtr file; + rv = CreateFileStream(value, getter_AddRefs(file), getter_AddRefs(fileStream)); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString leafName; + file->GetLeafName(leafName); + + postDataChunk += NS_LITERAL_CSTRING("; filename=\"") + + NS_ConvertUTF16toUTF8(leafName) + + NS_LITERAL_CSTRING("\""); + + // use mime service to get content-type + GetMimeTypeFromFile(file, contentType); + } + else if (encType == ELEMENT_ENCTYPE_STRING) + { + contentType.AssignLiteral("text/plain; charset=UTF-8"); + } + else + { + contentType.AssignLiteral("application/octet-stream"); + } + + postDataChunk += NS_LITERAL_CSTRING("\r\nContent-Type: ") + + contentType + NS_LITERAL_CSTRING("\r\n"); + + if (encType == ELEMENT_ENCTYPE_URI) + { + AppendPostDataChunk(postDataChunk, multiStream); + + multiStream->AppendStream(fileStream); + + postDataChunk += NS_LITERAL_CSTRING("\r\n"); + } + else + { + // for base64binary and hexBinary types, we assume that the data is + // already encoded. this assumption is based on section 8.1.6 of the + // xforms spec. + + // XXX UTF-8 ok? + NS_ConvertUTF16toUTF8 encValue(value); + encValue.Adopt(nsLinebreakConverter::ConvertLineBreaks(encValue.get(), + nsLinebreakConverter::eLinebreakAny, + nsLinebreakConverter::eLinebreakNet)); + postDataChunk += encValue + NS_LITERAL_CSTRING("\r\n"); + } + } + else + { + // call AppendMultipartFormData on each child node + do + { + rv = AppendMultipartFormData(child, boundary, postDataChunk, multiStream); + if (NS_FAILED(rv)) + return rv; + child->GetNextSibling(getter_AddRefs(sibling)); + child.swap(sibling); + } + while (child); + } + return NS_OK; +} + +nsresult +nsXFormsSubmissionElement::AppendPostDataChunk(nsCString &postDataChunk, + nsIMultiplexInputStream *multiStream) +{ + nsCOMPtr stream; + NS_NewCStringInputStream(getter_AddRefs(stream), postDataChunk); + NS_ENSURE_TRUE(stream, NS_ERROR_OUT_OF_MEMORY); + + multiStream->AppendStream(stream); + + postDataChunk.Truncate(); + return NS_OK; +} + +nsresult +nsXFormsSubmissionElement::GetElementEncodingType(nsIDOMNode *node, PRUint32 *encType) +{ + *encType = ELEMENT_ENCTYPE_STRING; // default + + nsCOMPtr element = do_QueryInterface(node); + NS_ENSURE_TRUE(element, NS_ERROR_UNEXPECTED); + + nsAutoString type; + element->GetAttributeNS(NAMESPACE_XML_SCHEMA_INSTANCE, + NS_LITERAL_STRING("type"), type); + if (!type.IsEmpty()) + { + // check for 'xsd:base64binary', 'xsd:hexBinary', or 'xsd:anyURI' + + // XXX need to handle derived types (fixing bug 263384 will help) + + // get 'xsd' namespace prefix + nsCOMPtr dom3Node = do_QueryInterface(node); + NS_ENSURE_TRUE(dom3Node, NS_ERROR_UNEXPECTED); + + nsAutoString prefix; + dom3Node->LookupPrefix(NAMESPACE_XML_SCHEMA, prefix); + + if (prefix.IsEmpty()) + { + NS_WARNING("namespace prefix not found! -- assuming 'xsd'"); + prefix.AssignLiteral("xsd"); // XXX HACK HACK HACK + } + + if (type.Length() > prefix.Length() && + prefix.Equals(StringHead(type, prefix.Length())) && + type.CharAt(prefix.Length()) == PRUnichar(':')) + { + const nsSubstring &tail = Substring(type, prefix.Length() + 1); + if (tail.Equals(NS_LITERAL_STRING("anyURI"))) + *encType = ELEMENT_ENCTYPE_URI; + else if (tail.Equals(NS_LITERAL_STRING("base64binary"))) + *encType = ELEMENT_ENCTYPE_BASE64; + else if (tail.Equals(NS_LITERAL_STRING("hexBinary"))) + *encType = ELEMENT_ENCTYPE_HEX; + } + } + + return NS_OK; +} + +nsresult +nsXFormsSubmissionElement::CreateFileStream(const nsString &absURI, + nsIFile **resultFile, + nsIInputStream **resultStream) +{ + LOG(("nsXFormsSubmissionElement::CreateFileStream [%s]\n", + NS_ConvertUTF16toUTF8(absURI).get())); + + nsCOMPtr uri; + NS_NewURI(getter_AddRefs(uri), absURI); + NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED); + + // restrict to file:// -- XXX is this correct? + PRBool schemeIsFile = PR_FALSE; + uri->SchemeIs("file", &schemeIsFile); + NS_ENSURE_TRUE(schemeIsFile, NS_ERROR_UNEXPECTED); + + // NOTE: QI to nsIFileURL just means that the URL corresponds to a + // local file resource, which is not restricted to file:// + nsCOMPtr fileURL = do_QueryInterface(uri); + NS_ENSURE_TRUE(fileURL, NS_ERROR_UNEXPECTED); + + fileURL->GetFile(resultFile); + NS_ENSURE_TRUE(*resultFile, NS_ERROR_UNEXPECTED); + + return NS_NewLocalFileInputStream(resultStream, *resultFile); +} + +nsresult +nsXFormsSubmissionElement::SendData(PRUint32 format, + const nsCString &uri, + nsIInputStream *stream, + const nsCString &contentType) +{ + LOG(("+++ sending to uri=%s [stream=%p]\n", uri.get(), (void*) stream)); + + // XXX need to properly support the various 'replace' modes and trigger + // xforms-submit-done or xforms-submit-error when appropriate. + + nsAutoString replace; + mElement->GetAttribute(NS_LITERAL_STRING("replace"), replace); + if (!replace.IsEmpty() && !replace.EqualsLiteral("all")) + { + NS_WARNING("replace != 'all' not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + } + + // XXX HACK HACK - wrap with mime stream if we are doing a POST + if (format & METHOD_POST) + { + nsCOMPtr mimeStream = + do_CreateInstance("@mozilla.org/network/mime-input-stream;1"); + NS_ENSURE_TRUE(mimeStream, NS_ERROR_UNEXPECTED); + + mimeStream->AddHeader("Content-Type", contentType.get()); + mimeStream->SetAddContentLength(PR_TRUE); + mimeStream->SetData(stream); + + stream->Release(); + NS_ADDREF(stream = mimeStream); + } + + // wrap the entire upload stream in a buffered input stream, so that + // it can be read in large chunks. + // XXX necko should probably do this (or something like this) for us. + nsCOMPtr bufferedStream; + if (stream) + { + NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, 4096); + NS_ENSURE_TRUE(bufferedStream, NS_ERROR_UNEXPECTED); + } + + nsCOMPtr domDoc; + mElement->GetOwnerDocument(getter_AddRefs(domDoc)); + + nsCOMPtr doc = do_QueryInterface(domDoc); + + nsCOMPtr container = doc->GetContainer(); + nsCOMPtr 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. + + NS_ConvertASCIItoUTF16 temp(uri); // XXX hack + return webNav->LoadURI(temp.get(), nsIWebNavigation::LOAD_FLAGS_NONE, + doc->GetDocumentURI(), bufferedStream, 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; +} diff --git a/extensions/xforms/nsXFormsSubmissionElement.h b/extensions/xforms/nsXFormsSubmissionElement.h new file mode 100644 index 00000000000..09c788f2866 --- /dev/null +++ b/extensions/xforms/nsXFormsSubmissionElement.h @@ -0,0 +1,95 @@ +/* -*- 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 + * + * 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 ***** */ + +#ifndef nsXFormsSubmissionElement_h_ +#define nsXFormsSubmissionElement_h_ + +#include "nsIXTFGenericElement.h" +#include "nsIDOMEventListener.h" +#include "nsXFormsElement.h" + +class nsIMultiplexInputStream; +class nsIInputStream; +class nsIDOMElement; +class nsIFile; +class nsCString; +class nsString; + +class SubmissionAttachmentArray; + +class nsXFormsSubmissionElement : public nsXFormsElement, + public nsIXTFGenericElement, + public nsIDOMEventListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIXTFELEMENT + NS_DECL_NSIXTFGENERICELEMENT + NS_DECL_NSIDOMEVENTLISTENER + + nsXFormsSubmissionElement() + : mElement(nsnull) + {} + + NS_HIDDEN_(void) Submit(); + NS_HIDDEN_(nsresult) SubmitEnd(PRBool succeeded); + NS_HIDDEN_(PRBool) GetBooleanAttr(const nsAString &attrName, PRBool defaultVal = PR_FALSE); + NS_HIDDEN_(void) GetDefaultInstanceData(nsIDOMNode **result); + NS_HIDDEN_(nsresult) GetSelectedInstanceData(nsIDOMNode **result); + NS_HIDDEN_(nsresult) SerializeData(nsIDOMNode *data, PRUint32 format, nsCString &uri, nsIInputStream **, nsCString &contentType); + NS_HIDDEN_(nsresult) SerializeDataXML(nsIDOMNode *data, PRUint32 format, nsIInputStream **, nsCString &contentType, SubmissionAttachmentArray *); + NS_HIDDEN_(nsresult) CreateSubmissionDoc(nsIDOMDocument *source, const nsString &encoding, SubmissionAttachmentArray *, nsIDOMDocument **result); + NS_HIDDEN_(nsresult) CopyChildren(nsIDOMNode *source, nsIDOMNode *dest, nsIDOMDocument *destDoc, SubmissionAttachmentArray *, const nsString &cdataElements, PRBool indent, PRUint32 depth); + NS_HIDDEN_(nsresult) SerializeDataURLEncoded(nsIDOMNode *data, PRUint32 format, nsCString &uri, nsIInputStream **, nsCString &contentType); + NS_HIDDEN_(void) AppendURLEncodedData(nsIDOMNode *data, const nsCString &sep, nsCString &buf); + NS_HIDDEN_(nsresult) SerializeDataMultipartRelated(nsIDOMNode *data, PRUint32 format, nsIInputStream **, nsCString &contentType); + NS_HIDDEN_(nsresult) SerializeDataMultipartFormData(nsIDOMNode *data, PRUint32 format, nsIInputStream **, nsCString &contentType); + NS_HIDDEN_(nsresult) AppendMultipartFormData(nsIDOMNode *data, const nsCString &boundary, nsCString &buf, nsIMultiplexInputStream *); + NS_HIDDEN_(nsresult) AppendPostDataChunk(nsCString &postDataChunk, nsIMultiplexInputStream *multiStream); + NS_HIDDEN_(nsresult) GetElementEncodingType(nsIDOMNode *data, PRUint32 *encType); + NS_HIDDEN_(nsresult) CreateFileStream(const nsString &absURI, nsIFile **file, nsIInputStream **stream); + NS_HIDDEN_(nsresult) SendData(PRUint32 format, const nsCString &uri, nsIInputStream *stream, const nsCString &contentType); + +private: + nsIDOMElement *mElement; +}; + +NS_HIDDEN_(nsresult) +NS_NewXFormsSubmissionElement(nsIXTFElement **aResult); + +#endif diff --git a/extensions/xforms/nsXFormsTypes.h b/extensions/xforms/nsXFormsTypes.h new file mode 100644 index 00000000000..2dee20cbe37 --- /dev/null +++ b/extensions/xforms/nsXFormsTypes.h @@ -0,0 +1,54 @@ +/* -*- 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): + * Brian Ryner + * Allan Beaufour + * + * 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 ***** */ +#ifndef __NSXFORMSTYPES_H__ +#define __NSXFORMSTYPES_H__ + +enum ModelItemPropName +{ + eModel_type, + eModel_readonly, + eModel_required, + eModel_relevant, + eModel_calculate, + eModel_constraint, + eModel_p3ptype, + eModel__count +}; + +#endif diff --git a/extensions/xforms/nsXFormsXPathAnalyzer.cpp b/extensions/xforms/nsXFormsXPathAnalyzer.cpp new file mode 100644 index 00000000000..e2a1c942c8d --- /dev/null +++ b/extensions/xforms/nsXFormsXPathAnalyzer.cpp @@ -0,0 +1,198 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#include "nsXFormsXPathAnalyzer.h" +#include "nsIDOMXPathResult.h" + +#ifdef DEBUG +// #define DEBUG_XF_ANALYZER +#endif + +MOZ_DECL_CTOR_COUNTER(nsXFormsXPathAnalyzer) + +nsXFormsXPathAnalyzer::nsXFormsXPathAnalyzer(nsIDOMXPathEvaluator* aEvaluator, nsIDOMXPathNSResolver* aResolver) + : mEvaluator(aEvaluator), mResolver(aResolver) +{ + MOZ_COUNT_CTOR(nsXFormsXPathAnalyzer); +} + +nsXFormsXPathAnalyzer::~nsXFormsXPathAnalyzer() +{ + MOZ_COUNT_DTOR(nsXFormsXPathAnalyzer); +} + +nsresult +nsXFormsXPathAnalyzer::Analyze(nsIDOMNode* aContextNode, const nsXFormsXPathNode* aNode, + nsIDOMXPathExpression* aExpression, const nsAString* aExprString, + nsXFormsMDGSet* aSet) +{ + NS_ENSURE_TRUE(aContextNode, NS_ERROR_INVALID_ARG); + NS_ENSURE_TRUE(aNode, NS_ERROR_INVALID_ARG); + NS_ENSURE_TRUE(aExpression, NS_ERROR_INVALID_ARG); + NS_ENSURE_TRUE(aExprString, NS_ERROR_INVALID_ARG); + NS_ENSURE_TRUE(aSet, NS_ERROR_INVALID_ARG); + + mCurExpression = aExpression; + mCurExprString = aExprString; + mCurSet = aSet; + +#ifdef DEBUG_XF_ANALYZER + printf("=====================================\n"); + printf("Analyzing: %s\n", NS_ConvertUCS2toUTF8(*mCurExprString).get()); + printf("=====================================\n"); +#endif + + nsresult rv = AnalyzeRecursively(aContextNode, aNode->mChild, 0); +#ifdef DEBUG_XF_ANALYZER + printf("-------------------------------------\n"); +#endif + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + + +nsresult +nsXFormsXPathAnalyzer::AnalyzeRecursively(nsIDOMNode* aContextNode, const nsXFormsXPathNode* aNode, + PRUint32 aIndent) +{ + nsXFormsXPathNode* t; + nsAutoString xp; + nsresult rv; + nsCOMPtr result; + nsCOMPtr node; + char strbuf[100]; + char* strpos; + +#ifdef DEBUG_beaufour_xxx + printf("nsXFormsXPathParser::AnalyzeRecursively(%p)\n", (void*) aNode); +#endif + + for (; aNode; aNode = aNode->mSibling) { +#ifdef DEBUG_beaufour_xxx + printf("\tChild: %p, Sibling: %p\n", (void*) aNode->mChild, (void*) aNode->mSibling); + printf("\tIndex: %d - %d\n", aNode->mStartIndex, aNode->mEndIndex); + printf("\tCon: %d, Predicate: %d, Literal: %d\n", aNode->mCon, aNode->mPredicate, aNode->mLiteral); +#endif + + if ( aNode->mEndIndex < 0 + || aNode->mStartIndex >= aNode->mEndIndex + || ((PRUint32) aNode->mEndIndex == mCurExprString->Length() && aNode->mStartIndex == 0)) { + continue; + } + + PRBool hasContinue = PR_FALSE; + strpos = strbuf; + *strpos = '\0'; + + // hasContinue == whether we have a child with a mCon + t = aNode->mChild; + while (t && !hasContinue) { + hasContinue = t->mCon; + t = t->mSibling; + } + +#ifdef DEBUG_XF_ANALYZER + for (PRUint32 j = 0; j < aIndent; ++j) { + strpos += sprintf(strpos, " "); + } + + strpos += sprintf(strpos, "<%s> ", hasContinue ? "C" : " "); + + if (aNode->mPredicate) { + if (!(aNode->mChild)) { + strpos += sprintf(strpos, "[PredicateVal], "); + } else { + strpos += sprintf(strpos, "[Predicate], "); + } + } else { + if (!(aNode->mChild)) { + strpos += sprintf(strpos, "[AxisStepsVal], "); + } else { + if (!hasContinue) { + strpos += printf(strpos, "[AxisSteps], "); + } + } + } +#endif + + if (aNode->mCon) { + // Remove the leading / + xp = Substring(*mCurExprString, aNode->mStartIndex + 1, aNode->mEndIndex - aNode->mStartIndex + 1); + } else { + xp = Substring(*mCurExprString, aNode->mStartIndex, aNode->mEndIndex - aNode->mStartIndex); + } + + rv = mEvaluator->Evaluate(xp, aContextNode, mResolver, + nsIDOMXPathResult::ANY_TYPE, + nsnull, getter_AddRefs(result)); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint16 type; + rv = result->GetResultType(&type); + NS_ENSURE_SUCCESS(rv, rv); + + // We are only interested in nodes + if ( type != nsIDOMXPathResult::UNORDERED_NODE_ITERATOR_TYPE + && type != nsIDOMXPathResult::ORDERED_NODE_ITERATOR_TYPE) { + continue; + } + + while (NS_SUCCEEDED(result->IterateNext(getter_AddRefs(node))) && node) { + NS_ENSURE_SUCCESS(rv, rv); +#ifdef DEBUG_XF_ANALYZER + printf(strbuf); +#endif + if (aNode->mChild || (!aNode->mChild && hasContinue)) { +#ifdef DEBUG_XF_ANALYZER + printf("iterating '%s'\n", NS_ConvertUCS2toUTF8(xp).get()); +#endif + } else { +#ifdef DEBUG_XF_ANALYZER + printf("collecting '%s'\n", NS_ConvertUCS2toUTF8(xp).get()); +#endif + mCurSet->AddNode(node); + } + rv = AnalyzeRecursively(node, aNode->mChild, aIndent + 1); + } + + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} diff --git a/extensions/xforms/nsXFormsXPathAnalyzer.h b/extensions/xforms/nsXFormsXPathAnalyzer.h new file mode 100644 index 00000000000..9e6ea389b2c --- /dev/null +++ b/extensions/xforms/nsXFormsXPathAnalyzer.h @@ -0,0 +1,77 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#include "nscore.h" +#include "nsCOMPtr.h" +#include "nsXFormsXPathNode.h" +#include "nsIDOMXPathExpression.h" +#include "nsIDOMXPathEvaluator.h" +#include "nsIDOMXPathNSResolver.h" +#include "nsXFormsMDGSet.h" +#include "nsIDOMNode.h" +#include "nsCOMPtr.h" +#include "nsString.h" + +/** + * This class analyzes an XPath Expression parse tree (nsXFormsXPathNode), and + * returns all the nodes that the expression depends on. + * + * @note Should be reimplemented and moved to Transformiix + * @note This class is not thread safe (mCur.*) + */ +class nsXFormsXPathAnalyzer { +private: + nsCOMPtr mEvaluator; + nsCOMPtr mResolver; + + nsXFormsMDGSet* mCurSet; + nsCOMPtr mCurExpression; + const nsAString* mCurExprString; + + nsresult AnalyzeRecursively(nsIDOMNode* aContextNode, const nsXFormsXPathNode* aNode, + PRUint32 aIndent); + +public: + nsXFormsXPathAnalyzer(nsIDOMXPathEvaluator* aEvaluator, nsIDOMXPathNSResolver* aResolver); + ~nsXFormsXPathAnalyzer(); + + nsresult Analyze(nsIDOMNode* aContextNode, const nsXFormsXPathNode* aNode, + nsIDOMXPathExpression* aExpression, const nsAString* aExprString, + nsXFormsMDGSet* aSet); +}; diff --git a/extensions/xforms/nsXFormsXPathNode.cpp b/extensions/xforms/nsXFormsXPathNode.cpp new file mode 100644 index 00000000000..e6774b05b84 --- /dev/null +++ b/extensions/xforms/nsXFormsXPathNode.cpp @@ -0,0 +1,64 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#include "nsXFormsXPathNode.h" +#include "nsCOMPtr.h" + +MOZ_DECL_CTOR_COUNTER(nsXFormsXPathNode) + +nsXFormsXPathNode::nsXFormsXPathNode(nsXFormsXPathNode* aParent, PRBool aContinue) + : mChild(nsnull), mEndIndex(-100), mCon(aContinue) +{ + MOZ_COUNT_CTOR(nsXFormsXPathNode); + + if (aParent) { + mSibling = aParent->mChild; + aParent->mChild = this; + } else { + mSibling = nsnull; + } +} + +nsXFormsXPathNode::~nsXFormsXPathNode() +{ + delete mChild; + delete mSibling; + + MOZ_COUNT_DTOR(nsXFormsXPathNode); +} diff --git a/extensions/xforms/nsXFormsXPathNode.h b/extensions/xforms/nsXFormsXPathNode.h new file mode 100644 index 00000000000..b122e0aa6a4 --- /dev/null +++ b/extensions/xforms/nsXFormsXPathNode.h @@ -0,0 +1,64 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ +#ifndef __NSXFORMSXPATHNODE_H__ +#define __NSXFORMSXPATHNODE_H__ + +#include "nscore.h" + +/** + * The node type of the XPath Expression parse tree that nsXFormsXPathParser + * creates. + * + * @note Should be reimplemented and moved to Transformiix + */ +class nsXFormsXPathNode { +public: + nsXFormsXPathNode* mChild; + nsXFormsXPathNode* mSibling; + PRInt32 mStartIndex; + PRInt32 mEndIndex; + PRBool mCon; + PRBool mPredicate; + PRBool mLiteral; + + nsXFormsXPathNode(nsXFormsXPathNode* aParent, PRBool aContinue = PR_FALSE); + ~nsXFormsXPathNode(); +}; + +#endif diff --git a/extensions/xforms/nsXFormsXPathParser.cpp b/extensions/xforms/nsXFormsXPathParser.cpp new file mode 100644 index 00000000000..16d33b48278 --- /dev/null +++ b/extensions/xforms/nsXFormsXPathParser.cpp @@ -0,0 +1,911 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#include "nsXFormsXPathParser.h" +#include "nscore.h" + +/* + * (TODO) GIANT hack. Without exceptions everything had to be rewritten, and + * that was to overdo it a bit, when the code probably does not survive... + */ +void +XPathCompilerException(const char* aMsg, nsAString& aExpression, PRInt32 aOffset = -1, PRInt32 aLength = -1) { + printf("XPathCompilerException: %s, %s [o: %d, l: %d]\n", + aMsg, + NS_ConvertUCS2toUTF8(aExpression).get(), aOffset, aLength); + + printf("WARNING: Houston we have a problem, and unlike Apollo 13, we're not going to make it!\n"); + NS_ABORT(); +} + + +MOZ_DECL_CTOR_COUNTER(nsXFormsXPathParser) + +nsXFormsXPathParser::nsXFormsXPathParser() + : mUsesDynamicFunc(PR_FALSE), mHead(nsnull), mAnalyzeStackPointer(nsnull) +{ + MOZ_COUNT_CTOR(nsXFormsXPathParser); +} + +nsXFormsXPathParser::~nsXFormsXPathParser() +{ + MOZ_COUNT_DTOR(nsXFormsXPathParser); +} + +void +nsXFormsXPathParser::PushContext(PRInt32 aStartIndex) +{ + mHead = new nsXFormsXPathNode(mHead); + if (aStartIndex == -100) { + mHead->mStartIndex = mScanner.Offset() + 1; + } else { + mHead->mStartIndex = aStartIndex; + } + mStack[++mAnalyzeStackPointer] = mHead; + mHead->mPredicate = mPredicateLevel != 0; + mHead->mLiteral = PR_FALSE; +} + +void +nsXFormsXPathParser::PushContext(nsXFormsXPathNode* aNode) +{ + mStack[++mAnalyzeStackPointer] = aNode; + mHead = aNode; +} + +void +nsXFormsXPathParser::EndContext() +{ + if (mHead) { + mHead->mEndIndex = mScanner.Offset() + 1; + } +} + +void +nsXFormsXPathParser::RestartContext() +{ + nsXFormsXPathNode* temp = new nsXFormsXPathNode(nsnull, PR_TRUE); + temp->mStartIndex = mScanner.Offset() + 1; + temp->mSibling = mHead->mChild; + temp->mPredicate = mPredicateLevel != 0; + mHead->mChild = temp; + mHead = temp; + mStack[mAnalyzeStackPointer] = mHead; +} + +nsXFormsXPathNode* +nsXFormsXPathParser::JustContext() +{ + nsXFormsXPathNode* t = mStack[mAnalyzeStackPointer]; + mHead = mStack[--mAnalyzeStackPointer]; + return t; +} + + +nsXFormsXPathNode* +nsXFormsXPathParser::PopContext() +{ + if (mHead->mEndIndex == -100) { + mHead->mEndIndex = mScanner.Offset() + 1; + } + + NS_ASSERTION(mHead->mStartIndex <= mHead->mEndIndex, ""); + NS_ASSERTION(mAnalyzeStackPointer - 1 >= 0, ""); + mHead = mStack[--mAnalyzeStackPointer]; + + return mHead; +} + +void +nsXFormsXPathParser::AbbreviatedStep() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::DOT: + PopToken(); + break; + + case nsXFormsXPathScanner::DOTDOT: + PopToken(); + break; + + default: + XPathCompilerException("Expected . or ..", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + break; + } +} + +void +nsXFormsXPathParser::AbsoluteLocationPath() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + nsXFormsXPathScanner::XPATHTOKEN r; + switch (t) { + case nsXFormsXPathScanner::SLASH: + PopToken(); + r = PeekToken(); + switch (r) { + case nsXFormsXPathScanner::ANCESTOR: + case nsXFormsXPathScanner::ANCESTOR_OR_SELF: + case nsXFormsXPathScanner::ATTRIBUTE: + case nsXFormsXPathScanner::CHILD: + case nsXFormsXPathScanner::DESCENDANT: + case nsXFormsXPathScanner::DESCENDANT_OR_SELF: + case nsXFormsXPathScanner::FOLLOWING: + case nsXFormsXPathScanner::FOLLOWING_SIBLING: + case nsXFormsXPathScanner::NAMESPACE: + case nsXFormsXPathScanner::PARENT: + case nsXFormsXPathScanner::PRECEDING: + case nsXFormsXPathScanner::PRECEDING_SIBLING: + case nsXFormsXPathScanner::SELF: + case nsXFormsXPathScanner::AT: + case nsXFormsXPathScanner::STAR: + case nsXFormsXPathScanner::NCNAME: + case nsXFormsXPathScanner::QNAME: + case nsXFormsXPathScanner::COMMENT: + case nsXFormsXPathScanner::TEXT: + case nsXFormsXPathScanner::PI: + case nsXFormsXPathScanner::NODE: + case nsXFormsXPathScanner::DOT: + case nsXFormsXPathScanner::DOTDOT: + RelativeLocationPath(); + break; + + default: + break; + } + break; + + case nsXFormsXPathScanner::SLASHSLASH: + PopToken(); + if (DoRelative()) { + RelativeLocationPath(); + } + break; + + default: + XPathCompilerException("Not an absolute location path", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } +} + +void +nsXFormsXPathParser::AdditiveExpression() +{ + PRBool con = PR_TRUE; + + MultiplicationExpr(); + + while (con) { + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::PLUS: + PopToken(); + MultiplicationExpr(); + break; + + case nsXFormsXPathScanner::MINUS: + PopToken(); + MultiplicationExpr(); + break; + + default: + con = PR_FALSE; + } + } +} + +void +nsXFormsXPathParser::AndExpr() +{ + EqualityExpr(); + if (PeekToken() == nsXFormsXPathScanner::AND) { + PopToken(); + AndExpr(); + } +} + +void +nsXFormsXPathParser::AxisSpecifier() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::ANCESTOR: + case nsXFormsXPathScanner::ANCESTOR_OR_SELF: + case nsXFormsXPathScanner::ATTRIBUTE: + case nsXFormsXPathScanner::CHILD: + case nsXFormsXPathScanner::DESCENDANT: + case nsXFormsXPathScanner::DESCENDANT_OR_SELF: + case nsXFormsXPathScanner::FOLLOWING: + case nsXFormsXPathScanner::FOLLOWING_SIBLING: + case nsXFormsXPathScanner::NAMESPACE: + case nsXFormsXPathScanner::PARENT: + case nsXFormsXPathScanner::PRECEDING: + case nsXFormsXPathScanner::PRECEDING_SIBLING: + case nsXFormsXPathScanner::SELF: + PopToken(); + case nsXFormsXPathScanner::AT: + PopToken(); + break; + + default: + XPathCompilerException("Not a axis specifier", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + break; + } +} + +PRBool +nsXFormsXPathParser::DoRelative() +{ + PRBool ret = PR_FALSE; + + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::ANCESTOR: + case nsXFormsXPathScanner::ANCESTOR_OR_SELF: + case nsXFormsXPathScanner::ATTRIBUTE: + case nsXFormsXPathScanner::CHILD: + case nsXFormsXPathScanner::DESCENDANT: + case nsXFormsXPathScanner::DESCENDANT_OR_SELF: + case nsXFormsXPathScanner::FOLLOWING: + case nsXFormsXPathScanner::FOLLOWING_SIBLING: + case nsXFormsXPathScanner::NAMESPACE: + case nsXFormsXPathScanner::PARENT: + case nsXFormsXPathScanner::PRECEDING: + case nsXFormsXPathScanner::PRECEDING_SIBLING: + case nsXFormsXPathScanner::SELF: + case nsXFormsXPathScanner::AT: + case nsXFormsXPathScanner::STAR: + case nsXFormsXPathScanner::NCNAME: + case nsXFormsXPathScanner::QNAME: + case nsXFormsXPathScanner::COMMENT: + case nsXFormsXPathScanner::TEXT: + case nsXFormsXPathScanner::PI: + case nsXFormsXPathScanner::NODE: + case nsXFormsXPathScanner::DOT: + case nsXFormsXPathScanner::DOTDOT: + ret = PR_TRUE; + break; + + default: + break; + } + return ret; +} + +void +nsXFormsXPathParser::EqualityExpr() +{ + RelationalExpression(); + + PRBool con = PR_TRUE; + while (con) { + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::EQUAL: + PopToken(); + RelationalExpression(); + break; + + case nsXFormsXPathScanner::NOTEQUAL: + PopToken(); + RelationalExpression(); + break; + + default: + con = PR_FALSE; + } + } +} + +void +nsXFormsXPathParser::Expr() +{ + OrExpr(); +} + +void +nsXFormsXPathParser::FilterExpr() +{ + if (PrimaryExpr() && PeekToken() == nsXFormsXPathScanner::LBRACK) { + Predicate(); + } +} + +void +nsXFormsXPathParser::FunctionCall() +{ + if (!mUsesDynamicFunc) { + nsDependentSubstring fname = Substring(mScanner.Expression(), mScanner.Offset() + 1, mScanner.Offset() + mScanner.Length() + 1); + if (fname.Equals(NS_LITERAL_STRING("now"))) { + mUsesDynamicFunc = PR_TRUE; + } + } + PopToken(); + PopToken(); + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + + PRBool con = t == nsXFormsXPathScanner::RPARAN ? PR_FALSE : PR_TRUE; + while (con) { + if (t == nsXFormsXPathScanner::XPATHEOF) { + XPathCompilerException("Expected ) got EOF", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } + nsXFormsXPathNode* c = JustContext(); + Expr(); + PushContext(c); + + t = PeekToken(); + if (t == nsXFormsXPathScanner::COMMA) { + PopToken(); // Another Argument + con = PR_TRUE; + } else { + con = PR_FALSE; + } + } + + if (t != nsXFormsXPathScanner::RPARAN) { + XPathCompilerException("Expected )", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } + + PopToken(); +} + +void +nsXFormsXPathParser::LocationPath() +{ + if (DoRelative()) { + PushContext(); + RelativeLocationPath(); + PopContext(); + } else { + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::SLASH: + case nsXFormsXPathScanner::SLASHSLASH: + PushContext(); + AbsoluteLocationPath(); + PopContext(); + break; + + default: + XPathCompilerException("Not a location path", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } + } +} + +void +nsXFormsXPathParser::MultiplicationExpr() +{ + UnaryExpr(); + PRBool con = PR_TRUE; + while (con) { + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::MULTIPLYOPERATOR: + case nsXFormsXPathScanner::DIV: + case nsXFormsXPathScanner::MOD: + PopToken(); + UnaryExpr(); + break; + default: + con = PR_FALSE; + } + } +} + +void +nsXFormsXPathParser::NameTest() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::STAR: + case nsXFormsXPathScanner::NCNAME: + case nsXFormsXPathScanner::QNAME: + PopToken(); + break; + default: + XPathCompilerException("NodeTest error", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } +} + +void +nsXFormsXPathParser::NodeTest() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::STAR: + case nsXFormsXPathScanner::NCNAME: + case nsXFormsXPathScanner::QNAME: + NameTest(); + break; + case nsXFormsXPathScanner::COMMENT: + case nsXFormsXPathScanner::TEXT: + case nsXFormsXPathScanner::PI: + case nsXFormsXPathScanner::NODE: + NodeType(); + break; + default: + XPathCompilerException("Not a node test", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } +} + +void +nsXFormsXPathParser::NodeType() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::COMMENT: + PopToken(); + PopToken(); + PopToken(); + break; + case nsXFormsXPathScanner::TEXT: + PopToken(); + PopToken(); + PopToken(); + break; + case nsXFormsXPathScanner::PI: + PopToken(); + if ((t = PeekToken()) == nsXFormsXPathScanner::LPARAN) { + PopToken(); + PopToken(); + PopToken(); + } + break; + + case nsXFormsXPathScanner::NODE: + PopToken(); + PopToken(); + PopToken(); + break; + + default: + XPathCompilerException("Not a valid NodeType", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } +} + +void +nsXFormsXPathParser::OrExpr() +{ + AndExpr(); + if (PeekToken() == nsXFormsXPathScanner::OR) { + PopToken(); + OrExpr(); + } +} + +void +nsXFormsXPathParser::PathExpr() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::ANCESTOR: + case nsXFormsXPathScanner::ANCESTOR_OR_SELF: + case nsXFormsXPathScanner::ATTRIBUTE: + case nsXFormsXPathScanner::CHILD: + case nsXFormsXPathScanner::DESCENDANT: + case nsXFormsXPathScanner::DESCENDANT_OR_SELF: + case nsXFormsXPathScanner::FOLLOWING: + case nsXFormsXPathScanner::FOLLOWING_SIBLING: + case nsXFormsXPathScanner::NAMESPACE: + case nsXFormsXPathScanner::PARENT: + case nsXFormsXPathScanner::PRECEDING: + case nsXFormsXPathScanner::PRECEDING_SIBLING: + case nsXFormsXPathScanner::SELF: + case nsXFormsXPathScanner::AT: + case nsXFormsXPathScanner::STAR: + case nsXFormsXPathScanner::NCNAME: + case nsXFormsXPathScanner::QNAME: + case nsXFormsXPathScanner::COMMENT: + case nsXFormsXPathScanner::TEXT: + case nsXFormsXPathScanner::PI: + case nsXFormsXPathScanner::NODE: + case nsXFormsXPathScanner::DOT: + case nsXFormsXPathScanner::DOTDOT: + case nsXFormsXPathScanner::SLASH: + case nsXFormsXPathScanner::SLASHSLASH: + LocationPath(); + break; + + default: + PushContext(); + FilterExpr(); + t = PeekToken(); + if (t == nsXFormsXPathScanner::SLASH || t == nsXFormsXPathScanner::SLASHSLASH) { + PopToken(); + + if (DoRelative()) { + RelativeLocationPath(); + } else { + nsAutoString nullstr; + XPathCompilerException("After / in a filter expression it is required to have a reletive path expression", nullstr); + } + + } + PopContext(); + } +} + +nsXFormsXPathScanner::XPATHTOKEN +nsXFormsXPathParser::PeekToken() +{ + return mPeek; +} + +nsXFormsXPathScanner::XPATHTOKEN +nsXFormsXPathParser::PopToken() +{ + nsXFormsXPathScanner::XPATHTOKEN temp = mPeek; + mPeek = mScanner.NextToken(); + if (mPeek == nsXFormsXPathScanner::WHITESPACE) { // Skip whitespaces + mPeek = mScanner.NextToken(); + } + return temp; +} + + +void +nsXFormsXPathParser::Predicate() +{ + EndContext(); + mPredicateLevel++; + while (PeekToken() == nsXFormsXPathScanner::LBRACK) { + PopToken(); + Expr(); + + nsXFormsXPathScanner::XPATHTOKEN t = PopToken(); + if (t != nsXFormsXPathScanner::RBRACK) { + XPathCompilerException("Expected ]", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } + } + mPredicateLevel--; + RestartContext(); +} + +PRBool +nsXFormsXPathParser::PrimaryExpr() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::VARIABLE: + PopToken(); + break; + + case nsXFormsXPathScanner::LPARAN: { + PopToken(); + nsXFormsXPathNode* c = JustContext(); + Expr(); + PushContext(c); + if (PeekToken() == nsXFormsXPathScanner::RPARAN) { + PopToken(); + } else { + XPathCompilerException("Expected )", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } + } + break; + + case nsXFormsXPathScanner::LITERAL: + mHead->mLiteral = PR_TRUE; + PopToken(); + break; + + case nsXFormsXPathScanner::NUMBER: + mHead->mLiteral = PR_TRUE; + PopToken(); + break; + + case nsXFormsXPathScanner::FUNCTIONNAME: + FunctionCall(); + return PeekToken() == nsXFormsXPathScanner::SLASH || PeekToken() == nsXFormsXPathScanner::SLASHSLASH || PeekToken() == nsXFormsXPathScanner::LBRACK; + break; + + default: + XPathCompilerException("Not a primary expression", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } + + return PR_FALSE; +} + +void +nsXFormsXPathParser::RelationalExpression() +{ + AdditiveExpression(); + PRBool con = PR_TRUE; + while (con) { + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::LESS: + PopToken(); + AdditiveExpression(); + break; + + case nsXFormsXPathScanner::LEQUAL: + PopToken(); + AdditiveExpression(); + break; + + case nsXFormsXPathScanner::GREATER: + PopToken(); + AdditiveExpression(); + break; + + case nsXFormsXPathScanner::GEQUAL: + PopToken(); + AdditiveExpression(); + break; + + default: + con = PR_FALSE; + } + } +} + +void +nsXFormsXPathParser::RelativeLocationPath() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::ANCESTOR: + case nsXFormsXPathScanner::ANCESTOR_OR_SELF: + case nsXFormsXPathScanner::ATTRIBUTE: + case nsXFormsXPathScanner::CHILD: + case nsXFormsXPathScanner::DESCENDANT: + case nsXFormsXPathScanner::DESCENDANT_OR_SELF: + case nsXFormsXPathScanner::FOLLOWING: + case nsXFormsXPathScanner::FOLLOWING_SIBLING: + case nsXFormsXPathScanner::NAMESPACE: + case nsXFormsXPathScanner::PARENT: + case nsXFormsXPathScanner::PRECEDING: + case nsXFormsXPathScanner::PRECEDING_SIBLING: + case nsXFormsXPathScanner::SELF: + case nsXFormsXPathScanner::AT: + case nsXFormsXPathScanner::STAR: + case nsXFormsXPathScanner::NCNAME: + case nsXFormsXPathScanner::QNAME: + case nsXFormsXPathScanner::COMMENT: + case nsXFormsXPathScanner::TEXT: + case nsXFormsXPathScanner::PI: + case nsXFormsXPathScanner::NODE: + Step(); + break; + + case nsXFormsXPathScanner::DOT: + case nsXFormsXPathScanner::DOTDOT: + AbbreviatedStep(); + break; + + default: + XPathCompilerException("Not a relative location path ", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } + + t = PeekToken(); + + if (t == nsXFormsXPathScanner::SLASH || t == nsXFormsXPathScanner::SLASHSLASH) { + PopToken(); + if (DoRelative()) { + RelativeLocationPath(); + } + } +} + +void +nsXFormsXPathParser::Step() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::ANCESTOR: + case nsXFormsXPathScanner::ANCESTOR_OR_SELF: + case nsXFormsXPathScanner::ATTRIBUTE: + case nsXFormsXPathScanner::CHILD: + case nsXFormsXPathScanner::DESCENDANT: + case nsXFormsXPathScanner::DESCENDANT_OR_SELF: + case nsXFormsXPathScanner::FOLLOWING: + case nsXFormsXPathScanner::FOLLOWING_SIBLING: + case nsXFormsXPathScanner::NAMESPACE: + case nsXFormsXPathScanner::PARENT: + case nsXFormsXPathScanner::PRECEDING: + case nsXFormsXPathScanner::PRECEDING_SIBLING: + case nsXFormsXPathScanner::SELF: + case nsXFormsXPathScanner::AT: + AxisSpecifier(); + break; + + default: + break; + } + + t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::STAR: + case nsXFormsXPathScanner::NCNAME: + case nsXFormsXPathScanner::QNAME: + case nsXFormsXPathScanner::COMMENT: + case nsXFormsXPathScanner::TEXT: + case nsXFormsXPathScanner::PI: + case nsXFormsXPathScanner::NODE: + NodeTest(); + break; + + default: + XPathCompilerException("Expected a NodeTest expression", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } + + t = PeekToken(); + if (t == nsXFormsXPathScanner::LBRACK) { + Predicate(); // set the predicates + } +} + +void +nsXFormsXPathParser::UnaryExpr() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::MINUS: + PopToken(); + UnaryExpr(); + break; + + default: + UnionExpr(); + } +} + +void +nsXFormsXPathParser::UnionExpr() +{ + nsXFormsXPathScanner::XPATHTOKEN t = PeekToken(); + switch (t) { + case nsXFormsXPathScanner::ANCESTOR: + case nsXFormsXPathScanner::ANCESTOR_OR_SELF: + case nsXFormsXPathScanner::ATTRIBUTE: + case nsXFormsXPathScanner::CHILD: + case nsXFormsXPathScanner::DESCENDANT: + case nsXFormsXPathScanner::DESCENDANT_OR_SELF: + case nsXFormsXPathScanner::FOLLOWING: + case nsXFormsXPathScanner::FOLLOWING_SIBLING: + case nsXFormsXPathScanner::NAMESPACE: + case nsXFormsXPathScanner::PARENT: + case nsXFormsXPathScanner::PRECEDING: + case nsXFormsXPathScanner::PRECEDING_SIBLING: + case nsXFormsXPathScanner::SELF: + case nsXFormsXPathScanner::AT: + case nsXFormsXPathScanner::STAR: + case nsXFormsXPathScanner::NCNAME: + case nsXFormsXPathScanner::QNAME: + case nsXFormsXPathScanner::COMMENT: + case nsXFormsXPathScanner::TEXT: + case nsXFormsXPathScanner::PI: + case nsXFormsXPathScanner::NODE: + case nsXFormsXPathScanner::DOT: + case nsXFormsXPathScanner::DOTDOT: + case nsXFormsXPathScanner::SLASH: + case nsXFormsXPathScanner::SLASHSLASH: + PathExpr(); + t = PeekToken(); + if (t == nsXFormsXPathScanner::UNION) { + PopToken(); + UnionExpr(); + } + break; + + case nsXFormsXPathScanner::VARIABLE: + case nsXFormsXPathScanner::LPARAN: + case nsXFormsXPathScanner::LITERAL: + case nsXFormsXPathScanner::NUMBER: + case nsXFormsXPathScanner::FUNCTIONNAME: + PathExpr(); + t = PeekToken(); + if (t == nsXFormsXPathScanner::UNION) { + PopToken(); + UnionExpr(); + } + break; + + default: + XPathCompilerException("Unexpected union token", mScanner.Expression(), mScanner.Offset(), mScanner.Length()); + } +} + +nsXFormsXPathNode* +nsXFormsXPathParser::Parse(const nsAString& aExpression) +{ +#ifdef DEBUG_XF_PARSER + printf("=====================================\n"); + printf("Parsing: %s\n", NS_ConvertUCS2toUTF8(aExpression).get()); + printf("=====================================\n"); +#endif + + mScanner.Init(aExpression); + mAnalyzeStackPointer = 0; + mPredicateLevel = 0; + mHead = nsnull; + PopToken(); + PushContext(); + + nsXFormsXPathNode* root = mHead; + Expr(); + PopContext(); + +#ifdef DEBUG_XF_PARSER + Dump(root); + printf("-------------------------------------\n"); +#endif + + return root; +} + +PRBool +nsXFormsXPathParser::UsesDynamicFunc() const { + return mUsesDynamicFunc; +} + +#ifdef DEBUG_XF_PARSER +void +nsXFormsXPathParser::Dump(nsXFormsXPathNode* aNode) +{ + DumpWithLevel(aNode->mChild, 0); +} + +void +nsXFormsXPathParser::DumpWithLevel(nsXFormsXPathNode* aNode, PRInt32 aLevel) +{ + while (aNode) { + if (aNode->mStartIndex < aNode->mEndIndex) { + for (int i = 0; i < aLevel; i++) { + printf(" "); + } + if (aNode->mCon) { + printf("(+)"); + } else { + printf("(|)"); + } + OutputSubExpression(aNode->mStartIndex, aNode->mEndIndex); + } + DumpWithLevel(aNode->mChild, aLevel + 2); + aNode = aNode->mSibling; + } +} + +void +nsXFormsXPathParser::OutputSubExpression(PRInt32 aOffset, PRInt32 aEndOffset) +{ + const nsDependentSubstring expr = Substring(mScanner.Expression(), aOffset, aEndOffset - aOffset); + printf("%s\n", NS_ConvertUCS2toUTF8(expr).get()); +} +#endif diff --git a/extensions/xforms/nsXFormsXPathParser.h b/extensions/xforms/nsXFormsXPathParser.h new file mode 100644 index 00000000000..b73fcdcfe96 --- /dev/null +++ b/extensions/xforms/nsXFormsXPathParser.h @@ -0,0 +1,112 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#include "nsXFormsXPathScanner.h" +#include "nsXFormsXPathNode.h" + +#define ANALYZE_STACK_SIZE 120 + +/** + * The XPath Expression parser. Uses nsXFormsXPathScanner to scan the expression + * and builds a parse tree of nsXFormsXPathNode's. + * + * @note Should be reimplemented and moved to Transformiix + * + */ +class nsXFormsXPathParser { +private: + nsXFormsXPathScanner mScanner; + nsXFormsXPathScanner::XPATHTOKEN mPeek; + + PRBool mUsesDynamicFunc; + + nsXFormsXPathNode* mHead; + int mAnalyzeStackPointer; + int mPredicateLevel; + nsXFormsXPathNode* mStack[ANALYZE_STACK_SIZE]; + + nsXFormsXPathScanner::XPATHTOKEN PeekToken(); + nsXFormsXPathScanner::XPATHTOKEN PopToken(); + + void PushContext(PRInt32 aStartIndex = -100); + void PushContext(nsXFormsXPathNode* mAnalyze); + void EndContext(); + void RestartContext(); + nsXFormsXPathNode* JustContext(); + nsXFormsXPathNode* PopContext(); + + PRBool DoRelative(); + + void AbbreviatedStep(); + void AbsoluteLocationPath(); + void AdditiveExpression(); + void AndExpr(); + void AxisSpecifier(); + void EqualityExpr(); + void Expr(); + void FilterExpr(); + void FunctionCall(); + void LocationPath(); + void MultiplicationExpr(); + void NameTest(); + void NodeTest(); + void NodeType(); + void OrExpr(); + void PathExpr(); + void Predicate(); + PRBool PrimaryExpr(); + void RelationalExpression(); + void RelativeLocationPath(); + void Step(); + void UnaryExpr(); + void UnionExpr(); + +#ifdef DEBUG_XF_PARSER + void Dump(nsXFormsXPathNode* aNode); + void DumpWithLevel(nsXFormsXPathNode* aNode, PRInt32 aLevel); + void OutputSubExpression(PRInt32 aOffset, PRInt32 aEndOffset); +#endif + +public: + nsXFormsXPathParser(); + ~nsXFormsXPathParser(); + + nsXFormsXPathNode* Parse(const nsAString& aExpression); + PRBool UsesDynamicFunc() const; +}; diff --git a/extensions/xforms/nsXFormsXPathScanner.cpp b/extensions/xforms/nsXFormsXPathScanner.cpp new file mode 100644 index 00000000000..636cf8501dd --- /dev/null +++ b/extensions/xforms/nsXFormsXPathScanner.cpp @@ -0,0 +1,441 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#include "nsXFormsXPathScanner.h" +#include "nsXFormsXPathXMLUtil.h" + +MOZ_DECL_CTOR_COUNTER(nsXFormsXPathScanner) + +nsXFormsXPathScanner::nsXFormsXPathScanner() +{ + MOZ_COUNT_CTOR(nsXFormsXPathScanner); +} + +nsXFormsXPathScanner::nsXFormsXPathScanner(const nsAString& aExpression) +{ + MOZ_COUNT_CTOR(nsXFormsXPathScanner); + + Init(aExpression); +} + +nsXFormsXPathScanner::~nsXFormsXPathScanner() +{ + MOZ_COUNT_DTOR(nsXFormsXPathScanner); +} + +void +nsXFormsXPathScanner::Init(const nsAString& aExpression) +{ + mExpression = aExpression; + mSize = mExpression.Length(); + mLength = 0; + mOffset = -1; + mLast = NONE; + mState = NONE; +} + +PRInt32 +nsXFormsXPathScanner::Length() +{ + return mLength; +} + +PRInt32 +nsXFormsXPathScanner::Offset() +{ + return mOffset; +} + +nsAString& +nsXFormsXPathScanner::Expression() +{ + return mExpression; +} + +PRUnichar +nsXFormsXPathScanner::PopChar() +{ + PRUnichar c = '\0'; + mLength++; + if (mOffset + mLength < mSize) { + c = mExpression[mOffset + mLength]; + } + return c; +} + +PRUnichar +nsXFormsXPathScanner::PeekChar() +{ + return PeekChar(mOffset + mLength + 1); +} + +PRUnichar +nsXFormsXPathScanner::PeekChar(PRInt32 aOffset) +{ + if (mSize > aOffset) + return mExpression[aOffset]; + return '\0'; +} + +PRUnichar +nsXFormsXPathScanner::NextNonWhite() +{ + return PeekChar(GetOffsetForNonWhite()); +} + +int +nsXFormsXPathScanner::GetOffsetForNonWhite() +{ + PRInt32 co = mOffset + mLength + 1; + while (nsXFormsXPathXMLUtil::IsWhitespace(PeekChar(co))) + co++; + return co; +} + +PRBool +nsXFormsXPathScanner::HasMoreTokens() +{ + return mState != XPATHEOF; +} + +nsXFormsXPathScanner::XPATHTOKEN +nsXFormsXPathScanner::NextToken() +{ + if (mState != WHITESPACE) + mLast = mState; + mOffset = mOffset + mLength; + mLength = 0; + + PRUnichar c = PeekChar(); + if (c == '\0') { + mState = XPATHEOF; + } else if (nsXFormsXPathXMLUtil::IsDigit(c)) { + mState = ScanNumber(); + } else if (c == '_' || nsXFormsXPathXMLUtil::IsLetter(c)) { + mState = ScanQName(); + } else if (c == '"' || c == '\'') { + mState = ScanLiteral(); + } else { + switch (c) { + case '(': + mState = LPARAN; + PopChar(); + break; + + case ')': + mState = RPARAN; + PopChar(); + break; + + case '[': + mState = LBRACK; + PopChar(); + break; + + case ']': + mState = RBRACK; + PopChar(); + break; + + case '@': + mState = AT; + PopChar(); + break; + + case ',': + mState = COMMA; + PopChar(); + break; + + case ':': + PopChar(); + if (PeekChar() == ':') { + mState = COLONCOLON; + PopChar(); + } else + mState = ERRORXPATHTOKEN; + break; + + case '.': + PopChar(); + if (PeekChar() == '.') { + mState = DOTDOT; + PopChar(); + } else if (nsXFormsXPathXMLUtil::IsDigit(PeekChar())) + mState = ScanNumber(); + else + mState = DOT; + break; + + case '$': + mState = ScanVariable(); + break; + + case '/': + PopChar(); + if (PeekChar() == '/') { + mState = SLASHSLASH; + PopChar(); + } else + mState = SLASH; + break; + + case '|': + PopChar(); + mState = UNION; + break; + + case '+': + PopChar(); + mState = PLUS; + break; + + case '-': + PopChar(); + mState = MINUS; + break; + + case '=': + PopChar(); + mState = EQUAL; + break; + + case '!': + PopChar(); + if (PeekChar() == '=') { + mState = NOTEQUAL; + PopChar(); + } else + mState = ERRORXPATHTOKEN; + break; + + case '<': + PopChar(); + if (PeekChar() == '=') { + mState = LEQUAL; + PopChar(); + } else + mState = LESS; + break; + + case '>': + PopChar(); + if (PeekChar() == '=') { + mState = GEQUAL; + PopChar(); + } else + mState = GREATER; + break; + + case '*': + PopChar(); + mState = SolveStar(); + break; + + case '\r': + case '\n': + case '\t': + case ' ': + mState = ScanWhitespace(); + break; + + default: + PopChar(); + mState = ERRORXPATHTOKEN; + } + } + + return mState; +} + +PRBool +nsXFormsXPathScanner::SolveDiambiguate() +{ + return mLast != NONE && (mLast != AT && mLast != COLONCOLON && mLast != LPARAN && mLast != LBRACK && + mLast != AND && mLast != OR && mLast != DIV && mLast != MOD && mLast != SLASH && mLast != MULTIPLYOPERATOR && + mLast != SLASHSLASH && mLast != UNION && mLast != PLUS && mLast != MINUS && mLast != NOTEQUAL && mLast != EQUAL && mLast != LESS && + mLast != LEQUAL && mLast != GEQUAL && mLast != GREATER && mLast != COMMA); +} + +nsXFormsXPathScanner::XPATHTOKEN +nsXFormsXPathScanner::SolveStar() +{ + if (SolveDiambiguate()) + return MULTIPLYOPERATOR; + return STAR; +} + +nsXFormsXPathScanner::XPATHTOKEN +nsXFormsXPathScanner::ScanNumber() +{ + PRUnichar c = PopChar(); + PRBool decimal = (c == '.'); + + while (c != '\0') { + c = PeekChar(); + if (!decimal && c == '.') { + decimal = PR_TRUE; + } else if (!nsXFormsXPathXMLUtil::IsDigit(c)) { + return NUMBER; + } + PopChar(); + } + return NUMBER; +} + +nsXFormsXPathScanner::XPATHTOKEN +nsXFormsXPathScanner::ScanVariable() +{ + PopChar(); + if (ScanQName() != ERRORXPATHTOKEN) + return VARIABLE; + return ERRORXPATHTOKEN; +} + +nsXFormsXPathScanner::XPATHTOKEN +nsXFormsXPathScanner::ScanWhitespace() +{ + PRUnichar c; + do { + PopChar(); + c = PeekChar(); + } while (nsXFormsXPathXMLUtil::IsWhitespace(c)); + + return WHITESPACE; +} + +nsXFormsXPathScanner::XPATHTOKEN +nsXFormsXPathScanner::ScanLiteral() +{ + PRUnichar c = PopChar(); + PRUnichar p; + while ((p = PeekChar()) != c && p != '\0') + PopChar(); + if (p == '\0') + return ERRORXPATHTOKEN; + PopChar(); + return LITERAL; +} + +nsXFormsXPathScanner::XPATHTOKEN +nsXFormsXPathScanner::ScanQName() +{ + XPATHTOKEN second = NONE; + ScanNCName(); + if (PeekChar() == ':' && PeekChar(mOffset + mLength + 2) != ':') { + PopChar(); + second = ScanNCName(); + } + + // TODO: nsSubString and .Equals() ? + nsDependentSubstring image = Substring(mExpression, Offset()); + + if (SolveDiambiguate()) { + if (image.EqualsASCII("and", 3)) + return AND; + else if (image.EqualsASCII("or", 2)) + return OR; + else if (image.EqualsASCII("mod", 3)) + return MOD; + else if (image.EqualsASCII("div", 3)) + return DIV; + return ERRORXPATHTOKEN; + } + + PRUnichar c = NextNonWhite(); + if (c == '(') { + if (image.EqualsASCII("comment", 7)) + return COMMENT; + else if (image.EqualsASCII("text", 4)) + return TEXT; + else if (image.EqualsASCII("processing-instruction", 22)) + return PI; + else if (image.EqualsASCII("node", 4)) + return NODE; + + return FUNCTIONNAME; + } + + PRInt32 of = GetOffsetForNonWhite(); + if (PeekChar(of) == ':' && PeekChar(of + 1) == ':') { + if (image.EqualsASCII("ancestor", 8)) + return ANCESTOR; + else if (image.EqualsASCII("ancestor-or-self", 16)) + return ANCESTOR_OR_SELF; + else if (image.EqualsASCII("attribute", 9)) + return ATTRIBUTE; + else if (image.EqualsASCII("child", 5)) + return CHILD; + else if (image.EqualsASCII("descendant", 10)) + return DESCENDANT; + else if (image.EqualsASCII("descendant-or-self", 18)) + return DESCENDANT_OR_SELF; + else if (image.EqualsASCII("following", 9)) + return FOLLOWING; + else if (image.EqualsASCII("following-sibling", 17)) + return FOLLOWING_SIBLING; + else if (image.EqualsASCII("namespace", 9)) + return NAMESPACE; + else if (image.EqualsASCII("parent", 6)) + return PARENT; + else if (image.EqualsASCII("preceding", 9)) + return PRECEDING; + else if (image.EqualsASCII("preceding-sibling", 17)) + return PRECEDING_SIBLING; + else if (image.EqualsASCII("self", 4)) + return SELF; + return ERRORXPATHTOKEN; + } + return second != NONE ? QNAME : NCNAME; +} + +nsXFormsXPathScanner::XPATHTOKEN +nsXFormsXPathScanner::ScanNCName() +{ + PRUnichar c = PopChar(); + if (c != '_' && !nsXFormsXPathXMLUtil::IsLetter(c)) { + return ERRORXPATHTOKEN; + } + + while (nsXFormsXPathXMLUtil::IsNCNameChar(PeekChar())) { + PopChar(); + } + + return NCNAME; +} diff --git a/extensions/xforms/nsXFormsXPathScanner.h b/extensions/xforms/nsXFormsXPathScanner.h new file mode 100644 index 00000000000..747ae371a0d --- /dev/null +++ b/extensions/xforms/nsXFormsXPathScanner.h @@ -0,0 +1,101 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#ifndef __NSXFORMSXPATHSCANNER_H__ +#define __NSXFORMSXPATHSCANNER_H__ + +#include "nsString.h" + +/** + * The XPath Expression scanner, used by nsXFormsXPathParser. + * + * @note Should be reimplemented and moved to Transformiix + * + */ +class nsXFormsXPathScanner +{ +public: + enum XPATHTOKEN { + LPARAN, RPARAN, LBRACK, RBRACK, AT, COMMA, + COLONCOLON, DOT, DOTDOT, SLASH, SLASHSLASH, UNION, + PLUS, MINUS, EQUAL, NOTEQUAL, LEQUAL, LESS, GEQUAL, + GREATER, MULTIPLYOPERATOR, WHITESPACE, NUMBER, + LITERAL, AND, OR, MOD, DIV, NODE, PI, TEXT, COMMENT, + FUNCTIONNAME, NCNAME, QNAME, VARIABLE, STAR, ANCESTOR, + ANCESTOR_OR_SELF, ATTRIBUTE, CHILD, DESCENDANT, + DESCENDANT_OR_SELF, FOLLOWING, FOLLOWING_SIBLING, NAMESPACE, + PARENT, PRECEDING, PRECEDING_SIBLING, SELF, XPATHEOF, ERRORXPATHTOKEN, NONE + }; + +private: + XPATHTOKEN mState, mLast; + nsAutoString mExpression; + PRInt32 mOffset, mLength, mSize; + + PRUnichar PopChar(); + PRUnichar PeekChar(); + PRUnichar PeekChar(PRInt32 offset); + PRUnichar NextNonWhite(); + PRInt32 GetOffsetForNonWhite(); + + PRBool SolveDiambiguate(); + XPATHTOKEN SolveStar(); + XPATHTOKEN ScanNumber(); + XPATHTOKEN ScanVariable(); + XPATHTOKEN ScanWhitespace(); + XPATHTOKEN ScanLiteral(); + XPATHTOKEN ScanQName(); + XPATHTOKEN ScanNCName(); + + +public: + nsXFormsXPathScanner(); + nsXFormsXPathScanner(const nsAString& aExpression); + ~nsXFormsXPathScanner(); + + void Init(const nsAString& aExpression); + XPATHTOKEN NextToken(); + PRInt32 Offset(); + PRInt32 Length(); + void Image(PRInt32& aOffset, PRInt32& aLength); + PRBool HasMoreTokens(); + nsAString& Expression(); +}; + +#endif diff --git a/extensions/xforms/nsXFormsXPathXMLUtil.cpp b/extensions/xforms/nsXFormsXPathXMLUtil.cpp new file mode 100644 index 00000000000..c0de2c4f93f --- /dev/null +++ b/extensions/xforms/nsXFormsXPathXMLUtil.cpp @@ -0,0 +1,137 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#include "nsXFormsXPathXMLUtil.h" + +static const PRUint32 BASECHAR_RANGE_LENGTH = 404; +static const PRUnichar BASECHAR_RANGE[] = { + L'\x0041',L'\x005a',L'\x0061',L'\x007a',L'\x00c0',L'\x00d6',L'\x00d8',L'\x00f6',L'\x00f8',L'\x00ff',L'\x0100',L'\x0131',L'\x0134',L'\x013e',L'\x0141',L'\x0148',L'\x014a',L'\x017e',L'\x0180',L'\x01c3',L'\x01cd',L'\x01f0',L'\x01f4',L'\x01f5',L'\x01fa',L'\x0217',L'\x0250',L'\x02a8',L'\x02bb',L'\x02c1',L'\x0386',L'\x0386',L'\x0388',L'\x038a',L'\x038c',L'\x038c',L'\x038e',L'\x03a1',L'\x03a3',L'\x03ce' +,L'\x03d0',L'\x03d6',L'\x03da',L'\x03da',L'\x03dc',L'\x03dc',L'\x03de',L'\x03de',L'\x03e0',L'\x03e0',L'\x03e2',L'\x03f3',L'\x0401',L'\x040c',L'\x040e',L'\x044f',L'\x0451',L'\x045c',L'\x045e',L'\x0481',L'\x0490',L'\x04c4',L'\x04c7',L'\x04c8',L'\x04cb',L'\x04cc',L'\x04d0',L'\x04eb',L'\x04ee',L'\x04f5',L'\x04f8',L'\x04f9',L'\x0531',L'\x0556',L'\x0559',L'\x0559',L'\x0561',L'\x0586',L'\x05d0',L'\x05ea' +,L'\x05f0',L'\x05f2',L'\x0621',L'\x063a',L'\x0641',L'\x064a',L'\x0671',L'\x06b7',L'\x06ba',L'\x06be',L'\x06c0',L'\x06ce',L'\x06d0',L'\x06d3',L'\x06d5',L'\x06d5',L'\x06e5',L'\x06e6',L'\x0905',L'\x0939',L'\x093d',L'\x093d',L'\x0958',L'\x0961',L'\x0985',L'\x098c',L'\x098f',L'\x0990',L'\x0993',L'\x09a8',L'\x09aa',L'\x09b0',L'\x09b2',L'\x09b2',L'\x09b6',L'\x09b9',L'\x09dc',L'\x09dd',L'\x09df',L'\x09e1' +,L'\x09f0',L'\x09f1',L'\x0a05',L'\x0a0a',L'\x0a0f',L'\x0a10',L'\x0a13',L'\x0a28',L'\x0a2a',L'\x0a30',L'\x0a32',L'\x0a33',L'\x0a35',L'\x0a36',L'\x0a38',L'\x0a39',L'\x0a59',L'\x0a5c',L'\x0a5e',L'\x0a5e',L'\x0a72',L'\x0a74',L'\x0a85',L'\x0a8b',L'\x0a8d',L'\x0a8d',L'\x0a8f',L'\x0a91',L'\x0a93',L'\x0aa8',L'\x0aaa',L'\x0ab0',L'\x0ab2',L'\x0ab3',L'\x0ab5',L'\x0ab9',L'\x0abd',L'\x0abd',L'\x0ae0',L'\x0ae0' +,L'\x0b05',L'\x0b0c',L'\x0b0f',L'\x0b10',L'\x0b13',L'\x0b28',L'\x0b2a',L'\x0b30',L'\x0b32',L'\x0b33',L'\x0b36',L'\x0b39',L'\x0b3d',L'\x0b3d',L'\x0b5c',L'\x0b5d',L'\x0b5f',L'\x0b61',L'\x0b85',L'\x0b8a',L'\x0b8e',L'\x0b90',L'\x0b92',L'\x0b95',L'\x0b99',L'\x0b9a',L'\x0b9c',L'\x0b9c',L'\x0b9e',L'\x0b9f',L'\x0ba3',L'\x0ba4',L'\x0ba8',L'\x0baa',L'\x0bae',L'\x0bb5',L'\x0bb7',L'\x0bb9',L'\x0c05',L'\x0c0c' +,L'\x0c0e',L'\x0c10',L'\x0c12',L'\x0c28',L'\x0c2a',L'\x0c33',L'\x0c35',L'\x0c39',L'\x0c60',L'\x0c61',L'\x0c85',L'\x0c8c',L'\x0c8e',L'\x0c90',L'\x0c92',L'\x0ca8',L'\x0caa',L'\x0cb3',L'\x0cb5',L'\x0cb9',L'\x0cde',L'\x0cde',L'\x0ce0',L'\x0ce1',L'\x0d05',L'\x0d0c',L'\x0d0e',L'\x0d10',L'\x0d12',L'\x0d28',L'\x0d2a',L'\x0d39',L'\x0d60',L'\x0d61',L'\x0e01',L'\x0e2e',L'\x0e30',L'\x0e30',L'\x0e32',L'\x0e33' +,L'\x0e40',L'\x0e45',L'\x0e81',L'\x0e82',L'\x0e84',L'\x0e84',L'\x0e87',L'\x0e88',L'\x0e8a',L'\x0e8a',L'\x0e8d',L'\x0e8d',L'\x0e94',L'\x0e97',L'\x0e99',L'\x0e9f',L'\x0ea1',L'\x0ea3',L'\x0ea5',L'\x0ea5',L'\x0ea7',L'\x0ea7',L'\x0eaa',L'\x0eab',L'\x0ead',L'\x0eae',L'\x0eb0',L'\x0eb0',L'\x0eb2',L'\x0eb3',L'\x0ebd',L'\x0ebd',L'\x0ec0',L'\x0ec4',L'\x0f40',L'\x0f47',L'\x0f49',L'\x0f69',L'\x10a0',L'\x10c5' +,L'\x10d0',L'\x10f6',L'\x1100',L'\x1100',L'\x1102',L'\x1103',L'\x1105',L'\x1107',L'\x1109',L'\x1109',L'\x110b',L'\x110c',L'\x110e',L'\x1112',L'\x113c',L'\x113c',L'\x113e',L'\x113e',L'\x1140',L'\x1140',L'\x114c',L'\x114c',L'\x114e',L'\x114e',L'\x1150',L'\x1150',L'\x1154',L'\x1155',L'\x1159',L'\x1159',L'\x115f',L'\x1161',L'\x1163',L'\x1163',L'\x1165',L'\x1165',L'\x1167',L'\x1167',L'\x1169',L'\x1169' +,L'\x116d',L'\x116e',L'\x1172',L'\x1173',L'\x1175',L'\x1175',L'\x119e',L'\x119e',L'\x11a8',L'\x11a8',L'\x11ab',L'\x11ab',L'\x11ae',L'\x11af',L'\x11b7',L'\x11b8',L'\x11ba',L'\x11ba',L'\x11bc',L'\x11c2',L'\x11eb',L'\x11eb',L'\x11f0',L'\x11f0',L'\x11f9',L'\x11f9',L'\x1e00',L'\x1e9b',L'\x1ea0',L'\x1ef9',L'\x1f00',L'\x1f15',L'\x1f18',L'\x1f1d',L'\x1f20',L'\x1f45',L'\x1f48',L'\x1f4d',L'\x1f50',L'\x1f57' +,L'\x1f59',L'\x1f59',L'\x1f5b',L'\x1f5b',L'\x1f5d',L'\x1f5d',L'\x1f5f',L'\x1f7d',L'\x1f80',L'\x1fb4',L'\x1fb6',L'\x1fbc',L'\x1fbe',L'\x1fbe',L'\x1fc2',L'\x1fc4',L'\x1fc6',L'\x1fcc',L'\x1fd0',L'\x1fd3',L'\x1fd6',L'\x1fdb',L'\x1fe0',L'\x1fec',L'\x1ff2',L'\x1ff4',L'\x1ff6',L'\x1ffc',L'\x2126',L'\x2126',L'\x212a',L'\x212b',L'\x212e',L'\x212e',L'\x2180',L'\x2182',L'\x3041',L'\x3094',L'\x30a1',L'\x30fa' +,L'\x3105',L'\x312c',L'\xac00',L'\xd7a3'}; + +static const PRUint32 IDEOGRAPHIC_RANGE_LENGTH = 6; +static const PRUnichar IDEOGRAPHIC_RANGE[] = { + L'\x4e00',L'\x9fa5',L'\x3007',L'\x3007',L'\x3021',L'\x3029' +}; + +static const PRUint32 COMBINING_CHAR_RANGE_LENGTH = 190; +static const PRUnichar COMBINING_CHAR_RANGE[] = { + L'\x0300',L'\x0345',L'\x0360',L'\x0361',L'\x0483',L'\x0486',L'\x0591',L'\x05a1',L'\x05a3',L'\x05b9',L'\x05bb',L'\x05bd',L'\x05bf',L'\x05bf',L'\x05c1',L'\x05c2',L'\x05c4',L'\x05c4',L'\x064b',L'\x0652',L'\x0670',L'\x0670',L'\x06d6',L'\x06dc',L'\x06dd',L'\x06df',L'\x06e0',L'\x06e4',L'\x06e7',L'\x06e8',L'\x06ea',L'\x06ed',L'\x0901',L'\x0903',L'\x093c',L'\x093c',L'\x093e',L'\x094c',L'\x094d',L'\x094d' +,L'\x0951',L'\x0954',L'\x0962',L'\x0963',L'\x0981',L'\x0983',L'\x09bc',L'\x09bc',L'\x09be',L'\x09be',L'\x09bf',L'\x09bf',L'\x09c0',L'\x09c4',L'\x09c7',L'\x09c8',L'\x09cb',L'\x09cd',L'\x09d7',L'\x09d7',L'\x09e2',L'\x09e3',L'\x0a02',L'\x0a02',L'\x0a3c',L'\x0a3c',L'\x0a3e',L'\x0a3e',L'\x0a3f',L'\x0a3f',L'\x0a40',L'\x0a42',L'\x0a47',L'\x0a48',L'\x0a4b',L'\x0a4d',L'\x0a70',L'\x0a71',L'\x0a81',L'\x0a83' +,L'\x0abc',L'\x0abc',L'\x0abe',L'\x0ac5',L'\x0ac7',L'\x0ac9',L'\x0acb',L'\x0acd',L'\x0b01',L'\x0b03',L'\x0b3c',L'\x0b3c',L'\x0b3e',L'\x0b43',L'\x0b47',L'\x0b48',L'\x0b4b',L'\x0b4d',L'\x0b56',L'\x0b57',L'\x0b82',L'\x0b83',L'\x0bbe',L'\x0bc2',L'\x0bc6',L'\x0bc8',L'\x0bca',L'\x0bcd',L'\x0bd7',L'\x0bd7',L'\x0c01',L'\x0c03',L'\x0c3e',L'\x0c44',L'\x0c46',L'\x0c48',L'\x0c4a',L'\x0c4d',L'\x0c55',L'\x0c56' +,L'\x0c82',L'\x0c83',L'\x0cbe',L'\x0cc4',L'\x0cc6',L'\x0cc8',L'\x0cca',L'\x0ccd',L'\x0cd5',L'\x0cd6',L'\x0d02',L'\x0d03',L'\x0d3e',L'\x0d43',L'\x0d46',L'\x0d48',L'\x0d4a',L'\x0d4d',L'\x0d57',L'\x0d57',L'\x0e31',L'\x0e31',L'\x0e34',L'\x0e3a',L'\x0e47',L'\x0e4e',L'\x0eb1',L'\x0eb1',L'\x0eb4',L'\x0eb9',L'\x0ebb',L'\x0ebc',L'\x0ec8',L'\x0ecd',L'\x0f18',L'\x0f19',L'\x0f35',L'\x0f35',L'\x0f37',L'\x0f37' +,L'\x0f39',L'\x0f39',L'\x0f3e',L'\x0f3e',L'\x0f3f',L'\x0f3f',L'\x0f71',L'\x0f84',L'\x0f86',L'\x0f8b',L'\x0f90',L'\x0f95',L'\x0f97',L'\x0f97',L'\x0f99',L'\x0fad',L'\x0fb1',L'\x0fb7',L'\x0fb9',L'\x0fb9',L'\x20d0',L'\x20dc',L'\x20e1',L'\x20e1',L'\x302a',L'\x302f',L'\x3099',L'\x3099',L'\x309a',L'\x309a' +}; + +static const PRUint32 DIGIT_RANGE_LENGTH = 30; +static const PRUnichar DIGIT_RANGE[] = { + L'\x0030',L'\x0039',L'\x0660',L'\x0669',L'\x06f0',L'\x06f9',L'\x0966',L'\x096f',L'\x09e6',L'\x09ef',L'\x0a66',L'\x0a6f',L'\x0ae6',L'\x0aef',L'\x0b66',L'\x0b6f',L'\x0be7',L'\x0bef',L'\x0c66',L'\x0c6f',L'\x0ce6',L'\x0cef',L'\x0d66',L'\x0d6f',L'\x0e50',L'\x0e59',L'\x0ed0',L'\x0ed9',L'\x0f20',L'\x0f29' +}; + +static const PRUint32 EXTENDER_RANGE_LENGTH = 22; +static const PRUnichar EXTENDER_RANGE[] = { + L'\x00b7',L'\x00b7',L'\x02d0',L'\x02d0',L'\x02d1',L'\x02d1',L'\x0387',L'\x0387',L'\x0640',L'\x0640',L'\x0e46',L'\x0e46',L'\x0ec6',L'\x0ec6',L'\x3005',L'\x3005',L'\x3031',L'\x3035',L'\x309d',L'\x309e',L'\x30fc',L'\x30fe' +}; + +PRBool +nsXFormsXPathXMLUtil::IsInRange(const PRUnichar* range, const PRUint32 range_length, const PRUnichar c) +{ + PRBool inrange = false; + for (PRUint32 i = 0; !inrange && c >= range[i] && i < range_length; i += 2) { + inrange = c >= range[i] && c <= range[i + 1]; + } + return inrange; +} + +PRBool +nsXFormsXPathXMLUtil::IsWhitespace(const PRUnichar c) +{ + return (c == L'\r' || c == L' ' || c == L'\n' || c == L'\t'); +} + +PRBool +nsXFormsXPathXMLUtil::IsDigit(const PRUnichar c) +{ + return IsInRange(DIGIT_RANGE, DIGIT_RANGE_LENGTH, c); +} + +PRBool +nsXFormsXPathXMLUtil::IsIdeographic(const PRUnichar c) +{ + return IsInRange(IDEOGRAPHIC_RANGE, IDEOGRAPHIC_RANGE_LENGTH, c); +} + +PRBool +nsXFormsXPathXMLUtil::IsLetter(const PRUnichar c) +{ + return IsBaseChar(c) || IsIdeographic(c); +} + +PRBool +nsXFormsXPathXMLUtil::IsBaseChar(const PRUnichar c) +{ + return IsInRange(BASECHAR_RANGE, BASECHAR_RANGE_LENGTH, c); +} + + +PRBool +nsXFormsXPathXMLUtil::IsNCNameChar(const PRUnichar c) +{ + return IsLetter(c) || IsDigit(c) || c == L'.' || c == L'-' || c == L'_' || IsCombiningChar(c) || IsExtender(c); +} + +PRBool +nsXFormsXPathXMLUtil::IsCombiningChar(const PRUnichar c) +{ + return IsInRange(COMBINING_CHAR_RANGE, COMBINING_CHAR_RANGE_LENGTH, c); +} + +PRBool +nsXFormsXPathXMLUtil::IsExtender(const PRUnichar c) +{ + return IsInRange(EXTENDER_RANGE, EXTENDER_RANGE_LENGTH, c); +} diff --git a/extensions/xforms/nsXFormsXPathXMLUtil.h b/extensions/xforms/nsXFormsXPathXMLUtil.h new file mode 100644 index 00000000000..e563dffd692 --- /dev/null +++ b/extensions/xforms/nsXFormsXPathXMLUtil.h @@ -0,0 +1,78 @@ +/* -*- 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 + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * David Landwehr + * + * 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 ***** */ + +#include "nsString.h" + +/** + * Misc. utility functions for determining the class of a character, used by + * nsXFormsXPathScanner. + * + * @note Should be reimplemented and moved to Transformiix + * + */ +class nsXFormsXPathXMLUtil { +private: + /** Helper function for public functions */ + static PRBool IsInRange(const PRUnichar* range, const PRUint32 range_length, const PRUnichar c); + +public: + /** Checks if a char is a whitespace */ + static PRBool IsWhitespace(const PRUnichar c); + + /** Checks of a char is a digit */ + static PRBool IsDigit(const PRUnichar c); + + /** Checks if a char is an ideographic */ + static PRBool IsIdeographic(const PRUnichar c); + + /** Checks if a char is a letter */ + static PRBool IsLetter(const PRUnichar c); + + /** Checks if a char is a base char */ + static PRBool IsBaseChar(const PRUnichar c); + + /** Check is a char matches the first rule of a NCName */ + static PRBool IsNCNameChar(const PRUnichar c); + + /** Check if a char is a combining char */ + static PRBool IsCombiningChar(const PRUnichar c); + + /** Check if a char is an extender */ + static PRBool IsExtender(const PRUnichar c); +};