[XForms] Make binds static. Bug 307421, r=aaronr+smaug

This commit is contained in:
allan%beaufour.dk 2006-02-23 11:25:07 +00:00
parent 426f83aecc
commit 32a42eb82c
15 changed files with 182 additions and 102 deletions

View File

@ -49,7 +49,7 @@ interface nsIDOMElement;
/**
* Interface implemented by all XForms form control classes.
*/
[uuid(8377c845-5d55-4eee-9a76-0f86751dcbc8)]
[uuid(1bea8750-2133-4a00-986a-04e5cbd129b2)]
interface nsIXFormsControl : nsIXFormsContextControl
{
/**
@ -86,4 +86,10 @@ interface nsIXFormsControl : nsIXFormsContextControl
* need to override IsEventTarget() and return PR_FALSE
*/
boolean isEventTarget();
/**
* Is true when the control is getting its instance data node binding from a
* model bind element, ie. it has a |bind| attribute.
*/
readonly attribute boolean usesModelBinding;
};

View File

@ -146,6 +146,7 @@ nsXFormsControlStubBase::ResetBoundNode(const nsString &aBindAttribute,
{
// Clear existing bound node, etc.
mBoundNode = nsnull;
mUsesModelBinding = PR_FALSE;
mDependencies.Clear();
RemoveIndexListeners();
@ -166,8 +167,13 @@ nsXFormsControlStubBase::ResetBoundNode(const nsString &aBindAttribute,
if (!result)
return NS_OK;
// Get context node, if any
result->GetSingleNodeValue(getter_AddRefs(mBoundNode));
// Get context node, if any
if (mUsesModelBinding) {
// When bound via @bind, we'll get a snapshot back
result->SnapshotItem(0, getter_AddRefs(mBoundNode));
} else {
result->GetSingleNodeValue(getter_AddRefs(mBoundNode));
}
if (mBoundNode && mModel) {
mModel->SetStates(this, mBoundNode);
@ -212,7 +218,13 @@ nsXFormsControlStubBase::IsEventTarget(PRBool *aOK)
*aOK = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsXFormsControlStubBase::GetUsesModelBinding(PRBool *aRes)
{
*aRes = mUsesModelBinding;
return NS_OK;
}
nsresult
nsXFormsControlStubBase::ProcessNodeBinding(const nsString &aBindingAttr,
@ -237,6 +249,7 @@ nsXFormsControlStubBase::ProcessNodeBinding(const nsString &aBindingAtt
}
nsresult rv;
PRBool usesModelBinding;
rv = nsXFormsUtils::EvaluateNodeBinding(mElement,
kElementFlags,
aBindingAttr,
@ -244,6 +257,7 @@ nsXFormsControlStubBase::ProcessNodeBinding(const nsString &aBindingAtt
aResultType,
getter_AddRefs(mModel),
aResult,
&usesModelBinding,
&mDependencies,
&indexesUsed);
NS_ENSURE_STATE(mModel);
@ -251,10 +265,11 @@ nsXFormsControlStubBase::ProcessNodeBinding(const nsString &aBindingAtt
mModel->AddFormControl(this);
if (aModel)
NS_ADDREF(*aModel = mModel);
mUsesModelBinding = usesModelBinding;
if (NS_SUCCEEDED(rv) && indexesUsed.Count()) {
// add index listeners on repeat elements
for (PRInt32 i = 0; i < indexesUsed.Count(); ++i) {
// Find the repeat element and add |this| as a listener
nsCOMPtr<nsIDOMElement> repElem;
@ -471,6 +486,7 @@ nsXFormsControlStubBase::OnDestroyed()
{
ResetHelpAndHint(PR_FALSE);
RemoveIndexListeners();
mDependencies.Clear();
if (mModel) {
mModel->RemoveFormControl(this);

View File

@ -89,6 +89,7 @@ public:
NS_IMETHOD Bind();
NS_IMETHOD TryFocus(PRBool* aOK);
NS_IMETHOD IsEventTarget(PRBool *aOK);
NS_IMETHOD GetUsesModelBinding(PRBool *aRes);
nsresult Create(nsIXTFElementWrapper *aWrapper);
// for nsIXTFElement
@ -140,6 +141,7 @@ public:
kElementFlags(nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR),
mHasParent(PR_FALSE),
mPreventLoop(PR_FALSE),
mUsesModelBinding(PR_FALSE),
mBindAttrsCount(0)
{};
@ -160,12 +162,17 @@ protected:
nsCOMPtr<nsIDOMEventListener> mEventListener;
/** State that tells whether control has a parent or not */
PRBool mHasParent;
PRPackedBool mHasParent;
/** State to prevent infinite loop when generating and handling xforms-next
* and xforms-previous events
*/
PRBool mPreventLoop;
PRPackedBool mPreventLoop;
/**
* Does the control use a model bind? That is, does it have a @bind.
*/
PRPackedBool mUsesModelBinding;
/**
* Array of repeat-elements of which the control uses repeat-index.

View File

@ -157,24 +157,15 @@ nsXFormsCopyElement::GetCopyNode(nsIDOMNode **aNode)
NS_ENSURE_ARG_POINTER(aNode);
*aNode = nsnull;
nsCOMPtr<nsIDOMNode> singleNode;
nsCOMPtr<nsIModelElementPrivate> model;
nsCOMPtr<nsIDOMXPathResult> result;
nsresult rv =
nsXFormsUtils::EvaluateNodeBinding(mElement,
nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
NS_LITERAL_STRING("ref"), EmptyString(),
nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE,
getter_AddRefs(model),
getter_AddRefs(result));
PRBool succeeded =
nsXFormsUtils::GetSingleNodeBinding(mElement,
getter_AddRefs(singleNode),
getter_AddRefs(model));
NS_ENSURE_SUCCESS(rv, rv);
if (result) {
nsCOMPtr<nsIDOMNode> singleNode;
result->GetSingleNodeValue(getter_AddRefs(singleNode));
NS_IF_ADDREF(*aNode = singleNode);
}
if (succeeded && singleNode)
NS_ADDREF(*aNode = singleNode);
return NS_OK;
}

View File

@ -97,13 +97,15 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent,
nsresult rv;
nsCOMPtr<nsIModelElementPrivate> model;
nsCOMPtr<nsIDOMXPathResult> nodeset;
PRBool usesModelBinding;
rv = nsXFormsUtils::EvaluateNodeBinding(mElement,
nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
NS_LITERAL_STRING("nodeset"),
EmptyString(),
nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
getter_AddRefs(model),
getter_AddRefs(nodeset));
getter_AddRefs(nodeset),
&usesModelBinding);
NS_ENSURE_SUCCESS(rv, rv);
if (!model || !nodeset)

View File

@ -169,6 +169,14 @@ GetModelList(nsIDOMDocument *domDoc)
doc->GetProperty(nsXFormsAtoms::modelListProperty));
}
static void
SupportsDtorFunc(void *aObject, nsIAtom *aPropertyName,
void *aPropertyValue, void *aData)
{
nsISupports *propertyValue = NS_STATIC_CAST(nsISupports*, aPropertyValue);
NS_IF_RELEASE(propertyValue);
}
//------------------------------------------------------------------------------
static const nsIID sScriptingIIDs[] = {
@ -650,7 +658,11 @@ nsXFormsModelElement::Rebuild()
mNodeToType.Clear();
mNodeToP3PType.Clear();
// 2. Re-attach all elements
// 2. Process bind elements
rv = ProcessBindElements();
NS_ENSURE_SUCCESS(rv, rv);
// 3. Re-attach all elements
if (mDocumentLoaded) { // if it's not during initializing phase
// Copy the form control list as it stands right now.
nsVoidArray *oldFormList = new nsVoidArray();
@ -685,10 +697,7 @@ nsXFormsModelElement::Rebuild()
mNeedsRefresh = PR_TRUE;
}
// 3. Rebuild graph
rv = ProcessBindElements();
NS_ENSURE_SUCCESS(rv, rv);
// 4. Rebuild graph
return mMDG.Rebuild();
}
@ -823,12 +832,22 @@ nsXFormsModelElement::Refresh()
if (mNeedsRefresh) {
refresh = PR_TRUE;
} else {
// Get dependencies
nsCOMArray<nsIDOMNode> *deps = nsnull;
control->GetDependencies(&deps);
PRBool usesModelBinding = PR_FALSE;
control->GetUsesModelBinding(&usesModelBinding);
#ifdef DEBUG_MODEL
nsCOMArray<nsIDOMNode> *deps = nsnull;
if (usesModelBinding) {
if (!boundNode)
// If a control uses a model binding, but has no bound node a
// rebuild is the only thing that'll (eventually) change it
continue;
} else {
// Get dependencies
control->GetDependencies(&deps);
}
PRUint32 depCount = deps ? deps->Count() : 0;
#ifdef DEBUG_MODEL
nsCOMPtr<nsIDOMElement> controlElement;
control->GetElement(getter_AddRefs(controlElement));
if (controlElement) {
@ -864,16 +883,25 @@ nsXFormsModelElement::Refresh()
// control (get updated node value from the bound node)
if (!refresh && boundNode) {
curChanged->IsSameNode(boundNode, &refresh);
if (refresh)
// We need to refresh the control. We cannot break out of the loop
// as we need to check dependencies
// Two ways to go here. Keep in mind that controls using model
// binding expressions never needs to have dependencies checked as
// they only rebind on xforms-rebuild
if (refresh && usesModelBinding) {
// 1) If the control needs a refresh, and uses model bindings,
// we can stop checking here
break;
}
if (refresh || usesModelBinding) {
// 2) If either the control needs a refresh or it uses a model
// binding we can continue to next changed node
continue;
}
}
// Check whether any dependencies are dirty. If so, we need to rebind
// the control (re-evaluate it's binding expression)
for (PRInt32 k = 0; k < deps->Count(); ++k) {
for (PRUint32 k = 0; k < depCount; ++k) {
/// @note beaufour: I'm not to happy about this ...
/// O(mChangedNodes.Count() * deps->Count()), but using the pointers
/// for sorting and comparing does not work...
@ -1378,7 +1406,8 @@ nsXFormsModelElement::ProcessBindElements()
child->GetNamespaceURI(namespaceURI);
if (namespaceURI.EqualsLiteral(NS_NAMESPACE_XFORMS)) {
rv = ProcessBind(xpath, firstInstanceRoot, 1, 1,
nsCOMPtr<nsIDOMElement>(do_QueryInterface(child)));
nsCOMPtr<nsIDOMElement>(do_QueryInterface(child)),
PR_TRUE);
if (NS_FAILED(rv)) {
return NS_OK;
}
@ -1575,7 +1604,8 @@ nsXFormsModelElement::ProcessBind(nsIXFormsXPathEvaluator *aEvaluator,
nsIDOMNode *aContextNode,
PRInt32 aContextPosition,
PRInt32 aContextSize,
nsIDOMElement *aBindElement)
nsIDOMElement *aBindElement,
PRBool aIsOuter)
{
// Get the model item properties specified by this \<bind\>.
nsCOMPtr<nsIDOMNSXPathExpression> props[eModel__count];
@ -1626,6 +1656,19 @@ nsXFormsModelElement::ProcessBind(nsIXFormsXPathEvaluator *aEvaluator,
}
NS_ENSURE_STATE(result);
// If this is an outer bind, store the nodeset, as controls binding to this
// bind will need this.
if (aIsOuter) {
nsCOMPtr<nsIContent> content(do_QueryInterface(aBindElement));
NS_ASSERTION(content, "nsIDOMElement not implementing nsIContent?!");
rv = content->SetProperty(nsXFormsAtoms::bind, result,
SupportsDtorFunc);
NS_ENSURE_SUCCESS(rv, rv);
// addref, circumventing nsDerivedSave
NS_ADDREF(NS_STATIC_CAST(nsIDOMXPathResult*, result));
}
PRUint32 snapLen;
rv = result->GetSnapshotLength(&snapLen);

View File

@ -196,7 +196,8 @@ private:
nsIDOMNode *aContextNode,
PRInt32 aContextPosition,
PRInt32 aContextSize,
nsIDOMElement *aBindElement);
nsIDOMElement *aBindElement,
PRBool aIsOuter = PR_FALSE);
NS_HIDDEN_(void) RemoveModelFromDocument();

View File

@ -124,7 +124,12 @@ nsXFormsOutputElement::Bind()
}
if (result) {
result->GetSingleNodeValue(getter_AddRefs(mBoundNode));
if (mUsesModelBinding) {
// When bound via @bind, we'll get a snapshot back
result->SnapshotItem(0, getter_AddRefs(mBoundNode));
} else {
result->GetSingleNodeValue(getter_AddRefs(mBoundNode));
}
}
if (mBoundNode && mModel) {

View File

@ -91,13 +91,15 @@ nsXFormsSetIndexElement::HandleAction(nsIDOMEvent *aEvent,
nsCOMPtr<nsIDOMXPathResult> result;
// EvaluateNodeBinding uses @bind if @index is not present, but we have
// checked that @index is present above
PRBool usesModelBinding;
rv = nsXFormsUtils::EvaluateNodeBinding(mElement,
0,
indexStr,
EmptyString(),
nsIDOMXPathResult::NUMBER_TYPE,
getter_AddRefs(model),
getter_AddRefs(result));
getter_AddRefs(result),
&usesModelBinding);
NS_ENSURE_SUCCESS(rv, rv);
if (!result) {
nsXFormsUtils::ReportError(NS_LITERAL_STRING("indexEvalError"),

View File

@ -66,23 +66,13 @@ nsXFormsSetValueElement::HandleAction(nsIDOMEvent* aEvent,
return NS_OK;
nsCOMPtr<nsIModelElementPrivate> modelPriv;
nsCOMPtr<nsIDOMXPathResult> result;
nsresult rv =
nsXFormsUtils:: EvaluateNodeBinding(mElement,
nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
NS_LITERAL_STRING("ref"),
EmptyString(),
nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE,
getter_AddRefs(modelPriv),
getter_AddRefs(result));
NS_ENSURE_SUCCESS(rv, rv);
if (!result | !modelPriv)
return NS_OK;
nsCOMPtr<nsIDOMNode> singleNode;
result->GetSingleNodeValue(getter_AddRefs(singleNode));
if (!singleNode)
PRBool succeeded =
nsXFormsUtils::GetSingleNodeBinding(mElement,
getter_AddRefs(singleNode),
getter_AddRefs(modelPriv));
if (!succeeded | !modelPriv | !singleNode)
return NS_OK;
nsAutoString value;
@ -105,7 +95,8 @@ nsXFormsSetValueElement::HandleAction(nsIDOMEvent* aEvent,
}
PRBool changed;
rv = modelPriv->SetNodeValue(singleNode, value, &changed);
nsresult rv =
modelPriv->SetNodeValue(singleNode, value, &changed);
NS_ENSURE_SUCCESS(rv, rv);
if (changed) {

View File

@ -665,18 +665,21 @@ nsXFormsSubmissionElement::GetBoundInstanceData(nsIDOMNode **result)
{
nsCOMPtr<nsIModelElementPrivate> model;
nsCOMPtr<nsIDOMXPathResult> xpRes;
PRBool usesModelBind;
nsresult rv =
nsXFormsUtils::EvaluateNodeBinding(mElement, 0,
NS_LITERAL_STRING("ref"),
NS_LITERAL_STRING("/"),
nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE,
getter_AddRefs(model),
getter_AddRefs(xpRes));
getter_AddRefs(xpRes),
&usesModelBind);
if (NS_FAILED(rv) || !xpRes)
return NS_ERROR_UNEXPECTED;
return xpRes->GetSingleNodeValue(result);
return usesModelBind ? xpRes->SnapshotItem(0, result)
: xpRes->GetSingleNodeValue(result);
}
PRBool

View File

@ -470,15 +470,17 @@ nsXFormsUtils::EvaluateNodeBinding(nsIDOMElement *aElement,
PRUint16 aResultType,
nsIModelElementPrivate **aModel,
nsIDOMXPathResult **aResult,
PRBool *aUsesModelBind,
nsCOMArray<nsIDOMNode> *aDeps,
nsStringArray *aIndexesUsed)
{
if (!aElement || !aModel || !aResult) {
return NS_OK;
if (!aElement || !aModel || !aResult || !aUsesModelBind) {
return NS_ERROR_FAILURE;
}
*aModel = nsnull;
*aResult = nsnull;
*aUsesModelBind = PR_FALSE;
nsCOMPtr<nsIDOMNode> contextNode;
nsCOMPtr<nsIDOMElement> bindElement;
@ -500,30 +502,44 @@ nsXFormsUtils::EvaluateNodeBinding(nsIDOMElement *aElement,
return NS_OK; // this will happen if the doc is still loading
}
nsAutoString expr;
////////////////////
// STEP 1: Handle @bind'ings
if (bindElement) {
if (!outerBind) {
if (outerBind) {
// If there is an outer bind element, we retrieve its nodeset.
nsCOMPtr<nsIContent> content(do_QueryInterface(bindElement));
NS_ASSERTION(content, "nsIDOMElement not implementing nsIContent?!");
NS_IF_ADDREF(*aResult =
NS_STATIC_CAST(nsIDOMXPathResult*,
content->GetProperty(nsXFormsAtoms::bind)));
*aUsesModelBind = PR_TRUE;
} else {
// References to inner binds are not defined.
// "When you refer to @id on a nested bind it returns an emtpy nodeset
// because it has no meaning. The XForms WG will assign meaning in the
// future."
// @see http://www.w3.org/MarkUp/Group/2004/11/f2f/2004Nov11#resolution6
nsXFormsUtils::ReportError(NS_LITERAL_STRING("innerBindRefError"),
aElement);
}
return NS_OK;
}
////////////////////
// STEP 2: No bind attribute
// If there's no bind attribute, we expect there to be a |aBindingAttr|
// attribute.
nsAutoString expr;
aElement->GetAttribute(aBindingAttr, expr);
if (expr.IsEmpty()) {
// if there's no default binding, bail out
if (aDefaultRef.IsEmpty())
return NS_OK;
} else {
// If there is a (outer) bind element, we retrieve its nodeset.
bindElement->GetAttribute(NS_LITERAL_STRING("nodeset"), expr);
}
} else {
// If there's no bind element, we expect there to be a |aBindingAttr| attribute.
aElement->GetAttribute(aBindingAttr, expr);
if (expr.IsEmpty())
{
if (aDefaultRef.IsEmpty())
return NS_OK;
expr.Assign(aDefaultRef);
}
expr.Assign(aDefaultRef);
}
// Evaluate |expr|
@ -536,6 +552,9 @@ nsXFormsUtils::EvaluateNodeBinding(nsIDOMElement *aElement,
aDeps,
aIndexesUsed);
////////////////////
// STEP 3: Check for lazy binding
// If the evaluation failed because the node wasn't there and we should be
// lazy authoring, then create it (if the situation qualifies, of course).
// Novell only allows lazy authoring of single bound nodes.
@ -769,6 +788,7 @@ nsXFormsUtils::GetSingleNodeBinding(nsIDOMElement* aElement,
return PR_FALSE;
nsCOMPtr<nsIModelElementPrivate> model;
nsCOMPtr<nsIDOMXPathResult> result;
PRBool usesModelBind;
nsresult rv = EvaluateNodeBinding(aElement,
nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
@ -776,13 +796,18 @@ nsXFormsUtils::GetSingleNodeBinding(nsIDOMElement* aElement,
EmptyString(),
nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE,
getter_AddRefs(model),
getter_AddRefs(result));
getter_AddRefs(result),
&usesModelBind);
if (NS_FAILED(rv) || !result)
return PR_FALSE;
nsCOMPtr<nsIDOMNode> singleNode;
result->GetSingleNodeValue(getter_AddRefs(singleNode));
if (usesModelBind) {
result->SnapshotItem(0, getter_AddRefs(singleNode));
} else {
result->GetSingleNodeValue(getter_AddRefs(singleNode));
}
if (!singleNode)
return PR_FALSE;

View File

@ -221,6 +221,7 @@ public:
PRUint16 aResultType,
nsIModelElementPrivate **aModel,
nsIDOMXPathResult **aResult,
PRBool *aUsesModelBind,
nsCOMArray<nsIDOMNode> *aDeps = nsnull,
nsStringArray *aIndexesUsed = nsnull);

View File

@ -94,26 +94,12 @@ nsXFormsValueElement::OnCreated(nsIXTFGenericElementWrapper *aWrapper)
NS_IMETHODIMP
nsXFormsValueElement::GetValue(nsAString &aValue)
{
nsCOMPtr<nsIModelElementPrivate> model;
nsCOMPtr<nsIDOMXPathResult> result;
nsXFormsUtils::EvaluateNodeBinding(mElement,
nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
NS_LITERAL_STRING("ref"),
EmptyString(),
nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE,
getter_AddRefs(model),
getter_AddRefs(result));
if (result) {
nsCOMPtr<nsIDOMNode> singleNode;
result->GetSingleNodeValue(getter_AddRefs(singleNode));
if (singleNode) {
nsString value;
nsXFormsUtils::GetNodeValue(singleNode, value);
aValue.Assign(value);
return NS_OK;
}
nsString value;
PRBool singleNodeVal =
nsXFormsUtils::GetSingleNodeBindingValue(mElement, value);
if (singleNodeVal) {
aValue.Assign(value);
return NS_OK;
}
nsCOMPtr<nsIDOM3Node> node3 = do_QueryInterface(mElement);

View File

@ -67,6 +67,7 @@ uploadBoundTypeError = XForms Error (28): Upload element not bound to valid data
copyError = XForms Error (29): A copy element was found whose parent is not an itemset element
submitMailtoInit = XForms Error (30): No mailto: handler found
submitMailtoFailed = XForms Error (31): Failed to load a mail client for a mailto: submission
innerBindRefError = XForms Error (32): You are refering to an inner bind, which is undefined in XForms 1.0
# Warning Messages:
warnSOAP = XForms Warning (1): You are using the SOAP post feature, which is an experimental feature! Beware that the functionality might change, and forms may stop working at any time.