Closed Bug 399384 Opened 17 years ago Closed 17 years ago

First-letter style ignored after span's parent's padding is changed

Categories

(Core :: Layout, defect)

x86
Linux
defect
Not set
normal

Tracking

()

RESOLVED FIXED

People

(Reporter: dholbert, Assigned: dholbert)

References

Details

Attachments

(5 files, 1 obsolete file)

Attached file testcase 1
See testcase.

Expected behavior:
  - The 'a' should stay red when padding is adjusted.

Actual behavior:
  - The 'a' loses its color when padding is adjusted. (1 second after page load)

Note that the padding doesn't actually change at all -- we're setting it to 0px, which is the default value.

It's also not specific to just padding.  I tried setting the borderWidth and top/right/bottom/left css attributes, and they all have the same effect.  (And probably lots of others do as well.)
Attached file testcase 2 (using rtl)
This is based on a testcase from bug 397844.

When we tweak the padding on this testcase, the 'a' not only loses its color, but it also gets separated into a different line.

I'm guessing that the color change and line-breaking are caused by the same problem, but I'm not sure.
Something's going wrong when we build the new styleContext for the nsFirstLetterFrame.

In processing the style change, we encounter this section of nsFrameManager.cpp (with aFrame being the nsFirstLetterFrame containing 'a'):

1154       if (newContext != oldContext) {
1155         aMinChange = CaptureChange(oldContext, newContext, aFrame,
1156                                    content, aChangeList, aMinChange,
1157                                    assumeDifferenceHint);
1158         if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
1159           // if frame gets regenerated, let it keep old context
1160           aFrame->SetStyleContext(newContext);
1161         }

Right after calling CaptureChange, I found this:
 color(oldContext) = 0xff0000ff  (red)
 color(newContext) = 0xff000000  (black)

where color($arg0) is defined as
  p/x *(nsStyleColor*)($arg0)->PeekStyleData(eStyleStruct_Color)

So, newContext isn't getting the correct color.
Assignee: nobody → dholbert
Status: NEW → ASSIGNED
Looks like the problem is back where we first set the value for newContext, here:

http://mxr.mozilla.org/seamonkey/source/layout/base/nsFrameManager.cpp#1129

