bool SVGGraphicsElement::hasAnimatedLocalTransform() const
{
    const ComputedStyle* style = layoutObject() ? layoutObject()->style() : nullptr;

    // Each of these is used in SVGGraphicsElement::calculateAnimatedLocalTransform to create an animated local transform.
    return (style && style->hasTransform()) || !m_transform->currentValue()->isEmpty() || hasSVGRareData();
}
Example #2
0
void InlineBox::adjustPosition(FloatWillBeLayoutUnit dx, FloatWillBeLayoutUnit dy)
{
    m_topLeft.move(dx, dy);

    if (layoutObject().isReplaced())
        toLayoutBox(layoutObject()).move(dx, dy);
}
bool SVGInlineTextBox::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit, LayoutUnit)
{
    // FIXME: integrate with InlineTextBox::nodeAtPoint better.
    ASSERT(!isLineBreak());

    PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, result.hitTestRequest(), layoutObject().style()->pointerEvents());
    bool isVisible = layoutObject().style()->visibility() == VISIBLE;
    if (isVisible || !hitRules.requireVisible) {
        if (hitRules.canHitBoundingBox
            || (hitRules.canHitStroke && (layoutObject().style()->svgStyle().hasStroke() || !hitRules.requireStroke))
            || (hitRules.canHitFill && (layoutObject().style()->svgStyle().hasFill() || !hitRules.requireFill))) {
            FloatPointWillBeLayoutPoint boxOrigin(x(), y());
            boxOrigin.moveBy(accumulatedOffset);
            FloatRectWillBeLayoutRect rect(boxOrigin, size());
            // FIXME: both calls to rawValue() below is temporary and should be removed once the transition
            // to LayoutUnit-based types is complete (crbug.com/321237)
            if (locationInContainer.intersects(rect.rawValue())) {
                layoutObject().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
                if (!result.addNodeToListBasedTestResult(layoutObject().node(), locationInContainer, rect.rawValue()))
                    return true;
            }
        }
    }
    return false;
}
void SVGPathElement::svgAttributeChanged(const QualifiedName& attrName)
{
    if (attrName == SVGNames::dAttr) {
        SVGElement::InvalidationGuard invalidationGuard(this);
        invalidateSVGPresentationAttributeStyle();
        setNeedsStyleRecalc(LocalStyleChange,
            StyleChangeReasonForTracing::fromAttribute(attrName));

        if (LayoutSVGShape* layoutPath = toLayoutSVGShape(this->layoutObject()))
            layoutPath->setNeedsShapeUpdate();

        invalidateMPathDependencies();
        if (layoutObject())
            markForLayoutAndParentResourceInvalidation(layoutObject());

        return;
    }

    if (attrName == SVGNames::pathLengthAttr) {
        SVGElement::InvalidationGuard invalidationGuard(this);
        if (layoutObject())
            markForLayoutAndParentResourceInvalidation(layoutObject());
        return;
    }

    SVGGeometryElement::svgAttributeChanged(attrName);
}
RawPtr<Text> Text::splitText(unsigned offset, ExceptionState& exceptionState)
{
    // IndexSizeError: Raised if the specified offset is negative or greater than
    // the number of 16-bit units in data.
    if (offset > length()) {
        exceptionState.throwDOMException(IndexSizeError, "The offset " + String::number(offset) + " is larger than the Text node's length.");
        return nullptr;
    }

    EventQueueScope scope;
    String oldStr = data();
    RawPtr<Text> newText = cloneWithData(oldStr.substring(offset));
    setDataWithoutUpdate(oldStr.substring(0, offset));

    didModifyData(oldStr, CharacterData::UpdateFromNonParser);

    if (parentNode())
        parentNode()->insertBefore(newText.get(), nextSibling(), exceptionState);
    if (exceptionState.hadException())
        return nullptr;

    if (layoutObject())
        layoutObject()->setTextWithOffset(dataImpl(), 0, oldStr.length());

    if (parentNode())
        document().didSplitTextNode(*this);

    return newText.release();
}
Example #6
0
void SVGGeometryElement::toClipPath(Path& path) const {
  path = asPath();
  path.transform(calculateAnimatedLocalTransform());

  ASSERT(layoutObject());
  ASSERT(layoutObject()->style());
  path.setWindRule(layoutObject()->style()->svgStyle().clipRule());
}
void SliderThumbElement::setPositionFromValue()
{
    // Since the code to calculate position is in the LayoutSliderThumb layout
    // path, we don't actually update the value here. Instead, we poke at the
    // renderer directly to trigger layout.
    if (layoutObject())
        layoutObject()->setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::SliderValueChanged);
}
void HTMLTextAreaElement::defaultEventHandler(Event* event)
{
    if (layoutObject() && (event->isMouseEvent() || event->isDragEvent() || event->hasInterface(EventNames::WheelEvent) || event->type() == EventTypeNames::blur))
        forwardEvent(event);
    else if (layoutObject() && event->isBeforeTextInsertedEvent())
        handleBeforeTextInsertedEvent(static_cast<BeforeTextInsertedEvent*>(event));

    HTMLTextFormControlElement::defaultEventHandler(event);
}
void SliderThumbElement::setPositionFromPoint(const LayoutPoint& point)
{
    RefPtrWillBeRawPtr<HTMLInputElement> input(hostInput());
    Element* trackElement = input->closedShadowRoot()->getElementById(ShadowElementNames::sliderTrack());

    if (!input->layoutObject() || !layoutBox() || !trackElement->layoutBox())
        return;

    LayoutPoint offset = roundedLayoutPoint(input->layoutObject()->absoluteToLocal(FloatPoint(point), UseTransforms));
    bool isVertical = hasVerticalAppearance(input.get());
    bool isLeftToRightDirection = layoutBox()->style()->isLeftToRightDirection();
    LayoutUnit trackSize;
    LayoutUnit position;
    LayoutUnit currentPosition;
    // We need to calculate currentPosition from absolute points becaue the
    // renderer for this node is usually on a layer and layoutBox()->x() and
    // y() are unusable.
    // FIXME: This should probably respect transforms.
    LayoutPoint absoluteThumbOrigin = layoutBox()->absoluteBoundingBoxRectIgnoringTransforms().location();
    LayoutPoint absoluteSliderContentOrigin = roundedLayoutPoint(input->layoutObject()->localToAbsolute());
    IntRect trackBoundingBox = trackElement->layoutObject()->absoluteBoundingBoxRectIgnoringTransforms();
    IntRect inputBoundingBox = input->layoutObject()->absoluteBoundingBoxRectIgnoringTransforms();
    if (isVertical) {
        trackSize = trackElement->layoutBox()->contentHeight() - layoutBox()->size().height();
        position = offset.y() - layoutBox()->size().height() / 2 - trackBoundingBox.y() + inputBoundingBox.y() - layoutBox()->marginBottom();
        currentPosition = absoluteThumbOrigin.y() - absoluteSliderContentOrigin.y();
    } else {
        trackSize = trackElement->layoutBox()->contentWidth() - layoutBox()->size().width();
        position = offset.x() - layoutBox()->size().width() / 2 - trackBoundingBox.x() + inputBoundingBox.x();
        position -= isLeftToRightDirection ? layoutBox()->marginLeft() : layoutBox()->marginRight();
        currentPosition = absoluteThumbOrigin.x() - absoluteSliderContentOrigin.x();
    }
    position = std::max<LayoutUnit>(0, std::min(position, trackSize));
    const Decimal ratio = Decimal::fromDouble(static_cast<double>(position) / trackSize);
    const Decimal fraction = isVertical || !isLeftToRightDirection ? Decimal(1) - ratio : ratio;
    StepRange stepRange(input->createStepRange(RejectAny));
    Decimal value = stepRange.clampValue(stepRange.valueFromProportion(fraction));

    Decimal closest = input->findClosestTickMarkValue(value);
    if (closest.isFinite()) {
        double closestFraction = stepRange.proportionFromValue(closest).toDouble();
        double closestRatio = isVertical || !isLeftToRightDirection ? 1.0 - closestFraction : closestFraction;
        LayoutUnit closestPosition = trackSize * closestRatio;
        const LayoutUnit snappingThreshold = 5;
        if ((closestPosition - position).abs() <= snappingThreshold)
            value = closest;
    }

    String valueString = serializeForNumberType(value);
    if (valueString == input->value())
        return;

    // FIXME: This is no longer being set from renderer. Consider updating the method name.
    input->setValueFromRenderer(valueString);
    if (layoutObject())
        layoutObject()->setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::SliderValueChanged);
}
bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
{
    return supportsPlaceholder()
        && isEmptyValue()
        && isEmptySuggestedValue()
        && !isPlaceholderEmpty()
        && (document().focusedElement() != this || (LayoutTheme::theme().shouldShowPlaceholderWhenFocused()))
        && (!layoutObject() || layoutObject()->style()->visibility() == VISIBLE);
}
Example #11
0
void HTMLTextAreaElement::parseAttribute(const QualifiedName& name,
                                         const AtomicString& oldValue,
                                         const AtomicString& value) {
  if (name == rowsAttr) {
    unsigned rows = 0;
    if (value.isEmpty() || !parseHTMLNonNegativeInteger(value, rows) ||
        rows <= 0)
      rows = defaultRows;
    if (m_rows != rows) {
      m_rows = rows;
      if (layoutObject())
        layoutObject()
            ->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
                LayoutInvalidationReason::AttributeChanged);
    }
  } else if (name == colsAttr) {
    unsigned cols = 0;
    if (value.isEmpty() || !parseHTMLNonNegativeInteger(value, cols) ||
        cols <= 0)
      cols = defaultCols;
    if (m_cols != cols) {
      m_cols = cols;
      if (LayoutObject* layoutObject = this->layoutObject())
        layoutObject->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
            LayoutInvalidationReason::AttributeChanged);
    }
  } else if (name == wrapAttr) {
    // The virtual/physical values were a Netscape extension of HTML 3.0, now
    // deprecated.  The soft/hard /off values are a recommendation for HTML 4
    // extension by IE and NS 4.
    WrapMethod wrap;
    if (equalIgnoringCase(value, "physical") ||
        equalIgnoringCase(value, "hard") || equalIgnoringCase(value, "on"))
      wrap = HardWrap;
    else if (equalIgnoringCase(value, "off"))
      wrap = NoWrap;
    else
      wrap = SoftWrap;
    if (wrap != m_wrap) {
      m_wrap = wrap;
      if (LayoutObject* layoutObject = this->layoutObject())
        layoutObject->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
            LayoutInvalidationReason::AttributeChanged);
    }
  } else if (name == accesskeyAttr) {
    // ignore for the moment
  } else if (name == maxlengthAttr) {
    UseCounter::count(document(), UseCounter::TextAreaMaxLength);
    setNeedsValidityCheck();
  } else if (name == minlengthAttr) {
    UseCounter::count(document(), UseCounter::TextAreaMinLength);
    setNeedsValidityCheck();
  } else {
    HTMLTextFormControlElement::parseAttribute(name, oldValue, value);
  }
}
FloatRect SVGGraphicsElement::getBBox()
{
    document().updateStyleAndLayoutIgnorePendingStylesheets();

    // FIXME: Eventually we should support getBBox for detached elements.
    if (!layoutObject())
        return FloatRect();

    return layoutObject()->objectBoundingBox();
}
void InlineBox::showBox(int printedCharacters) const
{
    printedCharacters += fprintf(stderr, "%s %p", boxName(), this);
    for (; printedCharacters < showTreeCharacterOffset; printedCharacters++)
        fputc(' ', stderr);
    fprintf(stderr, "\t%s %p {pos=%g,%g size=%g,%g} baseline=%i/%i\n",
        layoutObject().decoratedName().ascii().data(), &layoutObject(),
        x().toFloat(), y().toFloat(), width().toFloat(), height().toFloat(),
        baselinePosition(AlphabeticBaseline), baselinePosition(IdeographicBaseline));
}
Example #14
0
inline void HTMLLIElement::parseValue(const AtomicString& value) {
  DCHECK(layoutObject());
  DCHECK(layoutObject()->isListItem());

  bool valueOK;
  int requestedValue = value.toInt(&valueOK);
  if (valueOK)
    toLayoutListItem(layoutObject())->setExplicitValue(requestedValue);
  else
    toLayoutListItem(layoutObject())->clearExplicitValue();
}
void SliderThumbElement::stopDragging()
{
    if (!m_inDragMode)
        return;

    if (LocalFrame* frame = document().frame())
        frame->eventHandler().setCapturingMouseEventsNode(nullptr);
    m_inDragMode = false;
    if (layoutObject())
        layoutObject()->setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::SliderValueChanged);
    if (hostInput())
        hostInput()->dispatchFormControlChangeEvent();
}
FloatSize SVGSVGElement::currentViewportSize() const
{
    if (!layoutObject())
        return FloatSize();

    if (layoutObject()->isSVGRoot()) {
        LayoutRect contentBoxRect = toLayoutSVGRoot(layoutObject())->contentBoxRect();
        return FloatSize(contentBoxRect.width() / layoutObject()->style()->effectiveZoom(), contentBoxRect.height() / layoutObject()->style()->effectiveZoom());
    }

    FloatRect viewportRect = toLayoutSVGViewportContainer(layoutObject())->viewport();
    return FloatSize(viewportRect.width(), viewportRect.height());
}
bool SVGGeometryElement::isPointInStroke(PassRefPtrWillBeRawPtr<SVGPointTearOff> point) const
{
    document().updateLayoutIgnorePendingStylesheets();

    // FIXME: Eventually we should support isPointInStroke for display:none elements.
    if (!layoutObject() || !layoutObject()->isSVGShape())
        return false;

    HitTestRequest request(HitTestRequest::ReadOnly);
    PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request, layoutObject()->style()->pointerEvents());
    hitRules.canHitFill = false;
    return toLayoutSVGShape(layoutObject())->nodeAtFloatPointInternal(request, point->target()->value(), hitRules);
}
Example #18
0
void HTMLElement::adjustDirectionalityIfNeededAfterChildAttributeChanged(Element* child)
{
    ASSERT(selfOrAncestorHasDirAutoAttribute());
    TextDirection textDirection = directionality();
    if (layoutObject() && layoutObject()->style() && layoutObject()->style()->direction() != textDirection) {
        Element* elementToAdjust = this;
        for (; elementToAdjust; elementToAdjust = ComposedTreeTraversal::parentElement(*elementToAdjust)) {
            if (elementAffectsDirectionality(elementToAdjust)) {
                elementToAdjust->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::WritingModeChange));
                return;
            }
        }
    }
}
Example #19
0
void HTMLFrameElement::parseAttribute(const QualifiedName& name,
                                      const AtomicString& oldValue,
                                      const AtomicString& value) {
  if (name == frameborderAttr) {
    m_frameBorder = value.toInt();
    m_frameBorderSet = !value.isNull();
    // FIXME: If we are already attached, this has no effect.
  } else if (name == noresizeAttr) {
    if (layoutObject())
      layoutObject()->updateFromElement();
  } else {
    HTMLFrameElementBase::parseAttribute(name, oldValue, value);
  }
}
void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
{
    bool updateRelativeLengthsOrViewBox = false;
    bool widthChanged = attrName == SVGNames::widthAttr;
    bool heightChanged = attrName == SVGNames::heightAttr;
    if (widthChanged || heightChanged
        || attrName == SVGNames::xAttr
        || attrName == SVGNames::yAttr) {
        updateRelativeLengthsOrViewBox = true;
        updateRelativeLengthsInformation();
        invalidateRelativeLengthClients();

        // At the SVG/HTML boundary (aka LayoutSVGRoot), the width and
        // height attributes can affect the replaced size so we need
        // to mark it for updating.
        //
        // FIXME: For width/height animated as XML attributes on SVG
        // roots, there is an attribute synchronization missing. See
        // http://crbug.com/364807
        if (widthChanged || heightChanged) {
            LayoutObject* layoutObject = this->layoutObject();
            if (layoutObject && layoutObject->isSVGRoot()) {
                invalidateSVGPresentationAttributeStyle();
                setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SVGContainerSizeChange));
            }
        } else {
            invalidateSVGPresentationAttributeStyle();
            setNeedsStyleRecalc(LocalStyleChange,
                StyleChangeReasonForTracing::fromAttribute(attrName));
        }
    }

    if (SVGFitToViewBox::isKnownAttribute(attrName)) {
        updateRelativeLengthsOrViewBox = true;
        invalidateRelativeLengthClients();
        if (LayoutObject* object = layoutObject())
            object->setNeedsTransformUpdate();
    }

    if (updateRelativeLengthsOrViewBox
        || SVGZoomAndPan::isKnownAttribute(attrName)) {
        SVGElement::InvalidationGuard invalidationGuard(this);
        if (layoutObject())
            markForLayoutAndParentResourceInvalidation(layoutObject());
        return;
    }

    SVGGraphicsElement::svgAttributeChanged(attrName);
}
Example #21
0
void HTMLTextAreaElement::handleBeforeTextInsertedEvent(
    BeforeTextInsertedEvent* event) const {
  DCHECK(event);
  DCHECK(layoutObject());
  int signedMaxLength = maxLength();
  if (signedMaxLength < 0)
    return;
  unsigned unsignedMaxLength = static_cast<unsigned>(signedMaxLength);

  const String& currentValue = innerEditorValue();
  unsigned currentLength = computeLengthForAPIValue(currentValue);
  if (currentLength + computeLengthForAPIValue(event->text()) <
      unsignedMaxLength)
    return;

  // selectionLength represents the selection length of this text field to be
  // removed by this insertion.
  // If the text field has no focus, we don't need to take account of the
  // selection length. The selection is the source of text drag-and-drop in
  // that case, and nothing in the text field will be removed.
  unsigned selectionLength = 0;
  if (isFocused()) {
    // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
    // needs to be audited.  See http://crbug.com/590369 for more details.
    document().updateStyleAndLayoutIgnorePendingStylesheets();

    selectionLength = computeLengthForAPIValue(
        document().frame()->selection().selectedText());
  }
  DCHECK_GE(currentLength, selectionLength);
  unsigned baseLength = currentLength - selectionLength;
  unsigned appendableLength =
      unsignedMaxLength > baseLength ? unsignedMaxLength - baseLength : 0;
  event->setText(sanitizeUserInputValue(event->text(), appendableLength));
}
Example #22
0
void PseudoElement::attach(const AttachContext& context)
{
    ASSERT(!layoutObject());

    Element::attach(context);

    LayoutObject* renderer = this->layoutObject();
    if (!renderer)
        return;

    ComputedStyle& style = renderer->mutableStyleRef();
    if (style.styleType() != BEFORE && style.styleType() != AFTER)
        return;
    ASSERT(style.contentData());

    for (const ContentData* content = style.contentData(); content; content = content->next()) {
        LayoutObject* child = content->createLayoutObject(document(), style);
        if (renderer->isChildAllowed(child, style)) {
            renderer->addChild(child);
            if (child->isQuote())
                toLayoutQuote(child)->attachQuote();
        } else
            child->destroy();
    }
}
Example #23
0
PassOwnPtr<FloatingObject> FloatingObject::unsafeClone() const
{
    OwnPtr<FloatingObject> cloneObject = adoptPtr(new FloatingObject(layoutObject(), type(), m_frameRect, static_cast<Ownership>(m_ownership), false));
    cloneObject->m_paginationStrut = m_paginationStrut;
    cloneObject->m_isPlaced = m_isPlaced;
    return cloneObject.release();
}
void InlineBox::move(const LayoutSize& delta)
{
    m_topLeft.move(delta);

    if (lineLayoutItem().isReplaced())
        toLayoutBox(layoutObject()).move(delta.width(), delta.height());
}
Example #25
0
void NodeRareData::finalizeGarbageCollectedObject() {
    RELEASE_ASSERT(!layoutObject());
    if (m_isElementRareData)
        static_cast<ElementRareData*>(this)->~ElementRareData();
    else
        this->~NodeRareData();
}
void Text::reattachIfNeeded(const AttachContext& context)
{
    bool layoutObjectIsNeeded = false;
    ContainerNode* layoutParent = LayoutTreeBuilderTraversal::parent(*this);
    if (layoutParent) {
        if (LayoutObject* parentLayoutObject = layoutParent->layoutObject()) {
            if (textLayoutObjectIsNeeded(*parentLayoutObject->style(), *parentLayoutObject))
                layoutObjectIsNeeded = true;
        }
    }

    if (layoutObjectIsNeeded == !!layoutObject())
        return;

    // The following is almost the same as Node::reattach() except that we create a layoutObject only if needed.
    // Not calling reattach() to avoid repeated calls to Text::textLayoutObjectIsNeeded().
    AttachContext reattachContext(context);
    reattachContext.performingReattach = true;

    if (getStyleChangeType() < NeedsReattachStyleChange)
        detach(reattachContext);
    if (layoutObjectIsNeeded)
        LayoutTreeBuilderForText(*this, layoutParent->layoutObject()).createLayoutObject();
    CharacterData::attach(reattachContext);
}
Example #27
0
std::unique_ptr<FloatingObject> FloatingObject::unsafeClone() const {
  std::unique_ptr<FloatingObject> cloneObject =
      WTF::wrapUnique(new FloatingObject(layoutObject(), getType(), m_frameRect,
                                         m_shouldPaint, m_isDescendant, false));
  cloneObject->m_isPlaced = m_isPlaced;
  return cloneObject;
}
Example #28
0
void HTMLTextFormControlElement::setInnerEditorValue(const String& value)
{
    ASSERT(!openShadowRoot());
    if (!isTextFormControl() || openShadowRoot())
        return;

    bool textIsChanged = value != innerEditorValue();
    HTMLElement* innerEditor = innerEditorElement();
    if (!textIsChanged && innerEditor->hasChildren())
        return;

    // If the last child is a trailing <br> that's appended below, remove it
    // first so as to enable setInnerText() fast path of updating a text node.
    if (isHTMLBRElement(innerEditor->lastChild()))
        innerEditor->removeChild(innerEditor->lastChild(), ASSERT_NO_EXCEPTION);

    // We don't use setTextContent.  It triggers unnecessary paint.
    if (value.isEmpty())
        innerEditor->removeChildren();
    else
        replaceChildrenWithText(innerEditor, value, ASSERT_NO_EXCEPTION);

    // Add <br> so that we can put the caret at the next line of the last
    // newline.
    addPlaceholderBreakElementIfNecessary();

    if (textIsChanged && layoutObject()) {
        if (AXObjectCache* cache = document().existingAXObjectCache())
            cache->handleTextFormControlChanged(this);
    }
}
TextRun SVGInlineTextBox::constructTextRun(const ComputedStyle& style, const SVGTextFragment& fragment) const
{
    LayoutText* text = &layoutObject();

    // FIXME(crbug.com/264211): This should not be necessary but can occur if we
    //                          layout during layout. Remove this when 264211 is fixed.
    RELEASE_ASSERT(!text->needsLayout());

    TextRun run(static_cast<const LChar*>(0) // characters, will be set below if non-zero.
        , 0 // length, will be set below if non-zero.
        , 0 // xPos, only relevant with allowTabs=true
        , 0 // padding, only relevant for justified text, not relevant for SVG
        , TextRun::AllowTrailingExpansion
        , direction()
        , dirOverride() || style.rtlOrdering() == VisualOrder /* directionalOverride */);

    if (fragment.length) {
        if (text->is8Bit())
            run.setText(text->characters8() + fragment.characterOffset, fragment.length);
        else
            run.setText(text->characters16() + fragment.characterOffset, fragment.length);
    }

    // We handle letter & word spacing ourselves.
    run.disableSpacing();

    // Propagate the maximum length of the characters buffer to the TextRun, even when we're only processing a substring.
    run.setCharactersLength(text->textLength() - fragment.characterOffset);
    ASSERT(run.charactersLength() >= run.length());
    return run;
}
// If a whitespace node had no layoutObject and goes through a recalcStyle it may
// need to create one if the parent style now has white-space: pre.
bool Text::needsWhitespaceLayoutObject()
{
    ASSERT(!layoutObject());
    if (const ComputedStyle* style = parentComputedStyle())
        return style->preserveNewline();
    return false;
}