Closed Bug 64763 Opened 24 years ago Closed 9 years ago

Switch from nsRect mRect to nsArea in the frame model

Categories

(Core :: Layout, defect)

x86
All
defect
Not set
normal

Tracking

()

RESOLVED INCOMPLETE

People

(Reporter: rbs, Unassigned)

References

Details

Attachments

(5 files)

No description provided.
Bug 51684 suggests a number of fundamental items to evaluate prior freezing nsIFrame for Mozilla 1.0. One such suggestion is the switch from 'nsRect mRect' to 'nsArea mArea' in the geometric frame model. The pros and cons of this are detailed in bug 51684. Here is a summary of the motivations. Since the basic rectangular metrics (nsRect mRect) stored in the frames are too limited for layout purposes, the suggestion is to switch from nsRect mRect (defined as: struct nsRect { x, y, width, height}) to a nsArea mArea (defined as: struct nsArea { x, y, width [, height], ascent, descent }). Or to put literally, let the frame store its 'ascent' and 'descent' together with its other dimensions. (Although height = ascent + descent, the 'height' may be kept in nsArea for convenience. Indeed, as we shall we see below, storing the extra ascent, descent information can bring savings to the overall system. However, the 'height' could well be removed later to get additional savings). The fact that the ascent and descent are not stored in the base frame class has been the source of undue complications. Indeed, nearly all frame owners have come to the realisation that they need to keep track of the ascent and descent of their frames. As a result, people cache their aDesiredSize.ascent and/or aDesiredSize.descent from reflow, and this means that separate getter/setter methods are needed for them. Since (with hindsight) we now see that nearly all frames (XUL/CSS/Table/MathML) need their ascent/descent information long after the life time of aDesiredSize from reflow, nsArea will provide a unified way to store/retrieve the information, thus leading to leaner/cleaner code, and saving time to developers who would not need to devise work-around to the problem. Moreover, nsArea coincides with the metrics used in the box model of the XSL-FO spec, and so nsArea can be seen as a necessary building block with which to consolidate nsIFrame to later support XSL-FO. I have taken a first step in the switching direction by setting up nsArea.h/nsArea.cpp (like nsRect.h/nsRect.cpp). Then, using an '#ifdef MOZ_USE_NSAREA', I have added GetArea/SetArea in nsIFrame.h, and made the following change in nsFrame.h : class nsFrame : public nsIFrame { [...] #ifndef MOZ_USE_NSAREA nsRect mRect; #else nsArea mRect; #endif [...] } Then with related changes in nsFrame.cpp for consistency (see attachments), I was able to rebuild and run the application without problems. This worked straightaway because nsRect is a proper subset of nsArea (descent=0), and so a direct mapping to the present code is possible. However, if the switch has to be carried out properly, it will be necessary to rename mRect to mArea to avoid confusions. It will also be necessary to remove the various codes that permeate layout just because the ascent/descent are not stored in the base frame class. The later represent where the benefits will come from. Like string changes that have been made, this switch will require gradual passes over the tree. Supporting both GetRect/SetRect and GetArea/SetArea in layout, and removing the rect variants (and related work-around) when the changes are over. This bug has been opened to track the switch. If folks object to the switch, could they voice their concerns so that I don't waste further precious cycles on something that is doomed.
Attached file gfx/public/nsArea.h
Attached file gfx/src/nsArea.cpp
Blocks: 55456
Blocks: 189604
ccing roc, since he's in the process of rewriting nsIFrame anyway.... roc? dbaron? Thoughts on this?
As bug 189604 suggests, we may not need to track the descent at all.
I think we should keep the nsRect GetRect() that we have right now. We need the height much more often than we need the ascent or the descent. I think we should definitely have GetAscent() and GetDescent() methods on nsIFrame. I'm not convined we should move an mDescent member into nsIFrame. For some frames (e.g., SVG elements, images, OBJECT) it's not needed (right?). The default in nsIFrame should be to for GetDescent() to return 0 and for GetAscent() to return the height. We can of course move mDescent declarations as far up the class hierarchy as makes sense (maybe up to nsHTMLContainerFrame).
Right; we only need to track 2 of { ascent, descent, height }. The question is which two.
>I think we should keep the nsRect GetRect() that we have right now. Why do you think so? Legacy? (i.e., doing a switch over is too much hassle?) BTW, from bug 1777 nsLineBox got its separate mAscent too :-( Whereas if layout objects were switched to nsArea, so that people felt compelled to fill it properly, the foundation will be better. > We need the >height much more often than we need the ascent or the descent. > >I'm not convined we should move an mDescent member into nsIFrame. For some >frames (e.g., SVG elements, images, OBJECT) it's not needed (right?). Taking just ascent and height is fine too. SVG has its SGVtext that can follow a path an be aligned in various way. What I have found very disturbing with this issue is that it is a core issue. When these ascent (or descent) metrics are missing, it is very difficult to work-around the problem (e.g., bug 55456). One basically has to go all the way back to recover it, and the resulting code after that isn't clean. The code becomes more obscure than necessary. Another problem is when mixing/re-using different frames. Sometimes one may instantiate a certain frame just to benefit from the behavior that the frame provides, and if that frame didn't have its ascent (or descent), it is really a pain to recover that. Bug 130958 illustrates this well. If the ascent was computed from the outset, it would be trivial to fix the bug. But now, one has to go back and check (repeating pretty much what bz did in bug 167236 and suffered from regressions). So yes, having |mAscent| is good, but perhaps having |nsArea| is better as a foundation.
Check out bug 190375 where the nsIFrame cleanup patch is. > nsLineBox isn't an nsIFrame. > SVG has its SGVtext Yeah but for most SVG frames, e.g. line segements, there is no meaningful ascent/descent. In general there is only a meaningful ascent/descent for things that can contain text. There are a couple of overlapping issues here: what should the nsIFrame interface be, and how should it be implemented. *** INTERFACE *** I agree that all frames should be able to return their ascent and descent at any time. Currently my nsIFrame patch has three geometry getters: nsRect GetRect() const; nsPoint GetPosition() const; nsSize GetSize() const; I propose we add another getter for the descent: nscoord GetDescent() const; I don't think having an additional "nsArea GetArea()" is all that helpful, and I suspect it would mostly lead to confusion since "Area" isn't meaningfully different from "Rect". Chucking nsAreas instead of nsRects all over the place might make people think more about ascents, but I don't think it's the right solution to that problem. I suspect we'd end up in a mess of nsRect/nsArea conversion with mysterious ascent/descent lossage. One thing to remember is alternative writing-modes, e.g. tbrl. In some writing-modes the invariant is "ascent+descent = width". I assume we don't want to have multiple per-axis ascent/descent :-). Anyway, so that frames can have reasonable defaults without being writing-mode aware, I suggest we don't have an explicit method to return the ascent. Then a frame such as an image or an OBJECT can return 0 for the descent and its context can figure out if the ascent should be the width or the height. *** IMPLEMENTATION *** The implementation's not nearly as important as the interface, as long as there is one :-). As I said above, frames which can't contain text (including things like IFRAMEs where the text isn't logically part of the same document) can just return 0 for the descent. Some frames can just forward a GetDescent() call to a child frame and adjust the result. Other frames should cache it in an mDescent member.
Blocks: 190735
> > nsLineBox > isn't an nsIFrame. Yeah, but I was making the case that nsArea would have fitted the bill there too. Rather than nsLineBox::mRect + nsLineBox::mDescent, just have nsLineBox::mArea. > > SVG has its SGVtext > Yeah but for most SVG frames, e.g. line segements, there is no meaningful > ascent/descent. In general there is only a meaningful ascent/descent for things > that can contain text. True. But when it is missing somewhere, the suffering is shared. For example, in the midst of processing/aligning a frame child list, one doesn't know which child frame is which -- unless a special-casing is done and that's why I was saying that handling the issue adds undue obscurity in the code of consumers. nsRect GetRect() const; nsPoint GetPosition() const; nsSize GetSize() const; - nscoord GetDescent() const; + nscoord GetBaseline() const; do you buy this? might return the same as height (rather than returning 0 as GetDescent() would have done.) Although it could later be |nsPoint GetBase()| or something like that [e.g., nscoord GetYBaseline() / GetXBaseline() or some other names], if the intention is to support top-to-bottom layout as per CSS3, so that base.y could allow to stack frames horizontally while base.x could allow to stack them vertically. >I don't think having an additional "nsArea GetArea()" is all that helpful, and I >suspect it would mostly lead to confusion since "Area" isn't meaningfully >different from "Rect". I would have cut the APIs and gone for: nsArea Get/SetArea() const; nsPoint Get/SetPosition() const; Then, have some separate helpers, e.g., nsRect(const nsArea&), for the few consumers who want to pass this outside of layout. LXR showed that only a handful of consumers use nsIFrame::GetSize(). And there is a problem with SetSize() becauses it requires setting the baseline too so that things are kept in sync. > As I said above, frames which can't contain text (including things > like IFRAMEs where the text isn't logically part of the same document) can just > return 0 for the descent. An interesting observation so far is that nearly all frames need the ascent (or descent) information at some point, either directly or indirectly. Even <object> may have <object type="text/...">. And special-casing the problem adds undue obscurity. As you said the interface is what matters... Thinking of top-to-bottom layout (something that IE already does), nsArea could set a nice scenery because it would have helped to get the vanilla nsRect out of the way, and would have prepared people to think outside-the-rect-box. It could be defined to be "interpreted" with the extra. (Eh, I am making the case for it, of course :-)
> For example, in the midst of processing/aligning a frame child list, one > doesn't know which child frame is which Right, that is definitely going to be fixed by adding a GetAscent() or GetDescent() or GetBaseline() that will work on every frame. > - nscoord GetDescent() const; > + nscoord GetBaseline() const; > > do you buy this? might return the same as height (rather than returning 0 as > GetDescent() would have done.) [I just read the CSS3 text and line modules. Yow. Let's just ignore the non-alphabetic baselines for now.] The problem with GetBaseline(), if you intend it to be the offset from the top or left edge of the frame to the alphabetic baseline, is that for non-text leaf frames we'd have to check the current writing-mode and return either the height (for horizontal inline flows) or 0 (for vertical inline flows). How about "GetAlphabeticBaselineOffset()", which returns the offset from the bottom edge of the frame (for horizontal inline flows) or the left edge of the frame (for vertical inline flows) to the frame's alphabetic baseline? (i.e., 'GetDescent()' with a better name and definition)
Iterating on what you suggest, what about |nsPoint GetBaselineOffset()| and let the callee fill this anchor point according to the writing-mode so that callers use the returned (x,y) as they see fit.
But then leaf frames will (eventually) have to check the writing-mode. We may as well avoid that and just let them return 0.
Depends on: 367332
Assignee: rbs → nobody
QA Contact: chrispetersen → layout
It seems there isn't anything concrete to fix here. Closing.
Status: NEW → RESOLVED
Closed: 9 years ago
Resolution: --- → INCOMPLETE
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: