void RenderListItem::insertOrMoveMarkerRendererIfNeeded() { // Sanity check the location of our marker. if (!m_marker) return; RenderElement* currentParent = m_marker->parent(); RenderBlock* newParent = getParentOfFirstLineBox(this, m_marker); if (!newParent) { // If the marker is currently contained inside an anonymous box, // then we are the only item in that anonymous box (since no line box // parent was found). It's ok to just leave the marker where it is // in this case. if (currentParent && currentParent->isAnonymousBlock()) return; newParent = this; } if (newParent != currentParent) { // Removing and adding the marker can trigger repainting in // containers other than ourselves, so we need to disable LayoutState. LayoutStateDisabler layoutStateDisabler(&view()); m_marker->removeFromParent(); newParent->addChild(m_marker, firstNonMarkerChild(newParent)); m_marker->updateMarginsAndContent(); // If current parent is an anonymous block that has lost all its children, destroy it. if (currentParent && currentParent->isAnonymousBlock() && !currentParent->firstChild() && !toRenderBlock(currentParent)->continuation()) currentParent->destroy(); } }
void RenderMathMLSubSup::addChild(RenderObject* child, RenderObject* beforeChild) { // Note: The RenderMathMLBlock only allows element children to be added. Element* childElement = toElement(child->node()); if (childElement && !childElement->previousElementSibling()) { // Position 1 is always the base of the msub/msup/msubsup. RenderBlock* baseWrapper = createAlmostAnonymousBlock(INLINE_BLOCK); RenderMathMLBlock::addChild(baseWrapper, firstChild()); baseWrapper->addChild(child); // Make sure we have a script block for rendering. if (m_kind == SubSup && !m_scripts) { RefPtr<RenderStyle> scriptsStyle = RenderStyle::createAnonymousStyle(style()); scriptsStyle->setDisplay(INLINE_BLOCK); scriptsStyle->setVerticalAlign(TOP); scriptsStyle->setMarginLeft(Length(gSubsupScriptMargin, Fixed)); scriptsStyle->setTextAlign(LEFT); // Set this wrapper's font-size for its line-height & baseline position. scriptsStyle->setBlendedFontSize(static_cast<int>(0.75 * style()->fontSize())); m_scripts = new (renderArena()) RenderMathMLBlock(node()); m_scripts->setStyle(scriptsStyle); RenderMathMLBlock::addChild(m_scripts, beforeChild); } } else { if (m_kind == SubSup) { ASSERT(childElement); if (!childElement) return; RenderBlock* script = m_scripts->createAlmostAnonymousBlock(); // The order is always backwards so the first script is the subscript and the superscript // is last. That means the superscript is the first to render vertically. Element* previousSibling = childElement->previousElementSibling(); if (previousSibling && !previousSibling->previousElementSibling()) m_scripts->addChild(script); else m_scripts->addChild(script, m_scripts->firstChild()); script->addChild(child); } else RenderMathMLBlock::addChild(child, beforeChild); } }
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; }
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; }
void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild) { ASSERT(child); if (child->isRubyText()) { if (!beforeChild) { // RenderRuby has already ascertained that we can add the child here. ASSERT(!hasRubyText()); // prepend ruby texts as first child RenderBlockFlow::addChild(child, firstChild()); } else if (beforeChild->isRubyText()) { // New text is inserted just before another. // In this case the new text takes the place of the old one, and // the old text goes into a new run that is inserted as next sibling. ASSERT(beforeChild->parent() == this); RenderObject* ruby = parent(); ASSERT(ruby->isRuby()); RenderBlock* newRun = staticCreateRubyRun(ruby); ruby->addChild(newRun, nextSibling()); // Add the new ruby text and move the old one to the new run // Note: Doing it in this order and not using RenderRubyRun's methods, // in order to avoid automatic removal of the ruby run in case there is no // other child besides the old ruby text. RenderBlockFlow::addChild(child, beforeChild); RenderBlockFlow::removeChild(beforeChild); newRun->addChild(beforeChild); } else if (hasRubyBase()) { // Insertion before a ruby base object. // In this case we need insert a new run before the current one and split the base. RenderObject* ruby = parent(); RenderRubyRun* newRun = staticCreateRubyRun(ruby); ruby->addChild(newRun, this); newRun->addChild(child); rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild); } } else { // child is not a text -> insert it into the base // (append it instead if beforeChild is the ruby text) RenderRubyBase* base = rubyBaseSafe(); if (beforeChild == base) beforeChild = base->firstChild(); if (beforeChild && beforeChild->isRubyText()) beforeChild = 0; ASSERT(!beforeChild || beforeChild->isDescendantOf(base)); base->addChild(child, beforeChild); } }
void RenderMathMLRoot::addChild(RenderObject* child, RenderObject* ) { if (isEmpty()) { // Add a block for the index RenderBlock* indexWrapper = createAlmostAnonymousBlock(INLINE_BLOCK); RenderBlock::addChild(indexWrapper); // FIXME: the wrapping does not seem to be needed anymore. // this is the base, so wrap it so we can pad it RenderBlock* baseWrapper = createAlmostAnonymousBlock(INLINE_BLOCK); baseWrapper->style()->setPaddingLeft(Length(5 * gFrontWidthEms, Percent)); RenderBlock::addChild(baseWrapper); baseWrapper->addChild(child); } else { // always add to the index firstChild()->addChild(child); } }
void RenderListItem::insertOrMoveMarkerRendererIfNeeded() { // Sanity check the location of our marker. if (!m_marker) return; // FIXME: Do not even try to reposition the marker when we are not in layout // until after we fixed webkit.org/b/163789. if (!view().frameView().isInRenderTreeLayout()) return; RenderElement* currentParent = m_marker->parent(); RenderBlock* newParent = getParentOfFirstLineBox(*this, *m_marker); if (!newParent) { // If the marker is currently contained inside an anonymous box, // then we are the only item in that anonymous box (since no line box // parent was found). It's ok to just leave the marker where it is // in this case. if (currentParent && currentParent->isAnonymousBlock()) return; if (multiColumnFlowThread()) newParent = multiColumnFlowThread(); else newParent = this; } if (newParent != currentParent) { // Removing and adding the marker can trigger repainting in // containers other than ourselves, so we need to disable LayoutState. LayoutStateDisabler layoutStateDisabler(view()); // Mark the parent dirty so that when the marker gets inserted into the tree // and dirties ancestors, it stops at the parent. newParent->setChildNeedsLayout(MarkOnlyThis); m_marker->setNeedsLayout(MarkOnlyThis); m_marker->removeFromParent(); newParent->addChild(m_marker, firstNonMarkerChild(*newParent)); m_marker->updateMarginsAndContent(); // If current parent is an anonymous block that has lost all its children, destroy it. if (currentParent && currentParent->isAnonymousBlock() && !currentParent->firstChild() && !downcast<RenderBlock>(*currentParent).continuation()) currentParent->destroy(); } }
void RenderMathMLFenced::addChild(RenderObject* child, RenderObject*) { // make the fences if the render object is empty if (isEmpty()) updateFromElement(); if (m_separators.get()) { unsigned int count = 0; for (Node* position = child->node(); position; position = position->previousSibling()) { if (position->nodeType() == Node::ELEMENT_NODE) count++; } if (count > 1) { UChar separator; // Use the last separator if we've run out of specified separators. if ((count - 1) >= m_separators.get()->length()) separator = (*m_separators.get())[m_separators.get()->length() - 1]; else separator = (*m_separators.get())[count - 1]; RenderObject* separatorObj = new (renderArena()) RenderMathMLOperator(node(), separator); separatorObj->setStyle(makeOperatorStyle().release()); RenderBlock::addChild(separatorObj, lastChild()); } } // If we have a block, we'll wrap it in an inline-block. if (child->isBlockFlow() && child->style()->display() != INLINE_BLOCK) { // Block objects wrapper. RenderBlock* block = new (renderArena()) RenderBlock(node()); RefPtr<RenderStyle> newStyle = RenderStyle::create(); newStyle->inheritFrom(style()); newStyle->setDisplay(INLINE_BLOCK); block->setStyle(newStyle.release()); RenderBlock::addChild(block, lastChild()); block->addChild(child); } else RenderBlock::addChild(child, lastChild()); }
void RenderMathMLFraction::addChild(RenderObject* child, RenderObject* beforeChild) { RenderBlock* row = new (renderArena()) RenderMathMLBlock(node()); RefPtr<RenderStyle> rowStyle = makeBlockStyle(); rowStyle->setTextAlign(CENTER); Length pad(static_cast<int>(rowStyle->fontSize() * gHorizontalPad), Fixed); rowStyle->setPaddingLeft(pad); rowStyle->setPaddingRight(pad); // Only add padding for rows as denominators bool isNumerator = isEmpty(); if (!isNumerator) rowStyle->setPaddingTop(Length(2, Fixed)); row->setStyle(rowStyle.release()); RenderBlock::addChild(row, beforeChild); row->addChild(child); updateFromElement(); }
void RenderMathMLSubSup::addChild(RenderObject* child, RenderObject* beforeChild) { if (firstChild()) { // We already have a base, so this is the super/subscripts being added. if (m_kind == SubSup) { if (!m_scripts) { m_scripts = new (renderArena()) RenderMathMLBlock(node()); RefPtr<RenderStyle> scriptsStyle = RenderStyle::create(); scriptsStyle->inheritFrom(style()); scriptsStyle->setDisplay(INLINE_BLOCK); scriptsStyle->setVerticalAlign(MIDDLE); scriptsStyle->setMarginLeft(Length(gSubsupScriptMargin, Fixed)); scriptsStyle->setTextAlign(LEFT); m_scripts->setStyle(scriptsStyle.release()); RenderMathMLBlock::addChild(m_scripts, beforeChild); } RenderBlock* script = new (renderArena()) RenderMathMLBlock(node()); RefPtr<RenderStyle> scriptStyle = RenderStyle::create(); scriptStyle->inheritFrom(m_scripts->style()); scriptStyle->setDisplay(BLOCK); script->setStyle(scriptStyle.release()); m_scripts->addChild(script, m_scripts->firstChild()); script->addChild(child); } else RenderMathMLBlock::addChild(child, beforeChild); } else { RenderMathMLBlock* wrapper = new (renderArena()) RenderMathMLBlock(node()); RefPtr<RenderStyle> wrapperStyle = RenderStyle::create(); wrapperStyle->inheritFrom(style()); wrapperStyle->setDisplay(INLINE_BLOCK); wrapperStyle->setVerticalAlign(MIDDLE); wrapper->setStyle(wrapperStyle.release()); RenderMathMLBlock::addChild(wrapper, beforeChild); wrapper->addChild(child); } }
void RenderMathMLRoot::addChild(RenderObject* child, RenderObject* ) { if (isEmpty()) { // Add a block for the index RenderBlock* block = new (renderArena()) RenderBlock(node()); RefPtr<RenderStyle> indexStyle = makeBlockStyle(); indexStyle->setDisplay(INLINE_BLOCK); block->setStyle(indexStyle.release()); RenderBlock::addChild(block); // FIXME: the wrapping does not seem to be needed anymore. // this is the base, so wrap it so we can pad it block = new (renderArena()) RenderBlock(node()); RefPtr<RenderStyle> baseStyle = makeBlockStyle(); baseStyle->setDisplay(INLINE_BLOCK); baseStyle->setPaddingLeft(Length(5 * gRadicalWidth , Percent)); block->setStyle(baseStyle.release()); RenderBlock::addChild(block); block->addChild(child); } else { // always add to the index firstChild()->addChild(child); } }
// 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(); }
void RenderRubyAsBlock::addChild(RenderObject* child, RenderObject* beforeChild) { // Insert :before and :after content before/after the RenderRubyRun(s) if (child->isBeforeContent()) { if (child->isInline()) { // Add generated inline content normally RenderBlock::addChild(child, firstChild()); } else { // Wrap non-inline content with an anonymous inline-block. RenderBlock* beforeBlock = rubyBeforeBlock(this); if (!beforeBlock) { beforeBlock = createAnonymousRubyInlineBlock(this); RenderBlock::addChild(beforeBlock, firstChild()); } beforeBlock->addChild(child); } return; } if (child->isAfterContent()) { if (child->isInline()) { // Add generated inline content normally RenderBlock::addChild(child); } else { // Wrap non-inline content with an anonymous inline-block. RenderBlock* afterBlock = rubyAfterBlock(this); if (!afterBlock) { afterBlock = createAnonymousRubyInlineBlock(this); RenderBlock::addChild(afterBlock); } afterBlock->addChild(child); } return; } // If the child is a ruby run, just add it normally. if (child->isRubyRun()) { RenderBlock::addChild(child, beforeChild); return; } if (beforeChild && !isAfterContent(beforeChild)) { // insert child into run ASSERT(!beforeChild->isRubyRun()); RenderObject* run = beforeChild; while (run && !run->isRubyRun()) run = run->parent(); if (run) { run->addChild(child, beforeChild); return; } ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent! // Emergency fallback: fall through and just append. } // If the new child would be appended, try to add the child to the previous run // if possible, or create a new run otherwise. // (The RenderRubyRun object will handle the details) RenderRubyRun* lastRun = lastRubyRun(this); if (!lastRun || lastRun->hasRubyText()) { lastRun = RenderRubyRun::staticCreateRubyRun(this); RenderBlock::addChild(lastRun, beforeChild); } lastRun->addChild(child); }
// 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(); }
// 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(); }