• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
disabledTextColor(const Color & textColor,const Color & backgroundColor)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 
RenderTextControl(Node * node,bool placeholderVisible)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 
~RenderTextControl()78 RenderTextControl::~RenderTextControl()
79 {
80     // The children renderers have already been destroyed by destroyLeftoverChildren
81     if (m_innerText)
82         m_innerText->detach();
83 }
84 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)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 
setInnerTextStyle(PassRefPtr<RenderStyle> style)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 
updateUserModifyProperty(Node * node,RenderStyle * style)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 
adjustInnerTextStyle(const RenderStyle * startStyle,RenderStyle * textBlockStyle) const129 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 
createSubtreeIfNeeded(TextControlInnerElement * innerBlock)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 
textBlockHeight() const152 int RenderTextControl::textBlockHeight() const
153 {
154     return height() - paddingTop() - paddingBottom() - borderTop() - borderBottom();
155 }
156 
textBlockWidth() const157 int RenderTextControl::textBlockWidth() const
158 {
159     return width() - paddingLeft() - paddingRight() - borderLeft() - borderRight()
160            - m_innerText->renderBox()->paddingLeft() - m_innerText->renderBox()->paddingRight();
161 }
162 
updateFromElement()163 void RenderTextControl::updateFromElement()
164 {
165     updateUserModifyProperty(node(), m_innerText->renderer()->style());
166 }
167 
setInnerTextValue(const String & innerTextValue)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 
setLastChangeWasUserEdit(bool lastChangeWasUserEdit)205 void RenderTextControl::setLastChangeWasUserEdit(bool lastChangeWasUserEdit)
206 {
207     m_lastChangeWasUserEdit = lastChangeWasUserEdit;
208     document()->setIgnoreAutofocus(lastChangeWasUserEdit);
209 }
210 
selectionStart()211 int RenderTextControl::selectionStart()
212 {
213     Frame* frame = document()->frame();
214     if (!frame)
215         return 0;
216     return indexForVisiblePosition(frame->selection()->start());
217 }
218 
selectionEnd()219 int RenderTextControl::selectionEnd()
220 {
221     Frame* frame = document()->frame();
222     if (!frame)
223         return 0;
224     return indexForVisiblePosition(frame->selection()->end());
225 }
226 
setSelectionStart(int start)227 void RenderTextControl::setSelectionStart(int start)
228 {
229     setSelectionRange(start, max(start, selectionEnd()));
230 }
231 
setSelectionEnd(int end)232 void RenderTextControl::setSelectionEnd(int end)
233 {
234     setSelectionRange(min(end, selectionStart()), end);
235 }
236 
select()237 void RenderTextControl::select()
238 {
239     setSelectionRange(0, text().length());
240 }
241 
setSelectionRange(int start,int end)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 
selection(int start,int end) const276 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 
visiblePositionForIndex(int index)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 
indexForVisiblePosition(const VisiblePosition & pos)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 
subtreeHasChanged()313 void RenderTextControl::subtreeHasChanged()
314 {
315     m_wasChangedSinceLastChangeEvent = true;
316     m_lastChangeWasUserEdit = true;
317 }
318 
finishText(Vector<UChar> & result) const319 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 
text()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 
getNextSoftBreak(RootInlineBox * & line,Node * & breakNode,unsigned & breakOffset)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 
textWithHardLineBreaks()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 
scrollbarThickness() const418 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 
calcHeight()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 
hitInnerTextElement(HitTestResult & result,int xPos,int yPos,int tx,int ty)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 
forwardEvent(Event * event)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 
controlClipRect(int tx,int ty) const455 IntRect RenderTextControl::controlClipRect(int tx, int ty) const
456 {
457     IntRect clipRect = contentBoxRect();
458     clipRect.move(tx, ty);
459     return clipRect;
460 }
461 
calcPrefWidths()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 
selectionChanged(bool userTriggered)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 
addFocusRingRects(Vector<IntRect> & rects,int tx,int ty)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 
innerTextElement() const514 HTMLElement* RenderTextControl::innerTextElement() const
515 {
516     return m_innerText.get();
517 }
518 
updatePlaceholderVisibility(bool placeholderShouldBeVisible,bool placeholderValueChanged)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