Closed Bug 171830 Opened 22 years ago Closed 21 years ago

[FIX]Don't use nsCSSPropList hints for inline style changes

Categories

(Core :: CSS Parsing and Computation, defect, P1)

defect

Tracking

()

RESOLVED FIXED
mozilla1.4alpha

People

(Reporter: bzbarsky, Assigned: bzbarsky)

References

Details

(Keywords: testcase)

Attachments

(1 file, 7 obsolete files)

I'll attach a patch tomorrow if my floppy drive cooperates and I find my
floppies.  The basic idea of the patch is as follows:

1)  Add AttributeWillChange to nsIDocumentObserver
2)  Cache the old style data in nsCSSFrameConstructor::AttributeWillChange
3)  Compare the old to the new in nsCSSFrameConstructor::AttributeChanged

The current setup passes the cached data out as an nsISupports, then gets it
passed back in.  I talked to jst, and he does not like that very much.. I'll be
attaching the patch with that setup as-is (because it pretty much works) and
I'll also attach the conversation he and I had.

I'll work on posting some testcases for testing as well.
Blocks: 158713
Depends on: 171808
Attached file Conversation with jst (obsolete) —
Whiteboard: [dev notes]
Attached patch Work-in-progress (obsolete) — Splinter Review
This is pretty much complete, imo, pending resolution of jst's concern.  There
are some testcases to test with (and more are welcome) at:

http://web.mit.edu/bzbarsky/www/testcases/dynamic-inline-style-correctness/
http://web.mit.edu/bzbarsky/www/testcases/dynamic-inline-style-performance/
Keywords: testcase
Whiteboard: [dev notes]
Blocks: 171522
Blocks: 164840
Comment on attachment 101301 [details] [diff] [review]
Work-in-progress

Good news -- this seems to fix a few random bugs.  Bad news -- this regresses
bug 138120.  The testcase in that bug triggers the

NS_ASSERTION(!parentFrame,
	     "parent frame but no child frame or undisplayed entry");

assertion....  Which seems bad.
Comment on attachment 102414 [details] [diff] [review]
This fixes the assertion and the regression (apply in addition to, not instead of)

Why bother with asserts if they get ignored?  I've moved this patch to its
proper place -- bug 151883.
Attachment #102414 - Attachment is obsolete: true
Depends on: 151883
Depends on: 174178
Depends on: 140789
Comment on attachment 101301 [details] [diff] [review]
Work-in-progress

As suspected, this regresses bug 118415.  I'm still not sure whether the patch
for that _really_ fixed things in the old world or just papered over the
problem... in any case, I need to come up with a fix in the context of the new
code.
Attachment #101301 - Flags: needs-work+
so i think i like jsts suggestion with a servergenerated key better since it
less work in the common case (that we're not passing data from WillChange to
HasChanged).

One way to take care of the memorymanagement is to have the observer hold a hash
of nsISupports, that way you should never leak anything. You still might hold on
to an object much longer then you need though, if someone aborts, which
admittedly is a bad thing.

hmm.. though we could add an AttributeDidntChange notification as well, that is
called if someone aborts.
Hoping to get that regression addressed sometime this week.  We need to come to
a decision here on the exact way to pass the context around....
Priority: -- → P1
Target Milestone: --- → mozilla1.3alpha
Attached patch checkpoint (obsolete) — Splinter Review
This resolves all known regressions.  Remaining issues:

1)  How should the notifications work?
2)  What datatype should be used for storing the style data (nsCachedStyleData
    feels like a hack when all is said and done)... (I'd do a stub impl of
    nsIStyleContext, but we want to remove nsIStyleContext....)
3)  How to avoid code duplication in the CalcDifference functions?
4)  Disentangling from some other patches.

Point #4 means there's stuff in this patch that's pretty irrelevant to this
bug... I'm sorry about that; I'll try to disentangle as soon as I can... in the
meantime, some general feedback on the approach and such would be much
appreciated.
Attachment #101301 - Attachment is obsolete: true
Depends on: 177543
Yeah, there's got to be a way to eliminate the duplication of this
CalcStyleDifference function.

Maybe we could make them both small by using a loop to iterate through the
different style structs, instead of writing them all out explicitly?
Yeah... Another possibility is that we have an nsIStyleContextBase and
nsIStyleContext, with the latter inheriting from the former....
nsIStyleContextBase could be what CalcDifference() takes and instead of using
nsCachedStyleData to move this info around I could use an nsIStyleContextBase
(which would have the minimal number of methods needed to actually function in
CalcDifference).

Of course this represents even _more_ virtualization of nsIStyleContext, which
may not be a good idea.... :(
Can't you just make a 'fake' nsStyleContext with your nsCachedStyleData in it,
but  decoupled from rules and other style contexts?
I considered doing that too...  I'll give it a shot tonight or tomorrow and see
how it affects perf.  
Attached patch Use a temporary style context (obsolete) — Splinter Review
Roc, that works beautifully.  ;)  This patch has all the irrelevant stuff
stripped out and is basically reviewable.... (modulo the specific changes to
nsIDocumentObserver)

In case people are interested in the perf impact, here're the results on
http://web.mit.edu/bzbarsky/www/testcases/dynamic-inline-style-performance/ (3
or 5 trials for each test):

WITHOUT PATCH:
inlineStyleOnBody: 1224 1205 1224 1208 1225
inlineStyleOnBodyWithSelectorOnStyleAttr: 1440 1378 1379 1441 1379
uselessInlineStyleOnBody: 1601 1606 1626 1627 1626
movingEmptyDivs: 12963 12879 12969
movingFullDivs: 57013 56354 56344
notMovingFullDivs: 46770 46268 46241

WITH PATCH:
inlineStyleOnBody: 879 892 875 893 881
inlineStyleOnBodyWithSelectorOnStyleAttr: 1573 1533 1530 1535 1535
uselessInlineStyleOnBody: 431 415 436 419 436
movingEmptyDivs: 14035 13958 13923
movingFullDivs: 51825 51367 51483
notMovingFullDivs: 8693 8344 8217

And testing with http://alladyn.art.pl/gandalf/MozillaTests/dynl.html :

WITHOUT PATCH:
Creating 200 layers - time - 1174 1153 1195
Moving 200 layers 100 px to left - time - 8326 8627 8446
Cliping 200 layers - time - 310 310 311
Setting Opacity 10% - time - 8938* 8847* 8816*
Reverting z-index for 200 layers - time - 307 319 337
Moving with changing opacity and clip - time - 21116* 21031* 21022*
Inputing text into 200 layers - time - 512 509 509
Resizing 200 layers - time - 562 587 565

WITH PATCH:
Creating 200 layers - time - 1142 1143 1137
Moving 200 layers 100 px to left - time - 8635 8583 8652
Cliping 200 layers - time - 321 318 320
Setting Opacity 10% - time - 24867** 22199** 22133**
Reverting z-index for 200 layers - time - 325 297 292
Moving with changing opacity and clip - time - 42236** 40992** 41705**
Inputing text into 200 layers - time - 525 517 495
Resizing 200 layers - time - 575 565 567

*No repaints or reflows happen between opacity=100% and opacity=10% due to
 continuous reframing
