• 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)70 RenderTextControl::RenderTextControl(Node* node)
71     : RenderBlock(node)
72     , m_edited(false)
73     , m_userEdited(false)
74 {
75 }
76 
~RenderTextControl()77 RenderTextControl::~RenderTextControl()
78 {
79     // The children renderers have already been destroyed by destroyLeftoverChildren
80     if (m_innerText)
81         m_innerText->detach();
82 }
83 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)84 void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
85 {
86     RenderBlock::styleDidChange(diff, oldStyle);
87 
88     if (m_innerText) {
89         RenderBlock* textBlockRenderer = toRenderBlock(m_innerText->renderer());
90         RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(style());
91         // We may have set the width and the height in the old style in layout().
92         // Reset them now to avoid getting a spurious layout hint.
93         textBlockRenderer->style()->setHeight(Length());
94         textBlockRenderer->style()->setWidth(Length());
95         textBlockRenderer->setStyle(textBlockStyle);
96         for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) {
97             if (n->renderer())
98                 n->renderer()->setStyle(textBlockStyle);
99         }
100     }
101 
102     setReplaced(isInline());
103 }
104 
updateUserModifyProperty(Node * node,RenderStyle * style)105 static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
106 {
107     bool isEnabled = true;
108     bool isReadOnlyControl = false;
109 
110     if (node->isElementNode()) {
111         Element* element = static_cast<Element*>(node);
112         isEnabled = element->isEnabledFormControl();
113         isReadOnlyControl = element->isReadOnlyFormControl();
114     }
115 
116     style->setUserModify((isReadOnlyControl || !isEnabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
117     return !isEnabled;
118 }
119 
adjustInnerTextStyle(const RenderStyle * startStyle,RenderStyle * textBlockStyle) const120 void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
121 {
122     // The inner block, if present, always has its direction set to LTR,
123     // so we need to inherit the direction from the element.
124     textBlockStyle->setDirection(style()->direction());
125 
126     bool disabled = updateUserModifyProperty(node(), textBlockStyle);
127     if (disabled)
128         textBlockStyle->setColor(disabledTextColor(textBlockStyle->color(), startStyle->backgroundColor()));
129 }
130 
createSubtreeIfNeeded(TextControlInnerElement * innerBlock)131 void RenderTextControl::createSubtreeIfNeeded(TextControlInnerElement* innerBlock)
132 {
133     if (!m_innerText) {
134         // Create the text block element
135         // For non-search fields, there is no intermediate innerBlock as the shadow node.
136         // m_innerText will be the shadow node in that case.
137         RenderStyle* parentStyle = innerBlock ? innerBlock->renderer()->style() : style();
138         m_innerText = new TextControlInnerTextElement(document(), innerBlock ? 0 : node());
139         m_innerText->attachInnerElement(innerBlock ? innerBlock : node(), createInnerTextStyle(parentStyle), renderArena());
140     }
141 }
142 
textBlockHeight() const143 int RenderTextControl::textBlockHeight() const
144 {
145     return height() - paddingTop() - paddingBottom() - borderTop() - borderBottom();
146 }
147 
textBlockWidth() const148 int RenderTextControl::textBlockWidth() const
149 {
150     return width() - paddingLeft() - paddingRight() - borderLeft() - borderRight()
151            - m_innerText->renderBox()->paddingLeft() - m_innerText->renderBox()->paddingRight();
152 }
153 
updateFromElement()154 void RenderTextControl::updateFromElement()
155 {
156     updateUserModifyProperty(node(), m_innerText->renderer()->style());
157 }
158 
setInnerTextValue(const String & innerTextValue)159 void RenderTextControl::setInnerTextValue(const String& innerTextValue)
160 {
161     String value;
162 
163     if (innerTextValue.isNull())
164         value = "";
165     else {
166         value = innerTextValue;
167         value = document()->displayStringModifiedByEncoding(value);
168     }
169 
170     if (value != text() || !m_innerText->hasChildNodes()) {
171         if (value != text()) {
172             if (Frame* frame = document()->frame()) {
173                 frame->editor()->clearUndoRedoOperations();
174 
175                 if (AXObjectCache::accessibilityEnabled())
176                     document()->axObjectCache()->postNotification(this, "AXValueChanged", false);
177             }
178         }
179 
180         ExceptionCode ec = 0;
181         m_innerText->setInnerText(value, ec);
182         ASSERT(!ec);
183 
184         if (value.endsWith("\n") || value.endsWith("\r")) {
185             m_innerText->appendChild(new HTMLBRElement(brTag, document()), ec);
186             ASSERT(!ec);
187         }
188 
189         m_edited = false;
190         m_userEdited = false;
191     }
192 
193     static_cast<Element*>(node())->setFormControlValueMatchesRenderer(true);
194 }
195 
setUserEdited(bool isUserEdited)196 void RenderTextControl::setUserEdited(bool isUserEdited)
197 {
198     m_userEdited = isUserEdited;
199     document()->setIgnoreAutofocus(isUserEdited);
200 }
201 
selectionStart()202 int RenderTextControl::selectionStart()
203 {
204     Frame* frame = document()->frame();
205     if (!frame)
206         return 0;
207     return indexForVisiblePosition(frame->selection()->start());
208 }
209 
selectionEnd()210 int RenderTextControl::selectionEnd()
211 {
212     Frame* frame = document()->frame();
213     if (!frame)
214         return 0;
215     return indexForVisiblePosition(frame->selection()->end());
216 }
217 
setSelectionStart(int start)218 void RenderTextControl::setSelectionStart(int start)
219 {
220     setSelectionRange(start, max(start, selectionEnd()));
221 }
222 
setSelectionEnd(int end)223 void RenderTextControl::setSelectionEnd(int end)
224 {
225     setSelectionRange(min(end, selectionStart()), end);
226 }
227 
select()228 void RenderTextControl::select()
229 {
230     setSelectionRange(0, text().length());
231 }
232 
setSelectionRange(int start,int end)233 void RenderTextControl::setSelectionRange(int start, int end)
234 {
235     end = max(end, 0);
236     start = min(max(start, 0), end);
237 
238     ASSERT(!document()->childNeedsAndNotInStyleRecalc());
239 
240     if (style()->visibility() == HIDDEN || !m_innerText || !m_innerText->renderer() || !m_innerText->renderBox()->height()) {
241         cacheSelection(start, end);
242         return;
243     }
244     VisiblePosition startPosition = visiblePositionForIndex(start);
245     VisiblePosition endPosition;
246     if (start == end)
247         endPosition = startPosition;
248     else
249         endPosition = visiblePositionForIndex(end);
250 
251     // startPosition and endPosition can be null position for example when
252     // "-webkit-user-select: none" style attribute is specified.
253     if (startPosition.isNotNull() && endPosition.isNotNull()) {
254         ASSERT(startPosition.deepEquivalent().node()->shadowAncestorNode() == node() && endPosition.deepEquivalent().node()->shadowAncestorNode() == node());
255     }
256     VisibleSelection newSelection = VisibleSelection(startPosition, endPosition);
257 
258     if (Frame* frame = document()->frame())
259         frame->selection()->setSelection(newSelection);
260 
261     // FIXME: Granularity is stored separately on the frame, but also in the selection controller.
262     // The granularity in the selection controller should be used, and then this line of code would not be needed.
263     if (Frame* frame = document()->frame())
264         frame->setSelectionGranularity(CharacterGranularity);
265 }
266 
selection(int start,int end) const267 VisibleSelection RenderTextControl::selection(int start, int end) const
268 {
269     return VisibleSelection(VisiblePosition(m_innerText.get(), start, VP_DEFAULT_AFFINITY),
270                             VisiblePosition(m_innerText.get(), end, VP_DEFAULT_AFFINITY));
271 }
272 
visiblePositionForIndex(int index)273 VisiblePosition RenderTextControl::visiblePositionForIndex(int index)
274 {
275     if (index <= 0)
276         return VisiblePosition(m_innerText.get(), 0, DOWNSTREAM);
277     ExceptionCode ec = 0;
278     RefPtr<Range> range = Range::create(document());
279     range->selectNodeContents(m_innerText.get(), ec);
280     ASSERT(!ec);
281     CharacterIterator it(range.get());
282     it.advance(index - 1);
283     Node* endContainer = it.range()->endContainer(ec);
284     ASSERT(!ec);
285     int endOffset = it.range()->endOffset(ec);
286     ASSERT(!ec);
287     return VisiblePosition(endContainer, endOffset, UPSTREAM);
288 }
289 
indexForVisiblePosition(const VisiblePosition & pos)290 int RenderTextControl::indexForVisiblePosition(const VisiblePosition& pos)
291 {
292     Position indexPosition = pos.deepEquivalent();
293     if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != m_innerText)
294         return 0;
295     ExceptionCode ec = 0;
296     RefPtr<Range> range = Range::create(document());
297     range->setStart(m_innerText.get(), 0, ec);
298     ASSERT(!ec);
299     range->setEnd(indexPosition.node(), indexPosition.deprecatedEditingOffset(), ec);
300     ASSERT(!ec);
301     return TextIterator::rangeLength(range.get());
302 }
303 
subtreeHasChanged()304 void RenderTextControl::subtreeHasChanged()
305 {
306     m_edited = true;
307     m_userEdited = true;
308 }
309 
finishText(Vector<UChar> & result) const310 String RenderTextControl::finishText(Vector<UChar>& result) const
311 {
312     // ANDROID: This method was modified with a fix from WebKit r31081. This
313     // comment can be removed the next time we update.
314 
315     // Remove one trailing newline; there's always one that's collapsed out by rendering.
316     size_t size = result.size();
317     if (size && result[size - 1] == '\n')
318         result.shrink(--size);
319 
320     // Convert backslash to currency symbol.
321     document()->displayBufferModifiedByEncoding(result.data(), result.size());
322 
323     return String::adopt(result);
324 }
325 
text()326 String RenderTextControl::text()
327 {
328     if (!m_innerText)
329         return "";
330 
331     Vector<UChar> result;
332 
333     for (Node* n = m_innerText.get(); n; n = n->traverseNextNode(m_innerText.get())) {
334         if (n->hasTagName(brTag))
335             result.append(&newlineCharacter, 1);
336         else if (n->isTextNode()) {
337             String data = static_cast<Text*>(n)->data();
338             result.append(data.characters(), data.length());
339         }
340     }
341 
342     return finishText(result);
343 }
344 
getNextSoftBreak(RootInlineBox * & line,Node * & breakNode,unsigned & breakOffset)345 static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
346 {
347     RootInlineBox* next;
348     for (; line; line = next) {
349         next = line->nextRootBox();
350         if (next && !line->endsWithBreak()) {
351             ASSERT(line->lineBreakObj());
352             breakNode = line->lineBreakObj()->node();
353             breakOffset = line->lineBreakPos();
354             line = next;
355             return;
356         }
357     }
358     breakNode = 0;
359     breakOffset = 0;
360 }
361 
textWithHardLineBreaks()362 String RenderTextControl::textWithHardLineBreaks()
363 {
364     if (!m_innerText)
365         return "";
366     Node* firstChild = m_innerText->firstChild();
367     if (!firstChild)
368         return "";
369 
370     document()->updateLayout();
371 
372     RenderObject* renderer = firstChild->renderer();
373     if (!renderer)
374         return "";
375 
376     InlineBox* box = renderer->isText() ? toRenderText(renderer)->firstTextBox() : toRenderBox(renderer)->inlineBoxWrapper();
377     if (!box)
378         return "";
379 
380     Node* breakNode;
381     unsigned breakOffset;
382     RootInlineBox* line = box->root();
383     getNextSoftBreak(line, breakNode, breakOffset);
384 
385     Vector<UChar> result;
386 
387     for (Node* n = firstChild; n; n = n->traverseNextNode(m_innerText.get())) {
388         if (n->hasTagName(brTag))
389             result.append(&newlineCharacter, 1);
390         else if (n->isTextNode()) {
391             Text* text = static_cast<Text*>(n);
392             String data = text->data();
393             unsigned length = data.length();
394             unsigned position = 0;
395             while (breakNode == n && breakOffset <= length) {
396                 if (breakOffset > position) {
397                     result.append(data.characters() + position, breakOffset - position);
398                     position = breakOffset;
399                     result.append(&newlineCharacter, 1);
400                 }
401                 getNextSoftBreak(line, breakNode, breakOffset);
402             }
403             result.append(data.characters() + position, length - position);
404         }
405         while (breakNode == n)
406             getNextSoftBreak(line, breakNode, breakOffset);
407     }
408 
409     return finishText(result);
410 }
411 
scrollbarThickness() const412 int RenderTextControl::scrollbarThickness() const
413 {
414     // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
415     return ScrollbarTheme::nativeTheme()->scrollbarThickness();
416 }
417 
calcHeight()418 void RenderTextControl::calcHeight()
419 {
420     setHeight(m_innerText->renderBox()->borderTop() + m_innerText->renderBox()->borderBottom() +
421               m_innerText->renderBox()->paddingTop() + m_innerText->renderBox()->paddingBottom() +
422               m_innerText->renderBox()->marginTop() + m_innerText->renderBox()->marginBottom());
423 
424     adjustControlHeightBasedOnLineHeight(m_innerText->renderer()->lineHeight(true, true));
425     setHeight(height() + paddingTop() + paddingBottom() + borderTop() + borderBottom());
426 
427     // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
428     if (style()->overflowX() == OSCROLL ||  (style()->overflowX() == OAUTO && m_innerText->renderer()->style()->wordWrap() == NormalWordWrap))
429         setHeight(height() + scrollbarThickness());
430 
431     RenderBlock::calcHeight();
432 }
433 
hitInnerTextElement(HitTestResult & result,int xPos,int yPos,int tx,int ty)434 void RenderTextControl::hitInnerTextElement(HitTestResult& result, int xPos, int yPos, int tx, int ty)
435 {
436     result.setInnerNode(m_innerText.get());
437     result.setInnerNonSharedNode(m_innerText.get());
438     result.setLocalPoint(IntPoint(xPos - tx - x() - m_innerText->renderBox()->x(),
439                                   yPos - ty - y() - m_innerText->renderBox()->y()));
440 }
441 
forwardEvent(Event * event)442 void RenderTextControl::forwardEvent(Event* event)
443 {
444     if (event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)
445         return;
446     m_innerText->defaultEventHandler(event);
447 }
448 
controlClipRect(int tx,int ty) const449 IntRect RenderTextControl::controlClipRect(int tx, int ty) const
450 {
451     IntRect clipRect = contentBoxRect();
452     clipRect.move(tx, ty);
453     return clipRect;
454 }
455 
calcPrefWidths()456 void RenderTextControl::calcPrefWidths()
457 {
458     ASSERT(prefWidthsDirty());
459 
460     m_minPrefWidth = 0;
461     m_maxPrefWidth = 0;
462 
463     if (style()->width().isFixed() && style()->width().value() > 0)
464         m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value());
465     else {
466         // Use average character width. Matches IE.
467         float charWidth = style()->font().primaryFont()->avgCharWidth();
468         m_maxPrefWidth = preferredContentWidth(charWidth) + m_innerText->renderBox()->paddingLeft() + m_innerText->renderBox()->paddingRight();
469     }
470 
471     if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
472         m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
473         m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
474     } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
475         m_minPrefWidth = 0;
476     else
477         m_minPrefWidth = m_maxPrefWidth;
478 
479     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
480         m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
481         m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
482     }
483 
484     int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight();
485 
486     m_minPrefWidth += toAdd;
487     m_maxPrefWidth += toAdd;
488 
489     // FIXME: This causes cnn.com loading way slow. Comment it out for now
490 //#ifdef ANDROID_LAYOUT
491 #if 0
492     Frame* frame = document()->frame();
493     if (frame && frame->settings()->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) {
494         int maxWidth = frame->view()->visibleWidth() - 2 * ANDROID_FCTS_MARGIN_PADDING;
495         if (maxWidth > 0 && maxWidth < m_minPrefWidth)
496             m_minPrefWidth = maxWidth;
497         if (maxWidth > 0 && maxWidth < m_maxPrefWidth)
498             m_maxPrefWidth = maxWidth;
499     }
500 #endif
501     setPrefWidthsDirty(false);
502 }
503 
selectionChanged(bool userTriggered)504 void RenderTextControl::selectionChanged(bool userTriggered)
505 {
506     cacheSelection(selectionStart(), selectionEnd());
507 
508     if (Frame* frame = document()->frame()) {
509         if (frame->selection()->isRange() && userTriggered)
510             node()->dispatchEvent(eventNames().selectEvent, true, false);
511     }
512 }
513 
addFocusRingRects(GraphicsContext * graphicsContext,int tx,int ty)514 void RenderTextControl::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty)
515 {
516     graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height()));
517 }
518 
innerTextElement() const519 HTMLElement* RenderTextControl::innerTextElement() const
520 {
521     return m_innerText.get();
522 }
523 
524 } // namespace WebCore
525