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 "TextIterator.h"
#include "VisiblePosition.h"
#include <wtf/unicode/CharacterNames.h>
using namespace std;
namespace WebCore {
RenderTextControl::RenderTextControl(Node* node)
: RenderBlock(node)
{
ASSERT(toTextFormControl(node));
}
RenderTextControl::~RenderTextControl()
{
}
HTMLTextFormControlElement* RenderTextControl::textFormControlElement() const
{
return static_cast<HTMLTextFormControlElement*>(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 isEnabled = true;
bool isReadOnlyControl = false;
if (node->isElementNode()) {
Element* element = static_cast<Element*>(node);
isEnabled = element->isEnabledFormControl();
isReadOnlyControl = element->isReadOnlyFormControl();
}
style->setUserModify((isReadOnlyControl || !isEnabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
return !isEnabled;
}
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)));
if (textBlockStyle->textSecurity() != TSNONE && !textBlockStyle->isLeftToRightDirection()) {
switch (textBlockStyle->textAlign()) {
case TAAUTO:
case TASTART:
case JUSTIFY:
textBlockStyle->setTextAlign(RIGHT);
break;
case TAEND:
textBlockStyle->setTextAlign(LEFT);
break;
case LEFT:
case RIGHT:
case CENTER:
case WEBKIT_LEFT:
case WEBKIT_RIGHT:
case WEBKIT_CENTER:
break;
}
textBlockStyle->setDirection(LTR);
}
}
int RenderTextControl::textBlockHeight() const
{
return height() - borderAndPaddingHeight();
}
int RenderTextControl::textBlockWidth() const
{
Element* innerText = innerTextElement();
ASSERT(innerText);
return width() - borderAndPaddingWidth() - innerText->renderBox()->paddingLeft() - innerText->renderBox()->paddingRight();
}
void RenderTextControl::updateFromElement()
{
Element* innerText = innerTextElement();
if (innerText)
updateUserModifyProperty(node(), innerText->renderer()->style());
}
VisiblePosition RenderTextControl::visiblePositionForIndex(int index) const
{
if (index <= 0)
return VisiblePosition(firstPositionInNode(innerTextElement()), DOWNSTREAM);
ExceptionCode ec = 0;
RefPtr<Range> range = Range::create(document());
range->selectNodeContents(innerTextElement(), ec);
ASSERT(!ec);
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()
{
HTMLElement* innerText = innerTextElement();
ASSERT(innerText);
RenderBox* innerTextBox = innerText->renderBox();
LayoutUnit nonContentHeight = innerTextBox->borderAndPaddingHeight() + innerTextBox->marginHeight();
setHeight(computeControlHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight) + borderAndPaddingHeight());
if (style()->overflowX() == OSCROLL || (style()->overflowX() == OAUTO && innerText->renderer()->style()->wordWrap() == NormalWordWrap))
setHeight(height() + scrollbarThickness());
RenderBlock::computeLogicalHeight();
}
void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
{
LayoutPoint adjustedLocation = accumulatedOffset + location();
HTMLElement* innerText = innerTextElement();
result.setInnerNode(innerText);
result.setInnerNonSharedNode(innerText);
result.setLocalPoint(pointInContainer - toLayoutSize(adjustedLocation + innerText->renderBox()->location()));
}
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)
{
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::computePreferredLogicalWidths()
{
ASSERT(preferredLogicalWidthsDirty());
m_minPreferredLogicalWidth = 0;
m_maxPreferredLogicalWidth = 0;
if (style()->width().isFixed() && style()->width().value() >= 0)
m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value());
else {
AtomicString family = style()->font().family().family();
RenderBox* innerTextRenderBox = innerTextElement()->renderBox();
m_maxPreferredLogicalWidth = preferredContentWidth(getAvgCharWidth(family)) + innerTextRenderBox->paddingLeft() + innerTextRenderBox->paddingRight();
}
if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
} else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
m_minPreferredLogicalWidth = 0;
else
m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
if (style()->maxWidth().isFixed()) {
m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
}
LayoutUnit toAdd = borderAndPaddingWidth();
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 = toTextFormControl(node())->placeholderElement();
RenderObject* placeholderRenderer = placeholder ? placeholder->renderer() : 0;
if (!placeholderRenderer)
return 0;
if (relayoutChildren) {
placeholderRenderer->setChildNeedsLayout(true, MarkOnlyThis);
}
return placeholderRenderer;
}
bool RenderTextControl::canScroll() const
{
Element* innerText = innerTextElement();
return innerText && innerText->renderer() && innerText->renderer()->hasOverflowClip();
}
int RenderTextControl::innerLineHeight() const
{
Element* innerText = innerTextElement();
if (innerText && innerText->renderer() && innerText->renderer()->style())
return innerText->renderer()->style()->computedLineHeight();
return style()->computedLineHeight();
}
}