From d448e53e4804c3fde99a6f2922d4ca9b0a4e9620 Mon Sep 17 00:00:00 2001 From: "darin%meer.net" Date: Wed, 15 Dec 2004 02:08:53 +0000 Subject: [PATCH] fixes bug 271737 "Tracking bug for XForms Switch Module" patch by smaug@welho.com r=darin sr=bryner git-svn-id: svn://10.0.0.236/trunk@166734 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/extensions/xforms/Makefile.in | 6 + .../xforms/nsIXFormsCaseElement.idl | 56 ++ .../xforms/nsIXFormsSwitchElement.idl | 59 ++ mozilla/extensions/xforms/nsXFormsAtoms.cpp | 4 +- mozilla/extensions/xforms/nsXFormsAtoms.h | 1 + .../extensions/xforms/nsXFormsCaseElement.cpp | 185 ++++++ .../xforms/nsXFormsElementFactory.cpp | 10 + .../xforms/nsXFormsSwitchElement.cpp | 527 ++++++++++++++++++ .../xforms/nsXFormsToggleElement.cpp | 99 ++++ mozilla/extensions/xforms/nsXFormsUtils.cpp | 20 + mozilla/extensions/xforms/nsXFormsUtils.h | 7 + 11 files changed, 973 insertions(+), 1 deletion(-) create mode 100644 mozilla/extensions/xforms/nsIXFormsCaseElement.idl create mode 100644 mozilla/extensions/xforms/nsIXFormsSwitchElement.idl create mode 100644 mozilla/extensions/xforms/nsXFormsCaseElement.cpp create mode 100644 mozilla/extensions/xforms/nsXFormsSwitchElement.cpp create mode 100644 mozilla/extensions/xforms/nsXFormsToggleElement.cpp diff --git a/mozilla/extensions/xforms/Makefile.in b/mozilla/extensions/xforms/Makefile.in index 21780614dec..658557e4def 100644 --- a/mozilla/extensions/xforms/Makefile.in +++ b/mozilla/extensions/xforms/Makefile.in @@ -64,6 +64,7 @@ REQUIRES = \ caps \ xpconnect \ js \ + layout \ $(NULL) XPIDLSRCS = \ @@ -74,6 +75,8 @@ XPIDLSRCS = \ nsIXFormsContextControl.idl \ nsIXFormsActionElement.idl \ nsIXFormsActionModuleElement.idl \ + nsIXFormsSwitchElement.idl \ + nsIXFormsCaseElement.idl \ $(NULL) CPPSRCS = \ @@ -111,6 +114,9 @@ CPPSRCS = \ nsXFormsSetValueElement.cpp \ nsXFormsLoadElement.cpp \ nsXFormsLabelElement.cpp \ + nsXFormsToggleElement.cpp \ + nsXFormsCaseElement.cpp \ + nsXFormsSwitchElement.cpp \ $(NULL) EXTRA_DSO_LDOPTS = $(MOZ_COMPONENT_LIBS) diff --git a/mozilla/extensions/xforms/nsIXFormsCaseElement.idl b/mozilla/extensions/xforms/nsIXFormsCaseElement.idl new file mode 100644 index 00000000000..bc655e88500 --- /dev/null +++ b/mozilla/extensions/xforms/nsIXFormsCaseElement.idl @@ -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 + * Olli Pettay. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Olli Pettay (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +interface nsIDOMElement; + +/** + * Interface implemented by the case element. + */ +[uuid(7625097f-8bfb-4710-b74b-e4f5c0ffaaa5)] +interface nsIXFormsCaseElement : nsISupports +{ + /** + * Used by switch elements to mark case element selected or deselected. + */ + void setSelected(in boolean aEnable); +}; + + + diff --git a/mozilla/extensions/xforms/nsIXFormsSwitchElement.idl b/mozilla/extensions/xforms/nsIXFormsSwitchElement.idl new file mode 100644 index 00000000000..005532f7ccb --- /dev/null +++ b/mozilla/extensions/xforms/nsIXFormsSwitchElement.idl @@ -0,0 +1,59 @@ +/* -*- 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 + * Olli Pettay. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Olli Pettay (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +interface nsIDOMElement; + +/** + * Interface implemented by the switch element. + */ +[uuid(edeb1e48-5d5c-4091-8021-5975310caa66)] +interface nsIXFormsSwitchElement : nsISupports +{ + /** + * Used by case elements to select or deselect themselves. + * + * @param aCase The case element + * @param aValue The value of the selected attribute. + */ + void setSelected(in nsIDOMElement aCase, in boolean aValue); +}; + + + diff --git a/mozilla/extensions/xforms/nsXFormsAtoms.cpp b/mozilla/extensions/xforms/nsXFormsAtoms.cpp index 8d996902008..a229c9af1da 100644 --- a/mozilla/extensions/xforms/nsXFormsAtoms.cpp +++ b/mozilla/extensions/xforms/nsXFormsAtoms.cpp @@ -52,6 +52,7 @@ nsIAtom* nsXFormsAtoms::modelListProperty; nsIAtom *nsXFormsAtoms::ref; nsIAtom *nsXFormsAtoms::nodeset; nsIAtom *nsXFormsAtoms::model; +nsIAtom *nsXFormsAtoms::selected; const nsStaticAtom nsXFormsAtoms::Atoms_info[] = { { "src", &nsXFormsAtoms::src }, @@ -66,7 +67,8 @@ const nsStaticAtom nsXFormsAtoms::Atoms_info[] = { { "ModelListProperty", &nsXFormsAtoms::modelListProperty }, { "ref", &nsXFormsAtoms::ref }, { "nodeset", &nsXFormsAtoms::nodeset }, - { "model", &nsXFormsAtoms::model } + { "model", &nsXFormsAtoms::model }, + { "selected", &nsXFormsAtoms::selected } }; void diff --git a/mozilla/extensions/xforms/nsXFormsAtoms.h b/mozilla/extensions/xforms/nsXFormsAtoms.h index 75da483dd89..3a4eb22a74d 100644 --- a/mozilla/extensions/xforms/nsXFormsAtoms.h +++ b/mozilla/extensions/xforms/nsXFormsAtoms.h @@ -57,6 +57,7 @@ class nsXFormsAtoms static NS_HIDDEN_(nsIAtom *) ref; static NS_HIDDEN_(nsIAtom *) nodeset; static NS_HIDDEN_(nsIAtom *) model; + static NS_HIDDEN_(nsIAtom *) selected; static NS_HIDDEN_(void) InitAtoms(); diff --git a/mozilla/extensions/xforms/nsXFormsCaseElement.cpp b/mozilla/extensions/xforms/nsXFormsCaseElement.cpp new file mode 100644 index 00000000000..d7fefd05ae6 --- /dev/null +++ b/mozilla/extensions/xforms/nsXFormsCaseElement.cpp @@ -0,0 +1,185 @@ +/* -*- 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 + * Olli Pettay. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Olli Pettay (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsXFormsStubElement.h" +#include "nsXFormsAtoms.h" +#include "nsXFormsUtils.h" +#include "nsIXFormsSwitchElement.h" +#include "nsIXFormsCaseElement.h" +#include "nsIXTFXMLVisualWrapper.h" +#include "nsCOMPtr.h" +#include "nsIDOMElement.h" +#include "nsIDOMDocument.h" +#include "nsString.h" + +#define SELECTED_CASE_STYLE "" +#define DESELECTED_CASE_STYLE "visibility:hidden;width:0px;height:0px;" + +/** + * Implementation of the XForms case element. + */ + +class nsXFormsCaseElement : public nsIXFormsCaseElement, + public nsXFormsXMLVisualStub +{ +public: + nsXFormsCaseElement(); + + NS_DECL_ISUPPORTS_INHERITED + + NS_IMETHOD OnCreated(nsIXTFXMLVisualWrapper *aWrapper); + NS_IMETHOD OnDestroyed(); + NS_IMETHOD BeginAddingChildren(); + NS_IMETHOD DoneAddingChildren(); + + NS_IMETHOD GetVisualContent(nsIDOMElement **aContent); + NS_IMETHOD GetInsertionPoint(nsIDOMElement **aPoint); + + NS_IMETHOD AttributeSet(nsIAtom *aName, const nsAString &aNewValue); + + NS_DECL_NSIXFORMSCASEELEMENT + +private: + PRBool mDoneAddingChildren; + nsIDOMElement* mElement; + nsCOMPtr mVisual; +}; + +nsXFormsCaseElement::nsXFormsCaseElement() : mDoneAddingChildren(PR_TRUE) +{ +} + +NS_IMPL_ISUPPORTS_INHERITED1(nsXFormsCaseElement, + nsXFormsXMLVisualStub, + nsIXFormsCaseElement) + +NS_IMETHODIMP +nsXFormsCaseElement::OnCreated(nsIXTFXMLVisualWrapper *aWrapper) +{ + aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_ATTRIBUTE_SET | + nsIXTFElement::NOTIFY_BEGIN_ADDING_CHILDREN | + nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN); + + nsCOMPtr node; + aWrapper->GetElementNode(getter_AddRefs(node)); + mElement = node; + NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon"); + + nsCOMPtr domDoc; + mElement->GetOwnerDocument(getter_AddRefs(domDoc)); + + nsresult rv = domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XHTML), + NS_LITERAL_STRING("div"), + getter_AddRefs(mVisual)); + NS_ENSURE_SUCCESS(rv, rv); + mVisual->SetAttribute(NS_LITERAL_STRING("style"), + NS_LITERAL_STRING(DESELECTED_CASE_STYLE)); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsCaseElement::OnDestroyed() +{ + mElement = nsnull; + mVisual = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsCaseElement::GetVisualContent(nsIDOMElement **aContent) +{ + NS_ADDREF(*aContent = mVisual); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsCaseElement::GetInsertionPoint(nsIDOMElement **aPoint) +{ + NS_ADDREF(*aPoint = mVisual); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsCaseElement::BeginAddingChildren() +{ + mDoneAddingChildren = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsCaseElement::DoneAddingChildren() +{ + mDoneAddingChildren = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsCaseElement::AttributeSet(nsIAtom *aName, const nsAString &aNewValue) +{ + if (mDoneAddingChildren && mElement && aName == nsXFormsAtoms::selected) { + nsCOMPtr parent; + mElement->GetParentNode(getter_AddRefs(parent)); + nsCOMPtr switchEl(do_QueryInterface(parent)); + if (switchEl) + switchEl->SetSelected(mElement, aNewValue.EqualsLiteral("true")); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsCaseElement::SetSelected(PRBool aEnable) +{ + if (mVisual) { + mVisual->SetAttribute(NS_LITERAL_STRING("style"), + aEnable ? NS_LITERAL_STRING(SELECTED_CASE_STYLE) + : NS_LITERAL_STRING(DESELECTED_CASE_STYLE)); + } + return NS_OK; +} + +NS_HIDDEN_(nsresult) +NS_NewXFormsCaseElement(nsIXTFElement **aResult) +{ + *aResult = new nsXFormsCaseElement(); + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} diff --git a/mozilla/extensions/xforms/nsXFormsElementFactory.cpp b/mozilla/extensions/xforms/nsXFormsElementFactory.cpp index 54ae785b5c2..7e23cd90a82 100644 --- a/mozilla/extensions/xforms/nsXFormsElementFactory.cpp +++ b/mozilla/extensions/xforms/nsXFormsElementFactory.cpp @@ -73,6 +73,10 @@ NS_HIDDEN_(nsresult) NS_NewXFormsActionElement(nsIXTFElement **aResult); NS_HIDDEN_(nsresult) NS_NewXFormsLoadElement(nsIXTFElement **aResult); NS_HIDDEN_(nsresult) NS_NewXFormsSetValueElement(nsIXTFElement **aResult); +NS_HIDDEN_(nsresult) NS_NewXFormsToggleElement(nsIXTFElement **aResult); +NS_HIDDEN_(nsresult) NS_NewXFormsCaseElement(nsIXTFElement **aResult); +NS_HIDDEN_(nsresult) NS_NewXFormsSwitchElement(nsIXTFElement **aResult); + NS_IMPL_ISUPPORTS1(nsXFormsElementFactory, nsIXTFElementFactory) NS_IMETHODIMP @@ -129,6 +133,12 @@ nsXFormsElementFactory::CreateElement(const nsAString& aTagName, return NS_NewXFormsTriggerElement(aElement); if (aTagName.EqualsLiteral("submit")) return NS_NewXFormsSubmitElement(aElement); + if (aTagName.EqualsLiteral("toggle")) + return NS_NewXFormsToggleElement(aElement); + if (aTagName.EqualsLiteral("case")) + return NS_NewXFormsCaseElement(aElement); + if (aTagName.EqualsLiteral("switch")) + return NS_NewXFormsSwitchElement(aElement); *aElement = nsnull; return NS_ERROR_FAILURE; diff --git a/mozilla/extensions/xforms/nsXFormsSwitchElement.cpp b/mozilla/extensions/xforms/nsXFormsSwitchElement.cpp new file mode 100644 index 00000000000..e855804ed7a --- /dev/null +++ b/mozilla/extensions/xforms/nsXFormsSwitchElement.cpp @@ -0,0 +1,527 @@ +/* -*- 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 + * Olli Pettay. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Olli Pettay (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsCOMPtr.h" +#include "nsIDOMElement.h" +#include "nsIDOMDocument.h" +#include "nsString.h" +#include "nsIDocument.h" +#include "nsIDOMNodeList.h" + +// For focus control +#include "nsPIDOMWindow.h" +#include "nsIFocusController.h" +#include "nsIScriptGlobalObject.h" + +#include "nsIXTFXMLVisualWrapper.h" + +#include "nsXFormsAtoms.h" +#include "nsXFormsUtils.h" +#include "nsIXFormsControl.h" +#include "nsXFormsStubElement.h" +#include "nsIModelElementPrivate.h" +#include "nsIXFormsSwitchElement.h" +#include "nsIXFormsCaseElement.h" +#include "nsIXFormsContextControl.h" + +/** + * Implementation of the XForms \ element. + * + * @see http://www.w3.org/TR/xforms/slice9.html#id2631571 + * + * The implementation of the context control is based on + * nsXFormsGroupElement. + */ + +class nsXFormsSwitchElement : public nsXFormsXMLVisualStub, + public nsIXFormsSwitchElement, + public nsIXFormsContextControl, + public nsIXFormsControl +{ +public: + nsXFormsSwitchElement(); + + NS_DECL_ISUPPORTS_INHERITED + + NS_IMETHOD WillSetAttribute(nsIAtom *aName, const nsAString &aValue); + NS_IMETHOD AttributeSet(nsIAtom *aName, const nsAString &aValue); + NS_IMETHOD ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex); + NS_IMETHOD ChildAppended(nsIDOMNode *aChild); + NS_IMETHOD WillRemoveChild(PRUint32 aIndex); + NS_IMETHOD DoneAddingChildren(); + NS_IMETHOD OnDestroyed(); + + NS_IMETHOD OnCreated(nsIXTFXMLVisualWrapper *aWrapper); + NS_IMETHOD GetVisualContent(nsIDOMElement **aContent); + NS_IMETHOD GetInsertionPoint(nsIDOMElement **aPoint); + + NS_DECL_NSIXFORMSSWITCHELEMENT + + NS_DECL_NSIXFORMSCONTROL + + NS_DECL_NSIXFORMSCONTEXTCONTROL + +private: + /** + * http://www.w3.org/TR/xforms/slice9.html#ui-case + * "If multiple cases within a switch are marked as + * selected="true", the first selected case remains and all + * others are deselected. If none are selected, the first becomes selected." + * + * This method returns the first selected case. + */ + already_AddRefed + FindFirstSelectedCase(nsIDOMElement* aDeselected = nsnull); + + void Init(nsIDOMElement* aDeselected = nsnull); + + /** + * This is called when a case is added or removed dynamically. + */ + void CaseChanged(nsIDOMNode* aCase, PRBool aRemoved); + + /** + * Checks if any element in aDeselected has focus and if yes + * tries to focus first focusable element in aSelected. + */ + void SetFocus(nsIDOMElement* aDeselected, nsIDOMElement* aSelected); + + nsresult Process(); + + nsIDOMElement* mElement; + nsCOMPtr mVisual; + nsCOMPtr mSelected; + PRBool mDoneAddingChildren; + nsCOMPtr mContextNode; + nsString mModelID; +}; + +nsXFormsSwitchElement::nsXFormsSwitchElement() : mDoneAddingChildren(PR_FALSE) +{ +} + +NS_IMPL_ISUPPORTS_INHERITED3(nsXFormsSwitchElement, + nsXFormsXMLVisualStub, + nsIXFormsSwitchElement, + nsIXFormsControl, + nsIXFormsContextControl) + +NS_IMETHODIMP +nsXFormsSwitchElement::OnCreated(nsIXTFXMLVisualWrapper *aWrapper) +{ + aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN | + nsIXTFElement::NOTIFY_ATTRIBUTE_SET | + nsIXTFElement::NOTIFY_WILL_SET_ATTRIBUTE | + nsIXTFElement::NOTIFY_CHILD_APPENDED | + nsIXTFElement::NOTIFY_CHILD_INSERTED | + nsIXTFElement::NOTIFY_WILL_REMOVE_CHILD); + + nsCOMPtr node; + aWrapper->GetElementNode(getter_AddRefs(node)); + mElement = node; + NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon"); + + nsCOMPtr domDoc; + mElement->GetOwnerDocument(getter_AddRefs(domDoc)); + + nsresult rv = domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XHTML), + NS_LITERAL_STRING("div"), + getter_AddRefs(mVisual)); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSwitchElement::GetVisualContent(nsIDOMElement **aContent) +{ + NS_ADDREF(*aContent = mVisual); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSwitchElement::GetInsertionPoint(nsIDOMElement **aPoint) +{ + NS_ADDREF(*aPoint = mVisual); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSwitchElement::OnDestroyed() +{ + mVisual = nsnull; + mElement = nsnull; + mSelected = nsnull; + mContextNode = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSwitchElement::WillSetAttribute(nsIAtom *aName, const nsAString& aNewValue) +{ + if (aName == nsXFormsAtoms::bind || aName == nsXFormsAtoms::ref) { + nsCOMPtr modelNode = nsXFormsUtils::GetModel(mElement); + nsCOMPtr model = do_QueryInterface(modelNode); + if (model) + model->RemoveFormControl(this); + } + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSwitchElement::AttributeSet(nsIAtom *aName, const nsAString& aNewValue) +{ + if (aName == nsXFormsAtoms::bind || aName == nsXFormsAtoms::ref) { + Refresh(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSwitchElement::ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex) +{ + if (mDoneAddingChildren) + CaseChanged(aChild, PR_FALSE); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSwitchElement::ChildAppended(nsIDOMNode *aChild) +{ + if (mDoneAddingChildren) + CaseChanged(aChild, PR_FALSE); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSwitchElement::WillRemoveChild(PRUint32 aIndex) +{ + if (mDoneAddingChildren) { + nsCOMPtr list; + mElement->GetChildNodes(getter_AddRefs(list)); + if (list) { + nsCOMPtr child; + list->Item(aIndex, getter_AddRefs(child)); + CaseChanged(child, PR_TRUE); + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSwitchElement::DoneAddingChildren() +{ + if (!mElement) + return NS_OK; + + Init(); + mDoneAddingChildren = PR_TRUE; + Refresh(); + return NS_OK; +} + +nsresult +nsXFormsSwitchElement::Refresh() +{ + nsresult rv = NS_OK; + if (mDoneAddingChildren) { + rv = Process(); + } + return rv; +} + +nsresult +nsXFormsSwitchElement::Process() +{ + mModelID.Truncate(); + nsCOMPtr modelNode; + nsCOMPtr bindElement; + nsCOMPtr result = + nsXFormsUtils::EvaluateNodeBinding(mElement, + nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR, + NS_LITERAL_STRING("ref"), + EmptyString(), + nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE, + getter_AddRefs(modelNode), + getter_AddRefs(bindElement)); + + nsCOMPtr model = do_QueryInterface(modelNode); + + if (model) + model->AddFormControl(this); + + NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); + + // Get model ID + nsCOMPtr modelElement = do_QueryInterface(modelNode); + NS_ENSURE_TRUE(modelElement, NS_ERROR_FAILURE); + modelElement->GetAttribute(NS_LITERAL_STRING("id"), mModelID); + + // Get context node, if any + nsCOMPtr singleNode; + result->GetSingleNodeValue(getter_AddRefs(singleNode)); + mContextNode = do_QueryInterface(singleNode); + NS_ENSURE_TRUE(mContextNode, NS_ERROR_FAILURE); + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsSwitchElement::SetContextNode(nsIDOMElement *aContextNode) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +nsXFormsSwitchElement::GetContext(nsAString& aModelID, + nsIDOMElement **aContextNode, + PRInt32 *aContextPosition, + PRInt32 *aContextSize) +{ + NS_ENSURE_ARG(aContextSize); + NS_ENSURE_ARG(aContextPosition); + + /** @todo Not too elegant to call Process() here, but DoneAddingChildren is, + * logically, called on children before us. We need a notification + * that goes from the document node and DOWN, where the controls + * should Refresh(). + */ + *aContextPosition = 1; + *aContextSize = 1; + + nsresult rv = Process(); + NS_ENSURE_SUCCESS(rv, rv); + + NS_IF_ADDREF(*aContextNode = mContextNode); + aModelID = mModelID; + + return NS_OK; +} + +void +nsXFormsSwitchElement::Init(nsIDOMElement* aDeselected) +{ + nsCOMPtr firstCase = FindFirstSelectedCase(aDeselected); + mSelected = firstCase; + nsCOMPtr selected(do_QueryInterface(mSelected)); + if (selected) + selected->SetSelected(PR_TRUE); +} + +already_AddRefed +nsXFormsSwitchElement::FindFirstSelectedCase(nsIDOMElement* aDeselected) +{ + nsCOMPtr child; + mElement->GetFirstChild(getter_AddRefs(child)); + nsCOMPtr firstCase; + while (child) { + nsCOMPtr childElement(do_QueryInterface(child)); + if (childElement && childElement != aDeselected) { + if (nsXFormsUtils::IsXFormsElement(child, NS_LITERAL_STRING("case"))) { + if (!firstCase) + firstCase = childElement; + nsAutoString selected; + childElement->GetAttribute(NS_LITERAL_STRING("selected"), selected); + if (selected.EqualsLiteral("true")) { + firstCase = childElement; + break; + } + } + } + nsCOMPtr tmp; + child->GetNextSibling(getter_AddRefs(tmp)); + child.swap(tmp); + } + nsIDOMElement* result; + NS_IF_ADDREF(result = firstCase); + return result; +} + +NS_IMETHODIMP +nsXFormsSwitchElement::SetSelected(nsIDOMElement *aCase, PRBool aValue) +{ + if (!mElement) + return NS_OK; + + // There is no need to change the selected case, If we are trying to select + // a case which is already selected or if deselecting a case, + // which is not selected. + if (mSelected && + (((mSelected == aCase) && aValue) || ((mSelected != aCase) && !aValue))) + return NS_OK; + + // Swapping the status of the mSelected and aCase, dispatching events. + if (aValue && mSelected) { + nsCOMPtr oldSel = mSelected; + mSelected = aCase; + nsCOMPtr deselected(do_QueryInterface(oldSel)); + if (deselected) + deselected->SetSelected(PR_FALSE); + nsCOMPtr selected(do_QueryInterface(mSelected)); + if (selected) + selected->SetSelected(PR_TRUE); + SetFocus(oldSel, mSelected); + nsXFormsUtils::DispatchEvent(oldSel, eEvent_Deselect); + nsXFormsUtils::DispatchEvent(mSelected, eEvent_Select); + return NS_OK; + } + + nsCOMPtr firstCase = + FindFirstSelectedCase(aValue ? nsnull : aCase); + + // If a selectable case was found, bring it to front, set focus + // and dispatch events. + if (firstCase) { + mSelected = firstCase; + nsCOMPtr deselected(do_QueryInterface(aCase)); + if (deselected) + deselected->SetSelected(PR_FALSE); + nsCOMPtr selected(do_QueryInterface(mSelected)); + if (selected) + selected->SetSelected(PR_TRUE); + SetFocus(aCase, mSelected); + nsXFormsUtils::DispatchEvent(aCase, eEvent_Deselect); + nsXFormsUtils::DispatchEvent(mSelected, eEvent_Select); + } + + // If there is only one case, we (re)select it. No need to + // set focus. + else { + mSelected = aCase; + nsCOMPtr selected(do_QueryInterface(mSelected)); + if (selected) + selected->SetSelected(PR_TRUE); + nsXFormsUtils::DispatchEvent(mSelected, eEvent_Deselect); + nsXFormsUtils::DispatchEvent(mSelected, eEvent_Select); + } + return NS_OK; +} + +void +nsXFormsSwitchElement::SetFocus(nsIDOMElement* aDeselected, + nsIDOMElement* aSelected) +{ + if (aDeselected == aSelected) + return; + + nsCOMPtr domDoc; + mElement->GetOwnerDocument(getter_AddRefs(domDoc)); + nsCOMPtr doc(do_QueryInterface(domDoc)); + if (!doc) + return; + + nsCOMPtr win = do_QueryInterface(doc->GetScriptGlobalObject()); + if (!win) + return; + + nsIFocusController *focusController = win->GetRootFocusController(); + if (!focusController) + return; + + nsCOMPtr focused; + focusController->GetFocusedElement(getter_AddRefs(focused)); + if (!focused) + return; + + PRBool hasFocus = PR_FALSE; + nsCOMPtr current(do_QueryInterface(focused)); + do { + nsCOMPtr currentElement(do_QueryInterface(current)); + if (aDeselected == current) { + hasFocus = PR_TRUE; + break; + } + nsCOMPtr parent; + current->GetParentNode(getter_AddRefs(parent)); + current.swap(parent); + } while(current); + + if (hasFocus) { + // Flush layout before moving focus. Otherwise focus controller might + // (re)focus something in the deselected . + doc->FlushPendingNotifications(Flush_Layout); + focusController->MoveFocus(PR_TRUE, mElement); + } +} + +void +nsXFormsSwitchElement::CaseChanged(nsIDOMNode* aCase, PRBool aRemoved) +{ + if (!aCase) + return; + + if (aRemoved) { + if (aCase == mSelected) { + nsXFormsUtils::DispatchEvent(mSelected, eEvent_Deselect); + nsCOMPtr el(do_QueryInterface(aCase)); + Init(el); + if (mSelected) + nsXFormsUtils::DispatchEvent(mSelected, eEvent_Select); + } + return; + } + + nsCOMPtr element(do_QueryInterface(aCase)); + if (!element) + return; + + if (!mSelected) { + Init(); + if (mSelected) + nsXFormsUtils::DispatchEvent(mSelected, eEvent_Select); + return; + } + + if (!nsXFormsUtils::IsXFormsElement(aCase, NS_LITERAL_STRING("case"))) + return; + + nsAutoString sel; + element->GetAttribute(NS_LITERAL_STRING("selected"), sel); + if (sel.EqualsLiteral("true")) + SetSelected(element, PR_TRUE); +} + +NS_HIDDEN_(nsresult) +NS_NewXFormsSwitchElement(nsIXTFElement **aResult) +{ + *aResult = new nsXFormsSwitchElement(); + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} diff --git a/mozilla/extensions/xforms/nsXFormsToggleElement.cpp b/mozilla/extensions/xforms/nsXFormsToggleElement.cpp new file mode 100644 index 00000000000..6556489b844 --- /dev/null +++ b/mozilla/extensions/xforms/nsXFormsToggleElement.cpp @@ -0,0 +1,99 @@ +/* -*- 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 + * Olli Pettay. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Olli Pettay (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsXFormsActionModuleBase.h" +#include "nsIDOMDocument.h" +#include "nsIDOMEvent.h" +#include "nsIDOMElement.h" +#include "nsString.h" +#include "nsXFormsUtils.h" +#include "nsIXFormsSwitchElement.h" + +/** + * XForms Toggle element. According to the the XForms Schema it can be handled + * like Action Module elements. + */ +class nsXFormsToggleElement : public nsXFormsActionModuleBase +{ +public: + NS_DECL_NSIXFORMSACTIONMODULEELEMENT +}; + +NS_IMETHODIMP +nsXFormsToggleElement::HandleAction(nsIDOMEvent* aEvent, + nsIXFormsActionElement *aParentAction) +{ + if (!mElement) + return NS_OK; + + nsAutoString caseAttr; + mElement->GetAttribute(NS_LITERAL_STRING("case"), caseAttr); + if (caseAttr.IsEmpty()) + return NS_OK; + + nsCOMPtr doc; + mElement->GetOwnerDocument(getter_AddRefs(doc)); + if (!doc) + return NS_OK; + + nsCOMPtr caseEl; + doc->GetElementById(caseAttr, getter_AddRefs(caseEl)); + if (!caseEl) + return NS_OK; + + if (!nsXFormsUtils::IsXFormsElement(caseEl, NS_LITERAL_STRING("case"))) + return NS_OK; + + nsCOMPtr parent; + caseEl->GetParentNode(getter_AddRefs(parent)); + nsCOMPtr switchEl(do_QueryInterface(parent)); + if (switchEl) + switchEl->SetSelected(caseEl, PR_TRUE); + return NS_OK; +} + +NS_HIDDEN_(nsresult) +NS_NewXFormsToggleElement(nsIXTFElement **aResult) +{ + *aResult = new nsXFormsToggleElement(); + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} diff --git a/mozilla/extensions/xforms/nsXFormsUtils.cpp b/mozilla/extensions/xforms/nsXFormsUtils.cpp index f0935625343..4fa884d2618 100644 --- a/mozilla/extensions/xforms/nsXFormsUtils.cpp +++ b/mozilla/extensions/xforms/nsXFormsUtils.cpp @@ -866,3 +866,23 @@ nsXFormsUtils::CheckSameOrigin(nsIURI *aBaseURI, nsIURI *aTestURI) return PR_FALSE; } + +/*static*/ PRBool +nsXFormsUtils::IsXFormsElement(nsIDOMNode* aNode, const nsAString& aName) +{ + if (aNode) { + PRUint16 nodeType; + aNode->GetNodeType(&nodeType); + if (nodeType == nsIDOMNode::ELEMENT_NODE) { + nsAutoString name; + aNode->GetLocalName(name); + if (name.Equals(aName)) { + nsAutoString ns; + aNode->GetNamespaceURI(ns); + if (ns.EqualsLiteral(NS_NAMESPACE_XFORMS)) + return PR_TRUE; + } + } + } + return PR_FALSE; +} diff --git a/mozilla/extensions/xforms/nsXFormsUtils.h b/mozilla/extensions/xforms/nsXFormsUtils.h index 4bfdb858167..b011c106c10 100644 --- a/mozilla/extensions/xforms/nsXFormsUtils.h +++ b/mozilla/extensions/xforms/nsXFormsUtils.h @@ -298,6 +298,13 @@ public: */ static NS_HIDDEN_(PRBool) CheckSameOrigin(nsIURI *aBaseURI, nsIURI *aTestURI); + + /** + * @return true if aNode is element, its namespace URI is + * "http://www.w3.org/2002/xforms" and its name is aName. + */ + static NS_HIDDEN_(PRBool) IsXFormsElement(nsIDOMNode* aNode, + const nsAString& aName); }; #endif