**Repaints and reflows happen as opacity/position/clip change
Attachment #104642 - Attachment is obsolete: true
I considered subclassing nsStyleContext for the snapshot (to override
PeekStyleData not to look in the rulenode) , but wasn't sure whether making
PeekStyleData virtual was ok.... if someone has strong feelings one way or the
other, let me know.
Blocks: 175070
Flags: blocking1.3b?
I'm not getting to this till January, so this is not making 1.3.
Flags: blocking1.3b?
Target Milestone: mozilla1.3alpha → mozilla1.4alpha
> The current setup passes the cached data out as an nsISupports, then gets it
> passed back in.  I talked to jst, and he does not like that very much..
[...]
+/* Strategy for dealing with inline style:
+ *
+ * 1) During AttributeWillChange, see if we already have an inline style
+ *    rule.  If we do, get the style context and cache the style data and the
+ *    pointer (weak ref; we just care about the value) to the style rule.
+ *    Pass this data out as aContext.
+ * 2) During AttributeChanged, see if we have a style rule and if so whether
+ *    it's the same as the rule we remembered in AttributeWillChange.  If the
+ *    rules are the same, the style context will not be changing!
+ * 3) If the inline style rules are different, just do a normal re-resolve
+ *    and get the hint from that.  This will create new style contexts as
+ *    needed.
+ */

NS_IMETHODIMP
+nsCSSFrameConstructor::AttributeWillChange(nsIPresContext* aPresContext,
+                                           nsIContent*     aContent,
+                                           PRInt32         aNameSpaceID,
+                                           nsIAtom*        aAttribute,
+                                           nsISupports**   aContext)
+{
[...]
+  *aContext =
+    NS_STATIC_CAST(nsISupports*,
+                   new StyleAttrChangeContext(snapshot, rule, importantRule,
+                                              aPresContext));
[...]
+}

Possible suggestion/simplication to avoid this issue. What about using the
frame manager instead of dragging the context around? That is, rather than
returning the context as you are doing (just to get it back later on), just
stash it as a property in the frame manager. Then, when AttributeChanged() is
called later on, you can fetch it back from there.

+  //XXXbz We need to decide what the contract is here.  Is it OK to call
+  //AttributeWillChange but not AttributeChanged (eg if an error occurs while
+  //changing the attribute)?  If so, we should say so explicitly, I think.

It will also avoid this issue. In the case of success (i.e., perfect match of
"willchange" and "changed" pairs), the property is removed. In case of failure
("willchange" without "changed"), there will be at most a dangling entry but it
will overriden if "willchange" is called again, or it will be destroyed when the
manager goes away, leaving no leak behind.
What about the case when style is being changed on a display:none element 
(which has a style context in the undisplayed map but has no frame)?  I'm not 
sure I can use the frame manager there (though I _could_ maybe use the 
undisplayed map itself....)
> I _could_ maybe use the undisplayed map itself

Seems plausible since the map refers to the old style context data -- until
completion of the maintainance [set->ClearStyleData()] that AttributeChanged() does.

This actually raises one question with your |GetStyleContextFor|... Isn't there
a loophole in your patch in that you are getting the same old context in the
situation of display=none? i.e., are you not testing the same old context
against itself?
That's the general problem with the inline style changes -- the fact that the 
style context pointer does _not_ change in the process.  That's why I save the 
old style data, then clear the style context's cache, then compare, forcing the 
style context to recompute style data in the process...

There is an issue I'm looking into with this whole approach (and this may apply 
to the current code too) where style data may get cleared and this may cause 
later changes to not trigger the right change hint...
Blocks: 140789
No longer depends on: 140789
Depends on: 188803
Attached patch Alternate (cleaner) approach (obsolete) — Splinter Review
I'm a _lot_ happier with this one... It still fixes all the problems the old
one fixed, but mostly _removes_ code.  Perf testing shows that it's within 1-2%
of current trunk on all the DHTML tests I've run (except the ones I rigged to
show off the benefits of this patch -- we're 10-60% faster than trunk on those
;) ).

This patch depends on bug 117316 being fixed so we don't grow the ruletree like
mad.

