[XForms] Optimize repeat refreshing. Bug 331452, r=smaug+aaronr

This commit is contained in:
allan%beaufour.dk 2006-05-22 09:06:14 +00:00
Родитель 80dab191f0
Коммит 300027c505
11 изменённых файлов: 330 добавлений и 231 удалений

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

@ -49,7 +49,7 @@ interface nsIDOMElement;
/**
* Interface implemented by all XForms form control classes.
*/
[uuid(1bea8750-2133-4a00-986a-04e5cbd129b2)]
[uuid(8c84afe1-e071-4d45-b3da-c5aa93154343)]
interface nsIXFormsControl : nsIXFormsContextControl
{
/**
@ -62,13 +62,24 @@ interface nsIXFormsControl : nsIXFormsContextControl
*/
readonly attribute nsIDOMNode boundNode;
/**
* Binds the control to the model. Only handles attaching to the model
* (including reattaching from any old model).
*
* @note It can also set the boundNode, but does not do a proper node
* binding, as in setting up dependencies, attaching index() listeners, etc.
*
* @param setBoundNode Set boundNode too?
*/
void bindToModel(in boolean setBoundNode);
/**
* The instance nodes that the control depend on.
*
* In other words, all the instance nodes that could influence which node
* the control is bound to (mBoundNode). For example:
* If a node has @ref="/share[@owner = /me]", it depends on all /share
* nodes, all @owned attributes on /share nodes, and all /me nodes.
* nodes, all @owner attributes on /share nodes, and all /me nodes.
*/
readonly attribute nsCOMArrayPtr dependencies;

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

@ -36,14 +36,16 @@
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
[uuid(b8540fca-a671-4a1d-8471-6e89820f0848)]
[uuid(92b8a6aa-3692-4132-8df6-a61a936d5e9d)]
interface nsIXFormsControlBase : nsISupports
{
/**
* This tells the form control to update its node binding based on the
* current instance data.
*
* @return Did the binding change the context?
*/
void bind();
boolean bind();
/**
* This tells the form control to update its state based on the current

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

@ -89,11 +89,15 @@ protected:
PRInt32 mContextSize;
/** Does this element have the repeat-index? */
PRBool mHasIndex;
PRPackedBool mHasIndex;
/** Has context changed since last bind? */
PRPackedBool mContextIsDirty;
public:
nsXFormsContextContainer()
: mContextPosition(1), mContextSize(1), mHasIndex(PR_FALSE) {}
: mContextPosition(1), mContextSize(1), mHasIndex(PR_FALSE),
mContextIsDirty(PR_FALSE) {}
NS_DECL_ISUPPORTS_INHERITED
@ -102,7 +106,7 @@ public:
NS_IMETHOD DocumentChanged(nsIDOMDocument *aNewDocument);
// nsIXFormsControl
NS_IMETHOD Bind();
NS_IMETHOD Bind(PRBool *aContextChanged);
NS_IMETHOD SetContext(nsIDOMNode *aContextNode,
PRInt32 aContextPosition,
PRInt32 aContextSize);
@ -271,11 +275,18 @@ nsXFormsContextContainer::SetContext(nsIDOMNode *aContextNode,
PRInt32 aContextPosition,
PRInt32 aContextSize)
{
mBoundNode = aContextNode;
mContextPosition = aContextPosition;
mContextSize = aContextSize;
mContextIsDirty = (mContextIsDirty ||
mBoundNode != aContextNode ||
mContextPosition != aContextPosition ||
mContextSize != aContextSize);
return Bind();
if (mContextIsDirty) {
mBoundNode = aContextNode;
mContextPosition = aContextPosition;
mContextSize = aContextSize;
}
return BindToModel();
}
NS_IMETHODIMP
@ -299,12 +310,11 @@ nsXFormsContextContainer::GetContext(nsAString &aModelID,
// nsIXFormsControl
NS_IMETHODIMP
nsXFormsContextContainer::Bind()
nsXFormsContextContainer::Bind(PRBool *aContextChanged)
{
nsresult rv = BindToModel();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_ARG(aContextChanged);
*aContextChanged = mContextIsDirty;
mContextIsDirty = PR_FALSE;
return NS_OK;
}

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

@ -141,12 +141,16 @@ nsXFormsControlStubBase::RemoveIndexListeners()
}
NS_IMETHODIMP
nsXFormsControlStubBase::ResetBoundNode(const nsString &aBindAttribute,
PRUint16 aResultType,
nsIDOMXPathResult **aResult)
nsXFormsControlStubBase::ResetBoundNode(const nsString &aBindAttribute,
PRUint16 aResultType,
PRBool *aContextChanged)
{
NS_ENSURE_ARG(aContextChanged);
// Clear existing bound node, etc.
mBoundNode = nsnull;
*aContextChanged = mBoundNode ? PR_TRUE : PR_FALSE;
nsCOMPtr<nsIDOMNode> oldBoundNode;
oldBoundNode.swap(mBoundNode);
mUsesModelBinding = PR_FALSE;
mAppearDisabled = PR_FALSE;
mDependencies.Clear();
@ -166,7 +170,7 @@ nsXFormsControlStubBase::ResetBoundNode(const nsString &aBindAttribute,
if (rv == NS_OK_XFORMS_DEFERRED || !result) {
// Binding was deferred, or not bound
return NS_OK;
return rv;
}
// Get context node, if any
@ -177,6 +181,8 @@ nsXFormsControlStubBase::ResetBoundNode(const nsString &aBindAttribute,
result->GetSingleNodeValue(getter_AddRefs(mBoundNode));
}
*aContextChanged = (oldBoundNode != mBoundNode);
if (!mBoundNode) {
// If there's no result (ie, no instance node) returned by the above, it
// means that the binding is not pointing to an instance data node, so we
@ -189,19 +195,15 @@ nsXFormsControlStubBase::ResetBoundNode(const nsString &aBindAttribute,
return wrapper->SetIntrinsicState(kDisabledIntrinsicState);
}
if (aResult) {
*aResult = nsnull;
result.swap(*aResult); // transfers ref
}
return NS_OK;
}
NS_IMETHODIMP
nsXFormsControlStubBase::Bind()
nsXFormsControlStubBase::Bind(PRBool* aContextChanged)
{
return ResetBoundNode(NS_LITERAL_STRING("ref"),
nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE);
nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE,
aContextChanged);
}
NS_IMETHODIMP
@ -364,7 +366,7 @@ nsXFormsControlStubBase::ProcessNodeBinding(const nsString &aBindingAtt
return rv;
}
nsresult
NS_IMETHODIMP
nsXFormsControlStubBase::BindToModel(PRBool aSetBoundNode)
{
nsCOMPtr<nsIModelElementPrivate> oldModel(mModel);
@ -604,7 +606,8 @@ nsXFormsControlStubBase::ForceModelDetach(PRBool aRebind)
return NS_OK;
}
nsresult rv = Bind();
PRBool dummy;
nsresult rv = Bind(&dummy);
NS_ENSURE_SUCCESS(rv, rv);
return rv == NS_OK_XFORMS_DEFERRED ? NS_OK : Refresh();
}
@ -747,7 +750,8 @@ void
nsXFormsControlStubBase::AfterSetAttribute(nsIAtom *aName)
{
if (IsBindingAttribute(aName)) {
nsresult rv = Bind();
PRBool dummy;
nsresult rv = Bind(&dummy);
if (NS_SUCCEEDED(rv) && rv != NS_OK_XFORMS_DEFERRED)
Refresh();
}

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

@ -81,12 +81,13 @@ public:
// nsIXFormsControl
NS_IMETHOD GetBoundNode(nsIDOMNode **aBoundNode);
NS_IMETHOD BindToModel(PRBool aSetBoundNode = PR_FALSE);
NS_IMETHOD GetDependencies(nsCOMArray<nsIDOMNode> **aDependencies);
NS_IMETHOD GetElement(nsIDOMElement **aElement);
NS_IMETHOD ResetBoundNode(const nsString &aBindAttribute,
PRUint16 aResultType,
nsIDOMXPathResult **aResult = nsnull);
NS_IMETHOD Bind();
NS_IMETHOD ResetBoundNode(const nsString &aBindAttribute,
PRUint16 aResultType,
PRBool *aContextChanged);
NS_IMETHOD Bind(PRBool *aContextChanged);
NS_IMETHOD Refresh();
NS_IMETHOD GetOnDeferredBindList(PRBool *onList);
NS_IMETHOD SetOnDeferredBindList(PRBool putOnList);
@ -263,17 +264,6 @@ protected:
/** Removes the index change event listeners */
void RemoveIndexListeners();
/**
* Binds the control to the model. Just sets mModel, and handle attaching to
* the model (including reattaching from any old model).
*
* @note It can also set the mBoundNode, but does not do a proper node
* binding, as in setting up dependencies, attaching index() listeners, etc.
*
* @param aSetBoundNode Set mBoundNode too?
*/
nsresult BindToModel(PRBool aSetBoundNode = PR_FALSE);
/**
* Forces detaching from the model.
*
@ -350,9 +340,9 @@ public:
return nsXFormsControlStubBase::AttributeRemoved(aName);
}
NS_IMETHOD Bind()
NS_IMETHOD Bind(PRBool *aContextChanged)
{
return nsXFormsControlStubBase::Bind();
return nsXFormsControlStubBase::Bind(aContextChanged);
}
/** Constructor */

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

@ -116,10 +116,8 @@ nsXFormsDelegateStub::Refresh()
SetMozTypeAttribute();
nsCOMPtr<nsIXFormsUIWidget> widget = do_QueryInterface(mElement);
if (!widget)
return NS_ERROR_FAILURE;
return widget->Refresh();
return widget ? widget->Refresh() : NS_OK;
}
NS_IMETHODIMP

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

@ -70,7 +70,7 @@ public:
NS_IMETHOD DoneAddingChildren();
// nsIXFormsControlBase overrides
NS_IMETHOD Bind();
NS_IMETHOD Bind(PRBool *aContextChanged);
NS_IMETHOD Refresh();
// nsIXFormsSelectChild
@ -222,8 +222,10 @@ nsXFormsItemSetElement::SelectItemByNode(nsIDOMNode *aNode,
}
NS_IMETHODIMP
nsXFormsItemSetElement::Bind()
nsXFormsItemSetElement::Bind(PRBool *aContextChanged)
{
NS_ENSURE_ARG(aContextChanged);
*aContextChanged = PR_FALSE;
return BindToModel();
}
@ -303,23 +305,21 @@ nsXFormsItemSetElement::Refresh()
getter_AddRefs(itemNode));
NS_ENSURE_SUCCESS(rv, rv);
anonContent->AppendChild(itemNode, getter_AddRefs(tmpNode));
// XXX Could we get rid of the <contextcontainer>?
rv = domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
NS_LITERAL_STRING("contextcontainer"),
getter_AddRefs(contextContainer));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMElement> modelElement = do_QueryInterface(model);
nsAutoString modelID;
modelElement->GetAttribute(NS_LITERAL_STRING("id"), modelID);
contextContainer->SetAttribute(NS_LITERAL_STRING("model"), modelID);
itemNode->AppendChild(contextContainer, getter_AddRefs(tmpNode));
nsCOMPtr<nsIXFormsContextControl> ctx(do_QueryInterface(contextContainer));
if (ctx) {
ctx->SetContext(node, i + 1, nodeCount);
}
// Clone the template content under the item
for (PRUint32 j = 0; j < templateNodeCount; ++j) {
templateNodes->Item(j, getter_AddRefs(templateNode));
@ -327,8 +327,6 @@ nsXFormsItemSetElement::Refresh()
contextContainer->AppendChild(cloneNode, getter_AddRefs(templateNode));
}
itemNode->AppendChild(contextContainer, getter_AddRefs(tmpNode));
anonContent->AppendChild(itemNode, getter_AddRefs(tmpNode));
}
// refresh parent we

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

@ -639,7 +639,7 @@ nsXFormsModelElement::nsXFormsModelElement()
mSchemaTotal(0),
mPendingInstanceCount(0),
mDocumentLoaded(PR_FALSE),
mNeedsRefresh(PR_FALSE),
mRebindAllControls(PR_FALSE),
mInstancesInitialized(PR_FALSE),
mReadyHandled(PR_FALSE),
mLazyModel(PR_FALSE),
@ -1062,18 +1062,8 @@ nsXFormsModelElement::Rebuild()
NS_ENSURE_SUCCESS(rv, rv);
// 3. Re-attach all elements
if (mReadyHandled) { // if it's not during initializing phase
nsXFormsControlListItem::iterator it;
for (it = mFormControls.begin(); it != mFormControls.end(); ++it) {
nsCOMPtr<nsIXFormsControl> control = (*it)->Control();
NS_ASSERTION(control, "mFormControls has null control?!");
// run bind to reset mBoundNode for all of the model's controls
control->Bind();
}
// Triggers a refresh of all controls
mNeedsRefresh = PR_TRUE;
if (mDocumentLoaded) {
mRebindAllControls = PR_TRUE;
}
// 4. Rebuild graph
@ -1199,16 +1189,16 @@ nsXFormsModelElement::RefreshSubTree(nsXFormsControlListItem *aCurrent,
#ifdef DEBUG_MODEL
nsCOMPtr<nsIDOMElement> controlElement;
control->GetElement(getter_AddRefs(controlElement));
printf("rebind: %d, mNeedsRefresh: %d, rebindChildren: %d\n",
rebind, mNeedsRefresh, rebindChildren);
printf("rebind: %d, mRebindAllControls: %d, aForceRebind: %d\n",
rebind, mRebindAllControls, aForceRebind);
if (controlElement) {
printf("Checking control: ");
//DBG_TAGINFO(controlElement);
}
#endif
if (mNeedsRefresh || rebind) {
refresh = PR_TRUE;
if (mRebindAllControls || rebind) {
refresh = rebind = PR_TRUE;
} else {
PRBool usesModelBinding = PR_FALSE;
control->GetUsesModelBinding(&usesModelBinding);
@ -1299,22 +1289,18 @@ nsXFormsModelElement::RefreshSubTree(nsXFormsControlListItem *aCurrent,
// Handle rebinding
if (rebind) {
nsCOMPtr<nsIDOMNode> oldBoundNode;
control->GetBoundNode(getter_AddRefs(oldBoundNode));
rv = control->Bind();
rv = control->Bind(&rebindChildren);
NS_ENSURE_SUCCESS(rv, rv);
control->GetBoundNode(getter_AddRefs(boundNode));
rebindChildren = (oldBoundNode != boundNode);
}
// Handle refreshing
if (rebind || refresh) {
control->Refresh();
// XXX: we should really check the return result, but f.x. select1
// returns error because of no widget...? so we should ensure that an
// error is only returned when there actually is an error, and we should
// report that on the console... possibly we should then continue,
// instead of bailing totally.
// XXX: bug 336608: we should really check the return result, but
// f.x. select1 returns error because of no widget...? so we should
// ensure that an error is only returned when there actually is an
// error, and we should report that on the console... possibly we should
// then continue, instead of bailing totally.
// NS_ENSURE_SUCCESS(rv, rv);
}
@ -1341,13 +1327,17 @@ nsXFormsModelElement::Refresh()
return NS_OK;
}
// XXXbeaufour: Can we somehow suspend redraw / "screen update" while doing
// the refresh? That should save a lot of time, and avoid flickering of
// controls.
// Kick off refreshing on root node
nsresult rv = RefreshSubTree(mFormControls.FirstChild(), PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
// Clear refresh structures
mChangedNodes.Clear();
mNeedsRefresh = PR_FALSE;
mRebindAllControls = PR_FALSE;
mMDG.ClearDispatchFlags();
return NS_OK;
@ -1407,6 +1397,11 @@ NS_IMETHODIMP
nsXFormsModelElement::AddFormControl(nsIXFormsControl *aControl,
nsIXFormsControl *aParent)
{
#ifdef DEBUG_MODEL
printf("nsXFormsModelElement::AddFormControl(con: %p, parent: %p)\n",
(void*) aControl, (void*) aParent);
#endif
NS_ENSURE_ARG(aControl);
return mFormControls.AddControl(aControl, aParent);
}
@ -1414,6 +1409,11 @@ nsXFormsModelElement::AddFormControl(nsIXFormsControl *aControl,
NS_IMETHODIMP
nsXFormsModelElement::RemoveFormControl(nsIXFormsControl *aControl)
{
#ifdef DEBUG_MODEL
printf("nsXFormsModelElement::RemoveFormControl(con: %p)\n",
(void*) aControl);
#endif
NS_ENSURE_ARG(aControl);
PRBool removed;
nsresult rv = mFormControls.RemoveControl(aControl, removed);
@ -2109,6 +2109,7 @@ nsXFormsModelElement::InitializeControls()
nsXFormsControlListItem::iterator it;
nsresult rv;
PRBool dummy;
for (it = mFormControls.begin(); it != mFormControls.end(); ++it) {
// Get control
nsCOMPtr<nsIXFormsControl> control = (*it)->Control();
@ -2121,17 +2122,14 @@ nsXFormsModelElement::InitializeControls()
// DBG_TAGINFO(controlElement);
#endif
// Rebind
rv = control->Bind();
NS_ENSURE_SUCCESS(rv, rv);
// Get bound node
nsCOMPtr<nsIDOMNode> boundNode;
rv = control->GetBoundNode(getter_AddRefs(boundNode));
rv = control->Bind(&dummy);
NS_ENSURE_SUCCESS(rv, rv);
// Refresh controls
rv = control->Refresh();
NS_ENSURE_SUCCESS(rv, rv);
// XXX: Bug 336608, refresh still fails for some controls, for some
// reason.
// NS_ENSURE_SUCCESS(rv, rv);
}
mChangedNodes.Clear();
@ -2176,13 +2174,6 @@ nsXFormsModelElement::MaybeNotifyCompletion()
}
}
// Okay, dispatch xforms-model-construct-done and xforms-ready events!
for (i = 0; i < models->Count(); ++i) {
nsXFormsModelElement *model =
NS_STATIC_CAST(nsXFormsModelElement *, models->ElementAt(i));
nsXFormsUtils::DispatchEvent(model->mElement, eEvent_ModelConstructDone);
}
// validate the instance documents becauar we want schemaValidation to add
// schema type properties from the schema file unto our instance document
// elements. We don't care about the validation results.
@ -2214,8 +2205,17 @@ nsXFormsModelElement::MaybeNotifyCompletion()
}
}
// Register deferred binds with the model. It does not bind the controls,
// only bind them to the model they belong to.
nsXFormsModelElement::ProcessDeferredBinds(domDoc);
// Okay, dispatch xforms-model-construct-done
for (i = 0; i < models->Count(); ++i) {
nsXFormsModelElement *model =
NS_STATIC_CAST(nsXFormsModelElement *, models->ElementAt(i));
nsXFormsUtils::DispatchEvent(model->mElement, eEvent_ModelConstructDone);
}
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
if (doc) {
PRUint32 loadingMessages = NS_PTR_TO_UINT32(
@ -2229,6 +2229,7 @@ nsXFormsModelElement::MaybeNotifyCompletion()
}
}
// Backup instances and fire xforms-ready
for (i = 0; i < models->Count(); ++i) {
nsXFormsModelElement *model =
NS_STATIC_CAST(nsXFormsModelElement *, models->ElementAt(i));
@ -2488,8 +2489,8 @@ nsXFormsModelElement::MessageLoadFinished()
mElement->GetOwnerDocument(getter_AddRefs(domDoc));
const nsVoidArray *models = GetModelList(domDoc);
nsCOMPtr<nsIDocument>doc = do_QueryInterface(domDoc);
nsCOMArray<nsIXFormsControlBase> *deferredBindList =
NS_STATIC_CAST(nsCOMArray<nsIXFormsControlBase> *,
nsCOMArray<nsIXFormsControl> *deferredBindList =
NS_STATIC_CAST(nsCOMArray<nsIXFormsControl> *,
doc->GetProperty(nsXFormsAtoms::deferredBindListProperty));
// if we've already gotten the xforms-model-construct-done event and not
@ -2554,8 +2555,8 @@ DeleteBindList(void *aObject,
}
/* static */ nsresult
nsXFormsModelElement::DeferElementBind(nsIDOMDocument *aDoc,
nsIXFormsControlBase *aControl)
nsXFormsModelElement::DeferElementBind(nsIDOMDocument *aDoc,
nsIXFormsControl *aControl)
{
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
@ -2570,7 +2571,7 @@ nsXFormsModelElement::DeferElementBind(nsIDOMDocument *aDoc,
// We need to keep the document order of the controls AND don't want
// to walk the deferredBindList every time we want to check about adding a
// control.
nsCOMPtr<nsIXFormsControlBase> controlBase(do_QueryInterface(aControl));
nsCOMPtr<nsIXFormsControl> controlBase(do_QueryInterface(aControl));
NS_ENSURE_STATE(controlBase);
PRBool onList = PR_FALSE;
@ -2579,12 +2580,12 @@ nsXFormsModelElement::DeferElementBind(nsIDOMDocument *aDoc,
return NS_OK;
}
nsCOMArray<nsIXFormsControlBase> *deferredBindList =
NS_STATIC_CAST(nsCOMArray<nsIXFormsControlBase> *,
nsCOMArray<nsIXFormsControl> *deferredBindList =
NS_STATIC_CAST(nsCOMArray<nsIXFormsControl> *,
doc->GetProperty(nsXFormsAtoms::deferredBindListProperty));
if (!deferredBindList) {
deferredBindList = new nsCOMArray<nsIXFormsControlBase>(16);
deferredBindList = new nsCOMArray<nsIXFormsControl>(16);
NS_ENSURE_TRUE(deferredBindList, NS_ERROR_OUT_OF_MEMORY);
doc->SetProperty(nsXFormsAtoms::deferredBindListProperty, deferredBindList,
@ -2604,6 +2605,10 @@ nsXFormsModelElement::DeferElementBind(nsIDOMDocument *aDoc,
/* static */ void
nsXFormsModelElement::ProcessDeferredBinds(nsIDOMDocument *aDoc)
{
#ifdef DEBUG_MODEL
printf("nsXFormsModelElement::ProcessDeferredBinds()\n");
#endif
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
if (!doc) {
@ -2614,17 +2619,16 @@ nsXFormsModelElement::ProcessDeferredBinds(nsIDOMDocument *aDoc)
doc->SetProperty(nsXFormsAtoms::readyForBindProperty, doc);
nsCOMArray<nsIXFormsControlBase> *deferredBindList =
NS_STATIC_CAST(nsCOMArray<nsIXFormsControlBase> *,
nsCOMArray<nsIXFormsControl> *deferredBindList =
NS_STATIC_CAST(nsCOMArray<nsIXFormsControl> *,
doc->GetProperty(nsXFormsAtoms::deferredBindListProperty));
if (deferredBindList) {
for (int i = 0; i < deferredBindList->Count(); ++i) {
nsIXFormsControlBase *base = deferredBindList->ObjectAt(i);
if (base) {
base->Bind();
base->Refresh();
base->SetOnDeferredBindList(PR_FALSE);
for (PRInt32 i = 0; i < deferredBindList->Count(); ++i) {
nsIXFormsControl *control = deferredBindList->ObjectAt(i);
if (control) {
control->BindToModel(PR_FALSE);
control->SetOnDeferredBindList(PR_FALSE);
}
}

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

@ -248,7 +248,7 @@ public:
NS_IMETHOD OnCreated(nsIXTFGenericElementWrapper *aWrapper);
// nsIXFormsControlBase overrides
NS_IMETHOD Bind() {
NS_IMETHOD Bind(PRBool *aContextChanged) {
// dummy method, so does nothing
return NS_OK;
};
@ -275,7 +275,7 @@ public:
* @param aControl XForms control waiting to be bound
*/
static NS_HIDDEN_(nsresult) DeferElementBind(nsIDOMDocument *aDoc,
nsIXFormsControlBase *aControl);
nsIXFormsControl *aControl);
static nsresult NeedsPostRefresh(nsIXFormsControl* aControl);
@ -333,8 +333,11 @@ private:
nsXFormsEvent aOnEvent);
/**
* Call the Bind() and Refresh() on controls which was deferred because
* the model was not ready.
* Handle controls bindings which was deferred because the model was not
* ready.
*
* @note Only registers the controls with the model. Does not setup
* bindings, etc.
*
* @param aDoc Document that contains the XForms control
*/
@ -397,7 +400,7 @@ private:
* Indicates whether all controls should be refreshed on the next Refresh()
* run.
*/
PRPackedBool mNeedsRefresh;
PRPackedBool mRebindAllControls;
/**
* Indicates whether instance elements have been initialized

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

@ -77,7 +77,7 @@ class nsXFormsOutputElement : public nsXFormsDelegateStub
{
public:
// nsIXFormsControl
NS_IMETHOD Bind();
NS_IMETHOD Bind(PRBool* aContextChanged);
NS_IMETHOD Refresh();
NS_IMETHOD GetBoundNode(nsIDOMNode **aBoundNode);
@ -105,12 +105,12 @@ nsXFormsOutputElement::nsXFormsOutputElement()
// nsIXFormsControl
nsresult
nsXFormsOutputElement::Bind()
nsXFormsOutputElement::Bind(PRBool *aContextChanged)
{
SetDOMStringToNull(mValue);
mUseValueAttribute = PR_FALSE;
nsresult rv = nsXFormsDelegateStub::Bind();
nsresult rv = nsXFormsDelegateStub::Bind(aContextChanged);
NS_ENSURE_SUCCESS(rv, rv);
if (!mHasParent || !mElement || rv == NS_OK_XFORMS_DEFERRED)

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

@ -192,9 +192,6 @@ class nsXFormsRepeatElement : public nsXFormsDelegateStub,
public nsIXFormsRepeatElement
{
protected:
/** True while children are being added */
PRBool mAddingChildren;
/**
* The current repeat-index, 0 if no row is selected (can happen for nested
* repeats) or there are no rows at all.
@ -218,11 +215,21 @@ protected:
* ResetInnerRepeats().
*/
PRUint32 mLevel;
/**
* The number of "repeat rows" the repeat currently have unrolled.
*/
PRUint32 mCurrentRowCount;
/**
* True while children are being added
*/
PRPackedBool mAddingChildren;
/**
* Are we a parent for nested repeats
*/
PRBool mIsParent;
PRPackedBool mIsParent;
/**
* The currently selected repeat (nested repeats)
@ -288,11 +295,24 @@ protected:
*/
void SanitizeIndex(PRUint32 *aIndex, PRBool aIsScroll = PR_FALSE);
/**
* Unroll the template content into the visual rows by cloning template
* content and inserting into contextcontainers.
*/
nsresult UnrollRows(nsIDOMXPathResult *aNodeset);
/**
* Returns either the anonymous content of the repeat or null;
*/
already_AddRefed<nsIDOMElement> GetAnonymousContent();
/**
* Insert the template content for a repeat node into the given node.
*
* @param aNode The node to insert content into.
*/
nsresult InsertTemplateContent(nsIDOMNode *aNode);
public:
NS_DECL_ISUPPORTS_INHERITED
@ -305,7 +325,7 @@ public:
NS_IMETHOD DoneAddingChildren();
// nsIXFormsControl
NS_IMETHOD Bind();
NS_IMETHOD Bind(PRBool *aContextChanged);
NS_IMETHOD Refresh();
NS_IMETHOD TryFocus(PRBool* aOK);
NS_IMETHOD IsEventTarget(PRBool *aOK);
@ -315,10 +335,11 @@ public:
// nsXFormsRepeatElement
nsXFormsRepeatElement() :
mAddingChildren(PR_FALSE),
mCurrentIndex(0),
mMaxIndex(0),
mLevel(1),
mCurrentRowCount(0),
mAddingChildren(PR_FALSE),
mIsParent(PR_FALSE)
{}
@ -559,10 +580,17 @@ nsXFormsRepeatElement::IndexHasChanged()
// they are rebound and refreshed().
nsCOMArray<nsIXFormsControl> indexes(mIndexUsers);
PRBool contextChange;
nsresult rv;
for (PRInt32 i = 0; i < indexes.Count(); ++i) {
nsCOMPtr<nsIXFormsControl> control = indexes[i];
control->Bind();
control->Refresh();
rv = control->Bind(&contextChange);
NS_ENSURE_SUCCESS(rv, rv);
rv = control->Refresh();
if (contextChange) {
// XXX: bug 335525
NS_WARNING("Need to refresh children of index() user!\n");
}
}
return NS_OK;
@ -677,52 +705,164 @@ nsXFormsRepeatElement::HandleNodeInsert(nsIDOMNode *aNode)
// nsXFormsControl
NS_IMETHODIMP
nsXFormsRepeatElement::Bind()
nsXFormsRepeatElement::Bind(PRBool *aContextChanged)
{
NS_ENSURE_ARG(aContextChanged);
nsCOMPtr<nsIDOMDocument> domDoc;
mElement->GetOwnerDocument(getter_AddRefs(domDoc));
if (!nsXFormsUtils::IsDocumentReadyForBind(domDoc)) {
nsXFormsModelElement::DeferElementBind(domDoc, this);
*aContextChanged = PR_FALSE;
return NS_OK_XFORMS_DEFERRED;
}
return BindToModel(PR_TRUE);
nsresult rv = BindToModel();
NS_ENSURE_SUCCESS(rv, rv);
*aContextChanged = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsXFormsRepeatElement::Refresh()
nsresult
nsXFormsRepeatElement::InsertTemplateContent(nsIDOMNode *aNode)
{
if (!mElement || mAddingChildren || mIsParent) {
return NS_OK;
NS_ENSURE_ARG(aNode);
nsCOMPtr<nsIDOMNode> child;
nsresult rv = mElement->GetFirstChild(getter_AddRefs(child));
NS_ENSURE_SUCCESS(rv, rv);
while (child) {
nsCOMPtr<nsIDOMNode> childClone;
rv = CloneNode(child, getter_AddRefs(childClone));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> newNode;
rv = aNode->AppendChild(childClone, getter_AddRefs(newNode));
NS_ENSURE_SUCCESS(rv, rv);
rv = child->GetNextSibling(getter_AddRefs(newNode));
NS_ENSURE_SUCCESS(rv, rv);
child = newNode;
}
return NS_OK;
}
nsresult
nsXFormsRepeatElement::UnrollRows(nsIDOMXPathResult *aNodeset)
{
NS_ENSURE_STATE(mElement);
nsCOMPtr<nsIDOMElement> anon = GetAnonymousContent();
if (!anon) {
return NS_OK;
}
nsPostRefresh postRefresh = nsPostRefresh();
nsresult rv;
// Clear any existing children
nsCOMPtr<nsIDOMNode> cNode;
anon->GetFirstChild(getter_AddRefs(cNode));
while (cNode) {
nsCOMPtr<nsIDOMNode> retNode;
anon->RemoveChild(cNode, getter_AddRefs(retNode));
anon->GetFirstChild(getter_AddRefs(cNode));
if (!aNodeset || !mModel) {
mMaxIndex = 0;
} else {
PRUint32 contextSize;
rv = aNodeset->GetSnapshotLength(&contextSize);
NS_ENSURE_SUCCESS(rv, rv);
mMaxIndex = contextSize;
}
// Get the nodeset we are bound to
nsCOMPtr<nsIDOMXPathResult> result;
nsCOMPtr<nsIModelElementPrivate> model;
rv = ProcessNodeBinding(NS_LITERAL_STRING("nodeset"),
nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
getter_AddRefs(result),
getter_AddRefs(model));
// STEP 1: Remove rows
nsCOMPtr<nsIDOMNode> tmp;
if (mMaxIndex < mCurrentRowCount) {
for (PRUint32 i = mMaxIndex; i < mCurrentRowCount; ++i) {
nsCOMPtr<nsIDOMNode> lastChild;
rv = anon->GetLastChild(getter_AddRefs(lastChild));
NS_ENSURE_SUCCESS(rv, rv);
rv = anon->RemoveChild(lastChild, getter_AddRefs(tmp));
NS_ENSURE_SUCCESS(rv, rv);
}
} else if (mMaxIndex > mCurrentRowCount) {
// STEP 2: Add rows
nsCOMPtr<nsIDOMDocument> domDoc;
rv = anon->GetOwnerDocument(getter_AddRefs(domDoc));
NS_ENSURE_SUCCESS(rv, rv);
for (PRUint32 i = mCurrentRowCount; i < mMaxIndex; ++i) {
// Create <contextcontainer>
nsCOMPtr<nsIDOMElement> container;
rv = domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
NS_LITERAL_STRING("contextcontainer"),
getter_AddRefs(container));
NS_ENSURE_SUCCESS(rv, rv);
container->SetAttribute(NS_LITERAL_STRING("class"),
NS_LITERAL_STRING("xf-repeat-item"));
if (NS_FAILED(rv) | !result | !model)
return rv;
rv = anon->AppendChild(container, getter_AddRefs(tmp));
NS_ENSURE_SUCCESS(rv, rv);
}
}
// Repeat has no content
if (!mMaxIndex) {
mCurrentRowCount = 0;
return NS_OK;
}
// STEP 3: Update context on rows
nsCOMPtr<nsIDOMNode> child;
rv = anon->GetFirstChild(getter_AddRefs(child));
NS_ENSURE_SUCCESS(rv, rv);
for (PRUint32 i = 0; i < mMaxIndex; ++i) {
NS_ASSERTION(child, "Unrolled content does not match index size?!");
// Get context node
nsCOMPtr<nsIDOMNode> contextNode;
rv = aNodeset->SnapshotItem(i, getter_AddRefs(contextNode));
NS_ENSURE_SUCCESS(rv, rv);
// Set context node, position, and size
nsCOMPtr<nsIXFormsContextControl> childContext = do_QueryInterface(child);
NS_ASSERTION(childContext,
"content child not implementing nsIXFormsContextControl?!");
rv = childContext->SetContext(contextNode, i + 1, mMaxIndex);
NS_ENSURE_SUCCESS(rv, rv);
// Next child
rv = child->GetNextSibling(getter_AddRefs(tmp));
NS_ENSURE_SUCCESS(rv, rv);
tmp.swap(child);
}
// Rows deleted, nothing more to do
if (mCurrentRowCount >= mMaxIndex) {
mCurrentRowCount = mMaxIndex;
return NS_OK;
}
// STEP 4: Insert template content into newly created rows
nsCOMPtr<nsIDOMNodeList> containerList;
rv = anon->GetChildNodes(getter_AddRefs(containerList));
NS_ENSURE_SUCCESS(rv, rv);
for (PRUint32 i = mCurrentRowCount; i < mMaxIndex; ++i) {
nsCOMPtr<nsIDOMNode> container;
rv = containerList->Item(i, getter_AddRefs(container));
NS_ENSURE_SUCCESS(rv, rv);
rv = InsertTemplateContent(container);
NS_ENSURE_SUCCESS(rv, rv);
}
mCurrentRowCount = mMaxIndex;
return NS_OK;
}
NS_IMETHODIMP
nsXFormsRepeatElement::Refresh()
{
if (mAddingChildren || mIsParent)
return NS_OK;
nsPostRefresh postRefresh = nsPostRefresh();
/// @todo The spec says: "This node-set must consist of contiguous child
/// element nodes, with the same local name and namespace name of a common
@ -732,81 +872,20 @@ nsXFormsRepeatElement::Refresh()
///
/// Can/should we check this somehow? (XXX)
PRUint32 contextSize;
rv = result->GetSnapshotLength(&contextSize);
// Get the nodeset we are bound to
nsresult rv;
nsCOMPtr<nsIDOMXPathResult> result;
rv = ProcessNodeBinding(NS_LITERAL_STRING("nodeset"),
nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
getter_AddRefs(result));
NS_ENSURE_SUCCESS(rv, rv);
if (!contextSize)
return NS_OK;
// Get model ID
nsCOMPtr<nsIDOMElement> modelElement = do_QueryInterface(model);
NS_ENSURE_TRUE(modelElement, NS_ERROR_FAILURE);
nsAutoString modelID;
modelElement->GetAttribute(NS_LITERAL_STRING("id"), modelID);
// Get DOM document
nsCOMPtr<nsIDOMDocument> domDoc;
rv = mElement->GetOwnerDocument(getter_AddRefs(domDoc));
// Unroll the repeat rows
rv = UnrollRows(result);
NS_ENSURE_SUCCESS(rv, rv);
mMaxIndex = contextSize;
for (PRUint32 i = 1; i < mMaxIndex + 1; ++i) {
// Create <contextcontainer>
nsCOMPtr<nsIDOMElement> riElement;
rv = domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
NS_LITERAL_STRING("contextcontainer"),
getter_AddRefs(riElement));
NS_ENSURE_SUCCESS(rv, rv);
riElement->SetAttribute(NS_LITERAL_STRING("class"),
NS_LITERAL_STRING("xf-repeat-item"));
// Set model as attribute
if (!modelID.IsEmpty()) {
riElement->SetAttribute(NS_LITERAL_STRING("model"), modelID);
}
// Get context node
nsCOMPtr<nsIXFormsContextControl> riContext = do_QueryInterface(riElement);
NS_ENSURE_TRUE(riContext, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMNode> contextNode;
rv = result->SnapshotItem(i - 1, getter_AddRefs(contextNode));
NS_ENSURE_SUCCESS(rv, rv);
// Set context node, position, and size
rv = riContext->SetContext(contextNode, i, contextSize);
NS_ENSURE_SUCCESS(rv, rv);
// We need to insert the context node before adding the children, or the
// children will fail to set up their proper XForms context.
nsCOMPtr<nsIDOMNode> domNode;
rv = anon->AppendChild(riElement, getter_AddRefs(domNode));
NS_ENSURE_SUCCESS(rv, rv);
// Iterate over template children, clone them, and append them to
// \<contextcontainer\>
nsCOMPtr<nsIDOMNode> child;
rv = mElement->GetFirstChild(getter_AddRefs(child));
NS_ENSURE_SUCCESS(rv, rv);
while (child) {
/// XXX the node probably refreshes itself twice here, once on cloning
/// and once when it's inserted ... that's not necessary.
nsCOMPtr<nsIDOMNode> childClone;
rv = CloneNode(child, getter_AddRefs(childClone));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> newNode;
rv = riElement->AppendChild(childClone, getter_AddRefs(newNode));
NS_ENSURE_SUCCESS(rv, rv);
rv = child->GetNextSibling(getter_AddRefs(newNode));
NS_ENSURE_SUCCESS(rv, rv);
child = newNode;
}
}
// Maintain the index
if (mCurrentIndex) {
// somebody might have been fooling around with our children since last
// refresh (either using delete or through script, so check the index