1110     else if (pseudoTag) {
1111       nsIContent* pseudoContent =
1112           aParentContent ? aParentContent : localContent;
1113       if (pseudoTag == nsCSSPseudoElements::before ||
1114           pseudoTag == nsCSSPseudoElements::after) {
1115         // XXX what other pseudos do we need to treat like this?
1116         newContext = styleSet->ProbePseudoStyleFor(pseudoContent,
1117                                                    pseudoTag,
1118                                                    parentContext).get();
1119         if (!newContext) {
1120           // This pseudo should no longer exist; gotta reframe
1121           NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
1122           aChangeList->AppendChange(aFrame, pseudoContent,
1123                                     nsChangeHint_ReconstructFrame);
1124           // We're reframing anyway; just keep the same context
1125           newContext = oldContext;
1126           newContext->AddRef();
1127         }
1128       } else {
1129         newContext = styleSet->ResolvePseudoStyleFor(pseudoContent,
1130                                                      pseudoTag,
1131                                                      parentContext).get();
1132       }

Variables are as follows:
  - aFrame is the nsFirstLetterFrame
  - pseudoContent = aParentContent = localContent nsHTMLSpanElement
  - pseudoTag is nsCSSElements::firstLetter
  - parentContext is the nsStyleContext for the span's nsInlineFrame

In this case, we jump to line 1129 to initialize newContext.  And that line returns the style context for the **span**, not for its first letter.
Attached patch fix (obsolete) — Splinter Review
I think this fixes it. Still need to try reftests on it, though.
Comment on attachment 284576 [details] [diff] [review]
fix

Requesting approval1.9, because this blocks (and fixes) Bug 397844, which is blocking1.9
Attachment #284576 - Flags: superreview?(dbaron)
Attachment #284576 - Flags: review?(bzbarsky)
Attachment #284576 - Flags: approval1.9?
Comment on attachment 284576 [details] [diff] [review]
fix

>Index: nsFrameManager.cpp
>+              break;
>+            } else {

no need for the else-after-break.  Just remove it, and outdent the body of the else.

>+          if (candidate) {

I think we can safely assert that candidate is not null here. If it is, someone _really_ screwed up the frame tree and we're in trouble anyway.  Better to crash on a null deref.

>+            // Note: if block were null, then we could only have exited the

I think this code is close enough to the loop the note isn't needed.  Either way, though.

r=bzbarsky with the nits.
Attachment #284576 - Flags: review?(bzbarsky) → review+
Oh, and check in reftests for those two testcases?
Thanks bz.

Also, I forgot to mention -- tested this patch on a bunch of stuff, with these results:
 - passes reftests
 - fixes this bug's testcases
 - fixes Bug 23604's testcases
 - fixes Bug 398380's testcase
 - fixes Bug 397844's testcases (including crashes)
Comment on attachment 284576 [details] [diff] [review]
fix

(Cancelling sr and approval requests, 'cause I'm making a few changes and reposting.)
Attachment #284576 - Flags: superreview?(dbaron)
Attachment #284576 - Flags: approval1.9?
Second pass at fix.

Changes:
 - Added reftests
 - Addressed bz's suggestions from comment 7
 - Separated out the "look for lowest ancestor block" functionality into a standalone static function in nsBlockFrame, because it seems like it could be useful elsewhere.
 - Unlike the original fix, in nsFrameManager, *don't* replace parentContext -- just replace pseudoContent.  (The original fix triggered assertion failures on testcases involving both first-line and first-letter)
 - Made some comment-only typo fixes (2 occasions of flipped u/e in "pseudo", and one other place with missing letters)

This patch passes reftests, and the new (included) reftests fail pre-patch and pass post-patch.
Attachment #284576 - Attachment is obsolete: true
Attachment #285006 - Flags: superreview?(dbaron)
Attachment #285006 - Flags: review?(bzbarsky)
Attachment #285006 - Flags: approval1.9?
Attachment #285006 - Flags: superreview?(dbaron) → superreview?(bzbarsky)
Requesting blocking1.9, because this bug is the source of the crash in the blocker bug 397844.
Flags: blocking1.9?
Comment on attachment 285006 [details] [diff] [review]
fix v2 (with reftests)

This looks good to me, but I feel that I had enough to do with this approach that it needs a sanity-check from _someone_ else... trying roc, since dbaron is swamped.
Attachment #285006 - Flags: superreview?(roc)
Attachment #285006 - Flags: superreview?(bzbarsky)
Attachment #285006 - Flags: review?(bzbarsky)
Attachment #285006 - Flags: review+
Comment on attachment 285006 [details] [diff] [review]
fix v2 (with reftests)

Boris, I'm no match for you in a sanity contest...
Attachment #285006 - Flags: superreview?(roc)
Attachment #285006 - Flags: superreview+
Attachment #285006 - Flags: approval1.9?
Attachment #285006 - Flags: approval1.9+
(In reply to comment #14)
> Requesting blocking1.9, because this bug is the source of the crash in the
> blocker bug 397844.
> 

Canceling 'blocking1.9?' flag, because the patch already got approval1.9+. :)
Flags: blocking1.9?
Attached patch fix, as landedSplinter Review
Fix + reftests checked in.  From the previously posted patch, I just changed 2 instances of 
  nsBlockFrame *block
to
  nsBlockFrame* block
to match the convention in the surrounding code.


Checking in base/nsDisplayList.h;
/cvsroot/mozilla/layout/base/nsDisplayList.h,v  <--  nsDisplayList.h
new revision: 3.23; previous revision: 3.22
done
Checking in base/nsFrameManager.cpp;
/cvsroot/mozilla/layout/base/nsFrameManager.cpp,v  <--  nsFrameManager.cpp
new revision: 1.255; previous revision: 1.254
done
Checking in generic/nsBlockFrame.cpp;
/cvsroot/mozilla/layout/generic/nsBlockFrame.cpp,v  <--  nsBlockFrame.cpp
new revision: 3.881; previous revision: 3.880
done
Checking in generic/nsBlockFrame.h;
/cvsroot/mozilla/layout/generic/nsBlockFrame.h,v  <--  nsBlockFrame.h
new revision: 3.255; previous revision: 3.254
done
Checking in generic/nsFrame.cpp;
/cvsroot/mozilla/layout/generic/nsFrame.cpp,v  <--  nsFrame.cpp
new revision: 3.762; previous revision: 3.761
done
RCS file: /cvsroot/mozilla/layout/reftests/bugs/23604-1-ref.html,v
done
Checking in reftests/bugs/23604-1-ref.html;
/cvsroot/mozilla/layout/reftests/bugs/23604-1-ref.html,v  <--  23604-1-ref.html
initial revision: 1.1
done
RCS file: /cvsroot/mozilla/layout/reftests/bugs/23604-1.html,v
done
Checking in reftests/bugs/23604-1.html;
/cvsroot/mozilla/layout/reftests/bugs/23604-1.html,v  <--  23604-1.html
initial revision: 1.1
done
RCS file: /cvsroot/mozilla/layout/reftests/bugs/23604-2-ref.html,v
done
Checking in reftests/bugs/23604-2-ref.html;
/cvsroot/mozilla/layout/reftests/bugs/23604-2-ref.html,v  <--  23604-2-ref.html
initial revision: 1.1
done
RCS file: /cvsroot/mozilla/layout/reftests/bugs/23604-2.html,v
done
Checking in reftests/bugs/23604-2.html;
/cvsroot/mozilla/layout/reftests/bugs/23604-2.html,v  <--  23604-2.html
initial revision: 1.1
done
RCS file: /cvsroot/mozilla/layout/reftests/bugs/399384-1-ref.html,v
done
Checking in reftests/bugs/399384-1-ref.html;
/cvsroot/mozilla/layout/reftests/bugs/399384-1-ref.html,v  <--  399384-1-ref.html
initial revision: 1.1
done
RCS file: /cvsroot/mozilla/layout/reftests/bugs/399384-1.html,v
done
Checking in reftests/bugs/399384-1.html;
/cvsroot/mozilla/layout/reftests/bugs/399384-1.html,v  <--  399384-1.html
initial revision: 1.1
done
Checking in reftests/bugs/reftest.list;
/cvsroot/mozilla/layout/reftests/bugs/reftest.list,v  <--  reftest.list
new revision: 1.174; previous revision: 1.173
done
Status: ASSIGNED → RESOLVED
Closed: 17 years ago
Flags: in-testsuite+
Resolution: --- → FIXED
Oops -- I just stumbled across this function:
  nsBlockFrame*
nsLayoutUtils::FindNearestBlockAncestor

which seems to basically do the same thing as 
  nsBlockFrame::GetNearestAncestorBlock
(which was added by the patch for this bug)

Todo soon:  Remove GetNearestAncestorBlock and replace its uses with calls to nsLayoutUtils::FindNearestBlockAncestor.
Pretty sure this is sufficient for switching to FindNearestBlockAncestor.

I initially thought I'd be able to remove the "#include nsBlockFrame.h", too, since I've removed all mentions of the nsBlockFrame type and its methods, but it turns out that would cause compiler errors.  (see below)  So I left it in.

The compiler errors happen because nsLayoutUtils::FindNearestBlockAncestor returns a nsBlockFrame*, but its .h file forward-declares the nsBlockFrame type without actually #including the nsBlockFrame.h header.  

So in order to convert the nsBlockFrame* returned by nsLayoutUtils::FindNearestBlockAncestor into a nsIFrame, we need to include nsBlockFrame.h, so that we know nsBlockFrame extends nsIFrame and can be treated as one.
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: