[webkit-dev] How does Zooming Work?

Alex Milowski alex at milowski.org
Wed Oct 7 13:52:33 PDT 2009


On Tue, Oct 6, 2009 at 9:09 PM, Dan Bernstein <mitz at apple.com> wrote:
>
> On Oct 6, 2009, at 8:49 PM, Alex Milowski wrote:
>
>> What exactly happens during a zoom (command +/-) ?
>
> Depends on the flavor of zoom (“full-page” zoom vs. text-only zoom), but in
> both cases, a full style recalculation for the document is forced.
>
>> I have code that works well but layout doesn't seem to happen after
>> a zoom in/out operation.  If I then resize the window, that forces a
>> layout for the zoomed size and things adjust themselves
>> appropriately (because layout() eventually gets called).
>>
>> So, what sequence of events happens after a zoom?
>
> Frame::setZoomFactor() calls Document::recalcStyle(Force). If after that the
> document has a renderer (which would be a RenderView) and that renderer is
> marked for layout, then FrameView::layout() is called, which will call
> RenderView::layout() and recursively lay out every render tree object marked
> as needing layout.
>
> One explanation for what you’re seeing would be that as your objects’ style
> changes, they fail to call setNeedsLayout() (or
> setNeedsLayoutAndPerfWidthsRecalc()), and therefore layout doesn’t occur at
> that time. An alternative, less likely, explanation is that your objects
> have anonymous children, but they don’t propagate the style changes
> correctly to those children.

The style changes seem to be propagating properly.  I've dug into this
quite a bit more.  Here's the layout method for mrow that attempts to
notify the math operators to stretch to a certain height:

void RenderMathRow::layout() {
    RenderBlock::layout();

    // calculate the maximum height
    int maxHeight = 0;
    int currentHeight = offsetHeight();

    RenderObject* current = firstChild();
    while (current) {
        if (current->isBoxModelObject()) {

            RenderBoxModelObject* box = toRenderBoxModelObject(current);

            // Check to see if this box has a larger height
            if (box->offsetHeight()>maxHeight)
                maxHeight = box->offsetHeight();
        }
        current = current->nextSibling();
    }

    if (maxHeight!=currentHeight) {
       style()->setHeight(Length(maxHeight,Fixed));
       setNeedsLayoutAndPrefWidthsRecalc();

       // notify contained operators they may need to relayout their
stretched operators
        current = firstChild();
        while (current) {
            if (current->isRenderMathOperator()) {
                RenderMathOperator* mathop = toRenderMathOperator(current);
                mathop->stretchOperator(maxHeight);
            }
            current = current->nextSibling();
        }
    }
}


I modify the style of the mrow (a copy of the style) and change the height
of the container (the mrow).  This code correctly works the first time and
will adjust properly after a zoom in/out if you resize the window.

The stretchOperator() method marks the child as needing layout.  As the
children of mrow are stacked as a set of inlines (mostly likely all
inline-block),
there must be something that is not propagating.

Also, I notice on a zoom in that the render tree for the MathML is
being partially
recreated.  New instances of the children of mrow are created.  Their initial
layout() call then does not have the new maxHeight value.  Afterwards, they
are marked for layout just as in the initial layout.

I've narrowed the problem down to this sequence of events:

   1. RenderMathRow::layout() is called (from the outside)
   2. RenderMathRow::layout() calls RenderBlock::layout()
   3. Subsequent calls to RenderMathOperator::layout() are causes by (2).  The
       stretch height has not been set at that moment.
   4. If the height has changed:

       a. The height of RenderMathRow is set and it is told it needs layout
       b. Each RenderMathOperator child is notified of the stretch height and
           told it needs layout.
   5. RenderMathRow::layout() is called (from the outside)
   6. RenderMathRow::layout() calls RenderBlock::layout()
   7. Subsequent calls to RenderMathOperator::layout() are causes by (2).  The
       stetch height is now set properly and the height of the
inline-block is now correct.

What is interesting is that after a zoom, 1-4 happen.  5-7 only happen
after a resize
event.  My suspicion is that even though the RenderMathRow instance has marked
itself and its RenderMathOperator children as needing layout, that is
somehow not
noticed until the window resize.

I can solve this right now by adding another call to
RenderBlock::layout() in the
RenderMathRow::layout() method.  I shouldn't have to do that and I've
noticed that
such direct "double" calls can cause other problems.

Any ideas what is wrong?  Do I need to notify the parent of
RenderMathRow somehow?

-- 
--Alex Milowski
"The excellence of grammar as a guide is proportional to the paucity of the
inflexions, i.e. to the degree of analysis effected by the language
considered."

Bertrand Russell in a footnote of Principles of Mathematics


More information about the webkit-dev mailing list