void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.phase != PaintPhaseForeground) return; RenderStyle& style = flow.style(); if (style.visibility() != VISIBLE) return; bool debugBordersEnabled = flow.frame().settings().simpleLineLayoutDebugBordersEnabled(); TextPainter textPainter(paintInfo.context()); textPainter.setFont(style.fontCascade()); textPainter.setTextPaintStyle(computeTextPaintStyle(flow.frame(), style, paintInfo)); Optional<TextDecorationPainter> textDecorationPainter; if (style.textDecorationsInEffect() != TextDecorationNone) { const RenderText* textRenderer = childrenOfType<RenderText>(flow).first(); if (textRenderer) { textDecorationPainter = TextDecorationPainter(paintInfo.context(), style.textDecorationsInEffect(), *textRenderer, false); textDecorationPainter->setFont(style.fontCascade()); textDecorationPainter->setBaseline(style.fontMetrics().ascent()); } } LayoutRect paintRect = paintInfo.rect; paintRect.moveBy(-paintOffset); auto resolver = runResolver(flow, layout); float strokeOverflow = std::ceil(flow.style().textStrokeWidth()); float deviceScaleFactor = flow.document().deviceScaleFactor(); for (auto run : resolver.rangeForRect(paintRect)) { if (run.start() == run.end()) continue; FloatRect rect = run.rect(); FloatRect visualOverflowRect = rect; visualOverflowRect.inflate(strokeOverflow); if (paintRect.y() > visualOverflowRect.maxY() || paintRect.maxY() < visualOverflowRect.y()) continue; TextRun textRun(run.text()); textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize()); // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout. textRun.setXPos(0); FloatPoint textOrigin = FloatPoint(rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor)); textPainter.paintText(textRun, textRun.length(), rect, textOrigin); if (textDecorationPainter) { textDecorationPainter->setWidth(rect.width()); textDecorationPainter->paintTextDecoration(textRun, textOrigin, rect.location() + paintOffset); } if (debugBordersEnabled) paintDebugBorders(paintInfo.context(), LayoutRect(run.rect()), paintOffset); } }
RunResolver::RunResolver(const RenderBlockFlow& flow, const Layout& layout) : m_flowRenderer(flow) , m_layout(layout) , m_flowContents(flow) , m_lineHeight(lineHeightFromFlow(flow)) , m_baseline(baselineFromFlow(flow)) , m_borderAndPaddingBefore(flow.borderAndPaddingBefore()) , m_ascent(flow.style().fontCascade().fontMetrics().ascent()) , m_descent(flow.style().fontCascade().fontMetrics().descent()) , m_visualOverflowOffset(visualOverflowForDecorations(flow.style(), nullptr).bottom) , m_inQuirksMode(flow.document().inQuirksMode()) { }
void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.phase != PaintPhaseForeground) return; RenderStyle& style = flow.style(); if (style.visibility() != VISIBLE) return; RenderText& textRenderer = toRenderText(*flow.firstChild()); ASSERT(!textRenderer.firstTextBox()); bool debugBordersEnabled = flow.frame().settings().simpleLineLayoutDebugBordersEnabled(); GraphicsContext& context = *paintInfo.context; const Font& font = style.font(); TextPaintStyle textPaintStyle = computeTextPaintStyle(textRenderer, style, paintInfo); GraphicsContextStateSaver stateSaver(context, textPaintStyle.strokeWidth > 0); updateGraphicsContext(context, textPaintStyle); LayoutRect paintRect = paintInfo.rect; paintRect.moveBy(-paintOffset); auto resolver = runResolver(flow, layout); auto range = resolver.rangeForRect(paintRect); for (auto it = range.begin(), end = range.end(); it != end; ++it) { const auto& run = *it; if (!run.rect().intersects(paintRect)) continue; TextRun textRun(run.text()); textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize()); FloatPoint textOrigin = run.baseline() + paintOffset; textOrigin.setY(roundToDevicePixel(LayoutUnit(textOrigin.y()), flow.document().deviceScaleFactor())); context.drawText(font, textRun, textOrigin); if (debugBordersEnabled) paintDebugBorders(context, run.rect(), paintOffset); } }
void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.phase != PaintPhaseForeground) return; RenderStyle& style = flow.style(); if (style.visibility() != VISIBLE) return; bool debugBordersEnabled = flow.frame().settings().simpleLineLayoutDebugBordersEnabled(); GraphicsContext& context = paintInfo.context(); const FontCascade& font = style.fontCascade(); TextPaintStyle textPaintStyle = computeTextPaintStyle(flow.frame(), style, paintInfo); GraphicsContextStateSaver stateSaver(context, textPaintStyle.strokeWidth > 0); updateGraphicsContext(context, textPaintStyle); LayoutRect paintRect = paintInfo.rect; paintRect.moveBy(-paintOffset); auto resolver = runResolver(flow, layout); float strokeOverflow = ceilf(flow.style().textStrokeWidth()); float deviceScaleFactor = flow.document().deviceScaleFactor(); for (const auto& run : resolver.rangeForRect(paintRect)) { FloatRect rect = run.rect(); rect.inflate(strokeOverflow); if (!rect.intersects(paintRect) || run.start() == run.end()) continue; TextRun textRun(run.text()); textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize()); // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout. textRun.setXPos(0); FloatPoint textOrigin = FloatPoint(rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor)); context.drawText(font, textRun, textOrigin); if (debugBordersEnabled) paintDebugBorders(context, LayoutRect(run.rect()), paintOffset); } }
static AvoidanceReasonFlags canUseForWithReason(const RenderBlockFlow& flow, FallThrough fallthrough) { #ifndef NDEBUG static std::once_flag onceFlag; std::call_once(onceFlag, [] { registerNotifyCallback("com.apple.WebKit.showSimpleLineLayoutCoverage", printSimpleLineLayoutCoverage); registerNotifyCallback("com.apple.WebKit.showSimpleLineLayoutReasons", printSimpleLineLayoutBlockList); }); #endif AvoidanceReasonFlags reasons = NoReason; if (!flow.frame().settings().simpleLineLayoutEnabled()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FeatureIsDisabled, fallthrough); if (!flow.parent()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowHasNoParent, fallthrough); if (!flow.firstChild()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowHasNoChild, fallthrough); if (flow.flowThreadState() != RenderObject::NotInsideFlowThread) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowIsInsideRegion, fallthrough); if (!flow.isHorizontalWritingMode()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowHasHorizonalWritingMode, fallthrough); if (flow.hasOutline()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowHasOutline, fallthrough); if (flow.isRubyText() || flow.isRubyBase()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowIsRuby, fallthrough); // Printing does pagination without a flow thread. if (flow.document().paginated()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowIsPaginated, fallthrough); if (flow.isAnonymous() && flow.firstLineBlock()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowHasPseudoFirstLine, fallthrough); if (flow.isAnonymousBlock() && flow.parent()->style().textOverflow()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowHasTextOverflow, fallthrough); if (flow.parent()->isDeprecatedFlexibleBox()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowIsDepricatedFlexBox, fallthrough); // FIXME: Placeholders do something strange. if (is<RenderTextControl>(*flow.parent()) && downcast<RenderTextControl>(*flow.parent()).textFormControlElement().placeholderElement()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowParentIsPlaceholderElement, fallthrough); // FIXME: Implementation of wrap=hard looks into lineboxes. if (flow.parent()->isTextArea() && flow.parent()->element()->fastHasAttribute(HTMLNames::wrapAttr)) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowParentIsTextAreaWithWrapping, fallthrough); // This currently covers <blockflow>#text</blockflow>, <blockflow>#text<br></blockflow> and mutiple (sibling) RenderText cases. // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover. for (const auto* child = flow.firstChild(); child;) { if (is<RenderText>(*child)) { child = child->nextSibling(); continue; } if (is<RenderLineBreak>(child) && !downcast<RenderLineBreak>(*child).isWBR() && child->style().clear() == CNONE) { child = child->nextSibling(); continue; } SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowHasNonSupportedChild, fallthrough); break; } auto styleReasons = canUseForStyle(flow.style(), fallthrough); if (styleReasons != NoReason) SET_REASON_AND_RETURN_IF_NEEDED(reasons, styleReasons, fallthrough); // We can't use the code path if any lines would need to be shifted below floats. This is because we don't keep per-line y coordinates. if (flow.containsFloats()) { float minimumWidthNeeded = std::numeric_limits<float>::max(); for (const auto& textRenderer : childrenOfType<RenderText>(flow)) { minimumWidthNeeded = std::min(minimumWidthNeeded, textRenderer.minLogicalWidth()); for (auto& floatingObject : *flow.floatingObjectSet()) { ASSERT(floatingObject); #if ENABLE(CSS_SHAPES) // if a float has a shape, we cannot tell if content will need to be shifted until after we lay it out, // since the amount of space is not uniform for the height of the float. if (floatingObject->renderer().shapeOutsideInfo()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowHasUnsupportedFloat, fallthrough); #endif float availableWidth = flow.availableLogicalWidthForLine(floatingObject->y(), false); if (availableWidth < minimumWidthNeeded) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowHasUnsupportedFloat, fallthrough); } } } auto fontAndTextReasons = canUseForFontAndText(flow, fallthrough); if (fontAndTextReasons != NoReason) SET_REASON_AND_RETURN_IF_NEEDED(reasons, fontAndTextReasons, fallthrough); return reasons; }