From 648161bb0edfc3d43db63caed5cc5213bc6cb78f Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 18:28:41 -0800 Subject: auto import from //depot/cupcake/@135843 --- WebCore/rendering/RenderTextControl.cpp | 1272 ------------------------------- 1 file changed, 1272 deletions(-) delete mode 100644 WebCore/rendering/RenderTextControl.cpp (limited to 'WebCore/rendering/RenderTextControl.cpp') diff --git a/WebCore/rendering/RenderTextControl.cpp b/WebCore/rendering/RenderTextControl.cpp deleted file mode 100644 index a971028..0000000 --- a/WebCore/rendering/RenderTextControl.cpp +++ /dev/null @@ -1,1272 +0,0 @@ -/** - * 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 - -#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(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(m_innerText->renderer()); - RefPtr 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 RenderTextControl::createInnerBlockStyle(const RenderStyle* startStyle) -{ - RefPtr 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 RenderTextControl::createInnerTextStyle(const RenderStyle* startStyle) -{ - HTMLFormControlElement* element = static_cast(node()); - bool placeholderShouldBeVisible = !m_multiLine && static_cast(element)->placeholderShouldBeVisible(); - - RefPtr 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(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(element)->placeholderShouldBeVisible()) - textBlockStyle->setTextSecurity(TSNONE); - - if (!element->isEnabled()) - textBlockStyle->setColor(disabledTextColor(textBlockStyle->color(), startStyle->backgroundColor())); - - return textBlockStyle.release(); -} - -PassRefPtr RenderTextControl::createResultsButtonStyle(const RenderStyle* startStyle) -{ - ASSERT(!m_multiLine); - HTMLInputElement* input = static_cast(node()); - RefPtr 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 RenderTextControl::createCancelButtonStyle(const RenderStyle* startStyle) -{ - RefPtr cancelBlockStyle; - - if (RefPtr 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(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(node()); - - bool placeholderShouldBeVisible = !m_multiLine && static_cast(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(element)->getAttribute(placeholderAttr), ec); - } else if (!element->valueMatchesRenderer() || m_multiLine || placeholderVisibilityShouldChange) { - String value; - if (m_multiLine) - value = static_cast(element)->value(); - else - value = static_cast(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(node())->cacheSelection(start, end); - else - static_cast(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::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::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(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(node()); - if (m_multiLine) { - element->setValueMatchesRenderer(false); - if (element->focused()) - if (Frame* frame = document()->frame()) - frame->textDidChangeInTextArea(element); - } else { - HTMLInputElement* input = static_cast(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& 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 result; - - for (Node* n = m_innerText.get(); n; n = n->traverseNextNode(m_innerText.get())) { - if (n->isTextNode()) { - Text* text = static_cast(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(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 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(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(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(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(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(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(node())->size(); - if (factor <= 0) - factor = 20; - } - m_maxPrefWidth = static_cast(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(evt)->x() < m_innerText->renderer()->absoluteBoundingBoxRect().x()) - m_resultsButton->defaultEventHandler(evt); - else if (evt->isMouseEvent() && m_cancelButton && static_cast(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(node()); - if (m_multiLine) - static_cast(element)->cacheSelection(selectionStart(), selectionEnd()); - else - static_cast(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(node())->getAttribute(autosaveAttr); -} - -void RenderTextControl::addSearchResult() -{ - ASSERT(!m_multiLine); - - HTMLInputElement* input = static_cast(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(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(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(node()); - if (static_cast(m_recentSearches.size()) > input->maxResults()) { - do - m_recentSearches.removeLast(); - while (static_cast(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(listIndex) < listSize()); - HTMLInputElement* input = static_cast(node()); - if (static_cast(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(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 RenderTextControl::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize) -{ - RefPtr 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(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(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*) -{ - static_cast(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(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 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(n)->inputType() == HTMLInputElement::PASSWORD && - f->selection()->isFocusedAndActive() && d->focusedNode() == n && PlatformKeyboardEvent::currentCapsLockState(); - - if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) { - m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator; - repaint(); - } -} - -} // namespace WebCore -- cgit v1.1