summaryrefslogtreecommitdiffstats
path: root/WebCore/rendering/RenderTextControl.cpp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
commit8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (patch)
tree11425ea0b299d6fb89c6d3618a22d97d5bf68d0f /WebCore/rendering/RenderTextControl.cpp
parent648161bb0edfc3d43db63caed5cc5213bc6cb78f (diff)
downloadexternal_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.zip
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.gz
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebCore/rendering/RenderTextControl.cpp')
-rw-r--r--WebCore/rendering/RenderTextControl.cpp1272
1 files changed, 1272 insertions, 0 deletions
diff --git a/WebCore/rendering/RenderTextControl.cpp b/WebCore/rendering/RenderTextControl.cpp
new file mode 100644
index 0000000..a971028
--- /dev/null
+++ b/WebCore/rendering/RenderTextControl.cpp
@@ -0,0 +1,1272 @@
+/**
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "RenderTextControl.h"
+
+#include "CharacterNames.h"
+#include "CSSStyleSelector.h"
+#include "Document.h"
+#include "Editor.h"
+#include "EditorClient.h"
+#include "Event.h"
+#include "EventNames.h"
+#include "FontSelector.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "HTMLBRElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "HTMLTextAreaElement.h"
+#include "HitTestResult.h"
+#include "LocalizedStrings.h"
+#include "MouseEvent.h"
+#include "PlatformKeyboardEvent.h"
+#include "RenderScrollbar.h"
+#include "RenderTheme.h"
+#include "ScrollbarTheme.h"
+#include "SearchPopupMenu.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "Text.h"
+#include "TextControlInnerElements.h"
+#include "TextIterator.h"
+#include "htmlediting.h"
+#include "visible_units.h"
+#include <math.h>
+
+#ifdef ANDROID_LAYOUT
+#include "FrameView.h"
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+// Value chosen by observation. This can be tweaked.
+static const int minColorContrastValue = 1300;
+
+static Color disabledTextColor(const Color& textColor, const Color& backgroundColor)
+{
+ // The explicit check for black is an optimization for the 99% case (black on white).
+ // This also means that black on black will turn into grey on black when disabled.
+ Color disabledColor;
+ if (textColor.rgb() == Color::black || differenceSquared(textColor, Color::white) > differenceSquared(backgroundColor, Color::white))
+ disabledColor = textColor.light();
+ else
+ disabledColor = textColor.dark();
+
+ // If there's not very much contrast between the disabled color and the background color,
+ // just leave the text color alone. We don't want to change a good contrast color scheme so that it has really bad contrast.
+ // If the the contrast was already poor, then it doesn't do any good to change it to a different poor contrast color scheme.
+ if (differenceSquared(disabledColor, backgroundColor) < minColorContrastValue)
+ return textColor;
+
+ return disabledColor;
+}
+
+RenderTextControl::RenderTextControl(Node* node, bool multiLine)
+ : RenderBlock(node)
+ , m_dirty(false)
+ , m_multiLine(multiLine)
+ , m_placeholderVisible(false)
+ , m_userEdited(false)
+ , m_shouldDrawCapsLockIndicator(false)
+ , m_searchPopup(0)
+ , m_searchPopupIsVisible(false)
+ , m_searchEventTimer(this, &RenderTextControl::searchEventTimerFired)
+{
+}
+
+RenderTextControl::~RenderTextControl()
+{
+ if (m_searchPopup) {
+ m_searchPopup->disconnectClient();
+ m_searchPopup = 0;
+ }
+ if (m_multiLine && node())
+ static_cast<HTMLTextAreaElement*>(node())->rendererWillBeDestroyed();
+ // The children renderers have already been destroyed by destroyLeftoverChildren
+ if (m_innerBlock)
+ m_innerBlock->detach();
+ else if (m_innerText)
+ m_innerText->detach();
+}
+
+void RenderTextControl::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle)
+{
+ RenderBlock::styleDidChange(diff, oldStyle);
+ if (m_innerBlock) {
+ // We may have set the width and the height in the old style in layout(). Reset them now to avoid
+ // getting a spurious layout hint.
+ m_innerBlock->renderer()->style()->setHeight(Length());
+ m_innerBlock->renderer()->style()->setWidth(Length());
+ m_innerBlock->renderer()->setStyle(createInnerBlockStyle(style()));
+ }
+
+ if (m_innerText) {
+ RenderBlock* textBlockRenderer = static_cast<RenderBlock*>(m_innerText->renderer());
+ RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(style());
+ // We may have set the width and the height in the old style in layout(). Reset them now to avoid
+ // getting a spurious layout hint.
+ textBlockRenderer->style()->setHeight(Length());
+ textBlockRenderer->style()->setWidth(Length());
+ textBlockRenderer->setStyle(textBlockStyle);
+ for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) {
+ if (n->renderer())
+ n->renderer()->setStyle(textBlockStyle);
+ }
+ }
+ if (m_resultsButton && m_resultsButton->renderer())
+ m_resultsButton->renderer()->setStyle(createResultsButtonStyle(style()));
+
+ if (m_cancelButton && m_cancelButton->renderer())
+ m_cancelButton->renderer()->setStyle(createCancelButtonStyle(style()));
+
+ setHasOverflowClip(false);
+ setReplaced(isInline());
+}
+
+PassRefPtr<RenderStyle> RenderTextControl::createInnerBlockStyle(const RenderStyle* startStyle)
+{
+ RefPtr<RenderStyle> innerBlockStyle = RenderStyle::create();
+
+ innerBlockStyle->inheritFrom(startStyle);
+ innerBlockStyle->setDisplay(BLOCK);
+ innerBlockStyle->setDirection(LTR);
+ // We don't want the shadow dom to be editable, so we set this block to read-only in case the input itself is editable.
+ innerBlockStyle->setUserModify(READ_ONLY);
+
+ return innerBlockStyle.release();
+}
+
+PassRefPtr<RenderStyle> RenderTextControl::createInnerTextStyle(const RenderStyle* startStyle)
+{
+ HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(node());
+ bool placeholderShouldBeVisible = !m_multiLine && static_cast<HTMLInputElement*>(element)->placeholderShouldBeVisible();
+
+ RefPtr<RenderStyle> textBlockStyle;
+ if (placeholderShouldBeVisible) {
+ RenderStyle* pseudoStyle = getCachedPseudoStyle(RenderStyle::INPUT_PLACEHOLDER);
+ textBlockStyle = RenderStyle::clone(pseudoStyle);
+ } else {
+ textBlockStyle = RenderStyle::create();
+ textBlockStyle->inheritFrom(startStyle);
+ }
+
+ // The inner block, if present, always has its direction set to LTR,
+ // so we need to inherit the direction from the element.
+ textBlockStyle->setDirection(style()->direction());
+ textBlockStyle->setUserModify(element->isReadOnlyControl() || element->disabled() ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
+ if (m_innerBlock)
+ textBlockStyle->setDisplay(INLINE_BLOCK);
+ else
+ textBlockStyle->setDisplay(BLOCK);
+
+ if (m_multiLine) {
+ // Forward overflow properties.
+ textBlockStyle->setOverflowX(startStyle->overflowX() == OVISIBLE ? OAUTO : startStyle->overflowX());
+ textBlockStyle->setOverflowY(startStyle->overflowY() == OVISIBLE ? OAUTO : startStyle->overflowY());
+
+ // Set word wrap property based on wrap attribute.
+ if (!static_cast<HTMLTextAreaElement*>(element)->shouldWrapText()) {
+ textBlockStyle->setWhiteSpace(PRE);
+ textBlockStyle->setWordWrap(NormalWordWrap);
+ } else {
+ textBlockStyle->setWhiteSpace(PRE_WRAP);
+ textBlockStyle->setWordWrap(BreakWordWrap);
+ }
+ } else {
+ textBlockStyle->setWhiteSpace(PRE);
+ textBlockStyle->setWordWrap(NormalWordWrap);
+ textBlockStyle->setOverflowX(OHIDDEN);
+ textBlockStyle->setOverflowY(OHIDDEN);
+
+ // Do not allow line-height to be smaller than our default.
+ if (textBlockStyle->font().lineSpacing() > lineHeight(true, true))
+ textBlockStyle->setLineHeight(Length(-100.0f, Percent));
+ }
+
+ if (!m_multiLine) {
+ // We're adding one extra pixel of padding to match WinIE.
+ textBlockStyle->setPaddingLeft(Length(1, Fixed));
+ textBlockStyle->setPaddingRight(Length(1, Fixed));
+ } else {
+ // We're adding three extra pixels of padding to line textareas up with text fields.
+ textBlockStyle->setPaddingLeft(Length(3, Fixed));
+ textBlockStyle->setPaddingRight(Length(3, Fixed));
+ }
+
+ // When the placeholder is going to be displayed, temporarily override the text security to be "none".
+ // After this, updateFromElement will immediately update the text displayed.
+ // When the placeholder is no longer visible, updatePlaceholderVisiblity will reset the style,
+ // and the text security mode will be set back to the computed value correctly.
+ if (!m_multiLine && static_cast<HTMLInputElement*>(element)->placeholderShouldBeVisible())
+ textBlockStyle->setTextSecurity(TSNONE);
+
+ if (!element->isEnabled())
+ textBlockStyle->setColor(disabledTextColor(textBlockStyle->color(), startStyle->backgroundColor()));
+
+ return textBlockStyle.release();
+}
+
+PassRefPtr<RenderStyle> RenderTextControl::createResultsButtonStyle(const RenderStyle* startStyle)
+{
+ ASSERT(!m_multiLine);
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+ RefPtr<RenderStyle> resultsBlockStyle;
+ if (input->maxResults() < 0)
+ resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_DECORATION);
+ else if (!input->maxResults())
+ resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_RESULTS_DECORATION);
+ else
+ resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_RESULTS_BUTTON);
+
+ if (!resultsBlockStyle)
+ resultsBlockStyle = RenderStyle::create();
+
+ if (startStyle)
+ resultsBlockStyle->inheritFrom(startStyle);
+
+ return resultsBlockStyle.release();
+}
+
+PassRefPtr<RenderStyle> RenderTextControl::createCancelButtonStyle(const RenderStyle* startStyle)
+{
+ RefPtr<RenderStyle> cancelBlockStyle;
+
+ if (RefPtr<RenderStyle> pseudoStyle = getCachedPseudoStyle(RenderStyle::SEARCH_CANCEL_BUTTON))
+ // We may be sharing style with another search field, but we must not share the cancel button style.
+ cancelBlockStyle = RenderStyle::clone(pseudoStyle.get());
+ else
+ cancelBlockStyle = RenderStyle::create();
+
+ if (startStyle)
+ cancelBlockStyle->inheritFrom(startStyle);
+
+ updateCancelButtonVisibility(cancelBlockStyle.get());
+
+ return cancelBlockStyle.release();
+}
+
+void RenderTextControl::createSubtreeIfNeeded()
+{
+ bool isSearchField = !m_multiLine && static_cast<HTMLInputElement*>(node())->isSearchField();
+ if (isSearchField && !m_innerBlock) {
+ // Create the inner block element
+ m_innerBlock = new TextControlInnerElement(document(), node());
+ m_innerBlock->attachInnerElement(node(), createInnerBlockStyle(style()), renderArena());
+ }
+ if (isSearchField && !m_resultsButton) {
+ // Create the search results button element
+ m_resultsButton = new SearchFieldResultsButtonElement(document());
+ m_resultsButton->attachInnerElement(m_innerBlock.get(), createResultsButtonStyle(m_innerBlock->renderer()->style()), renderArena());
+ }
+ if (!m_innerText) {
+ // Create the text block element
+ // For non-search fields, there is no intermediate m_innerBlock as the shadow node.
+ // m_innerText will be the shadow node in that case.
+
+ RenderStyle* parentStyle = style();
+ if (m_innerBlock)
+ parentStyle = m_innerBlock->renderer()->style();
+ m_innerText = new TextControlInnerTextElement(document(), m_innerBlock ? 0 : node());
+ m_innerText->attachInnerElement(m_innerBlock ? m_innerBlock.get() : node(), createInnerTextStyle(parentStyle), renderArena());
+ }
+ if (isSearchField && !m_cancelButton) {
+ // Create the cancel button element
+ m_cancelButton = new SearchFieldCancelButtonElement(document());
+ m_cancelButton->attachInnerElement(m_innerBlock.get(), createCancelButtonStyle(m_innerBlock->renderer()->style()), renderArena());
+ }
+}
+
+void RenderTextControl::updateFromElement()
+{
+ HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(node());
+
+ bool placeholderShouldBeVisible = !m_multiLine && static_cast<HTMLInputElement*>(element)->placeholderShouldBeVisible();
+ bool placeholderVisibilityShouldChange = m_placeholderVisible != placeholderShouldBeVisible;
+ m_placeholderVisible = placeholderShouldBeVisible;
+
+ createSubtreeIfNeeded();
+
+ if (m_cancelButton && m_cancelButton->renderer())
+ updateCancelButtonVisibility(m_cancelButton->renderer()->style());
+
+ m_innerText->renderer()->style()->setUserModify(element->isReadOnlyControl() || element->disabled() ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
+
+ if (m_placeholderVisible) {
+ ExceptionCode ec;
+ m_innerText->setInnerText(static_cast<HTMLInputElement*>(element)->getAttribute(placeholderAttr), ec);
+ } else if (!element->valueMatchesRenderer() || m_multiLine || placeholderVisibilityShouldChange) {
+ String value;
+ if (m_multiLine)
+ value = static_cast<HTMLTextAreaElement*>(element)->value();
+ else
+ value = static_cast<HTMLInputElement*>(element)->value();
+ if (value.isNull())
+ value = "";
+ else
+ value = value.replace('\\', backslashAsCurrencySymbol());
+ if (value != text() || !m_innerText->hasChildNodes()) {
+ if (value != text()) {
+ if (Frame* frame = document()->frame())
+ frame->editor()->clearUndoRedoOperations();
+ }
+ ExceptionCode ec = 0;
+ m_innerText->setInnerText(value, ec);
+ if (value.endsWith("\n") || value.endsWith("\r"))
+ m_innerText->appendChild(new HTMLBRElement(document()), ec);
+ m_dirty = false;
+ m_userEdited = false;
+ }
+ element->setValueMatchesRenderer();
+ }
+
+ if (m_searchPopupIsVisible)
+ m_searchPopup->updateFromElement();
+}
+
+void RenderTextControl::setUserEdited(bool isUserEdited)
+{
+ m_userEdited = isUserEdited;
+ document()->setIgnoreAutofocus(isUserEdited);
+}
+
+int RenderTextControl::selectionStart()
+{
+ Frame* frame = document()->frame();
+ if (!frame)
+ return 0;
+ return indexForVisiblePosition(frame->selection()->start());
+}
+
+int RenderTextControl::selectionEnd()
+{
+ Frame* frame = document()->frame();
+ if (!frame)
+ return 0;
+ return indexForVisiblePosition(frame->selection()->end());
+}
+
+void RenderTextControl::setSelectionStart(int start)
+{
+ setSelectionRange(start, max(start, selectionEnd()));
+}
+
+void RenderTextControl::setSelectionEnd(int end)
+{
+ setSelectionRange(min(end, selectionStart()), end);
+}
+
+void RenderTextControl::select()
+{
+ setSelectionRange(0, text().length());
+}
+
+void RenderTextControl::setSelectionRange(int start, int end)
+{
+ end = max(end, 0);
+ start = min(max(start, 0), end);
+
+ document()->updateLayout();
+
+ if (style()->visibility() == HIDDEN || !m_innerText || !m_innerText->renderer() || !m_innerText->renderer()->height()) {
+ if (m_multiLine)
+ static_cast<HTMLTextAreaElement*>(node())->cacheSelection(start, end);
+ else
+ static_cast<HTMLInputElement*>(node())->cacheSelection(start, end);
+ return;
+ }
+ VisiblePosition startPosition = visiblePositionForIndex(start);
+ VisiblePosition endPosition;
+ if (start == end)
+ endPosition = startPosition;
+ else
+ endPosition = visiblePositionForIndex(end);
+
+ ASSERT(startPosition.isNotNull() && endPosition.isNotNull());
+ ASSERT(startPosition.deepEquivalent().node()->shadowAncestorNode() == node() && endPosition.deepEquivalent().node()->shadowAncestorNode() == node());
+
+ Selection newSelection = Selection(startPosition, endPosition);
+
+ if (Frame* frame = document()->frame())
+ frame->selection()->setSelection(newSelection);
+
+ // FIXME: Granularity is stored separately on the frame, but also in the selection controller.
+ // The granularity in the selection controller should be used, and then this line of code would not be needed.
+ if (Frame* frame = document()->frame())
+ frame->setSelectionGranularity(CharacterGranularity);
+}
+
+Selection RenderTextControl::selection(int start, int end) const
+{
+ return Selection(VisiblePosition(m_innerText.get(), start, VP_DEFAULT_AFFINITY),
+ VisiblePosition(m_innerText.get(), end, VP_DEFAULT_AFFINITY));
+}
+
+VisiblePosition RenderTextControl::visiblePositionForIndex(int index)
+{
+ if (index <= 0)
+ return VisiblePosition(m_innerText.get(), 0, DOWNSTREAM);
+ ExceptionCode ec = 0;
+ RefPtr<Range> range = Range::create(document());
+ range->selectNodeContents(m_innerText.get(), ec);
+ CharacterIterator it(range.get());
+ it.advance(index - 1);
+ return VisiblePosition(it.range()->endContainer(ec), it.range()->endOffset(ec), UPSTREAM);
+}
+
+int RenderTextControl::indexForVisiblePosition(const VisiblePosition& pos)
+{
+ Position indexPosition = pos.deepEquivalent();
+ if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != m_innerText)
+ return 0;
+ ExceptionCode ec = 0;
+ RefPtr<Range> range = Range::create(document());
+ range->setStart(m_innerText.get(), 0, ec);
+ range->setEnd(indexPosition.node(), indexPosition.offset(), ec);
+ return TextIterator::rangeLength(range.get());
+}
+
+void RenderTextControl::updateCancelButtonVisibility(RenderStyle* style)
+{
+ ASSERT(!m_multiLine);
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+ if (input->value().isEmpty())
+ style->setVisibility(HIDDEN);
+ else
+ style->setVisibility(VISIBLE);
+}
+
+void RenderTextControl::subtreeHasChanged()
+{
+ bool wasDirty = m_dirty;
+ m_dirty = true;
+ m_userEdited = true;
+ HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(node());
+ if (m_multiLine) {
+ element->setValueMatchesRenderer(false);
+ if (element->focused())
+ if (Frame* frame = document()->frame())
+ frame->textDidChangeInTextArea(element);
+ } else {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(element);
+ input->setValueFromRenderer(input->constrainValue(text()));
+ if (m_cancelButton && m_cancelButton->renderer())
+ updateCancelButtonVisibility(m_cancelButton->renderer()->style());
+
+ // If the incremental attribute is set, then dispatch the search event
+ if (!input->getAttribute(incrementalAttr).isNull())
+ startSearchEventTimer();
+
+ if (!wasDirty) {
+ if (input->focused())
+ if (Frame* frame = document()->frame())
+ frame->textFieldDidBeginEditing(input);
+ }
+ if (input->focused())
+ if (Frame* frame = document()->frame())
+ frame->textDidChangeInTextField(input);
+ }
+}
+
+String RenderTextControl::finishText(Vector<UChar>& result) const
+{
+ // ANDROID: This method was modified with a fix from WebKit r31081. This
+ // comment can be removed the next time we update.
+
+ // Remove one trailing newline; there's always one that's collapsed out by rendering.
+ size_t size = result.size();
+ if (size && result[size - 1] == '\n')
+ result.shrink(--size);
+
+ // Convert backslash to currency symbol.
+ UChar symbol = backslashAsCurrencySymbol();
+ if (symbol != '\\') {
+ for (size_t i = 0; i < size; ++i) {
+ if (result[i] == '\\')
+ result[i] = symbol;
+ }
+ }
+
+ return String::adopt(result);
+}
+
+HTMLElement* RenderTextControl::innerTextElement() const
+{
+ return m_innerText.get();
+}
+
+String RenderTextControl::text()
+{
+ if (!m_innerText)
+ return "";
+
+ Frame* frame = document()->frame();
+ Text* compositionNode = frame ? frame->editor()->compositionNode() : 0;
+
+ Vector<UChar> result;
+
+ for (Node* n = m_innerText.get(); n; n = n->traverseNextNode(m_innerText.get())) {
+ if (n->isTextNode()) {
+ Text* text = static_cast<Text*>(n);
+ String data = text->data();
+ unsigned length = data.length();
+ if (text != compositionNode)
+ result.append(data.characters(), length);
+ else {
+ unsigned compositionStart = min(frame->editor()->compositionStart(), length);
+ unsigned compositionEnd = min(max(compositionStart, frame->editor()->compositionEnd()), length);
+ result.append(data.characters(), compositionStart);
+ result.append(data.characters() + compositionEnd, length - compositionEnd);
+ }
+ }
+ }
+
+ return finishText(result);
+}
+
+static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
+{
+ RootInlineBox* next;
+ for (; line; line = next) {
+ next = line->nextRootBox();
+ if (next && !line->endsWithBreak()) {
+ ASSERT(line->lineBreakObj());
+ breakNode = line->lineBreakObj()->node();
+ breakOffset = line->lineBreakPos();
+ line = next;
+ return;
+ }
+ }
+ breakNode = 0;
+}
+
+String RenderTextControl::textWithHardLineBreaks()
+{
+ if (!m_innerText)
+ return "";
+ Node* firstChild = m_innerText->firstChild();
+ if (!firstChild)
+ return "";
+
+ document()->updateLayout();
+
+ RenderObject* renderer = firstChild->renderer();
+ if (!renderer)
+ return "";
+
+ InlineBox* box = renderer->isText() ? static_cast<RenderText*>(renderer)->firstTextBox() : renderer->inlineBoxWrapper();
+ if (!box)
+ return "";
+
+ Frame* frame = document()->frame();
+ Text* compositionNode = frame ? frame->editor()->compositionNode() : 0;
+
+ Node* breakNode;
+ unsigned breakOffset;
+ RootInlineBox* line = box->root();
+ getNextSoftBreak(line, breakNode, breakOffset);
+
+ Vector<UChar> result;
+
+ for (Node* n = firstChild; n; n = n->traverseNextNode(m_innerText.get())) {
+ if (n->hasTagName(brTag))
+ result.append(&newlineCharacter, 1);
+ else if (n->isTextNode()) {
+ Text* text = static_cast<Text*>(n);
+ String data = text->data();
+ unsigned length = data.length();
+ unsigned compositionStart = (text == compositionNode)
+ ? min(frame->editor()->compositionStart(), length) : 0;
+ unsigned compositionEnd = (text == compositionNode)
+ ? min(max(compositionStart, frame->editor()->compositionEnd()), length) : 0;
+ unsigned position = 0;
+ while (breakNode == n && breakOffset < compositionStart) {
+ result.append(data.characters() + position, breakOffset - position);
+ position = breakOffset;
+ result.append(&newlineCharacter, 1);
+ getNextSoftBreak(line, breakNode, breakOffset);
+ }
+ result.append(data.characters() + position, compositionStart - position);
+ position = compositionEnd;
+ while (breakNode == n && breakOffset <= length) {
+ if (breakOffset > position) {
+ result.append(data.characters() + position, breakOffset - position);
+ position = breakOffset;
+ result.append(&newlineCharacter, 1);
+ }
+ getNextSoftBreak(line, breakNode, breakOffset);
+ }
+ result.append(data.characters() + position, length - position);
+ }
+ while (breakNode == n)
+ getNextSoftBreak(line, breakNode, breakOffset);
+ }
+
+ return finishText(result);
+}
+
+void RenderTextControl::calcHeight()
+{
+ int rows = 1;
+ if (m_multiLine)
+ rows = static_cast<HTMLTextAreaElement*>(node())->rows();
+
+ int line = m_innerText->renderer()->lineHeight(true, true);
+ int toAdd = paddingTop() + paddingBottom() + borderTop() + borderBottom();
+
+ int innerToAdd = m_innerText->renderer()->borderTop() + m_innerText->renderer()->borderBottom() +
+ m_innerText->renderer()->paddingTop() + m_innerText->renderer()->paddingBottom() +
+ m_innerText->renderer()->marginTop() + m_innerText->renderer()->marginBottom();
+
+ if (m_resultsButton && m_resultsButton->renderer()) {
+ static_cast<RenderBlock*>(m_resultsButton->renderer())->calcHeight();
+ innerToAdd = max(innerToAdd,
+ m_resultsButton->renderer()->borderTop() + m_resultsButton->renderer()->borderBottom() +
+ m_resultsButton->renderer()->paddingTop() + m_resultsButton->renderer()->paddingBottom() +
+ m_resultsButton->renderer()->marginTop() + m_resultsButton->renderer()->marginBottom());
+ line = max(line, m_resultsButton->renderer()->height());
+ }
+ if (m_cancelButton && m_cancelButton->renderer()) {
+ static_cast<RenderBlock*>(m_cancelButton->renderer())->calcHeight();
+ innerToAdd = max(innerToAdd,
+ m_cancelButton->renderer()->borderTop() + m_cancelButton->renderer()->borderBottom() +
+ m_cancelButton->renderer()->paddingTop() + m_cancelButton->renderer()->paddingBottom() +
+ m_cancelButton->renderer()->marginTop() + m_cancelButton->renderer()->marginBottom());
+ line = max(line, m_cancelButton->renderer()->height());
+ }
+ toAdd += innerToAdd;
+
+ // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
+ int scrollbarSize = 0;
+ // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
+ if (m_innerText->renderer()->style()->overflowX() == OSCROLL || (m_innerText->renderer()->style()->overflowX() == OAUTO && m_innerText->renderer()->style()->wordWrap() == NormalWordWrap))
+ scrollbarSize = ScrollbarTheme::nativeTheme()->scrollbarThickness();
+
+ m_height = line * rows + toAdd + scrollbarSize;
+
+ RenderBlock::calcHeight();
+}
+
+int RenderTextControl::baselinePosition(bool b, bool isRootLineBox) const
+{
+ if (m_multiLine)
+ return height() + marginTop() + marginBottom();
+ return RenderBlock::baselinePosition(b, isRootLineBox);
+}
+
+bool RenderTextControl::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
+{
+ // If we're within the text control, we want to act as if we've hit the inner text block element, in case the point
+ // was on the control but not on the inner element (see Radar 4617841).
+
+ // In a search field, we want to act as if we've hit the results block if we're to the left of the inner text block,
+ // and act as if we've hit the close block if we're to the right of the inner text block.
+
+ if (RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction) &&
+ (result.innerNode() == element() || result.innerNode() == m_innerBlock)) {
+ IntPoint localPoint = IntPoint(x - tx - m_x, y - ty - m_y);
+ if (m_innerBlock) {
+ int textLeft = tx + m_x + m_innerBlock->renderer()->xPos() + m_innerText->renderer()->xPos();
+ int textRight = textLeft + m_innerText->renderer()->width();
+ if (m_resultsButton && m_resultsButton->renderer() && x < textLeft) {
+ result.setInnerNode(m_resultsButton.get());
+ result.setLocalPoint(IntPoint(localPoint.x() - m_innerText->renderer()->xPos() - m_innerBlock->renderer()->xPos() - m_resultsButton->renderer()->xPos(),
+ localPoint.y() - m_innerText->renderer()->yPos() - m_innerBlock->renderer()->yPos() - m_resultsButton->renderer()->yPos()));
+ return true;
+ }
+ if (m_cancelButton && m_cancelButton->renderer() && x > textRight) {
+ result.setInnerNode(m_cancelButton.get());
+ result.setLocalPoint(IntPoint(localPoint.x() - m_innerText->renderer()->xPos() - m_innerBlock->renderer()->xPos() - m_cancelButton->renderer()->xPos(),
+ localPoint.y() - m_innerText->renderer()->yPos() - m_innerBlock->renderer()->yPos() - m_cancelButton->renderer()->yPos()));
+ return true;
+ }
+ }
+
+ // Hit the inner text block.
+ result.setInnerNode(m_innerText.get());
+ result.setLocalPoint(IntPoint(localPoint.x() - m_innerText->renderer()->xPos() - (m_innerBlock.get() ? m_innerBlock->renderer()->xPos() : 0),
+ localPoint.y() - m_innerText->renderer()->yPos() - (m_innerBlock.get() ? m_innerBlock->renderer()->yPos() : 0)));
+
+ return true;
+ }
+
+ return false;
+}
+
+IntRect RenderTextControl::controlClipRect(int tx, int ty) const
+{
+ IntRect clipRect = contentBox();
+ clipRect.move(tx, ty);
+ return clipRect;
+}
+
+void RenderTextControl::layout()
+{
+ int oldHeight = m_height;
+ calcHeight();
+ bool relayoutChildren = oldHeight != m_height;
+
+ // Set the text block's height
+ int textBlockHeight = m_height - paddingTop() - paddingBottom() - borderTop() - borderBottom();
+ int currentTextBlockHeight = m_innerText->renderer()->height();
+ if (m_multiLine || m_innerBlock || currentTextBlockHeight > m_height) {
+ if (textBlockHeight != currentTextBlockHeight)
+ relayoutChildren = true;
+ m_innerText->renderer()->style()->setHeight(Length(textBlockHeight, Fixed));
+ }
+ if (m_innerBlock) {
+ if (textBlockHeight != m_innerBlock->renderer()->height())
+ relayoutChildren = true;
+ m_innerBlock->renderer()->style()->setHeight(Length(textBlockHeight, Fixed));
+ }
+
+#ifdef ANDROID_LAYOUT
+ int oldVisibleWidth = m_visibleWidth;
+#endif
+
+ int oldWidth = m_width;
+ calcWidth();
+
+ // FIXME: This causes cnn.com loading way slow. Comment it out for now
+//#ifdef ANDROID_LAYOUT
+#if 0
+ Frame* frame = document()->frame();
+ if (frame && frame->settings()->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) {
+ int maxWidth = frame->view()->visibleWidth() - 2 * ANDROID_FCTS_MARGIN_PADDING;
+ if (maxWidth > 0 && maxWidth < m_width) {
+ m_width = maxWidth;
+ // not only modify the control's width, we also need to modify its
+ // parent (even grandparent) width if it is not auto
+ Length styleWidth = style()->width();
+ if (styleWidth.isFixed()) {
+ // now we need to get max style width
+ maxWidth -= borderLeft() + borderRight() + paddingLeft() + paddingRight();
+ style()->setWidth(Length(maxWidth, Fixed));
+ } else if (styleWidth.isPercent()) {
+ RenderObject* o = this;
+ while (RenderBox* p = (RenderBox*)(o->parent())) {
+ int maxParentWidth = (int)(maxWidth * 100 / styleWidth.percent());
+ if (p->width() <= maxParentWidth)
+ break;
+ p->setWidth(maxParentWidth);
+ styleWidth = p->style()->width();
+ if (!styleWidth.isPercent())
+ break;
+ o = p;
+ maxWidth = maxParentWidth;
+ }
+ }
+ }
+ }
+#endif
+ if (oldWidth != m_width)
+ relayoutChildren = true;
+
+#ifdef ANDROID_LAYOUT
+ if (oldVisibleWidth != m_visibleWidth
+ && document()->settings()->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen)
+ relayoutChildren = true;
+#endif
+
+ int searchExtrasWidth = 0;
+ if (m_resultsButton && m_resultsButton->renderer()) {
+ m_resultsButton->renderer()->calcWidth();
+ searchExtrasWidth += m_resultsButton->renderer()->width();
+ }
+ if (m_cancelButton && m_cancelButton->renderer()) {
+ m_cancelButton->renderer()->calcWidth();
+ searchExtrasWidth += m_cancelButton->renderer()->width();
+ }
+
+ // Set the text block's width
+ int textBlockWidth = m_width - paddingLeft() - paddingRight() - borderLeft() - borderRight() -
+ m_innerText->renderer()->paddingLeft() - m_innerText->renderer()->paddingRight() - searchExtrasWidth;
+ if (textBlockWidth != m_innerText->renderer()->width())
+ relayoutChildren = true;
+ m_innerText->renderer()->style()->setWidth(Length(textBlockWidth, Fixed));
+ if (m_innerBlock) {
+ int innerBlockWidth = m_width - paddingLeft() - paddingRight() - borderLeft() - borderRight();
+ if (innerBlockWidth != m_innerBlock->renderer()->width())
+ relayoutChildren = true;
+ m_innerBlock->renderer()->style()->setWidth(Length(innerBlockWidth, Fixed));
+ }
+
+ RenderBlock::layoutBlock(relayoutChildren);
+
+ // For text fields, center the inner text vertically
+ // Don't do this for search fields, since we don't honor height for them
+ if (!m_multiLine) {
+ currentTextBlockHeight = m_innerText->renderer()->height();
+ if (!m_innerBlock && currentTextBlockHeight < m_height)
+ m_innerText->renderer()->setPos(m_innerText->renderer()->xPos(), (m_height - currentTextBlockHeight) / 2);
+ }
+}
+
+void RenderTextControl::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ RenderBlock::paint(paintInfo, tx, ty);
+ if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator)
+ theme()->paintCapsLockIndicator(this, paintInfo, absoluteContentBox());
+}
+
+void RenderTextControl::calcPrefWidths()
+{
+ ASSERT(prefWidthsDirty());
+
+ m_minPrefWidth = 0;
+ m_maxPrefWidth = 0;
+
+ if (style()->width().isFixed() && style()->width().value() > 0)
+ m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value());
+ else {
+ // Figure out how big a text control needs to be for a given number of characters
+ // (using "0" as the nominal character).
+ const UChar ch = '0';
+ float charWidth = style()->font().floatWidth(TextRun(&ch, 1, false, 0, 0, false, false, false));
+ int factor;
+ int scrollbarSize = 0;
+ if (m_multiLine) {
+ factor = static_cast<HTMLTextAreaElement*>(node())->cols();
+ // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
+ if (m_innerText->renderer()->style()->overflowY() != OHIDDEN)
+ scrollbarSize = ScrollbarTheme::nativeTheme()->scrollbarThickness();
+ } else {
+ factor = static_cast<HTMLInputElement*>(node())->size();
+ if (factor <= 0)
+ factor = 20;
+ }
+ m_maxPrefWidth = static_cast<int>(ceilf(charWidth * factor)) + scrollbarSize +
+ m_innerText->renderer()->paddingLeft() + m_innerText->renderer()->paddingRight();
+
+ if (m_resultsButton && m_resultsButton->renderer())
+ m_maxPrefWidth += m_resultsButton->renderer()->borderLeft() + m_resultsButton->renderer()->borderRight() +
+ m_resultsButton->renderer()->paddingLeft() + m_resultsButton->renderer()->paddingRight();
+ if (m_cancelButton && m_cancelButton->renderer())
+ m_maxPrefWidth += m_cancelButton->renderer()->borderLeft() + m_cancelButton->renderer()->borderRight() +
+ m_cancelButton->renderer()->paddingLeft() + m_cancelButton->renderer()->paddingRight();
+ }
+
+ if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
+ m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
+ m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
+ } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
+ m_minPrefWidth = 0;
+ else
+ m_minPrefWidth = m_maxPrefWidth;
+
+ if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
+ m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
+ m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
+ }
+
+ int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight();
+
+ m_minPrefWidth += toAdd;
+ m_maxPrefWidth += toAdd;
+
+ // FIXME: This causes cnn.com loading way slow. Comment it out for now
+//#ifdef ANDROID_LAYOUT
+#if 0
+ Frame* frame = document()->frame();
+ if (frame && frame->settings()->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) {
+ int maxWidth = frame->view()->visibleWidth() - 2 * ANDROID_FCTS_MARGIN_PADDING;
+ if (maxWidth > 0 && maxWidth < m_minPrefWidth)
+ m_minPrefWidth = maxWidth;
+ if (maxWidth > 0 && maxWidth < m_maxPrefWidth)
+ m_maxPrefWidth = maxWidth;
+ }
+#endif
+ setPrefWidthsDirty(false);
+}
+
+void RenderTextControl::forwardEvent(Event* evt)
+{
+ if (evt->type() == eventNames().blurEvent) {
+ RenderObject* innerRenderer = m_innerText->renderer();
+ if (innerRenderer) {
+ RenderLayer* innerLayer = innerRenderer->layer();
+ if (innerLayer && !m_multiLine)
+ innerLayer->scrollToOffset(style()->direction() == RTL ? innerLayer->scrollWidth() : 0, 0);
+ }
+ capsLockStateMayHaveChanged();
+ } else if (evt->type() == eventNames().focusEvent)
+ capsLockStateMayHaveChanged();
+ else {
+ if (evt->isMouseEvent() && m_resultsButton && static_cast<MouseEvent*>(evt)->x() < m_innerText->renderer()->absoluteBoundingBoxRect().x())
+ m_resultsButton->defaultEventHandler(evt);
+ else if (evt->isMouseEvent() && m_cancelButton && static_cast<MouseEvent*>(evt)->x() > m_innerText->renderer()->absoluteBoundingBoxRect().right())
+ m_cancelButton->defaultEventHandler(evt);
+ else
+ m_innerText->defaultEventHandler(evt);
+ }
+}
+
+void RenderTextControl::selectionChanged(bool userTriggered)
+{
+ HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(node());
+ if (m_multiLine)
+ static_cast<HTMLTextAreaElement*>(element)->cacheSelection(selectionStart(), selectionEnd());
+ else
+ static_cast<HTMLInputElement*>(element)->cacheSelection(selectionStart(), selectionEnd());
+ if (Frame* frame = document()->frame())
+ if (frame->selection()->isRange() && userTriggered)
+ element->dispatchEventForType(eventNames().selectEvent, true, false);
+}
+
+void RenderTextControl::autoscroll()
+{
+ RenderLayer* layer = m_innerText->renderer()->layer();
+ if (layer)
+ layer->autoscroll();
+}
+
+int RenderTextControl::scrollWidth() const
+{
+ if (m_innerText)
+ return m_innerText->scrollWidth();
+ return RenderBlock::scrollWidth();
+}
+
+int RenderTextControl::scrollHeight() const
+{
+ if (m_innerText)
+ return m_innerText->scrollHeight();
+ return RenderBlock::scrollHeight();
+}
+
+int RenderTextControl::scrollLeft() const
+{
+ if (m_innerText)
+ return m_innerText->scrollLeft();
+ return RenderBlock::scrollLeft();
+}
+
+int RenderTextControl::scrollTop() const
+{
+ if (m_innerText)
+ return m_innerText->scrollTop();
+ return RenderBlock::scrollTop();
+}
+
+void RenderTextControl::setScrollLeft(int newLeft)
+{
+ if (m_innerText)
+ m_innerText->setScrollLeft(newLeft);
+}
+
+void RenderTextControl::setScrollTop(int newTop)
+{
+ if (m_innerText)
+ m_innerText->setScrollTop(newTop);
+}
+
+const AtomicString& RenderTextControl::autosaveName() const
+{
+ return static_cast<Element*>(node())->getAttribute(autosaveAttr);
+}
+
+void RenderTextControl::addSearchResult()
+{
+ ASSERT(!m_multiLine);
+
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+ if (input->maxResults() <= 0)
+ return;
+
+ String value = input->value();
+ if (value.isEmpty())
+ return;
+
+ Settings* settings = document()->settings();
+ if (!settings || settings->privateBrowsingEnabled())
+ return;
+
+ int size = static_cast<int>(m_recentSearches.size());
+ for (int i = size - 1; i >= 0; --i)
+ if (m_recentSearches[i] == value)
+ m_recentSearches.remove(i);
+
+ m_recentSearches.insert(0, value);
+ while (static_cast<int>(m_recentSearches.size()) > input->maxResults())
+ m_recentSearches.removeLast();
+
+ const AtomicString& name = autosaveName();
+ if (!m_searchPopup)
+ m_searchPopup = SearchPopupMenu::create(this);
+ m_searchPopup->saveRecentSearches(name, m_recentSearches);
+}
+
+void RenderTextControl::showPopup()
+{
+ if (m_searchPopupIsVisible)
+ return;
+
+ if (!m_searchPopup)
+ m_searchPopup = SearchPopupMenu::create(this);
+
+ if (!m_searchPopup->enabled())
+ return;
+
+ m_searchPopupIsVisible = true;
+
+ const AtomicString& name = autosaveName();
+ m_searchPopup->loadRecentSearches(name, m_recentSearches);
+
+ // Trim the recent searches list if the maximum size has changed since we last saved.
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+ if (static_cast<int>(m_recentSearches.size()) > input->maxResults()) {
+ do
+ m_recentSearches.removeLast();
+ while (static_cast<int>(m_recentSearches.size()) > input->maxResults());
+ m_searchPopup->saveRecentSearches(name, m_recentSearches);
+ }
+
+ m_searchPopup->show(absoluteBoundingBoxRect(), document()->view(), -1);
+}
+
+void RenderTextControl::hidePopup()
+{
+ if (m_searchPopup)
+ m_searchPopup->hide();
+ m_searchPopupIsVisible = false;
+}
+
+void RenderTextControl::valueChanged(unsigned listIndex, bool fireEvents)
+{
+ ASSERT(static_cast<int>(listIndex) < listSize());
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+ if (static_cast<int>(listIndex) == (listSize() - 1)) {
+ if (fireEvents) {
+ m_recentSearches.clear();
+ const AtomicString& name = autosaveName();
+ if (!name.isEmpty()) {
+ if (!m_searchPopup)
+ m_searchPopup = SearchPopupMenu::create(this);
+ m_searchPopup->saveRecentSearches(name, m_recentSearches);
+ }
+ }
+ } else {
+ input->setValue(itemText(listIndex));
+ if (fireEvents)
+ input->onSearch();
+ input->select();
+ }
+}
+
+String RenderTextControl::itemText(unsigned listIndex) const
+{
+ int size = listSize();
+ if (size == 1) {
+ ASSERT(!listIndex);
+ return searchMenuNoRecentSearchesText();
+ }
+ if (!listIndex)
+ return searchMenuRecentSearchesText();
+ if (itemIsSeparator(listIndex))
+ return String();
+ if (static_cast<int>(listIndex) == (size - 1))
+ return searchMenuClearRecentSearchesText();
+ return m_recentSearches[listIndex - 1];
+}
+
+bool RenderTextControl::itemIsEnabled(unsigned listIndex) const
+{
+ if (!listIndex || itemIsSeparator(listIndex))
+ return false;
+ return true;
+}
+
+PopupMenuStyle RenderTextControl::itemStyle(unsigned listIndex) const
+{
+ return menuStyle();
+}
+
+PopupMenuStyle RenderTextControl::menuStyle() const
+{
+ return PopupMenuStyle(style()->color(), style()->backgroundColor(), style()->font(), style()->visibility() == VISIBLE);
+}
+
+HostWindow* RenderTextControl::hostWindow() const
+{
+ return document()->view()->hostWindow();
+}
+
+PassRefPtr<Scrollbar> RenderTextControl::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize)
+{
+ RefPtr<Scrollbar> widget;
+ bool hasCustomScrollbarStyle = style()->hasPseudoStyle(RenderStyle::SCROLLBAR);
+ if (hasCustomScrollbarStyle)
+ widget = RenderScrollbar::createCustomScrollbar(client, orientation, this);
+ else
+ widget = Scrollbar::createNativeScrollbar(client, orientation, controlSize);
+ return widget.release();
+}
+
+int RenderTextControl::clientInsetLeft() const
+{
+ // Inset the menu by the radius of the cap on the left so that
+ // it only runs along the straight part of the bezel.
+ return height() / 2;
+}
+
+int RenderTextControl::clientInsetRight() const
+{
+ // Inset the menu by the radius of the cap on the right so that
+ // it only runs along the straight part of the bezel (unless it needs
+ // to be wider).
+ return height() / 2;
+}
+
+int RenderTextControl::clientPaddingLeft() const
+{
+ int padding = paddingLeft();
+ if (m_resultsButton->renderer())
+ padding += m_resultsButton->renderer()->width();
+ return padding;
+}
+
+int RenderTextControl::clientPaddingRight() const
+{
+ int padding = paddingRight();
+ if (m_cancelButton->renderer())
+ padding += m_cancelButton->renderer()->width();
+ return padding;
+}
+
+int RenderTextControl::listSize() const
+{
+ // If there are no recent searches, then our menu will have 1 "No recent searches" item.
+ if (!m_recentSearches.size())
+ return 1;
+ // Otherwise, leave room in the menu for a header, a separator, and the "Clear recent searches" item.
+ return m_recentSearches.size() + 3;
+}
+
+int RenderTextControl::selectedIndex() const
+{
+ return -1;
+}
+
+bool RenderTextControl::itemIsSeparator(unsigned listIndex) const
+{
+ // The separator will be the second to last item in our list.
+ return static_cast<int>(listIndex) == (listSize() - 2);
+}
+
+bool RenderTextControl::itemIsLabel(unsigned listIndex) const
+{
+ return listIndex == 0;
+}
+
+bool RenderTextControl::itemIsSelected(unsigned listIndex) const
+{
+ return false;
+}
+
+void RenderTextControl::setTextFromItem(unsigned listIndex)
+{
+ static_cast<HTMLInputElement*>(node())->setValue(itemText(listIndex));
+}
+
+bool RenderTextControl::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
+{
+ RenderLayer* layer = m_innerText->renderer()->layer();
+ if (layer && layer->scroll(direction, granularity, multiplier))
+ return true;
+ return RenderObject::scroll(direction, granularity, multiplier);
+}
+
+void RenderTextControl::searchEventTimerFired(Timer<RenderTextControl>*)
+{
+ static_cast<HTMLInputElement*>(node())->onSearch();
+}
+
+void RenderTextControl::stopSearchEventTimer()
+{
+ m_searchEventTimer.stop();
+}
+
+void RenderTextControl::startSearchEventTimer()
+{
+ unsigned length = text().length();
+
+ // If there's no text, fire the event right away.
+ if (!length) {
+ m_searchEventTimer.stop();
+ static_cast<HTMLInputElement*>(node())->onSearch();
+ return;
+ }
+
+ // After typing the first key, we wait 0.5 seconds.
+ // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on.
+ m_searchEventTimer.startOneShot(max(0.2, 0.6 - 0.1 * length));
+}
+
+bool RenderTextControl::isScrollable() const
+{
+ if (m_innerText && m_innerText->renderer()->isScrollable())
+ return true;
+ return RenderObject::isScrollable();
+}
+
+FontSelector* RenderTextControl::fontSelector() const
+{
+ return document()->styleSelector()->fontSelector();
+}
+
+void RenderTextControl::updatePlaceholderVisibility()
+{
+ RenderStyle* parentStyle = m_innerBlock ? m_innerBlock->renderer()->style() : style();
+ RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(parentStyle);
+ m_innerText->renderer()->setStyle(textBlockStyle);
+ for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) {
+ if (n->renderer())
+ n->renderer()->setStyle(textBlockStyle);
+ }
+ updateFromElement();
+}
+
+void RenderTextControl::capsLockStateMayHaveChanged()
+{
+ // Only draw the caps lock indicator if these things are true:
+ // 1) The field is a password field
+ // 2) The frame is active
+ // 3) The element is focused
+ // 4) The caps lock is on
+
+ bool shouldDrawCapsLockIndicator = false;
+ if (Node* n = node())
+ if (Document* d = document())
+ if (Frame* f = d->frame())
+ shouldDrawCapsLockIndicator = !m_multiLine && static_cast<HTMLInputElement*>(n)->inputType() == HTMLInputElement::PASSWORD &&
+ f->selection()->isFocusedAndActive() && d->focusedNode() == n && PlatformKeyboardEvent::currentCapsLockState();
+
+ if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
+ m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
+ repaint();
+ }
+}
+
+} // namespace WebCore