Closed Bug 284469 Opened 20 years ago Closed 20 years ago

Calling ProcessNodeBind unnecessarily

Categories

(Core Graveyard :: XForms, defect)

x86
All
defect
Not set
normal

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: aaronr, Assigned: aaronr)

Details

Attachments

(2 files, 9 obsolete files)

Right now we'll call ProcessNodeBind for all nsXFormsContrlStub controls whether
they have any kind of binding attribute or not.  It causes a lot of unnecessary
processing during document load.
Status: NEW → ASSIGNED
I'm going to set a flag on the nsXFormsControlStub::kElementFlags during
::AttributeSet if a binding attribute is getting set or removed.  Then we can
check for that flag inside each control's ::Bind (or in the default
nsXFormsControlStub::ResetBoundNode) and only continue on if that flag is set. 
Seems to cut down on the calls to ProcessNodeBind by 50-75% in some of my testcases.
Attached patch first attempt (obsolete) — Splinter Review
This works for me.
Attached patch second attempt (obsolete) — Splinter Review
fixed output element problem.  Also changed repeat element to seperate
::Refresh from ::Bind so that ::Refresh doesn't get called twice in some
instances (like via nsXFormsControlStub::DocumentChanged).

I still need to add RemoveAttribute handling so that the flag properly reflects
the element's state.
Attachment #176061 - Attachment is obsolete: true
Attached patch added attribute removal support (obsolete) — Splinter Review
probably not ready for prime time, yet.  But contains support for attribute
removal on all controls.
Attachment #176094 - Attachment is obsolete: true
(In reply to comment #4)
> Created an attachment (id=176310) [edit]
> added attribute removal support
> 
> probably not ready for prime time, yet.  But contains support for attribute
> removal on all controls.

Does this patch correctly handle having a <output bind="b1" ref="n1"/>, and then
remove either of the attributes? Moving @bind should make the output use @ref
for binding instead, and removing @ref should do nothing.
(In reply to comment #5)
> (In reply to comment #4)
> > Created an attachment (id=176310) [edit] [edit]
> > added attribute removal support
> > 
> > probably not ready for prime time, yet.  But contains support for attribute
> > removal on all controls.
> 
> Does this patch correctly handle having a <output bind="b1" ref="n1"/>, and then
> remove either of the attributes? Moving @bind should make the output use @ref
> for binding instead, and removing @ref should do nothing.

Nope, not yet.  My next (hopefully final) patch should handle that.
this should be pretty close.  Only extra thing that I could think of was
somehow merging the bind/ref attribute detection into a central function, but
then we'd end up having to execute one more GetAttribute than we really need to
and it would add another method to all controls.  But it would clean up the
code a bunch if I did do it.

Also, I don't know what we should do with the MDG.  If a control has a bind
that makes it irrelevant and we remove that bind, then it should be relevant,
right?	But how should we toggle the attribute on the control?	Something for
me to think about tomorrow.
Attachment #176310 - Attachment is obsolete: true
Attached file testcase using various controls (obsolete) —
testcase with repeat, input, output, select and trigger.
Attached patch hopefully final patch (obsolete) — Splinter Review
This latest patch includes removing the MIP attributes on the control.
Attachment #176677 - Attachment is obsolete: true
Attachment #178982 - Flags: review?(allan)
Attached file updated testcase
updated testcase.  Note that the checkbox turning into an input field but not
accepting input is a known problem.  Doron is opening a bug on it, probably
Attachment #176678 - Attachment is obsolete: true
Comment on attachment 178982 [details] [diff] [review]
hopefully final patch

>Index: nsXFormsControlStub.cpp
>===================================================================

Boy, there is a lot of duplicated code here.

> NS_IMETHODIMP
> nsXFormsControlStub::AttributeSet(nsIAtom *aName, const nsAString &aValue)
> {
>+  // have to make sure that ELEMENT_WITH_SNB_ATTR flag stays accurate if we
>+  // are being told our single node binding attributes are changing.
>+
>+  PRBool bindAndRefresh = PR_FALSE;
>+  if (aName == nsXFormsAtoms::bind ) {
>+    PRUint32 flags = GetElementFlags();
>+    bindAndRefresh = PR_TRUE;
>+    if (!aValue.IsEmpty()) {
>+      flags |= nsXFormsUtils::ELEMENT_WITH_SNB_ATTR;
>+    } else { 
>+      nsAutoString attrStr;
>+      mElement->GetAttribute(NS_LITERAL_STRING("ref"), attrStr);
>+      if (attrStr.IsEmpty()) {
>+        flags &= ~nsXFormsUtils::ELEMENT_WITH_SNB_ATTR;
>+        ResetProperties();
>+      }
>+    }
>+    SetElementFlags(flags);
>+  } else if (aName == nsXFormsAtoms::ref) {
>+    PRUint32 flags = GetElementFlags();
>+    bindAndRefresh = PR_TRUE;
>+    if (!aValue.IsEmpty()) {
>+      flags |= nsXFormsUtils::ELEMENT_WITH_SNB_ATTR;
>+    } else { 
>+      nsAutoString attrStr;
>+      mElement->GetAttribute(NS_LITERAL_STRING("bind"), attrStr);
>+      if (attrStr.IsEmpty()) {
>+        flags &= ~nsXFormsUtils::ELEMENT_WITH_SNB_ATTR;
>+        ResetProperties();
>+      }
>+    }
>+    SetElementFlags(flags);
>+  } else if (aName == nsXFormsAtoms::model) {
>+    // make sure that we run through bind and refresh again if @model added

"@model set"

>+    bindAndRefresh = PR_TRUE;
>+  }
>+      
>+  if (bindAndRefresh) {
>+    Bind();
>+    Refresh();
>+  }
>+
>+  return NS_OK;
>+}

Here is my quick go at it:
{
  if (aName == nsXFormsAtoms::bind || aName == nsXFormsAtoms::ref) {
    PRUint32 flags = GetElementFlags();
    if (!aValue.IsEmpty()) {
      flags |= nsXFormsUtils::ELEMENT_WITH_SNB_ATTR;
    } else { 
      nsAutoString attrStr;
      mElement->GetAttribute(aName == nsXFormsAtoms::bind ?
NS_LITERAL_STRING("ref") : NS_LITERAL_STRING("ref"), attrStr);
      if (attrStr.IsEmpty()) {
	flags &= ~nsXFormsUtils::ELEMENT_WITH_SNB_ATTR;
	ResetProperties();
      }
    }
    SetElementFlags(flags);
  } else if (aName == nsXFormsAtoms::model) {
    // make sure that we run through bind and refresh again if @model set
  } else {
    return NS_OK;
  }

  Bind();
  Refresh();

  return NS_OK;
}


>+NS_IMETHODIMP
>+nsXFormsControlStub::AttributeRemoved(nsIAtom *aName)
>+{

Apply the same tactics as above, and do consider using a common function.

 // nsIXFormsContextControl
@@ -403,8 +497,29 @@ nsXFormsControlStub::GetContext(nsAStrin
   nsCOMPtr<nsIDOMElement> model = do_QueryInterface(mModel);
   if (model) {
     model->GetAttribute(NS_LITERAL_STRING("id"), aModelID);
   }

   return NS_OK;
 }

>+// This function removes all of the attributes that may have been added to the
>+// control due to binding with an instance node.

Move comment to header file.

>+NS_IMETHODIMP
>+nsXFormsControlStub::ResetProperties()

Not an interface-function is it? Return |nsresult|, or rather |void|, since you
are not returning anything.

>+  mElement->RemoveAttribute(NS_LITERAL_STRING("valid"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("invalid"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("enabled"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("disabled"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("required"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("optional"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("read-only"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("read-write"));

:( We should really use atoms... (ignore me, I just hate all the string
comparisons we do)


>Index: nsXFormsControlStub.h
>===================================================================

>+  NS_IMETHOD ResetProperties();

This is not an interface function is it? Then return void.

>+  PRUint32 GetElementFlags() {return mElementFlags;}
>+  void     SetElementFlags(PRUint32 flags) {mElementFlags = flags;}

Add some space between the braces.

>+  /**
>+   * The element flags for the controls passed to
>+   * nsXFormsUtils:EvaluateNodeBinding()
>+   */
>+  PRUint32 mElementFlags;

Add comment about the new usage of the flags.

>Index: nsXFormsOutputElement.cpp
>===================================================================

>+NS_IMETHODIMP
>+nsXFormsOutputElement::AttributeSet(nsIAtom *aName, const nsAString &aValue)
>+{
[...]

Ouch.

How about doing a |nsXFormsControlStrub::AttributeSetInternal(nsIAtom*,
nsAString&, PRBool isOutput)| and doing the value check there?

>+NS_IMETHODIMP
>+nsXFormsOutputElement::AttributeRemoved(nsIAtom *aName)
>+{

[...]

Ouch... same as above.

>+void nsXFormsOutputElement::MaybeRemoveFromModel(nsIAtom *aName)

void on seperate line.

>Index: nsXFormsRepeatElement.cpp
>===================================================================
>+void nsXFormsRepeatElement::MaybeBindAndRefresh(nsIAtom *aName)

void on seperate line.

>+void nsXFormsRepeatElement::MaybeRemoveFromModel(nsIAtom *aName)

void on seperate line.

>Index: nsXFormsUtils.h
>===================================================================

>+    /**
>+     * The element has a "single node binding" attribute (either @bind or @ref)
>+     */

Or @value for output.


I'm not sure your flag-tactics is good. How about storing the SNB-value as a
counter that holds the number of non-empty binding attributes instead? Then you
just need to check whether the value == 0, and count up and down in
willset/set/remove. Would eliminate the |if remove bind, check ref (and
value)|-stuff.
Attachment #178982 - Flags: review?(allan) → review-
Da*n, a counter.  MUCH better idea than my flag.  I'll see if I can't rework my
logic using that approach.  Thanks.
Attached patch patch with Allan's approach (obsolete) — Splinter Review
Addresses Allan's concerns with last patch except for all of the
ns_literal_string's in nsXFormsControlStub::ResetProperties.  I didn't know if
we should add atoms for non-spec strings that aren't oft used, like
"read-write".  I can certainly make these into atoms and use f.x.
nsXFormsAtoms::required.ToString() instead.  Just let me know.
Attachment #178982 - Attachment is obsolete: true
Attachment #179232 - Flags: review?(allan)
Comment on attachment 179232 [details] [diff] [review]
patch with Allan's approach

oops, forgot the most important part...the test in ResetBoundNode!
Attachment #179232 - Flags: review?(allan)
Attached patch updated patch (obsolete) — Splinter Review
added the test in ResetBoundNode that makes sure that the binding attr count >
0 before calling ProcessNodeBinding.  I had forgotten to move it when I changed
from the flag approach to the counting approach.

In testcase I just tried (my alphatestcase) only 54 calls to
ProcessNodeBinding, as opposed to 153 before patch.
Attachment #179232 - Attachment is obsolete: true
Attachment #179235 - Flags: review?(allan)
Comment on attachment 179235 [details] [diff] [review]
updated patch

>+void
>+nsXFormsControlStub::ResetProperties()
>+{
>+  if (!mElement) {
>+    return;
>+  }
>+
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("valid"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("invalid"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("enabled"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("disabled"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("required"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("optional"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("read-only"));
>+  mElement->RemoveAttribute(NS_LITERAL_STRING("read-write"));
>+
>+  return;
>+}

That last return is kinda useless, not?

>+
>+void
>+nsXFormsControlStub::AddRemoveSNBAttr(nsIAtom *aName, 
>+                                      const nsAString &aValue)
>+{
>+  nsAutoString attrStr, attrValue;
>+  aName->ToString(attrStr);
>+  mElement->GetAttribute(attrStr, attrValue);
>+
>+  // if we are setting an single node binding attribute that we don't already
>+  // have, bump the count.
>+  if (!aValue.IsEmpty()) {
>+    if (attrValue.IsEmpty()) {
>+      ++mCountBindAttrs;
>+    }
>+  }
Why not if (!aValue.IsEmpty() && attrValue.IsEmpty()) ?
Comment on attachment 179235 [details] [diff] [review]
updated patch

>Index: extensions/xforms/nsXFormsControlStub.h
...
>+  void nsXFormsControlStub::MaybeRemoveFromModel(nsIAtom *aName, 
>+                                                 const nsAString &aValue);

*ahem* ...
Comment on attachment 179235 [details] [diff] [review]
updated patch

<beaufour:mega-nit-picking>

>Index: extensions/xforms/nsXFormsControlStub.cpp
>===================================================================
...
>+  // if we are setting an single node binding attribute that we don't already

_a_ single node

>+  // have, bump the count.
>+  if (!aValue.IsEmpty()) {
>+    if (attrValue.IsEmpty()) {
>+      ++mCountBindAttrs;
>+    }
>+  }
>+  else if (!attrValue.IsEmpty()) {

move else up


>Index: extensions/xforms/nsXFormsControlStub.h
>===================================================================
...
>+  /**
>+   * This function manages the mCountBindAttrs value.  mCountBindAttrs will
>+   * be incremented if a single node binding attribute is specified that isn't
>+   * currently on the control AND aValue isn't empty.  Otherwise, if the control
>+   * already contains this attribute with a non-empty value and aValue is empty,
>+   * then mCountBindAttrs is decremented.
>+   *
>+   *   aName -  Atom of the single node binding attribute ('bind', 'ref', etc.).

@param aName

>+   *            Using an atom to make it a tad harder to misuse by passing in
>+   *            any old string, for instance.  Since different controls may have
>+   *            different binding attrs, we do NO validation on the attr.  We
>+   *            assume that the caller is smart enough to only send us a
>+   *            binding attr atom.
>+   *   aValue - The string value that the SNB attribute is being set to.

@param aValue

>+  /** 
>+   * Used to keep track of whether this control has any single node binding
>+   * attributes.

Holds the number of non-empty binding attributes on the element. Used to limit
unnecessary refreshes etc. ?

>+   */
>+  PRInt32 mCountBindAttrs;
>+

'mBindAttrsCount' seems to be more natural?

>+
>+  /**
>+   * This function removes all of the attributes that may have been added to
>+   * the control due to binding with an instance node.
>+   */
>+  void ResetProperties();

We know it's a function :)

>+  /**
>+   * This function causes bind() and refresh() to be called if aName is
Bind() and Refresh()

>+   * the atom of a single node binding attribute for this control.  Called by
>+   * AttributeSet and AttributeRemoved.
>+   */
>+  void MaybeBindAndRefresh(nsIAtom *aName);

same.

>+  /**
>+   * Removes this control from its model's list of controls if a single node
>+   * binding attribute is removed.  Called by WillSetAttribute and
>+   * WillRemoveAttribute.
>+   * 
>+   * aName  - atom of the attribute being changed
>+   * aValue - value that the attribute is being changed to.

@param ...

>+   */
>+  void nsXFormsControlStub::MaybeRemoveFromModel(nsIAtom *aName, 
>+                                                 const nsAString &aValue);

</beaufour:mega-nit-picking>

Good patch!
Attachment #179235 - Flags: review?(allan) → review+
Attached patch updated with fixed nits (obsolete) — Splinter Review
fixed Doron and Allan's nits.  Doron, could you please give 2nd review?
Attachment #179235 - Attachment is obsolete: true
Attachment #179983 - Flags: review?(doronr)
Comment on attachment 179983 [details] [diff] [review]
updated with fixed nits

Looks good
Attachment #179983 - Flags: review?(doronr) → review+
Attached patch updated to trunkSplinter Review
same patch, merged with latest trunk
Attachment #179983 - Attachment is obsolete: true
checked in.
Status: ASSIGNED → RESOLVED
Closed: 20 years ago
Resolution: --- → FIXED
Product: Core → Core Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: