Let controls using index() listen for changes to repeat indexes. Bug 289534, r=smaug+doronr, a=mkaply, NPOTB

This commit is contained in:
allan%beaufour.dk 2005-04-28 07:17:49 +00:00
Родитель 2d9c8da55d
Коммит c553b66916
11 изменённых файлов: 246 добавлений и 135 удалений

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

@ -43,6 +43,8 @@
#include "nsISupports.idl"
interface nsIXFormsControl;
[uuid(419e780d-4f31-4aa4-bae8-a18099d77bb6)]
interface nsIXFormsRepeatElement : nsISupports
{
@ -88,4 +90,20 @@ interface nsIXFormsRepeatElement : nsISupports
*/
void setCurrentRepeat(in nsIXFormsRepeatElement aCurrentRepeat,
in unsigned long aIndex);
/**
* Add user of repeat-index. The control will have Bind() and Refresh()
* called on it, when the index changes.
*/
void addIndexUser(in nsIXFormsControl aControl);
/**
* Remove user of repeat-index.
*/
void removeIndexUser(in nsIXFormsControl aControl);
/**
* Informs repeat-index users of index change.
*/
void indexHasChanged();
};

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

@ -41,6 +41,7 @@
#include "nsXFormsControlStub.h"
#include "nsXFormsMDGEngine.h"
#include "nsIDOMDocument.h"
#include "nsIDOMEvent.h"
#include "nsIDOMKeyEvent.h"
#include "nsIDOMEventTarget.h"
@ -106,10 +107,27 @@ nsXFormsControlStub::GetElement(nsIDOMElement **aElement)
return NS_OK;
}
void
nsXFormsControlStub::RemoveIndexListeners()
{
if (!mIndexesUsed.Count())
return;
for (PRInt32 i = 0; i < mIndexesUsed.Count(); ++i) {
nsCOMPtr<nsIXFormsRepeatElement> rep = mIndexesUsed[i];
rep->RemoveIndexUser(this);
}
mIndexesUsed.Clear();
}
NS_IMETHODIMP
nsXFormsControlStub::ResetBoundNode()
{
// Clear existing bound node, etc.
mBoundNode = nsnull;
mDependencies.Clear();
RemoveIndexListeners();
if (!mHasParent || !mBindAttrsCount)
return NS_OK;
@ -157,7 +175,7 @@ nsXFormsControlStub::ProcessNodeBinding(const nsString &aBindingAttr,
nsIDOMXPathResult **aResult,
nsIModelElementPrivate **aModel)
{
mDependencies.Clear();
nsStringArray indexesUsed;
nsresult rv;
rv = nsXFormsUtils::EvaluateNodeBinding(mElement,
@ -167,12 +185,32 @@ nsXFormsControlStub::ProcessNodeBinding(const nsString &aBindingAttr,
aResultType,
getter_AddRefs(mModel),
aResult,
&mDependencies);
&mDependencies,
&indexesUsed);
NS_ENSURE_STATE(mModel);
mModel->AddFormControl(this);
if (aModel)
NS_ADDREF(*aModel = mModel);
if (mModel) {
mModel->AddFormControl(this);
if (aModel) {
NS_ADDREF(*aModel = mModel);
if (NS_SUCCEEDED(rv) && indexesUsed.Count()) {
// add index listeners on repeat elements
nsCOMPtr<nsIDOMDocument> doc;
mElement->GetOwnerDocument(getter_AddRefs(doc));
NS_ENSURE_STATE(doc);
for (PRInt32 i = 0; i < indexesUsed.Count(); ++i) {
// Find the repeat element and add |this| as a listener
nsCOMPtr<nsIDOMElement> repElem;
doc->GetElementById(*(indexesUsed[i]), getter_AddRefs(repElem));
nsCOMPtr<nsIXFormsRepeatElement> rep(do_QueryInterface(repElem));
NS_ENSURE_STATE(rep);
rv = rep->AddIndexUser(this);
if (NS_FAILED(rv)) {
return rv;
}
rv = mIndexesUsed.AppendObject(rep);
NS_ENSURE_SUCCESS(rv, rv);
}
}
@ -314,6 +352,7 @@ NS_IMETHODIMP
nsXFormsControlStub::OnDestroyed()
{
ResetHelpAndHint(PR_FALSE);
RemoveIndexListeners();
if (mModel) {
mModel->RemoveFormControl(this);

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

@ -49,6 +49,7 @@
#include "nsIModelElementPrivate.h"
#include "nsIXFormsControl.h"
#include "nsIXFormsRepeatElement.h"
#include "nsXFormsStubElement.h"
#include "nsXFormsUtils.h"
@ -143,22 +144,27 @@ public:
protected:
/** The nsIXTFXMLVisualWrapper */
nsCOMPtr<nsIDOMElement> mElement;
nsCOMPtr<nsIDOMElement> mElement;
/** The node that the control is bound to. */
nsCOMPtr<nsIDOMNode> mBoundNode;
nsCOMPtr<nsIDOMNode> mBoundNode;
/** Array of nsIDOMNodes that the control depends on. */
nsCOMArray<nsIDOMNode> mDependencies;
nsCOMArray<nsIDOMNode> mDependencies;
/** The model for the control */
nsCOMPtr<nsIModelElementPrivate> mModel;
nsCOMPtr<nsIModelElementPrivate> mModel;
/** This event listener is used to create xforms-hint and xforms-help events. */
nsCOMPtr<nsIDOMEventListener> mEventListener;
nsCOMPtr<nsIDOMEventListener> mEventListener;
/** State that tells whether control has a parent or not */
PRBool mHasParent;
PRBool mHasParent;
/**
* Array of repeat-elements of which the control uses repeat-index.
*/
nsCOMArray<nsIXFormsRepeatElement> mIndexesUsed;
/**
* Used to keep track of whether this control has any single node binding
@ -219,6 +225,9 @@ protected:
* @param aValue - value that the attribute is being changed to.
*/
void MaybeRemoveFromModel(nsIAtom *aName, const nsAString &aValue);
/** Removes the index change event listeners */
void RemoveIndexListeners();
};
#endif

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

@ -232,6 +232,11 @@ protected:
* The currently selected repeat (nested repeats)
*/
nsCOMPtr<nsIXFormsRepeatElement> mCurrentRepeat;
/**
* Array of controls using the repeat-index
*/
nsCOMArray<nsIXFormsControl> mIndexUsers;
/**
* Retrieves an integer attribute and checks its type.
@ -369,6 +374,7 @@ NS_IMETHODIMP
nsXFormsRepeatElement::OnDestroyed()
{
mHTMLElement = nsnull;
mIndexUsers.Clear();
return nsXFormsControlStub::OnDestroyed();
}
@ -490,6 +496,9 @@ nsXFormsRepeatElement::SetIndex(PRUint32 *aIndex,
// Set current index to new value
mCurrentIndex = *aIndex;
// Inform of index change
mParent ? mParent->IndexHasChanged() : IndexHasChanged();
return NS_OK;
}
@ -532,6 +541,42 @@ nsXFormsRepeatElement::SetCurrentRepeat(nsIXFormsRepeatElement *aRepeat,
return NS_OK;
}
NS_IMETHODIMP
nsXFormsRepeatElement::AddIndexUser(nsIXFormsControl *aControl)
{
nsresult rv = NS_OK;
if (mIndexUsers.IndexOf(aControl) == -1 && !mIndexUsers.AppendObject(aControl))
rv = NS_ERROR_FAILURE;
return rv;
}
NS_IMETHODIMP
nsXFormsRepeatElement::RemoveIndexUser(nsIXFormsControl *aControl)
{
return mIndexUsers.RemoveObject(aControl) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsXFormsRepeatElement::IndexHasChanged()
{
///
/// @bug We need to handle \<bind\> elements too (XXX)
// copy the index array, as index users might add/remove themselves when
// they are rebound and refreshed().
nsCOMArray<nsIXFormsControl> indexes(mIndexUsers);
for (PRInt32 i = 0; i < indexes.Count(); ++i) {
nsCOMPtr<nsIXFormsControl> control = indexes[i];
control->Bind();
control->Refresh();
}
return NS_OK;
}
// NB: CloneNode() assumes that this always succeeds
NS_IMETHODIMP
nsXFormsRepeatElement::GetIsParent(PRBool *aIsParent)

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

@ -279,49 +279,64 @@ nsXFormsUtils::GetNodeContext(nsIDOMElement *aElement,
*aContextSize = 1;
if (aContextPosition)
*aContextPosition = 1;
return FindBindContext(*aBindElement,
aOuterBind,
aModel,
aContextNode);
}
if (aElementFlags & ELEMENT_WITH_MODEL_ATTR) {
// CASE 2: Use @model
// If bind did not set model, and the element has a model attribute we use this
nsAutoString modelId;
NS_NAMED_LITERAL_STRING(modelStr, "model");
aElement->GetAttribute(modelStr, modelId);
if (!modelId.IsEmpty()) {
nsCOMPtr<nsIDOMElement> modelElement;
domDoc->GetElementById(modelId, getter_AddRefs(modelElement));
nsCOMPtr<nsIModelElementPrivate> model = do_QueryInterface(modelElement);
*aOuterBind = GetParentModel(*aBindElement, aModel);
NS_ENSURE_STATE(*aModel);
} else {
if (aElementFlags & ELEMENT_WITH_MODEL_ATTR) {
// CASE 2: Use @model
// If bind did not set model, and the element has a model attribute we use this
nsAutoString modelId;
NS_NAMED_LITERAL_STRING(modelStr, "model");
aElement->GetAttribute(modelStr, modelId);
// No element found, or element not a \<model\> element
if (!model) {
const PRUnichar *strings[] = { modelId.get(), modelStr.get() };
nsXFormsUtils::ReportError(NS_LITERAL_STRING("idRefError"),
strings, 2, aElement, aElement);
nsXFormsUtils::DispatchEvent(aElement, eEvent_BindingException);
return NS_ERROR_FAILURE;
if (!modelId.IsEmpty()) {
nsCOMPtr<nsIDOMElement> modelElement;
domDoc->GetElementById(modelId, getter_AddRefs(modelElement));
nsCOMPtr<nsIModelElementPrivate> model = do_QueryInterface(modelElement);
// No element found, or element not a \<model\> element
if (!model) {
const PRUnichar *strings[] = { modelId.get(), modelStr.get() };
nsXFormsUtils::ReportError(NS_LITERAL_STRING("idRefError"),
strings, 2, aElement, aElement);
nsXFormsUtils::DispatchEvent(aElement, eEvent_BindingException);
return NS_ERROR_FAILURE;
}
NS_ADDREF(*aModel = model);
}
NS_ADDREF(*aModel = model);
}
// Search for a parent setting context for us
nsresult rv = FindParentContext(aElement,
aModel,
aContextNode,
aContextPosition,
aContextSize);
// CASE 3/4: Use parent's model / first model in document.
// If FindParentContext() does not find a parent context but |aModel| is not
// set, it sets the model to the first model in the document.
NS_ENSURE_SUCCESS(rv, rv);
}
// Search for a parent setting context for us
nsresult rv = FindParentContext(aElement,
aModel,
aContextNode,
aContextPosition,
aContextSize);
// CASE 3/4: Use parent's model / first model in document.
// If FindParentContext() does not find a parent context but |aModel| is not
// set, it sets the model to the first model in the document.
NS_ENSURE_SUCCESS(rv, rv);
// if context node is not set, it's the document element of the model's
// default instance
if (!*aContextNode) {
nsCOMPtr<nsIXFormsModelElement> modelInt = do_QueryInterface(*aModel);
NS_ENSURE_STATE(modelInt);
nsCOMPtr<nsIDOMDocument> instanceDoc;
modelInt->GetInstanceDocument(NS_LITERAL_STRING(""),
getter_AddRefs(instanceDoc));
NS_ENSURE_STATE(instanceDoc);
nsIDOMElement* docElement;
instanceDoc->GetDocumentElement(&docElement); // addrefs
NS_ENSURE_STATE(docElement);
*aContextNode = docElement; // addref'ed above
}
return NS_OK;
}
@ -358,7 +373,8 @@ nsXFormsUtils::EvaluateXPath(const nsAString &aExpression,
PRUint16 aResultType,
PRInt32 aContextPosition,
PRInt32 aContextSize,
nsCOMArray<nsIDOMNode> *aSet)
nsCOMArray<nsIDOMNode> *aSet,
nsStringArray *aIndexesUsed)
{
nsCOMPtr<nsIDOMDocument> doc;
aContextNode->GetOwnerDocument(getter_AddRefs(doc));
@ -397,13 +413,15 @@ nsXFormsUtils::EvaluateXPath(const nsAString &aExpression,
nsXFormsXPathParser parser;
nsXFormsXPathAnalyzer analyzer(eval, aResolverNode);
nsAutoPtr<nsXFormsXPathNode> xNode(parser.Parse(aExpression));
rv = analyzer.Analyze(aContextNode,
xNode,
expression,
&aExpression,
aSet);
NS_ENSURE_SUCCESS(rv, nsnull);
if (aIndexesUsed)
*aIndexesUsed = analyzer.IndexesUsed();
}
CallQueryInterface(supResult, &result); // addrefs
}
@ -421,36 +439,6 @@ nsXFormsUtils::EvaluateXPath(const nsAString &aExpression,
return result;
}
/* static */ nsresult
nsXFormsUtils::FindBindContext(nsIDOMElement *aBindElement,
PRBool *aOuterBind,
nsIModelElementPrivate **aModel,
nsIDOMNode **aContextNode)
{
NS_ENSURE_ARG_POINTER(aContextNode);
*aContextNode = nsnull;
// 1) Find the model for the bind
*aOuterBind = GetParentModel(aBindElement, aModel);
NS_ENSURE_STATE(*aModel);
// 2) Find the context node
nsCOMPtr<nsIXFormsModelElement> modelInt = do_QueryInterface(*aModel);
NS_ENSURE_STATE(modelInt);
nsCOMPtr<nsIDOMDocument> instanceDoc;
modelInt->GetInstanceDocument(NS_LITERAL_STRING(""),
getter_AddRefs(instanceDoc));
NS_ENSURE_STATE(instanceDoc);
nsIDOMElement* docElement;
instanceDoc->GetDocumentElement(&docElement); // addrefs
NS_ENSURE_STATE(docElement);
*aContextNode = docElement; // addref'ed above
return NS_OK;
}
/* static */ nsresult
nsXFormsUtils::EvaluateNodeBinding(nsIDOMElement *aElement,
PRUint32 aElementFlags,
@ -459,7 +447,8 @@ nsXFormsUtils::EvaluateNodeBinding(nsIDOMElement *aElement,
PRUint16 aResultType,
nsIModelElementPrivate **aModel,
nsIDOMXPathResult **aResult,
nsCOMArray<nsIDOMNode> *aDeps)
nsCOMArray<nsIDOMNode> *aDeps,
nsStringArray *aIndexesUsed)
{
if (!aElement || !aModel || !aResult) {
return NS_OK;
@ -484,6 +473,10 @@ nsXFormsUtils::EvaluateNodeBinding(nsIDOMElement *aElement,
NS_ENSURE_SUCCESS(rv, rv);
if (!contextNode) {
return NS_OK; // this will happen if the doc is still loading
}
nsAutoString expr;
if (bindElement) {
if (!outerBind) {
@ -507,26 +500,6 @@ nsXFormsUtils::EvaluateNodeBinding(nsIDOMElement *aElement,
return NS_OK;
expr.Assign(aDefaultRef);
}
if (!contextNode) {
nsCOMPtr<nsIDOMDocument> instanceDoc;
nsCOMPtr<nsIXFormsModelElement> model = do_QueryInterface(*aModel);
NS_ENSURE_STATE(model); // The referenced model is not actually a model element, or does not exist.
model->GetInstanceDocument(NS_LITERAL_STRING(""),
getter_AddRefs(instanceDoc));
NS_ENSURE_STATE(instanceDoc);
nsCOMPtr<nsIDOMElement> docElement;
instanceDoc->GetDocumentElement(getter_AddRefs(docElement));
contextNode = docElement;
if (!contextNode) {
return NS_OK; // this will happen if the doc is still loading
}
}
}
@ -537,7 +510,8 @@ nsXFormsUtils::EvaluateNodeBinding(nsIDOMElement *aElement,
aResultType,
contextSize,
contextPosition,
aDeps);
aDeps,
aIndexesUsed);
res.swap(*aResult); // exchanges ref

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

@ -47,6 +47,7 @@
#include "nsIDOMXPathResult.h"
#include "nsIModelElementPrivate.h"
#include "nsIScriptError.h"
#include "nsVoidArray.h"
class nsIDOMElement;
class nsIXFormsModelElement;
@ -210,18 +211,9 @@ public:
PRUint16 aResultType,
nsIModelElementPrivate **aModel,
nsIDOMXPathResult **aResult,
nsCOMArray<nsIDOMNode> *aDeps = nsnull);
nsCOMArray<nsIDOMNode> *aDeps = nsnull,
nsStringArray *aIndexesUsed = nsnull);
/**
* Given a bind element |aBindElement|, find the model and the context node
* for it. |aOuterBind| tells whether the bind element is an outermost bind.
*/
static NS_HIDDEN_(nsresult)
FindBindContext(nsIDOMElement *aBindElement,
PRBool *aOuterBind,
nsIModelElementPrivate **aModel,
nsIDOMNode **aContextNode);
/**
* Convenience method for doing XPath evaluations. This gets a
* nsIXFormsXPathEvaluator from |aContextNode|'s ownerDocument, and calls
@ -235,7 +227,8 @@ public:
PRUint16 aResultType,
PRInt32 aContextPosition = 1,
PRInt32 aContextSize = 1,
nsCOMArray<nsIDOMNode> *aSet = nsnull);
nsCOMArray<nsIDOMNode> *aSet = nsnull,
nsStringArray *aIndexesUsed = nsnull);
/**
* Given a node in the instance data, get its string value according

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

@ -47,11 +47,9 @@
MOZ_DECL_CTOR_COUNTER(nsXFormsXPathAnalyzer)
nsXFormsXPathAnalyzer::nsXFormsXPathAnalyzer(
nsIXFormsXPathEvaluator *aEvaluator,
nsIDOMNode *aResolver)
: mEvaluator(aEvaluator),
mResolver(aResolver)
nsXFormsXPathAnalyzer::nsXFormsXPathAnalyzer(nsIXFormsXPathEvaluator *aEvaluator,
nsIDOMNode *aResolver)
: mEvaluator(aEvaluator), mResolver(aResolver)
{
MOZ_COUNT_CTOR(nsXFormsXPathAnalyzer);
}
@ -119,6 +117,7 @@ nsXFormsXPathAnalyzer::AnalyzeRecursively(nsIDOMNode *aContextNode,
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);
printf("\tIsIndex: %d\n", aNode->mIsIndex);
#endif
if (aNode->mEndIndex < 0 || aNode->mStartIndex >= aNode->mEndIndex) {
@ -176,6 +175,24 @@ nsXFormsXPathAnalyzer::AnalyzeRecursively(nsIDOMNode *aContextNode,
rv = result->GetResultType(&type);
NS_ENSURE_SUCCESS(rv, rv);
if (aNode->mIsIndex) {
// Extract index parameter, xp is "index(parameter)"
const PRUint32 indexSize = sizeof("index(") - 1;
nsDependentSubstring indexExpr = Substring(xp,
indexSize,
xp.Length() - indexSize - 1); // remove final ')' too
nsCOMPtr<nsIDOMXPathResult> stringRes;
rv = mEvaluator->Evaluate(indexExpr, aContextNode, mResolver,
nsIDOMXPathResult::STRING_TYPE,
nsnull, getter_AddRefs(stringRes));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString indexId;
rv = stringRes->GetStringValue(indexId);
NS_ENSURE_SUCCESS(rv, rv);
mIndexesUsed.AppendString(indexId);
}
// We are only interested in nodes
if ( type != nsIDOMXPathResult::UNORDERED_NODE_ITERATOR_TYPE
&& type != nsIDOMXPathResult::ORDERED_NODE_ITERATOR_TYPE) {
@ -204,3 +221,9 @@ nsXFormsXPathAnalyzer::AnalyzeRecursively(nsIDOMNode *aContextNode,
}
return NS_OK;
}
const nsStringArray&
nsXFormsXPathAnalyzer::IndexesUsed() const
{
return mIndexesUsed;
}

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

@ -47,6 +47,7 @@
#include "nsIDOMNode.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsVoidArray.h"
/**
* This class analyzes an XPath Expression parse tree (nsXFormsXPathNode), and
@ -58,11 +59,12 @@
class nsXFormsXPathAnalyzer {
private:
nsCOMPtr<nsIXFormsXPathEvaluator> mEvaluator;
nsCOMPtr<nsIDOMNode> mResolver;
nsCOMPtr<nsIDOMNode> mResolver;
nsCOMArray<nsIDOMNode> *mCurSet;
nsCOMPtr<nsIDOMXPathExpression> mCurExpression;
const nsAString *mCurExprString;
nsCOMArray<nsIDOMNode> *mCurSet;
nsCOMPtr<nsIDOMXPathExpression> mCurExpression;
const nsAString *mCurExprString;
nsStringArray mIndexesUsed;
nsresult AnalyzeRecursively(nsIDOMNode *aContextNode,
const nsXFormsXPathNode *aNode,
@ -78,4 +80,6 @@ public:
nsIDOMXPathExpression *aExpression,
const nsAString *aExprString,
nsCOMArray<nsIDOMNode> *aSet);
const nsStringArray& IndexesUsed() const;
};

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

@ -43,7 +43,7 @@
MOZ_DECL_CTOR_COUNTER(nsXFormsXPathNode)
nsXFormsXPathNode::nsXFormsXPathNode(nsXFormsXPathNode* aParent, PRBool aContinue)
: mChild(nsnull), mEndIndex(-100), mCon(aContinue)
: mChild(nsnull), mEndIndex(-100), mCon(aContinue), mIsIndex(PR_FALSE)
{
MOZ_COUNT_CTOR(nsXFormsXPathNode);

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

@ -56,6 +56,7 @@ public:
PRBool mCon;
PRBool mPredicate;
PRBool mLiteral;
PRBool mIsIndex;
nsXFormsXPathNode(nsXFormsXPathNode* aParent, PRBool aContinue = PR_FALSE);
~nsXFormsXPathNode();

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

@ -354,12 +354,13 @@ nsXFormsXPathParser::FilterExpr()
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;
}
nsDependentSubstring fname = Substring(mScanner.Expression(),
mScanner.Offset() + 1,
mScanner.Length());
if (!mUsesDynamicFunc && fname.Equals(NS_LITERAL_STRING("now"))) {
mUsesDynamicFunc = PR_TRUE;
}
PopToken();
PopToken();
nsXFormsXPathScanner::XPATHTOKEN t = PeekToken();
@ -367,18 +368,22 @@ nsXFormsXPathParser::FunctionCall()
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());
XPathCompilerException("Expected ) got EOF",
mScanner.Expression(),
mScanner.Offset(),
mScanner.Length());
}
nsXFormsXPathNode* c = JustContext();
Expr();
if (fname.Equals(NS_LITERAL_STRING("index"))) {
c->mIsIndex = PR_TRUE;
}
PushContext(c);
t = PeekToken();
if (t == nsXFormsXPathScanner::COMMA) {
con = (t == nsXFormsXPathScanner::COMMA);
if (con) {
PopToken(); // Another Argument
con = PR_TRUE;
} else {
con = PR_FALSE;
}
}