Once that happens, the perf here should be retested, but I suspect that it's
not going to change much.
Depends on: 191794
Blocks: 173373
Attachment #104971 - Attachment is obsolete: true
Attachment #113089 - Flags: superreview?(roc+moz)
Attachment #113089 - Flags: review?(dbaron)
Summary: Don't use nsCSSPropList hints for inline style changes → [FIX]Don't use nsCSSPropList hints for inline style changes
Comment on attachment 113089 [details] [diff] [review]
Alternate (cleaner) approach

r=dbaron (although maybe we shouldn't be using the literal 0x7fffffff as the
weight without a macro to hide it)
Attachment #113089 - Flags: review?(dbaron) → review+
Comment on attachment 113089 [details] [diff] [review]
Alternate (cleaner) approach

isn't there a PR_INT32_MAX macro for that?
Yeah, good point.  I'll change that to PR_INT32_MAX
Comment on attachment 113089 [details] [diff] [review]
Alternate (cleaner) approach

yeah baby!

I hope that we can hunt down and kill the other users of the hint from the
parser...
Attachment #113089 - Flags: superreview?(roc+moz) → superreview+
Blocks: 193275
Blocks: 74951
Attachment #101209 - Attachment is obsolete: true
Attachment #113089 - Attachment is obsolete: true
Blocks: 194615
No longer blocks: 194615
*** Bug 194928 has been marked as a duplicate of this bug. ***
Blocks: 195073
Request changing SEVERITY to CRITICAL because this blocks 195073 which is
SEVERITY = CRITICAL.  Also, comment 22 states this patch depends on 117316.
Let me know when you're ready to land.
Pretty much any time (as in the patch is ready to go).  The problem is finding a
time to watch the tree...  Can you do Tuesday morning?  Should we ask for a carpool?
I could watch the tree for you if needed (find me on IRC to make sure I'm
around), and I don't think there's a need for a carpool.
This should apply to a current trunk.....
Attachment #115276 - Attachment is obsolete: true
I already checked it in (based on the merging I did a week or so ago), actually.
Ah, cool.  Thank you!
Fix checked in to trunk, 2003-03-06 11:06/07 PST with additional unused variable
removal at 13:01 PST.
Status: NEW → RESOLVED
Closed: 21 years ago
Resolution: --- → FIXED
+  if (!decl) {
+    return result;
+  }
 
+  nsCOMPtr<nsICSSLoader> cssLoader;
+  nsCOMPtr<nsICSSParser> cssParser;
+  nsCOMPtr<nsIURI> baseURI;
+
+  result = GetCSSParsingEnvironment(mContent,
+                                    getter_AddRefs(baseURI),
+                                    getter_AddRefs(cssLoader),
+                                    getter_AddRefs(cssParser));
 
+  if (NS_FAILED(result)) {
+    return result;
+  }

Possible leak of |decl|. (2 places).

Also missing the check for success in the call of setDecl() and the ruleAbort
business in case of failure.
> Possible leak of |decl|. (2 places).

Actually, no.  |decl| is not addrefed by GetCSSDeclaration.

> and the ruleAbort business in case of failure

Nom the only case in which RuleAbort() is needed is if the decl was never set in
a rule (and the decl we get out of GetCSSDeclaration is guaranteed to be set in
a rule).  RuleAbort() is a fancy name for "delete" in the case of
CSSDeclarations... and a decl owned by a rule will be released by the rule when
the rule dies.

Now we _could_ end up with a double-delete of the decl if SetHTMLAttribute fails
in SetCSSDeclaration when called from GetCSSDeclaration... Bug 196271 filed. 
Also filed bug 196273 on general nsCSSDeclaration memory management.
Blocks: 175686
Hmm... the not clearing of display data seems to have fixed some {ib} bugs (eg
bug 175686).  It'd be good to retest the other {ib} bugs.
Blocks: 179935
*** Bug 156206 has been marked as a duplicate of this bug. ***
Blocks: 143202
Blocks: 190558
Blocks: 196603
Note that this caused regression bug 196603
*** Bug 81678 has been marked as a duplicate of this bug. ***
Blocks: 130800
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: