bool canUseFor(const RenderBlockFlow& flow) { if (!flow.frame().settings().simpleLineLayoutEnabled()) return false; if (!flow.firstChild()) return false; // This currently covers <blockflow>#text</blockflow> case. // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover. if (flow.firstChild() != flow.lastChild()) return false; if (!flow.firstChild()->isText()) return false; if (!flow.isHorizontalWritingMode()) return false; if (flow.flowThreadState() != RenderObject::NotInsideFlowThread) return false; if (flow.hasOutline()) return false; if (flow.isRubyText() || flow.isRubyBase()) return false; if (flow.parent()->isDeprecatedFlexibleBox()) return false; // FIXME: Implementation of wrap=hard looks into lineboxes. if (flow.parent()->isTextArea() && flow.parent()->element()->fastHasAttribute(HTMLNames::wrapAttr)) return false; // FIXME: Placeholders do something strange. if (flow.parent()->isTextControl() && toRenderTextControl(*flow.parent()).textFormControlElement().placeholderElement()) return false; const RenderStyle& style = flow.style(); if (style.textDecorationsInEffect() != TextDecorationNone) return false; if (style.textAlign() == JUSTIFY) return false; // Non-visible overflow should be pretty easy to support. if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE) return false; if (!style.textIndent().isZero()) return false; if (!style.wordSpacing().isZero() || style.letterSpacing()) return false; if (style.textTransform() != TTNONE) return false; if (!style.isLeftToRightDirection()) return false; if (style.lineBoxContain() != RenderStyle::initialLineBoxContain()) return false; if (style.writingMode() != TopToBottomWritingMode) return false; if (style.lineBreak() != LineBreakAuto) return false; if (style.wordBreak() != NormalWordBreak) return false; if (style.unicodeBidi() != UBNormal || style.rtlOrdering() != LogicalOrder) return false; if (style.lineAlign() != LineAlignNone || style.lineSnap() != LineSnapNone) return false; if (style.hyphens() == HyphensAuto) return false; if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone) return false; if (style.textShadow()) return false; if (style.textOverflow() || (flow.isAnonymousBlock() && flow.parent()->style().textOverflow())) return false; if (style.hasPseudoStyle(FIRST_LINE) || style.hasPseudoStyle(FIRST_LETTER)) return false; if (style.hasTextCombine()) return false; if (style.backgroundClip() == TextFillBox) return false; if (style.borderFit() == BorderFitLines) return false; const RenderText& textRenderer = toRenderText(*flow.firstChild()); if (flow.containsFloats()) { // 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. float minimumWidthNeeded = textRenderer.minLogicalWidth(); for (auto& floatRenderer : *flow.floatingObjectSet()) { ASSERT(floatRenderer); float availableWidth = flow.availableLogicalWidthForLine(floatRenderer->y(), false); if (availableWidth < minimumWidthNeeded) return false; } } if (textRenderer.isCombineText() || textRenderer.isCounter() || textRenderer.isQuote() || textRenderer.isTextFragment() || textRenderer.isSVGInlineText()) return false; if (style.font().codePath(TextRun(textRenderer.text())) != Font::Simple) return false; if (style.font().isSVGFont()) return false; // We assume that all lines have metrics based purely on the primary font. auto& primaryFontData = *style.font().primaryFont(); if (primaryFontData.isLoading()) return false; if (!canUseForText(textRenderer, primaryFontData)) return false; return true; }
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; }