RenderTextControl.cpp [plain text]
#include "config.h"
#include "RenderTextControl.h"
#include "HTMLTextFormControlElement.h"
#include "HitTestResult.h"
#include "RenderText.h"
#include "RenderTheme.h"
#include "ScrollbarTheme.h"
#include "StyleInheritedData.h"
#include "TextIterator.h"
#include "VisiblePosition.h"
#include <wtf/unicode/CharacterNames.h>
using namespace std;
namespace WebCore {
RenderTextControl::RenderTextControl(Element* element)
: RenderBlock(element)
{
ASSERT(isHTMLTextFormControlElement(element));
}
RenderTextControl::~RenderTextControl()
{
}
HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const
{
return toHTMLTextFormControlElement(node());
}
HTMLElement* RenderTextControl::innerTextElement() const
{
return textFormControlElement()->innerTextElement();
}
void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderBlock::styleDidChange(diff, oldStyle);
Element* innerText = innerTextElement();
if (!innerText)
return;
RenderBlock* innerTextRenderer = toRenderBlock(innerText->renderer());
if (innerTextRenderer) {
innerTextRenderer->style()->setHeight(Length());
innerTextRenderer->style()->setWidth(Length());
innerTextRenderer->setStyle(createInnerTextStyle(style()));
innerText->setNeedsStyleRecalc();
}
textFormControlElement()->updatePlaceholderVisibility(false);
}
static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
{
bool isDisabled = false;
bool isReadOnlyControl = false;
if (node->isElementNode()) {
Element* element = toElement(node);
isDisabled = element->isDisabledFormControl();
isReadOnlyControl = element->isTextFormControl() && toHTMLTextFormControlElement(element)->isReadOnly();
}
style->setUserModify((isReadOnlyControl || isDisabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
return isDisabled;
}
void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
{
textBlockStyle->setDirection(style()->direction());
textBlockStyle->setUnicodeBidi(style()->unicodeBidi());
bool disabled = updateUserModifyProperty(node(), textBlockStyle);
if (disabled)
textBlockStyle->setColor(theme()->disabledTextColor(textBlockStyle->visitedDependentColor(CSSPropertyColor), startStyle->visitedDependentColor(CSSPropertyBackgroundColor)));
}
int RenderTextControl::textBlockLogicalHeight() const
{
return logicalHeight() - borderAndPaddingLogicalHeight();
}
int RenderTextControl::textBlockLogicalWidth() const
{
Element* innerText = innerTextElement();
ASSERT(innerText);
LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth();
if (innerText->renderer())
unitWidth -= innerText->renderBox()->paddingStart() + innerText->renderBox()->paddingEnd();
return unitWidth;
}
void RenderTextControl::updateFromElement()
{
Element* innerText = innerTextElement();
if (innerText && innerText->renderer())
updateUserModifyProperty(node(), innerText->renderer()->style());
}
VisiblePosition RenderTextControl::visiblePositionForIndex(int index) const
{
if (index <= 0)
return VisiblePosition(firstPositionInNode(innerTextElement()), DOWNSTREAM);
RefPtr<Range> range = Range::create(document());
range->selectNodeContents(innerTextElement(), ASSERT_NO_EXCEPTION);
CharacterIterator it(range.get());
it.advance(index - 1);
return VisiblePosition(it.range()->endPosition(), UPSTREAM);
}
int RenderTextControl::scrollbarThickness() const
{
return ScrollbarTheme::theme()->scrollbarThickness();
}
void RenderTextControl::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
{
HTMLElement* innerText = innerTextElement();
ASSERT(innerText);
if (RenderBox* innerTextBox = innerText->renderBox()) {
LayoutUnit nonContentHeight = innerTextBox->borderAndPaddingHeight() + innerTextBox->marginHeight();
logicalHeight = computeControlLogicalHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight) + borderAndPaddingHeight();
if ((isHorizontalWritingMode() && (style()->overflowX() == OSCROLL || (style()->overflowX() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap)))
|| (!isHorizontalWritingMode() && (style()->overflowY() == OSCROLL || (style()->overflowY() == OAUTO && innerText->renderer()->style()->overflowWrap() == NormalOverflowWrap))))
logicalHeight += scrollbarThickness();
}
RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
}
void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
{
HTMLElement* innerText = innerTextElement();
if (!innerText->renderer())
return;
LayoutPoint adjustedLocation = accumulatedOffset + location();
LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerText->renderBox()->location());
if (hasOverflowClip())
localPoint += scrolledContentOffset();
result.setInnerNode(innerText);
result.setInnerNonSharedNode(innerText);
result.setLocalPoint(localPoint);
}
static const char* fontFamiliesWithInvalidCharWidth[] = {
"American Typewriter",
"Arial Hebrew",
"Chalkboard",
"Cochin",
"Corsiva Hebrew",
"Courier",
"Euphemia UCAS",
"Geneva",
"Gill Sans",
"Hei",
"Helvetica",
"Hoefler Text",
"InaiMathi",
"Kai",
"Lucida Grande",
"Marker Felt",
"Monaco",
"Mshtakan",
"New Peninim MT",
"Osaka",
"Raanana",
"STHeiti",
"Symbol",
"Times",
"Apple Braille",
"Apple LiGothic",
"Apple LiSung",
"Apple Symbols",
"AppleGothic",
"AppleMyungjo",
"#GungSeo",
"#HeadLineA",
"#PCMyungjo",
"#PilGi",
};
bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
{
if (family.isEmpty())
return false;
if (family.startsWith('.'))
return false;
static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
if (!fontFamiliesWithInvalidCharWidthMap) {
fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
}
return !fontFamiliesWithInvalidCharWidthMap->contains(family);
}
float RenderTextControl::getAvgCharWidth(AtomicString family)
{
if (hasValidAvgCharWidth(family))
return roundf(style()->font().primaryFont()->avgCharWidth());
const UChar ch = '0';
const String str = String(&ch, 1);
const Font& font = style()->font();
TextRun textRun = constructTextRun(this, font, str, style(), TextRun::AllowTrailingExpansion);
textRun.disableRoundingHacks();
return font.width(textRun);
}
float RenderTextControl::scaleEmToUnits(int x) const
{
float unitsPerEm = 2048.0f;
return roundf(style()->font().size() * x / unitsPerEm);
}
void RenderTextControl::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
{
const AtomicString& family = style()->font().firstFamily();
maxLogicalWidth = preferredContentLogicalWidth(const_cast<RenderTextControl*>(this)->getAvgCharWidth(family));
if (RenderBox* innerTextRenderBox = innerTextElement()->renderBox())
maxLogicalWidth += innerTextRenderBox->paddingStart() + innerTextRenderBox->paddingEnd();
if (!style()->logicalWidth().isPercent())
minLogicalWidth = maxLogicalWidth;
}
void RenderTextControl::computePreferredLogicalWidths()
{
ASSERT(preferredLogicalWidthsDirty());
m_minPreferredLogicalWidth = 0;
m_maxPreferredLogicalWidth = 0;
if (style()->logicalWidth().isFixed() && style()->logicalWidth().value() >= 0)
m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->logicalWidth().value());
else
computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
if (style()->logicalMinWidth().isFixed() && style()->logicalMinWidth().value() > 0) {
m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMinWidth().value()));
m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMinWidth().value()));
}
if (style()->logicalMaxWidth().isFixed()) {
m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMaxWidth().value()));
m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->logicalMaxWidth().value()));
}
LayoutUnit toAdd = borderAndPaddingLogicalWidth();
m_minPreferredLogicalWidth += toAdd;
m_maxPreferredLogicalWidth += toAdd;
setPreferredLogicalWidthsDirty(false);
}
void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset)
{
if (!size().isEmpty())
rects.append(pixelSnappedIntRect(additionalOffset, size()));
}
RenderObject* RenderTextControl::layoutSpecialExcludedChild(bool relayoutChildren)
{
HTMLElement* placeholder = toHTMLTextFormControlElement(node())->placeholderElement();
RenderObject* placeholderRenderer = placeholder ? placeholder->renderer() : 0;
if (!placeholderRenderer)
return 0;
if (relayoutChildren) {
placeholderRenderer->setChildNeedsLayout(true, MarkOnlyThis);
}
return placeholderRenderer;
}
bool RenderTextControl::canBeReplacedWithInlineRunIn() const
{
return false;
}
}