Home | History | Annotate | Download | only in rendering
      1 /**
      2  * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
      3  *           (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  *
     20  */
     21 
     22 #include "config.h"
     23 #include "RenderTextControl.h"
     24 
     25 #include "AXObjectCache.h"
     26 #include "CharacterNames.h"
     27 #include "Editor.h"
     28 #include "Event.h"
     29 #include "EventNames.h"
     30 #include "Frame.h"
     31 #include "HTMLBRElement.h"
     32 #include "HTMLNames.h"
     33 #include "HitTestResult.h"
     34 #include "RenderLayer.h"
     35 #include "RenderText.h"
     36 #include "ScrollbarTheme.h"
     37 #include "SelectionController.h"
     38 #include "Text.h"
     39 #include "TextControlInnerElements.h"
     40 #include "TextIterator.h"
     41 
     42 using namespace std;
     43 
     44 namespace WebCore {
     45 
     46 using namespace HTMLNames;
     47 
     48 // Value chosen by observation.  This can be tweaked.
     49 static const int minColorContrastValue = 1300;
     50 
     51 static Color disabledTextColor(const Color& textColor, const Color& backgroundColor)
     52 {
     53     // The explicit check for black is an optimization for the 99% case (black on white).
     54     // This also means that black on black will turn into grey on black when disabled.
     55     Color disabledColor;
     56     if (textColor.rgb() == Color::black || differenceSquared(textColor, Color::white) > differenceSquared(backgroundColor, Color::white))
     57         disabledColor = textColor.light();
     58     else
     59         disabledColor = textColor.dark();
     60 
     61     // If there's not very much contrast between the disabled color and the background color,
     62     // just leave the text color alone.  We don't want to change a good contrast color scheme so that it has really bad contrast.
     63     // If the the contrast was already poor, then it doesn't do any good to change it to a different poor contrast color scheme.
     64     if (differenceSquared(disabledColor, backgroundColor) < minColorContrastValue)
     65         return textColor;
     66 
     67     return disabledColor;
     68 }
     69 
     70 RenderTextControl::RenderTextControl(Node* node, bool placeholderVisible)
     71     : RenderBlock(node)
     72     , m_placeholderVisible(placeholderVisible)
     73     , m_wasChangedSinceLastChangeEvent(false)
     74     , m_lastChangeWasUserEdit(false)
     75 {
     76 }
     77 
     78 RenderTextControl::~RenderTextControl()
     79 {
     80     // The children renderers have already been destroyed by destroyLeftoverChildren
     81     if (m_innerText)
     82         m_innerText->detach();
     83 }
     84 
     85 void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
     86 {
     87     RenderBlock::styleDidChange(diff, oldStyle);
     88 
     89     if (m_innerText) {
     90         RenderBlock* textBlockRenderer = toRenderBlock(m_innerText->renderer());
     91         RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(style());
     92         // We may have set the width and the height in the old style in layout().
     93         // Reset them now to avoid getting a spurious layout hint.
     94         textBlockRenderer->style()->setHeight(Length());
     95         textBlockRenderer->style()->setWidth(Length());
     96         setInnerTextStyle(textBlockStyle);
     97     }
     98 
     99     setReplaced(isInline());
    100 }
    101 
    102 void RenderTextControl::setInnerTextStyle(PassRefPtr<RenderStyle> style)
    103 {
    104     if (m_innerText) {
    105         RefPtr<RenderStyle> textStyle = style;
    106         m_innerText->renderer()->setStyle(textStyle);
    107         for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) {
    108             if (n->renderer())
    109                 n->renderer()->setStyle(textStyle);
    110         }
    111     }
    112 }
    113 
    114 static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
    115 {
    116     bool isEnabled = true;
    117     bool isReadOnlyControl = false;
    118 
    119     if (node->isElementNode()) {
    120         Element* element = static_cast<Element*>(node);
    121         isEnabled = element->isEnabledFormControl();
    122         isReadOnlyControl = element->isReadOnlyFormControl();
    123     }
    124 
    125     style->setUserModify((isReadOnlyControl || !isEnabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
    126     return !isEnabled;
    127 }
    128 
    129 void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
    130 {
    131     // The inner block, if present, always has its direction set to LTR,
    132     // so we need to inherit the direction from the element.
    133     textBlockStyle->setDirection(style()->direction());
    134 
    135     bool disabled = updateUserModifyProperty(node(), textBlockStyle);
    136     if (disabled)
    137         textBlockStyle->setColor(disabledTextColor(textBlockStyle->color(), startStyle->backgroundColor()));
    138 }
    139 
    140 void RenderTextControl::createSubtreeIfNeeded(TextControlInnerElement* innerBlock)
    141 {
    142     if (!m_innerText) {
    143         // Create the text block element
    144         // For non-search fields, there is no intermediate innerBlock as the shadow node.
    145         // m_innerText will be the shadow node in that case.
    146         RenderStyle* parentStyle = innerBlock ? innerBlock->renderer()->style() : style();
    147         m_innerText = new TextControlInnerTextElement(document(), innerBlock ? 0 : node());
    148         m_innerText->attachInnerElement(innerBlock ? innerBlock : node(), createInnerTextStyle(parentStyle), renderArena());
    149     }
    150 }
    151 
    152 int RenderTextControl::textBlockHeight() const
    153 {
    154     return height() - paddingTop() - paddingBottom() - borderTop() - borderBottom();
    155 }
    156 
    157 int RenderTextControl::textBlockWidth() const
    158 {
    159     return width() - paddingLeft() - paddingRight() - borderLeft() - borderRight()
    160            - m_innerText->renderBox()->paddingLeft() - m_innerText->renderBox()->paddingRight();
    161 }
    162 
    163 void RenderTextControl::updateFromElement()
    164 {
    165     updateUserModifyProperty(node(), m_innerText->renderer()->style());
    166 }
    167 
    168 void RenderTextControl::setInnerTextValue(const String& innerTextValue)
    169 {
    170     String value;
    171 
    172     if (innerTextValue.isNull())
    173         value = "";
    174     else {
    175         value = innerTextValue;
    176         value = document()->displayStringModifiedByEncoding(value);
    177     }
    178 
    179     if (value != text() || !m_innerText->hasChildNodes()) {
    180         if (value != text()) {
    181             if (Frame* frame = document()->frame()) {
    182                 frame->editor()->clearUndoRedoOperations();
    183 
    184                 if (AXObjectCache::accessibilityEnabled())
    185                     document()->axObjectCache()->postNotification(this, AXObjectCache::AXValueChanged, false);
    186             }
    187         }
    188 
    189         ExceptionCode ec = 0;
    190         m_innerText->setInnerText(value, ec);
    191         ASSERT(!ec);
    192 
    193         if (value.endsWith("\n") || value.endsWith("\r")) {
    194             m_innerText->appendChild(new HTMLBRElement(brTag, document()), ec);
    195             ASSERT(!ec);
    196         }
    197 
    198         // We set m_lastChangeWasUserEdit to false since this change was not explicitly made by the user (say, via typing on the keyboard), see <rdar://problem/5359921>.
    199         m_lastChangeWasUserEdit = false;
    200     }
    201 
    202     static_cast<Element*>(node())->setFormControlValueMatchesRenderer(true);
    203 }
    204 
    205 void RenderTextControl::setLastChangeWasUserEdit(bool lastChangeWasUserEdit)
    206 {
    207     m_lastChangeWasUserEdit = lastChangeWasUserEdit;
    208     document()->setIgnoreAutofocus(lastChangeWasUserEdit);
    209 }
    210 
    211 int RenderTextControl::selectionStart()
    212 {
    213     Frame* frame = document()->frame();
    214     if (!frame)
    215         return 0;
    216     return indexForVisiblePosition(frame->selection()->start());
    217 }
    218 
    219 int RenderTextControl::selectionEnd()
    220 {
    221     Frame* frame = document()->frame();
    222     if (!frame)
    223         return 0;
    224     return indexForVisiblePosition(frame->selection()->end());
    225 }
    226 
    227 void RenderTextControl::setSelectionStart(int start)
    228 {
    229     setSelectionRange(start, max(start, selectionEnd()));
    230 }
    231 
    232 void RenderTextControl::setSelectionEnd(int end)
    233 {
    234     setSelectionRange(min(end, selectionStart()), end);
    235 }
    236 
    237 void RenderTextControl::select()
    238 {
    239     setSelectionRange(0, text().length());
    240 }
    241 
    242 void RenderTextControl::setSelectionRange(int start, int end)
    243 {
    244     end = max(end, 0);
    245     start = min(max(start, 0), end);
    246 
    247     ASSERT(!document()->childNeedsAndNotInStyleRecalc());
    248 
    249     if (style()->visibility() == HIDDEN || !m_innerText || !m_innerText->renderer() || !m_innerText->renderBox()->height()) {
    250         cacheSelection(start, end);
    251         return;
    252     }
    253     VisiblePosition startPosition = visiblePositionForIndex(start);
    254     VisiblePosition endPosition;
    255     if (start == end)
    256         endPosition = startPosition;
    257     else
    258         endPosition = visiblePositionForIndex(end);
    259 
    260     // startPosition and endPosition can be null position for example when
    261     // "-webkit-user-select: none" style attribute is specified.
    262     if (startPosition.isNotNull() && endPosition.isNotNull()) {
    263         ASSERT(startPosition.deepEquivalent().node()->shadowAncestorNode() == node() && endPosition.deepEquivalent().node()->shadowAncestorNode() == node());
    264     }
    265     VisibleSelection newSelection = VisibleSelection(startPosition, endPosition);
    266 
    267     if (Frame* frame = document()->frame())
    268         frame->selection()->setSelection(newSelection);
    269 
    270     // FIXME: Granularity is stored separately on the frame, but also in the selection controller.
    271     // The granularity in the selection controller should be used, and then this line of code would not be needed.
    272     if (Frame* frame = document()->frame())
    273         frame->setSelectionGranularity(CharacterGranularity);
    274 }
    275 
    276 VisibleSelection RenderTextControl::selection(int start, int end) const
    277 {
    278     return VisibleSelection(VisiblePosition(m_innerText.get(), start, VP_DEFAULT_AFFINITY),
    279                             VisiblePosition(m_innerText.get(), end, VP_DEFAULT_AFFINITY));
    280 }
    281 
    282 VisiblePosition RenderTextControl::visiblePositionForIndex(int index)
    283 {
    284     if (index <= 0)
    285         return VisiblePosition(m_innerText.get(), 0, DOWNSTREAM);
    286     ExceptionCode ec = 0;
    287     RefPtr<Range> range = Range::create(document());
    288     range->selectNodeContents(m_innerText.get(), ec);
    289     ASSERT(!ec);
    290     CharacterIterator it(range.get());
    291     it.advance(index - 1);
    292     Node* endContainer = it.range()->endContainer(ec);
    293     ASSERT(!ec);
    294     int endOffset = it.range()->endOffset(ec);
    295     ASSERT(!ec);
    296     return VisiblePosition(endContainer, endOffset, UPSTREAM);
    297 }
    298 
    299 int RenderTextControl::indexForVisiblePosition(const VisiblePosition& pos)
    300 {
    301     Position indexPosition = pos.deepEquivalent();
    302     if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != m_innerText)
    303         return 0;
    304     ExceptionCode ec = 0;
    305     RefPtr<Range> range = Range::create(document());
    306     range->setStart(m_innerText.get(), 0, ec);
    307     ASSERT(!ec);
    308     range->setEnd(indexPosition.node(), indexPosition.deprecatedEditingOffset(), ec);
    309     ASSERT(!ec);
    310     return TextIterator::rangeLength(range.get());
    311 }
    312 
    313 void RenderTextControl::subtreeHasChanged()
    314 {
    315     m_wasChangedSinceLastChangeEvent = true;
    316     m_lastChangeWasUserEdit = true;
    317 }
    318 
    319 String RenderTextControl::finishText(Vector<UChar>& result) const
    320 {
    321     // Remove one trailing newline; there's always one that's collapsed out by rendering.
    322     size_t size = result.size();
    323     if (size && result[size - 1] == '\n')
    324         result.shrink(--size);
    325 
    326     // Convert backslash to currency symbol.
    327     document()->displayBufferModifiedByEncoding(result.data(), result.size());
    328 
    329     return String::adopt(result);
    330 }
    331 
    332 String RenderTextControl::text()
    333 {
    334     if (!m_innerText)
    335         return "";
    336 
    337     Vector<UChar> result;
    338 
    339     for (Node* n = m_innerText.get(); n; n = n->traverseNextNode(m_innerText.get())) {
    340         if (n->hasTagName(brTag))
    341             result.append(&newlineCharacter, 1);
    342         else if (n->isTextNode()) {
    343             String data = static_cast<Text*>(n)->data();
    344             result.append(data.characters(), data.length());
    345         }
    346     }
    347 
    348     return finishText(result);
    349 }
    350 
    351 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
    352 {
    353     RootInlineBox* next;
    354     for (; line; line = next) {
    355         next = line->nextRootBox();
    356         if (next && !line->endsWithBreak()) {
    357             ASSERT(line->lineBreakObj());
    358             breakNode = line->lineBreakObj()->node();
    359             breakOffset = line->lineBreakPos();
    360             line = next;
    361             return;
    362         }
    363     }
    364     breakNode = 0;
    365     breakOffset = 0;
    366 }
    367 
    368 String RenderTextControl::textWithHardLineBreaks()
    369 {
    370     if (!m_innerText)
    371         return "";
    372     Node* firstChild = m_innerText->firstChild();
    373     if (!firstChild)
    374         return "";
    375 
    376     document()->updateLayout();
    377 
    378     RenderObject* renderer = firstChild->renderer();
    379     if (!renderer)
    380         return "";
    381 
    382     InlineBox* box = renderer->isText() ? toRenderText(renderer)->firstTextBox() : toRenderBox(renderer)->inlineBoxWrapper();
    383     if (!box)
    384         return "";
    385 
    386     Node* breakNode;
    387     unsigned breakOffset;
    388     RootInlineBox* line = box->root();
    389     getNextSoftBreak(line, breakNode, breakOffset);
    390 
    391     Vector<UChar> result;
    392 
    393     for (Node* n = firstChild; n; n = n->traverseNextNode(m_innerText.get())) {
    394         if (n->hasTagName(brTag))
    395             result.append(&newlineCharacter, 1);
    396         else if (n->isTextNode()) {
    397             Text* text = static_cast<Text*>(n);
    398             String data = text->data();
    399             unsigned length = data.length();
    400             unsigned position = 0;
    401             while (breakNode == n && breakOffset <= length) {
    402                 if (breakOffset > position) {
    403                     result.append(data.characters() + position, breakOffset - position);
    404                     position = breakOffset;
    405                     result.append(&newlineCharacter, 1);
    406                 }
    407                 getNextSoftBreak(line, breakNode, breakOffset);
    408             }
    409             result.append(data.characters() + position, length - position);
    410         }
    411         while (breakNode == n)
    412             getNextSoftBreak(line, breakNode, breakOffset);
    413     }
    414 
    415     return finishText(result);
    416 }
    417 
    418 int RenderTextControl::scrollbarThickness() const
    419 {
    420     // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
    421     return ScrollbarTheme::nativeTheme()->scrollbarThickness();
    422 }
    423 
    424 void RenderTextControl::calcHeight()
    425 {
    426     setHeight(m_innerText->renderBox()->borderTop() + m_innerText->renderBox()->borderBottom() +
    427               m_innerText->renderBox()->paddingTop() + m_innerText->renderBox()->paddingBottom() +
    428               m_innerText->renderBox()->marginTop() + m_innerText->renderBox()->marginBottom());
    429 
    430     adjustControlHeightBasedOnLineHeight(m_innerText->renderer()->lineHeight(true, true));
    431     setHeight(height() + paddingTop() + paddingBottom() + borderTop() + borderBottom());
    432 
    433     // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
    434     if (style()->overflowX() == OSCROLL ||  (style()->overflowX() == OAUTO && m_innerText->renderer()->style()->wordWrap() == NormalWordWrap))
    435         setHeight(height() + scrollbarThickness());
    436 
    437     RenderBlock::calcHeight();
    438 }
    439 
    440 void RenderTextControl::hitInnerTextElement(HitTestResult& result, int xPos, int yPos, int tx, int ty)
    441 {
    442     result.setInnerNode(m_innerText.get());
    443     result.setInnerNonSharedNode(m_innerText.get());
    444     result.setLocalPoint(IntPoint(xPos - tx - x() - m_innerText->renderBox()->x(),
    445                                   yPos - ty - y() - m_innerText->renderBox()->y()));
    446 }
    447 
    448 void RenderTextControl::forwardEvent(Event* event)
    449 {
    450     if (event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)
    451         return;
    452     m_innerText->defaultEventHandler(event);
    453 }
    454 
    455 IntRect RenderTextControl::controlClipRect(int tx, int ty) const
    456 {
    457     IntRect clipRect = contentBoxRect();
    458     clipRect.move(tx, ty);
    459     return clipRect;
    460 }
    461 
    462 void RenderTextControl::calcPrefWidths()
    463 {
    464     ASSERT(prefWidthsDirty());
    465 
    466     m_minPrefWidth = 0;
    467     m_maxPrefWidth = 0;
    468 
    469     if (style()->width().isFixed() && style()->width().value() > 0)
    470         m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value());
    471     else {
    472         // Use average character width. Matches IE.
    473         float charWidth = style()->font().primaryFont()->avgCharWidth();
    474         m_maxPrefWidth = preferredContentWidth(charWidth) + m_innerText->renderBox()->paddingLeft() + m_innerText->renderBox()->paddingRight();
    475     }
    476 
    477     if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
    478         m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
    479         m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
    480     } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
    481         m_minPrefWidth = 0;
    482     else
    483         m_minPrefWidth = m_maxPrefWidth;
    484 
    485     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
    486         m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
    487         m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
    488     }
    489 
    490     int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight();
    491 
    492     m_minPrefWidth += toAdd;
    493     m_maxPrefWidth += toAdd;
    494 
    495     setPrefWidthsDirty(false);
    496 }
    497 
    498 void RenderTextControl::selectionChanged(bool userTriggered)
    499 {
    500     cacheSelection(selectionStart(), selectionEnd());
    501 
    502     if (Frame* frame = document()->frame()) {
    503         if (frame->selection()->isRange() && userTriggered)
    504             node()->dispatchEvent(Event::create(eventNames().selectEvent, true, false));
    505     }
    506 }
    507 
    508 void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
    509 {
    510     if (width() && height())
    511         rects.append(IntRect(tx, ty, width(), height()));
    512 }
    513 
    514 HTMLElement* RenderTextControl::innerTextElement() const
    515 {
    516     return m_innerText.get();
    517 }
    518 
    519 void RenderTextControl::updatePlaceholderVisibility(bool placeholderShouldBeVisible, bool placeholderValueChanged)
    520 {
    521     bool oldPlaceholderVisible = m_placeholderVisible;
    522     m_placeholderVisible = placeholderShouldBeVisible;
    523     if (oldPlaceholderVisible != m_placeholderVisible || placeholderValueChanged) {
    524         // Sets the inner text style to the normal style or :placeholder style.
    525         setInnerTextStyle(createInnerTextStyle(textBaseStyle()));
    526 
    527         // updateFromElement() of the subclasses updates the text content
    528         // to the element's value(), placeholder(), or the empty string.
    529         updateFromElement();
    530     }
    531 }
    532 
    533 } // namespace WebCore
    534