void NodeRenderingContext::createRendererForTextIfNeeded()
{
    ASSERT(!m_node->renderer());

    Text* textNode = toText(m_node);

    if (!shouldCreateRenderer())
        return;

    RenderObject* parentRenderer = this->parentRenderer();

    if (m_parentDetails.resetStyleInheritance())
        m_style = textNode->document()->styleResolver()->defaultStyleForElement();
    else
        m_style = parentRenderer->style();

    if (!textNode->textRendererIsNeeded(*this))
        return;

    RenderText* newRenderer = textNode->createTextRenderer(m_style.get());
    if (!parentRenderer->isChildAllowed(newRenderer, m_style.get())) {
        newRenderer->destroy();
        return;
    }

    // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style
    // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail.
    newRenderer->setFlowThreadState(parentRenderer->flowThreadState());

    RenderObject* nextRenderer = this->nextRenderer();
    textNode->setRenderer(newRenderer);
    // Parent takes care of the animations, no need to call setAnimatableStyle.
    newRenderer->setStyle(m_style.release());
    parentRenderer->addChild(newRenderer, nextRenderer);
}
Ejemplo n.º 2
0
void RenderTreeBuilder::createRendererForTextIfNeeded()
{
    ASSERT(!m_node->renderer());

    // If we're out of composition then we can't render since there's no parent to inherit from.
    if (!m_renderingParent)
        return;

    if (!shouldCreateRenderer())
        return;

    Text* textNode = toText(m_node);
    RenderObject* parentRenderer = this->parentRenderer();

    m_style = parentRenderer->style();

    if (!textNode->textRendererIsNeeded(*m_style, *parentRenderer))
        return;

    RenderText* newRenderer = textNode->createTextRenderer(m_style.get());
    if (!parentRenderer->isChildAllowed(newRenderer, m_style.get())) {
        newRenderer->destroy();
        return;
    }

    // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style
    // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail.
    newRenderer->setFlowThreadState(parentRenderer->flowThreadState());

    RenderObject* nextRenderer = this->nextRenderer();
    textNode->setRenderer(newRenderer);
    // Parent takes care of the animations, no need to call setAnimatableStyle.
    newRenderer->setStyle(m_style.release());
    parentRenderer->addChild(newRenderer, nextRenderer);
}
Ejemplo n.º 3
0
static void updateTextStyle(Text* text, RenderStyle* parentElementStyle, Style::Change change)
{
    RenderText* renderer = toRenderText(text->renderer());

    if (change != Style::NoChange && renderer)
        renderer->setStyle(parentElementStyle);

    if (!text->needsStyleRecalc())
        return;
    if (renderer)
        renderer->setText(text->dataImpl());
    else
        text->attachText();
    text->clearNeedsStyleRecalc();
}
void Text::recalcTextStyle(StyleChange change)
{
    RenderText* renderer = toRenderText(this->renderer());

    if (change != NoChange && renderer)
        renderer->setStyle(document()->ensureStyleResolver()->styleForText(this));

    if (needsStyleRecalc()) {
        if (renderer)
            renderer->setText(dataImpl());
        else
            reattach();
    }
    clearNeedsStyleRecalc();
}
Ejemplo n.º 5
0
static void createTextRendererIfNeeded(Text& textNode)
{
    ASSERT(!textNode.renderer());

    ContainerNode* renderingParentNode = NodeRenderingTraversal::parent(&textNode);
    if (!renderingParentNode)
        return;
    RenderObject* parentRenderer = renderingParentNode->renderer();
    if (!parentRenderer || !parentRenderer->canHaveChildren())
        return;
    if (!renderingParentNode->childShouldCreateRenderer(&textNode))
        return;

    Document& document = textNode.document();
    RefPtr<RenderStyle> style;
    bool resetStyleInheritance = textNode.parentNode()->isShadowRoot() && toShadowRoot(textNode.parentNode())->resetStyleInheritance();
    if (resetStyleInheritance)
        style = document.ensureStyleResolver().defaultStyleForElement();
    else
        style = parentRenderer->style();

    if (!textRendererIsNeeded(textNode, *parentRenderer, *style))
        return;
    RenderText* newRenderer = textNode.createTextRenderer(document.renderArena(), style.get());
    if (!newRenderer)
        return;
    if (!parentRenderer->isChildAllowed(newRenderer, style.get())) {
        newRenderer->destroy();
        return;
    }

    // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style
    // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail.
    newRenderer->setFlowThreadState(parentRenderer->flowThreadState());

    RenderObject* nextRenderer = nextSiblingRenderer(textNode);
    textNode.setRenderer(newRenderer);
    // Parent takes care of the animations, no need to call setAnimatableStyle.
    newRenderer->setStyle(style.release());
    parentRenderer->addChild(newRenderer, nextRenderer);

    Node* sibling = textNode.nextSibling();
    if (sibling && !sibling->renderer() && sibling->attached())
        createTextRenderersForSiblingsAfterAttachIfNeeded(sibling);
}
Ejemplo n.º 6
0
RenderBlock* RenderMathMLOperator::createGlyph(UChar glyph, int lineHeight, int maxHeightForRenderer, int charRelative, int topRelative)
{
    RenderBlock* container = new (renderArena()) RenderMathMLBlock(node());
    container->setStyle(createStackableStyle(lineHeight, maxHeightForRenderer, topRelative));
    addChild(container);
    RenderBlock* parent = container;
    if (charRelative) {
        RenderBlock* charBlock = new (renderArena()) RenderBlock(node());
        RefPtr<RenderStyle> charStyle = RenderStyle::create();
        charStyle->inheritFrom(container->style());
        charStyle->setDisplay(INLINE_BLOCK);
        charStyle->setTop(Length(charRelative, Fixed));
        charStyle->setPosition(RelativePosition);
        charBlock->setStyle(charStyle);
        parent->addChild(charBlock);
        parent = charBlock;
    }
    
    RenderText* text = new (renderArena()) RenderText(node(), StringImpl::create(&glyph, 1));
    text->setStyle(container->style());
    parent->addChild(text);
    return container;
}
Ejemplo n.º 7
0
RenderBlock* RenderMathMLOperator::createGlyph(UChar glyph, int maxHeightForRenderer, int charRelative)
{
    RenderBlock* container = new (renderArena()) RenderMathMLBlock(toElement(node()));
    toRenderMathMLBlock(container)->setIgnoreInAccessibilityTree(true);
    container->setStyle(createStackableStyle(maxHeightForRenderer));
    addChild(container);
    RenderBlock* parent = container;
    if (charRelative) {
        RenderBlock* charBlock = createAnonymous(document());
        RefPtr<RenderStyle> charStyle = RenderStyle::create();
        charStyle->inheritFrom(container->style());
        charStyle->setDisplay(INLINE_BLOCK);
        charStyle->setTop(Length(charRelative, Fixed));
        charStyle->setPosition(RelativePosition);
        charBlock->setStyle(charStyle);
        parent->addChild(charBlock);
        parent = charBlock;
    }

    RenderText* text = new (renderArena()) RenderText(node(), StringImpl::create(&glyph, 1));
    text->setStyle(container->style());
    parent->addChild(text);
    return container;
}
Ejemplo n.º 8
0
// FIXME: It's cleaner to only call updateFromElement when an attribute has changed. The body of
// this method should probably be moved to a private stretchHeightChanged or checkStretchHeight
// method. Probably at the same time, addChild/removeChild methods should be made to work for
// dynamic DOM changes.
void RenderMathMLOperator::updateFromElement()
{
    RenderObject* savedRenderer = node()->renderer();

    // Destroy our current children
    children()->destroyLeftoverChildren();

    // Since we share a node with our children, destroying our children may set our node's
    // renderer to 0, so we need to restore it.
    node()->setRenderer(savedRenderer);
    
    // If the operator is fixed, it will be contained in m_operator
    UChar firstChar = m_operator;
    
    // This boolean indicates whether stretching is disabled via the markup.
    bool stretchDisabled = false;
    
    // We may need the element later if we can't stretch.
    if (node()->nodeType() == Node::ELEMENT_NODE) {
        if (Element* mo = static_cast<Element*>(node())) {
            AtomicString stretchyAttr = mo->getAttribute(MathMLNames::stretchyAttr);
            stretchDisabled = equalIgnoringCase(stretchyAttr, "false");
            
            // If stretching isn't disabled, get the character from the text content.
            if (!stretchDisabled && !firstChar) {
                String opText = mo->textContent();
                for (unsigned int i = 0; !firstChar && i < opText.length(); i++) {
                    if (!isSpaceOrNewline(opText[i]))
                        firstChar = opText[i];
                }
            }
        }
    }
    
    // The 'index' holds the stretchable character's glyph information
    int index = -1;
    
    // isStretchy indicates whether the character is streatchable via a number of factors.
    bool isStretchy = false;
    
    // Check for a stretchable character.
    if (!stretchDisabled && firstChar) {
        const int maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters);
        for (index++; index < maxIndex; index++) {
            if (stretchyCharacters[index].character == firstChar) {
                isStretchy = true;
                break;
            }
        }
    }
    
    // We only stretch character if the stretch height is larger than a minimum size (e.g. 24px).
    bool shouldStretch = isStretchy && m_stretchHeight>gMinimumStretchHeight;
    
    // Either stretch is disabled or we don't have a stretchable character over the minimum height
    if (stretchDisabled || !shouldStretch) {
        m_isStacked = false;
        RenderBlock* container = new (renderArena()) RenderMathMLBlock(node());
        
        RefPtr<RenderStyle> newStyle = RenderStyle::create();
        newStyle->inheritFrom(style());
        newStyle->setDisplay(INLINE_BLOCK);
        newStyle->setVerticalAlign(BASELINE);
        
        // Check for a stretchable character that is under the minimum height and use the
        // font size to adjust the glyph size.
        int currentFontSize = style()->fontSize();
        if (!stretchDisabled && isStretchy && m_stretchHeight > 0 && m_stretchHeight <= gMinimumStretchHeight  && m_stretchHeight > currentFontSize) {
            FontDescription desc = style()->fontDescription();
            desc.setIsAbsoluteSize(true);
            desc.setSpecifiedSize(m_stretchHeight);
            desc.setComputedSize(m_stretchHeight);
            newStyle->setFontDescription(desc);
            newStyle->font().update(style()->font().fontSelector());
        }

        container->setStyle(newStyle.release());
        addChild(container);
        
        // Build the text of the operator.
        RenderText* text = 0;
        if (m_operator) 
            text = new (renderArena()) RenderText(node(), StringImpl::create(&m_operator, 1));
        else if (node()->nodeType() == Node::ELEMENT_NODE)
            if (Element* mo = static_cast<Element*>(node()))
                text = new (renderArena()) RenderText(node(), mo->textContent().replace(hyphenMinus, minusSign).impl());
        // If we can't figure out the text, leave it blank.
        if (text) {
            RefPtr<RenderStyle> textStyle = RenderStyle::create();
            textStyle->inheritFrom(container->style());
            text->setStyle(textStyle.release());
            container->addChild(text);
        }
    } else {
        // Build stretchable characters as a stack of glyphs.
        m_isStacked = true;
    
        int extensionGlyphLineHeight = lineHeightForCharacter(stretchyCharacters[index].extensionGlyph);
        int topGlyphLineHeight = lineHeightForCharacter(stretchyCharacters[index].topGlyph);
        int bottomGlyphLineHeight = lineHeightForCharacter(stretchyCharacters[index].bottomGlyph);
        
        if (stretchyCharacters[index].middleGlyph) {
            // We have a middle glyph (e.g. a curly bracket) that requires special processing.
            int glyphHeight = glyphHeightForCharacter(stretchyCharacters[index].middleGlyph);
            int middleGlyphLineHeight = lineHeightForCharacter(stretchyCharacters[index].middleGlyph);
            int half = (m_stretchHeight - glyphHeight) / 2;
            if (half <= glyphHeight) {
                // We only have enough space for a single middle glyph.
                createGlyph(stretchyCharacters[index].topGlyph, topGlyphLineHeight, half, gTopGlyphTopAdjust);
                createGlyph(stretchyCharacters[index].middleGlyph, middleGlyphLineHeight, glyphHeight, gMiddleGlyphTopAdjust);
                createGlyph(stretchyCharacters[index].bottomGlyph, bottomGlyphLineHeight, 0, gBottomGlyphTopAdjust);
            } else {
                // We have to extend both the top and bottom to the middle.
                createGlyph(stretchyCharacters[index].topGlyph, topGlyphLineHeight, glyphHeight, gTopGlyphTopAdjust);
                int remaining = half - glyphHeight;
                while (remaining > 0) {
                    if (remaining < glyphHeight) {
                        createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, remaining);
                        remaining = 0;
                    } else {
                        createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, glyphHeight);
                        remaining -= glyphHeight;
                    }
                }
                
                // The middle glyph in the stack.
                createGlyph(stretchyCharacters[index].middleGlyph, middleGlyphLineHeight, glyphHeight, gMiddleGlyphTopAdjust);
                
                // The remaining is the top half minus the middle glyph height.
                remaining = half - glyphHeight;
                // We need to make sure we have the full height in case the height is odd.
                if (m_stretchHeight % 2 == 1)
                    remaining++;
                
                // Extend to the bottom glyph.
                while (remaining > 0) {
                    if (remaining < glyphHeight) {
                        createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, remaining);
                        remaining = 0;
                    } else {
                        createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, glyphHeight);
                        remaining -= glyphHeight;
                    }
                }
                
                // The bottom glyph in the stack.
                createGlyph(stretchyCharacters[index].bottomGlyph, bottomGlyphLineHeight, 0, gBottomGlyphTopAdjust);
            }
        } else {
            // We do not have a middle glyph and so we just extend from the top to the bottom glyph.
            int glyphHeight = glyphHeightForCharacter(stretchyCharacters[index].extensionGlyph);
            int remaining = m_stretchHeight - 2 * glyphHeight;
            createGlyph(stretchyCharacters[index].topGlyph, topGlyphLineHeight, glyphHeight, gTopGlyphTopAdjust);
            while (remaining > 0) {
                if (remaining < glyphHeight) {
                    createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, remaining);
                    remaining = 0;
                } else {
                    createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, glyphHeight);
                    remaining -= glyphHeight;
                }
            }
            createGlyph(stretchyCharacters[index].bottomGlyph, bottomGlyphLineHeight, 0, gBottomGlyphTopAdjust);
        }
    }
    
    setNeedsLayoutAndPrefWidthsRecalc();
}
Ejemplo n.º 9
0
// FIXME: It's cleaner to only call updateFromElement when an attribute has changed. The body of
// this method should probably be moved to a private stretchHeightChanged or checkStretchHeight
// method. Probably at the same time, addChild/removeChild methods should be made to work for
// dynamic DOM changes.
void RenderMathMLOperator::updateFromElement()
{
    RenderObject* savedRenderer = node()->renderer();

    // Destroy our current children
    children()->destroyLeftoverChildren();

    // Since we share a node with our children, destroying our children may set our node's
    // renderer to 0, so we need to restore it.
    node()->setRenderer(savedRenderer);

    int index = stretchyCharacterIndex();
    bool isStretchy = index >= 0;

    // We only stack glyphs if the stretch height is larger than a minimum size.
    bool shouldStack = isStretchy && m_stretchHeight > style()->fontSize();

    struct StretchyCharacter* partsData = 0;
    float topGlyphHeight = 0;
    float extensionGlyphHeight = 0;
    float bottomGlyphHeight = 0;
    float middleGlyphHeight = 0;
    if (isStretchy) {
        partsData = &stretchyCharacters[index];

        FontCachePurgePreventer fontCachePurgePreventer;

        topGlyphHeight = glyphHeightForCharacter(partsData->topGlyph);
        extensionGlyphHeight = glyphHeightForCharacter(partsData->extensionGlyph) - 1;
        bottomGlyphHeight = glyphHeightForCharacter(partsData->bottomGlyph);
        if (partsData->middleGlyph)
            middleGlyphHeight = glyphHeightForCharacter(partsData->middleGlyph) - 1;
        shouldStack = m_stretchHeight >= topGlyphHeight + middleGlyphHeight + bottomGlyphHeight && extensionGlyphHeight > 0;
    }

    bool stretchDisabled = stretchDisabledByMarkup();
    // Either stretch is disabled or we don't have a stretchable character over the minimum height
    if (stretchDisabled || !shouldStack) {
        RenderBlock* container = new (renderArena()) RenderMathMLBlock(toElement(node()));
        toRenderMathMLBlock(container)->setIgnoreInAccessibilityTree(true);

        RefPtr<RenderStyle> newStyle = RenderStyle::create();
        newStyle->inheritFrom(style());
        newStyle->setDisplay(FLEX);
        newStyle->setJustifyContent(JustifyCenter);

        UChar firstCharacter = firstTextCharacter();
        m_isStretched = firstCharacter && isStretchy && m_stretchHeight > style()->fontMetrics().floatHeight();
        if (m_isStretched)
            newStyle->setHeight(Length(m_stretchHeight, Fixed));
        container->setStyle(newStyle.release());
        addChild(container);

        if (m_isStretched) {
            float scaleY = m_stretchHeight / glyphHeightForCharacter(firstCharacter);
            TransformOperations transform;
            transform.operations().append(ScaleTransformOperation::create(1.0, scaleY, ScaleTransformOperation::SCALE_X));

            RefPtr<RenderStyle> innerStyle = RenderStyle::create();
            innerStyle->inheritFrom(style());
            innerStyle->setTransform(transform);
            innerStyle->setTransformOriginY(Length(0, Fixed));

            RenderBlock* innerContainer = new (renderArena()) RenderMathMLBlock(toElement(node()));
            toRenderMathMLBlock(innerContainer)->setIgnoreInAccessibilityTree(true);
            innerContainer->setStyle(innerStyle);
            container->addChild(innerContainer);

            container = innerContainer;
        }

        // Build the text of the operator.
        RenderText* text = 0;
        if (m_operator)
            text = new (renderArena()) RenderText(node(), StringImpl::create(&m_operator, 1));
        else if (node()->isElementNode())
            if (Element* mo = static_cast<Element*>(node()))
                text = new (renderArena()) RenderText(node(), mo->textContent().replace(hyphenMinus, minusSign).impl());
        // If we can't figure out the text, leave it blank.
        if (text) {
            RefPtr<RenderStyle> textStyle = RenderStyle::create();
            textStyle->inheritFrom(container->style());
            text->setStyle(textStyle.release());
            container->addChild(text);
        }
    } else {
        // Build stretchable characters as a stack of glyphs.
        m_isStretched = true;

        // To avoid gaps, we position glyphs after the top glyph upward by 1px. We also truncate
        // glyph heights to ints, and then reduce all but the top & bottom such heights by 1px.

        int remaining = m_stretchHeight - topGlyphHeight - bottomGlyphHeight;
        createGlyph(partsData->topGlyph, topGlyphHeight, 0);
        if (partsData->middleGlyph) {
            // We have a middle glyph (e.g. a curly bracket) that requires special processing.
            remaining -= middleGlyphHeight;
            int half = (remaining + 1) / 2;
            remaining -= half;
            while (remaining > 0) {
                int height = std::min<int>(remaining, extensionGlyphHeight);
                createGlyph(partsData->extensionGlyph, height, -1);
                remaining -= height;
            }

            // The middle glyph in the stack.
            createGlyph(partsData->middleGlyph, middleGlyphHeight, -1);

            remaining = half;
            while (remaining > 0) {
                int height = std::min<int>(remaining, extensionGlyphHeight);
                createGlyph(partsData->extensionGlyph, height, -1);
                remaining -= height;
            }
        } else {
            // We do not have a middle glyph and so we just extend from the top to the bottom glyph.
            while (remaining > 0) {
                int height = std::min<int>(remaining, extensionGlyphHeight);
                createGlyph(partsData->extensionGlyph, height, -1);
                remaining -= height;
            }
        }
        createGlyph(partsData->bottomGlyph, bottomGlyphHeight, -1);
    }

    setNeedsLayoutAndPrefWidthsRecalc();
}
void RenderContainer::updatePseudoChild(RenderStyle::PseudoId type, RenderObject* child)
{
    // In CSS2, before/after pseudo-content cannot nest.  Check this first.
    if (style()->styleType() == RenderStyle::BEFORE || style()->styleType() == RenderStyle::AFTER)
        return;
    
    RenderStyle* pseudo = getPseudoStyle(type);

    // Whether or not we currently have generated content attached.
    bool oldContentPresent = child && (child->style()->styleType() == type);

    // Whether or not we now want generated content.  
    bool newContentWanted = pseudo && pseudo->display() != NONE;

    // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate
    // :after content and not :before content.
    if (type == RenderStyle::BEFORE && isInlineContinuation())
        newContentWanted = false;

    // Similarly, if we're the beginning of a <q>, and there's an inline continuation for our object,
    // then we don't generate the :after content.
    if (type == RenderStyle::AFTER && isRenderInline() && continuation())
        newContentWanted = false;
    
    // If we don't want generated content any longer, or if we have generated content, but it's no longer
    // identical to the new content data we want to build render objects for, then we nuke all
    // of the old generated content.
    if (!newContentWanted ||
        (oldContentPresent && !child->style()->contentDataEquivalent(pseudo))) {
        // Nuke the child. 
        if (child && child->style()->styleType() == type) {
            oldContentPresent = false;
            removeChild(child);
            child = (type == RenderStyle::BEFORE) ? firstChild() : lastChild();
        }
    }

    // If we have no pseudo-style or if the pseudo's display type is NONE, then we
    // have no generated content and can now return.
    if (!newContentWanted)
        return;

    if (isInlineFlow() && pseudo->display() != INLINE)
        // According to the CSS2 spec (the end of section 12.1), the only allowed
        // display values for the pseudo style are NONE and INLINE.  Since we already
        // determined that the pseudo is not display NONE, any display other than
        // inline should be mutated to INLINE.
        pseudo->setDisplay(INLINE);
    
    if (oldContentPresent) {
        if (child && child->style()->styleType() == type) {
            // We have generated content present still.  We want to walk this content and update our
            // style information with the new pseudo style.
            child->setStyle(pseudo);

            // Note that if we ever support additional types of generated content (which should be way off
            // in the future), this code will need to be patched.
            for (RenderObject* genChild = child->firstChild(); genChild; genChild = genChild->nextSibling()) {
                if (genChild->isText())
                    // Generated text content is a child whose style also needs to be set to the pseudo
                    // style.
                    genChild->setStyle(pseudo);
                else {
                    // Images get an empty style that inherits from the pseudo.
                    RenderStyle* style = new (renderArena()) RenderStyle();
                    style->inheritFrom(pseudo);
                    genChild->setStyle(style);
                }
            }
        }
        return; // We've updated the generated content. That's all we needed to do.
    }
    
    RenderObject* insertBefore = (type == RenderStyle::BEFORE) ? child : 0;

    // Generated content consists of a single container that houses multiple children (specified
    // by the content property).  This pseudo container gets the pseudo style set on it.
    RenderObject* pseudoContainer = 0;
    
    // Now walk our list of generated content and create render objects for every type
    // we encounter.
    for (ContentData* contentData = pseudo->contentData();
         contentData; contentData = contentData->_nextContent) {
        if (!pseudoContainer)
            pseudoContainer = RenderFlow::createAnonymousFlow(document(), pseudo); /* anonymous box */
        
        if (contentData->contentType() == CONTENT_TEXT)
        {
            RenderText* t = new (renderArena()) RenderTextFragment(document() /*anonymous object */, contentData->contentText());
            t->setStyle(pseudo);
            pseudoContainer->addChild(t);
        }
        else if (contentData->contentType() == CONTENT_OBJECT)
        {
            RenderImage* img = new (renderArena()) RenderImage(document()); /* Anonymous object */
            RenderStyle* style = new (renderArena()) RenderStyle();
            style->inheritFrom(pseudo);
            img->setStyle(style);
            img->setContentObject(contentData->contentObject());
            pseudoContainer->addChild(img);
        }
    }

    if (pseudoContainer) {
        // Add the pseudo after we've installed all our content, so that addChild will be able to find the text
        // inside the inline for e.g., first-letter styling.
        addChild(pseudoContainer, insertBefore);
    }
}
// FIXME: It's cleaner to only call updateFromElement when an attribute has changed. The body of
// this method should probably be moved to a private stretchHeightChanged or checkStretchHeight
// method. Probably at the same time, addChild/removeChild methods should be made to work for
// dynamic DOM changes.
void RenderMathMLOperator::updateFromElement()
{
    RenderObject* savedRenderer = node()->renderer();

    // Destroy our current children
    children()->destroyLeftoverChildren();

    // Since we share a node with our children, destroying our children may set our node's
    // renderer to 0, so we need to restore it.
    node()->setRenderer(savedRenderer);
    
    // If the operator is fixed, it will be contained in m_operator
    UChar firstChar = m_operator;
    
    // This boolean indicates whether stretching is disabled via the markup.
    bool stretchDisabled = false;
    
    // We may need the element later if we can't stretch.
    if (node()->isElementNode()) {
        if (Element* mo = toElement(node())) {
            AtomicString stretchyAttr = mo->getAttribute(MathMLNames::stretchyAttr);
            stretchDisabled = equalIgnoringCase(stretchyAttr, "false");
            
            // If stretching isn't disabled, get the character from the text content.
            if (!stretchDisabled && !firstChar) {
                String opText = mo->textContent();
                for (unsigned int i = 0; !firstChar && i < opText.length(); i++) {
                    if (!isSpaceOrNewline(opText[i]))
                        firstChar = opText[i];
                }
            }
        }
    }
    
    // The 'index' holds the stretchable character's glyph information
    int index = -1;
    
    // isStretchy indicates whether the character is streatchable via a number of factors.
    bool isStretchy = false;
    
    // Check for a stretchable character.
    if (!stretchDisabled && firstChar) {
        const int maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters);
        for (index++; index < maxIndex; index++) {
            if (stretchyCharacters[index].character == firstChar) {
                isStretchy = true;
                break;
            }
        }
    }
    
    // We only stack glyphs if the stretch height is larger than a minimum size.
    bool shouldStack = isStretchy && m_stretchHeight > style()->fontSize();
    struct StretchyCharacter* partsData = 0;
    int topGlyphHeight = 0;
    int extensionGlyphHeight = 0;
    int bottomGlyphHeight = 0;
    int middleGlyphHeight = 0;
    if (shouldStack) {
        partsData = &stretchyCharacters[index];
        
        FontCachePurgePreventer fontCachePurgePreventer;
        
        topGlyphHeight = glyphHeightForCharacter(partsData->topGlyph);
        extensionGlyphHeight = glyphHeightForCharacter(partsData->extensionGlyph) - 1;
        bottomGlyphHeight = glyphHeightForCharacter(partsData->bottomGlyph);
        if (partsData->middleGlyph)
            middleGlyphHeight = glyphHeightForCharacter(partsData->middleGlyph) - 1;
        shouldStack = m_stretchHeight >= topGlyphHeight + middleGlyphHeight + bottomGlyphHeight && extensionGlyphHeight > 0;
    }
    
    // Either stretch is disabled or we don't have a stretchable character over the minimum height
    if (stretchDisabled || !shouldStack) {
        m_isStacked = false;
        RenderBlock* container = new (renderArena()) RenderMathMLBlock(toElement(node()));
        // This container doesn't offer any useful information to accessibility.
        toRenderMathMLBlock(container)->setIgnoreInAccessibilityTree(true);
        
        RefPtr<RenderStyle> newStyle = RenderStyle::create();
        newStyle->inheritFrom(style());
        newStyle->setDisplay(FLEX);
        
        // Check for a stretchable character that is under the minimum height.
        if (!stretchDisabled && isStretchy && m_stretchHeight > style()->fontSize()) {
            FontDescription desc = style()->fontDescription();
            desc.setIsAbsoluteSize(true);
            desc.setSpecifiedSize(m_stretchHeight);
            desc.setComputedSize(m_stretchHeight);
            newStyle->setFontDescription(desc);
            newStyle->font().update(style()->font().fontSelector());
        }

        container->setStyle(newStyle.release());
        addChild(container);
        
        // Build the text of the operator.
        RenderText* text = 0;
        if (m_operator) 
            text = new (renderArena()) RenderText(node(), StringImpl::create(&m_operator, 1));
        else if (node()->isElementNode())
            if (Element* mo = toElement(node()))
                text = new (renderArena()) RenderText(node(), mo->textContent().replace(hyphenMinus, minusSign).impl());
        // If we can't figure out the text, leave it blank.
        if (text) {
            RefPtr<RenderStyle> textStyle = RenderStyle::create();
            textStyle->inheritFrom(container->style());
            text->setStyle(textStyle.release());
            container->addChild(text);
        }
    } else {
        // Build stretchable characters as a stack of glyphs.
        m_isStacked = true;
    
        // To avoid gaps, we position glyphs after the top glyph upward by 1px. We also truncate
        // glyph heights to ints, and then reduce all but the top & bottom such heights by 1px.
        
        int remaining = m_stretchHeight - topGlyphHeight - bottomGlyphHeight;
        createGlyph(partsData->topGlyph, topGlyphHeight, 0);
        if (partsData->middleGlyph) {
            // We have a middle glyph (e.g. a curly bracket) that requires special processing.
            remaining -= middleGlyphHeight;
            int half = (remaining + 1) / 2;
            remaining -= half;
                while (remaining > 0) {
                int height = std::min<int>(remaining, extensionGlyphHeight);
                createGlyph(partsData->extensionGlyph, height, -1);
                remaining -= height;
                }
                
                // The middle glyph in the stack.
            createGlyph(partsData->middleGlyph, middleGlyphHeight, -1);
                
            remaining = half;
                while (remaining > 0) {
                int height = std::min<int>(remaining, extensionGlyphHeight);
                createGlyph(partsData->extensionGlyph, height, -1);
                remaining -= height;
            }
        } else {
            // We do not have a middle glyph and so we just extend from the top to the bottom glyph.
            while (remaining > 0) {
                int height = std::min<int>(remaining, extensionGlyphHeight);
                createGlyph(partsData->extensionGlyph, height, -1);
                remaining -= height;
                }
            }
        createGlyph(partsData->bottomGlyph, bottomGlyphHeight, -1);
    }
    
    setNeedsLayoutAndPrefWidthsRecalc();
}