зеркало из https://github.com/mozilla/pjs.git
[XForms] Improve refresh inefficiencies with selects, items. Bug 329935, r=smaug+me, patch by aaronr
This commit is contained in:
Родитель
812abbe9e8
Коммит
b30198a897
|
@ -50,4 +50,9 @@ interface nsIXFormsControlBase : nsISupports
|
|||
* instance data.
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
/**
|
||||
* Determines whether this control is already on the deferred bind list
|
||||
*/
|
||||
attribute boolean onDeferredBindList;
|
||||
};
|
||||
|
|
|
@ -234,6 +234,21 @@ nsXFormsControlStubBase::GetUsesModelBinding(PRBool *aRes)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXFormsControlStubBase::GetOnDeferredBindList(PRBool *aOnList)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aOnList);
|
||||
*aOnList = mOnDeferredBindList;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXFormsControlStubBase::SetOnDeferredBindList(PRBool aPutOnList)
|
||||
{
|
||||
mOnDeferredBindList = aPutOnList;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXFormsControlStubBase::MaybeAddToModel(nsIModelElementPrivate *aOldModel,
|
||||
nsIXFormsControl *aParent)
|
||||
|
|
|
@ -88,6 +88,8 @@ public:
|
|||
nsIDOMXPathResult **aResult = nsnull);
|
||||
NS_IMETHOD Bind();
|
||||
NS_IMETHOD Refresh();
|
||||
NS_IMETHOD GetOnDeferredBindList(PRBool *onList);
|
||||
NS_IMETHOD SetOnDeferredBindList(PRBool putOnList);
|
||||
NS_IMETHOD TryFocus(PRBool* aOK);
|
||||
NS_IMETHOD IsEventTarget(PRBool *aOK);
|
||||
NS_IMETHOD GetUsesModelBinding(PRBool *aRes);
|
||||
|
@ -144,6 +146,7 @@ public:
|
|||
mPreventLoop(PR_FALSE),
|
||||
mUsesModelBinding(PR_FALSE),
|
||||
mAppearDisabled(PR_FALSE),
|
||||
mOnDeferredBindList(PR_FALSE),
|
||||
mBindAttrsCount(0)
|
||||
{};
|
||||
|
||||
|
@ -183,15 +186,21 @@ protected:
|
|||
PRPackedBool mAppearDisabled;
|
||||
|
||||
/**
|
||||
* Array of repeat-elements of which the control uses repeat-index.
|
||||
* Indicates whether this control is already on the deferred bind list
|
||||
*/
|
||||
nsCOMArray<nsIXFormsRepeatElement> mIndexesUsed;
|
||||
PRPackedBool mOnDeferredBindList;
|
||||
|
||||
/**
|
||||
* Used to keep track of whether this control has any single node binding
|
||||
* attributes.
|
||||
*/
|
||||
PRInt32 mBindAttrsCount;
|
||||
PRInt8 mBindAttrsCount;
|
||||
|
||||
/**
|
||||
* List of repeats that the node binding depends on. This happens when using
|
||||
* the index() function in the binding expression.
|
||||
*/
|
||||
nsCOMArray<nsIXFormsRepeatElement> mIndexesUsed;
|
||||
|
||||
/**
|
||||
* Does control have a binding attribute?
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
#include "nsIDOMDocumentXBL.h"
|
||||
#include "nsIProgrammingLanguage.h"
|
||||
#include "nsDOMError.h"
|
||||
#include "nsXFormsControlStub.h"
|
||||
|
||||
#define XFORMS_LAZY_INSTANCE_BINDING \
|
||||
"chrome://xforms/content/xforms.xml#xforms-lazy-instance"
|
||||
|
@ -501,6 +502,7 @@ static nsIAtom* sModelPropsList[eModel__count];
|
|||
// This can be nsVoidArray because elements will remove
|
||||
// themselves from the list if they are deleted during refresh.
|
||||
static nsVoidArray* sPostRefreshList = nsnull;
|
||||
static nsVoidArray* sContainerPostRefreshList = nsnull;
|
||||
|
||||
static PRInt32 sRefreshing = 0;
|
||||
|
||||
|
@ -517,7 +519,13 @@ nsPostRefresh::~nsPostRefresh()
|
|||
#ifdef DEBUG_smaug
|
||||
printf("~nsPostRefresh\n");
|
||||
#endif
|
||||
if (sPostRefreshList && sRefreshing == 1) {
|
||||
|
||||
if (sRefreshing != 1) {
|
||||
--sRefreshing;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sPostRefreshList) {
|
||||
while (sPostRefreshList->Count()) {
|
||||
// Iterating this way because refresh can lead to
|
||||
// additions/deletions in sPostRefreshList.
|
||||
|
@ -535,7 +543,26 @@ nsPostRefresh::~nsPostRefresh()
|
|||
sPostRefreshList = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
--sRefreshing;
|
||||
|
||||
// process sContainerPostRefreshList after we've decremented sRefreshing.
|
||||
// container->refresh below could ask for ContainerNeedsPostRefresh which
|
||||
// will add an item to the sContainerPostRefreshList if sRefreshing > 0.
|
||||
// So keeping this under sRefreshing-- will avoid an infinite loop.
|
||||
if (sContainerPostRefreshList) {
|
||||
while (sContainerPostRefreshList->Count()) {
|
||||
PRInt32 last = sContainerPostRefreshList->Count() - 1;
|
||||
nsIXFormsControl* container =
|
||||
NS_STATIC_CAST(nsIXFormsControl*, sContainerPostRefreshList->ElementAt(last));
|
||||
sContainerPostRefreshList->RemoveElementAt(last);
|
||||
if (container) {
|
||||
container->Refresh();
|
||||
}
|
||||
}
|
||||
delete sContainerPostRefreshList;
|
||||
sContainerPostRefreshList = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
const nsVoidArray*
|
||||
|
@ -564,11 +591,43 @@ nsXFormsModelElement::NeedsPostRefresh(nsIXFormsControl* aControl)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsXFormsModelElement::ContainerNeedsPostRefresh(nsIXFormsControl* aControl)
|
||||
{
|
||||
|
||||
if (sRefreshing) {
|
||||
if (!sContainerPostRefreshList) {
|
||||
sContainerPostRefreshList = new nsVoidArray();
|
||||
if (!sContainerPostRefreshList) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (sContainerPostRefreshList->IndexOf(aControl) < 0) {
|
||||
sContainerPostRefreshList->AppendElement(aControl);
|
||||
}
|
||||
|
||||
// return PR_TRUE to show that the control's refresh will be delayed,
|
||||
// whether as a result of this call or a previous call to this function.
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
// Delaying the refresh doesn't make any sense. But since this
|
||||
// function may be called from inside the control node's refresh already,
|
||||
// we shouldn't just assume that we can call the refresh here. So
|
||||
// we'll just return PR_FALSE to signal that we couldn't delay the refresh.
|
||||
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
nsXFormsModelElement::CancelPostRefresh(nsIXFormsControl* aControl)
|
||||
{
|
||||
if (sPostRefreshList)
|
||||
sPostRefreshList->RemoveElement(aControl);
|
||||
|
||||
if (sContainerPostRefreshList)
|
||||
sContainerPostRefreshList->RemoveElement(aControl);
|
||||
}
|
||||
|
||||
nsXFormsModelElement::nsXFormsModelElement()
|
||||
|
@ -2381,9 +2440,25 @@ nsXFormsModelElement::DeferElementBind(nsIDOMDocument *aDoc,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// We are using a PRBool on each control to mark whether the control is on the
|
||||
// deferredBindList. We are running into too many scenarios where a control
|
||||
// could be added more than once which will lead to inefficiencies because
|
||||
// calling bind and refresh on some controls is getting pretty expensive.
|
||||
// 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));
|
||||
NS_ENSURE_STATE(controlBase);
|
||||
|
||||
PRBool onList = PR_FALSE;
|
||||
controlBase->GetOnDeferredBindList(&onList);
|
||||
if (onList) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMArray<nsIXFormsControlBase> *deferredBindList =
|
||||
NS_STATIC_CAST(nsCOMArray<nsIXFormsControlBase> *,
|
||||
doc->GetProperty(nsXFormsAtoms::deferredBindListProperty));
|
||||
NS_STATIC_CAST(nsCOMArray<nsIXFormsControlBase> *,
|
||||
doc->GetProperty(nsXFormsAtoms::deferredBindListProperty));
|
||||
|
||||
if (!deferredBindList) {
|
||||
deferredBindList = new nsCOMArray<nsIXFormsControlBase>(16);
|
||||
|
@ -2398,6 +2473,7 @@ nsXFormsModelElement::DeferElementBind(nsIDOMDocument *aDoc,
|
|||
// when an element is trying to bind and should use its parent as a context
|
||||
// for the xpath evaluation but the parent isn't bound yet.
|
||||
deferredBindList->AppendObject(aControl);
|
||||
controlBase->SetOnDeferredBindList(PR_TRUE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2425,6 +2501,7 @@ nsXFormsModelElement::ProcessDeferredBinds(nsIDOMDocument *aDoc)
|
|||
if (base) {
|
||||
base->Bind();
|
||||
base->Refresh();
|
||||
base->SetOnDeferredBindList(PR_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -253,6 +253,16 @@ public:
|
|||
return NS_OK;
|
||||
};
|
||||
|
||||
NS_IMETHOD GetOnDeferredBindList(PRBool *onList) {
|
||||
// dummy method, so does nothing
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
NS_IMETHOD SetOnDeferredBindList(PRBool putOnList) {
|
||||
// dummy method, so does nothing
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
// Called after nsXFormsAtoms is registered
|
||||
static NS_HIDDEN_(void) Startup();
|
||||
|
||||
|
@ -268,6 +278,16 @@ public:
|
|||
nsIXFormsControlBase *aControl);
|
||||
|
||||
static nsresult NeedsPostRefresh(nsIXFormsControl* aControl);
|
||||
|
||||
// Sometimes a child that is on the post refresh list will want to force
|
||||
// its containing xforms control to refresh. Like if an xf:item's label
|
||||
// refreshes, it wants to reflect any of its changes in the xf:select or
|
||||
// xf:select1 that contains it. This function will try to minimize that by
|
||||
// allowing xforms controls that function as containers (like select/select1)
|
||||
// to defer their refresh until most or all of its children have refreshed.
|
||||
// Well, at least until every control on the sPostRefreshList has been
|
||||
// processed. This will return PR_TRUE if the refresh will be deferred.
|
||||
static PRBool ContainerNeedsPostRefresh(nsIXFormsControl* aControl);
|
||||
static void CancelPostRefresh(nsIXFormsControl* aControl);
|
||||
|
||||
/**
|
||||
|
|
|
@ -71,6 +71,9 @@ public:
|
|||
NS_IMETHOD BeginAddingChildren();
|
||||
NS_IMETHOD DoneAddingChildren();
|
||||
|
||||
// nsIXFormsControl
|
||||
NS_IMETHOD Refresh();
|
||||
|
||||
nsXFormsSelect1Element(const nsAString& aType)
|
||||
: nsXFormsDelegateStub(aType)
|
||||
{}
|
||||
|
@ -138,6 +141,19 @@ nsXFormsSelect1Element::ChildRemoved(PRUint32 aIndex)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIXFormsControl
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXFormsSelect1Element::Refresh()
|
||||
{
|
||||
PRBool delayRefresh = nsXFormsModelElement::ContainerNeedsPostRefresh(this);
|
||||
if (delayRefresh) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return nsXFormsDelegateStub::Refresh();
|
||||
}
|
||||
|
||||
NS_HIDDEN_(nsresult)
|
||||
NS_NewXFormsSelect1Element(nsIXTFElement **aResult)
|
||||
{
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "nsIXFormsUIWidget.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsXFormsModelElement.h"
|
||||
|
||||
class nsXFormsSelectElement : public nsXFormsDelegateStub
|
||||
{
|
||||
|
@ -65,6 +66,9 @@ public:
|
|||
NS_IMETHOD ChildAppended(nsIDOMNode *aChild);
|
||||
NS_IMETHOD ChildRemoved(PRUint32 aIndex);
|
||||
|
||||
// nsIXFormsControl
|
||||
NS_IMETHOD Refresh();
|
||||
|
||||
#ifdef DEBUG_smaug
|
||||
virtual const char* Name() { return "select"; }
|
||||
#endif
|
||||
|
@ -86,6 +90,8 @@ nsXFormsSelectElement::OnCreated(nsIXTFBindableElementWrapper *aWrapper)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIXTFElement overrides
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXFormsSelectElement::ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex)
|
||||
{
|
||||
|
@ -107,6 +113,19 @@ nsXFormsSelectElement::ChildRemoved(PRUint32 aIndex)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIXFormsControl
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXFormsSelectElement::Refresh()
|
||||
{
|
||||
PRBool delayRefresh = nsXFormsModelElement::ContainerNeedsPostRefresh(this);
|
||||
if (delayRefresh) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return nsXFormsDelegateStub::Refresh();
|
||||
}
|
||||
|
||||
NS_HIDDEN_(nsresult)
|
||||
NS_NewXFormsSelectElement(nsIXTFElement **aResult)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче