blob: a762ac54999fb08290ff8b4ba1b46965268fa7c9 [file] [log] [blame]
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +00001/**
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 "RenderTextControlSingleLine.h"
24
25#include "CSSStyleSelector.h"
26#include "Event.h"
27#include "EventNames.h"
28#include "Frame.h"
29#include "FrameView.h"
30#include "HitTestResult.h"
31#include "HTMLInputElement.h"
32#include "HTMLNames.h"
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +000033#include "InputElement.h"
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +000034#include "LocalizedStrings.h"
35#include "MouseEvent.h"
36#include "PlatformKeyboardEvent.h"
37#include "RenderScrollbar.h"
38#include "RenderTheme.h"
39#include "SearchPopupMenu.h"
40#include "SelectionController.h"
41#include "Settings.h"
ojan@chromium.org149606a2009-04-29 20:31:53 +000042#include "SimpleFontData.h"
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +000043#include "TextControlInnerElements.h"
44
zimmermann@webkit.orgd5c12892008-12-29 00:16:19 +000045using namespace std;
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +000046
47namespace WebCore {
48
49using namespace HTMLNames;
50
eric@webkit.orge6cd79c2009-09-26 18:56:04 +000051RenderTextControlSingleLine::RenderTextControlSingleLine(Node* node, bool placeholderVisible)
52 : RenderTextControl(node, placeholderVisible)
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +000053 , m_searchPopupIsVisible(false)
54 , m_shouldDrawCapsLockIndicator(false)
55 , m_searchEventTimer(this, &RenderTextControlSingleLine::searchEventTimerFired)
56 , m_searchPopup(0)
57{
58}
59
60RenderTextControlSingleLine::~RenderTextControlSingleLine()
61{
62 if (m_searchPopup) {
63 m_searchPopup->disconnectClient();
64 m_searchPopup = 0;
65 }
66
67 if (m_innerBlock)
68 m_innerBlock->detach();
69}
70
eric@webkit.orgb05364f2009-08-24 08:28:51 +000071RenderStyle* RenderTextControlSingleLine::textBaseStyle() const
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +000072{
eric@webkit.orgb05364f2009-08-24 08:28:51 +000073 return m_innerBlock ? m_innerBlock->renderer()->style() : style();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +000074}
75
76void RenderTextControlSingleLine::addSearchResult()
77{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +000078 ASSERT(node()->isHTMLElement());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +000079 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
80 if (input->maxResults() <= 0)
81 return;
82
83 String value = input->value();
84 if (value.isEmpty())
85 return;
86
87 Settings* settings = document()->settings();
88 if (!settings || settings->privateBrowsingEnabled())
89 return;
90
91 int size = static_cast<int>(m_recentSearches.size());
92 for (int i = size - 1; i >= 0; --i) {
93 if (m_recentSearches[i] == value)
94 m_recentSearches.remove(i);
95 }
96
97 m_recentSearches.insert(0, value);
98 while (static_cast<int>(m_recentSearches.size()) > input->maxResults())
99 m_recentSearches.removeLast();
100
101 const AtomicString& name = autosaveName();
102 if (!m_searchPopup)
103 m_searchPopup = SearchPopupMenu::create(this);
104
105 m_searchPopup->saveRecentSearches(name, m_recentSearches);
106}
107
108void RenderTextControlSingleLine::stopSearchEventTimer()
109{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000110 ASSERT(node()->isHTMLElement());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000111 m_searchEventTimer.stop();
112}
113
114void RenderTextControlSingleLine::showPopup()
115{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000116 ASSERT(node()->isHTMLElement());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000117 if (m_searchPopupIsVisible)
118 return;
119
120 if (!m_searchPopup)
121 m_searchPopup = SearchPopupMenu::create(this);
122
123 if (!m_searchPopup->enabled())
124 return;
125
126 m_searchPopupIsVisible = true;
127
128 const AtomicString& name = autosaveName();
129 m_searchPopup->loadRecentSearches(name, m_recentSearches);
130
131 // Trim the recent searches list if the maximum size has changed since we last saved.
132 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
133 if (static_cast<int>(m_recentSearches.size()) > input->maxResults()) {
134 do {
135 m_recentSearches.removeLast();
136 } while (static_cast<int>(m_recentSearches.size()) > input->maxResults());
137
138 m_searchPopup->saveRecentSearches(name, m_recentSearches);
139 }
140
141 m_searchPopup->show(absoluteBoundingBoxRect(true), document()->view(), -1);
142}
143
144void RenderTextControlSingleLine::hidePopup()
145{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000146 ASSERT(node()->isHTMLElement());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000147 if (m_searchPopup)
148 m_searchPopup->hide();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000149}
150
151void RenderTextControlSingleLine::subtreeHasChanged()
152{
eric@webkit.org6e55fa22009-11-18 23:46:06 +0000153 bool wasChanged = wasChangedSinceLastChangeEvent();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000154 RenderTextControl::subtreeHasChanged();
155
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000156 InputElement* input = inputElement();
eric@webkit.orge2353172009-09-17 02:13:49 +0000157 // We don't need to call sanitizeUserInputValue() function here because
158 // InputElement::handleBeforeTextInsertedEvent() has already called
159 // sanitizeUserInputValue().
160 // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent.
161 input->setValueFromRenderer(input->sanitizeValue(text()));
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000162
simon.fraser@apple.comf71e81d2009-03-11 19:00:13 +0000163 if (m_cancelButton)
164 updateCancelButtonVisibility();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000165
166 // If the incremental attribute is set, then dispatch the search event
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000167 if (input->searchEventsShouldBeDispatched())
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000168 startSearchEventTimer();
169
eric@webkit.org6e55fa22009-11-18 23:46:06 +0000170 if (!wasChanged && node()->focused()) {
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000171 if (Frame* frame = document()->frame())
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000172 frame->textFieldDidBeginEditing(static_cast<Element*>(node()));
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000173 }
174
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000175 if (node()->focused()) {
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000176 if (Frame* frame = document()->frame())
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000177 frame->textDidChangeInTextField(static_cast<Element*>(node()));
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000178 }
179}
180
181void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, int tx, int ty)
182{
183 RenderTextControl::paint(paintInfo, tx, ty);
184
185 if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
hyatt@apple.comd885df72009-01-22 02:31:52 +0000186 IntRect contentsRect = contentBoxRect();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000187
188 // Convert the rect into the coords used for painting the content
hyatt@apple.comd885df72009-01-22 02:31:52 +0000189 contentsRect.move(tx + x(), ty + y());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000190 theme()->paintCapsLockIndicator(this, paintInfo, contentsRect);
191 }
192}
193
194void RenderTextControlSingleLine::layout()
195{
hyatt@apple.comd885df72009-01-22 02:31:52 +0000196 int oldHeight = height();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000197 calcHeight();
198
hyatt@apple.comd885df72009-01-22 02:31:52 +0000199 int oldWidth = width();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000200 calcWidth();
201
hyatt@apple.comd885df72009-01-22 02:31:52 +0000202 bool relayoutChildren = oldHeight != height() || oldWidth != width();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000203
hyatt@apple.comd885df72009-01-22 02:31:52 +0000204 RenderBox* innerTextRenderer = innerTextElement()->renderBox();
205 RenderBox* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderBox() : 0;
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000206
207 // Set the text block height
208 int desiredHeight = textBlockHeight();
209 int currentHeight = innerTextRenderer->height();
210
mitz@apple.comae136e52009-06-18 17:54:16 +0000211 if (currentHeight > height()) {
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000212 if (desiredHeight != currentHeight)
213 relayoutChildren = true;
214 innerTextRenderer->style()->setHeight(Length(desiredHeight, Fixed));
mitz@apple.comae136e52009-06-18 17:54:16 +0000215 if (m_innerBlock)
216 innerBlockRenderer->style()->setHeight(Length(desiredHeight, Fixed));
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000217 }
218
219 // Set the text block width
220 int desiredWidth = textBlockWidth();
221 if (desiredWidth != innerTextRenderer->width())
222 relayoutChildren = true;
223 innerTextRenderer->style()->setWidth(Length(desiredWidth, Fixed));
224
225 if (m_innerBlock) {
hyatt@apple.comd885df72009-01-22 02:31:52 +0000226 int innerBlockWidth = width() - paddingLeft() - paddingRight() - borderLeft() - borderRight();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000227 if (innerBlockWidth != innerBlockRenderer->width())
228 relayoutChildren = true;
229 innerBlockRenderer->style()->setWidth(Length(innerBlockWidth, Fixed));
230 }
231
232 RenderBlock::layoutBlock(relayoutChildren);
233
mitz@apple.comae136e52009-06-18 17:54:16 +0000234 // Center the child block vertically
235 RenderBox* childBlock = innerBlockRenderer ? innerBlockRenderer : innerTextRenderer;
236 currentHeight = childBlock->height();
237 if (currentHeight < height())
238 childBlock->setLocation(childBlock->x(), (height() - currentHeight) / 2);
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000239}
240
hyatt@apple.comd885df72009-01-22 02:31:52 +0000241bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction hitTestAction)
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000242{
243 // 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
244 // was on the control but not on the inner element (see Radar 4617841).
245
246 // 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,
247 // and act as if we've hit the close block if we're to the right of the inner text block.
248
hyatt@apple.comd885df72009-01-22 02:31:52 +0000249 if (!RenderTextControl::nodeAtPoint(request, result, xPos, yPos, tx, ty, hitTestAction))
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000250 return false;
251
simon.fraser@apple.com68ed99e2009-03-11 05:43:58 +0000252 // If we hit a node inside the inner text element, say that we hit that element,
253 // and if we hit our node (e.g. we're over the border or padding), also say that we hit the
254 // inner text element so that it gains focus.
255 if (result.innerNode()->isDescendantOf(innerTextElement()) || result.innerNode() == node())
256 hitInnerTextElement(result, xPos, yPos, tx, ty);
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000257
simon.fraser@apple.com68ed99e2009-03-11 05:43:58 +0000258 // If we're not a search field, or we already found the results or cancel buttons, we're done.
259 if (!m_innerBlock || result.innerNode() == m_resultsButton || result.innerNode() == m_cancelButton)
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000260 return true;
261
262 Node* innerNode = 0;
hyatt@apple.comd885df72009-01-22 02:31:52 +0000263 RenderBox* innerBlockRenderer = m_innerBlock->renderBox();
264 RenderBox* innerTextRenderer = innerTextElement()->renderBox();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000265
266 IntPoint localPoint = result.localPoint();
hyatt@apple.comd885df72009-01-22 02:31:52 +0000267 localPoint.move(-innerBlockRenderer->x(), -innerBlockRenderer->y());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000268
hyatt@apple.comd885df72009-01-22 02:31:52 +0000269 int textLeft = tx + x() + innerBlockRenderer->x() + innerTextRenderer->x();
270 if (m_resultsButton && m_resultsButton->renderer() && xPos < textLeft)
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000271 innerNode = m_resultsButton.get();
272
273 if (!innerNode) {
274 int textRight = textLeft + innerTextRenderer->width();
hyatt@apple.comd885df72009-01-22 02:31:52 +0000275 if (m_cancelButton && m_cancelButton->renderer() && xPos > textRight)
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000276 innerNode = m_cancelButton.get();
277 }
278
279 if (innerNode) {
280 result.setInnerNode(innerNode);
hyatt@apple.comd885df72009-01-22 02:31:52 +0000281 localPoint.move(-innerNode->renderBox()->x(), -innerNode->renderBox()->y());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000282 }
283
284 result.setLocalPoint(localPoint);
285 return true;
286}
287
288void RenderTextControlSingleLine::forwardEvent(Event* event)
289{
hyatt@apple.comd885df72009-01-22 02:31:52 +0000290 RenderBox* innerTextRenderer = innerTextElement()->renderBox();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000291
292 if (event->type() == eventNames().blurEvent) {
293 if (innerTextRenderer) {
294 if (RenderLayer* innerLayer = innerTextRenderer->layer())
295 innerLayer->scrollToOffset(style()->direction() == RTL ? innerLayer->scrollWidth() : 0, 0);
296 }
297
298 capsLockStateMayHaveChanged();
299 } else if (event->type() == eventNames().focusEvent)
300 capsLockStateMayHaveChanged();
301
302 if (!event->isMouseEvent()) {
303 RenderTextControl::forwardEvent(event);
304 return;
305 }
306
simon.fraser@apple.com032683d2009-03-22 17:35:38 +0000307 FloatPoint localPoint = innerTextRenderer->absoluteToLocal(static_cast<MouseEvent*>(event)->absoluteLocation(), false, true);
hyatt@apple.comd885df72009-01-22 02:31:52 +0000308 if (m_resultsButton && localPoint.x() < innerTextRenderer->borderBoxRect().x())
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000309 m_resultsButton->defaultEventHandler(event);
hyatt@apple.comd885df72009-01-22 02:31:52 +0000310 else if (m_cancelButton && localPoint.x() > innerTextRenderer->borderBoxRect().right())
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000311 m_cancelButton->defaultEventHandler(event);
312 else
313 RenderTextControl::forwardEvent(event);
314}
315
weinig@apple.com1a57c822009-02-04 22:27:50 +0000316void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000317{
318 RenderTextControl::styleDidChange(diff, oldStyle);
319
320 if (RenderObject* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderer() : 0) {
321 // We may have set the width and the height in the old style in layout().
322 // Reset them now to avoid getting a spurious layout hint.
323 innerBlockRenderer->style()->setHeight(Length());
324 innerBlockRenderer->style()->setWidth(Length());
325 innerBlockRenderer->setStyle(createInnerBlockStyle(style()));
326 }
327
328 if (RenderObject* resultsRenderer = m_resultsButton ? m_resultsButton->renderer() : 0)
329 resultsRenderer->setStyle(createResultsButtonStyle(style()));
330
331 if (RenderObject* cancelRenderer = m_cancelButton ? m_cancelButton->renderer() : 0)
332 cancelRenderer->setStyle(createCancelButtonStyle(style()));
eric@webkit.orgd2bb5a02009-03-17 23:44:11 +0000333
334 setHasOverflowClip(false);
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000335}
336
337void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
338{
339 if (!node() || !document())
340 return;
341
342 // Only draw the caps lock indicator if these things are true:
343 // 1) The field is a password field
344 // 2) The frame is active
345 // 3) The element is focused
346 // 4) The caps lock is on
347 bool shouldDrawCapsLockIndicator = false;
348
349 if (Frame* frame = document()->frame())
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000350 shouldDrawCapsLockIndicator = inputElement()->isPasswordField()
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000351 && frame->selection()->isFocusedAndActive()
352 && document()->focusedNode() == node()
353 && PlatformKeyboardEvent::currentCapsLockState();
354
355 if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
356 m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
357 repaint();
358 }
359}
360
361int RenderTextControlSingleLine::textBlockWidth() const
362{
363 int width = RenderTextControl::textBlockWidth();
364
hyatt@apple.comd885df72009-01-22 02:31:52 +0000365 if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) {
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000366 resultsRenderer->calcWidth();
adele@apple.comf32f2672009-01-15 02:05:25 +0000367 width -= resultsRenderer->width() + resultsRenderer->marginLeft() + resultsRenderer->marginRight();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000368 }
369
hyatt@apple.comd885df72009-01-22 02:31:52 +0000370 if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) {
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000371 cancelRenderer->calcWidth();
adele@apple.comf32f2672009-01-15 02:05:25 +0000372 width -= cancelRenderer->width() + cancelRenderer->marginLeft() + cancelRenderer->marginRight();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000373 }
374
375 return width;
376}
ojan@chromium.org68b773d2010-02-13 03:20:20 +0000377
378float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family)
379{
380 // Since Lucida Grande is the default font, we want this to match the width
381 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
382 // IE for some encodings (in IE, the default font is encoding specific).
383 // 901 is the avgCharWidth value in the OS/2 table for MS Shell Dlg.
384 if (family == AtomicString("Lucida Grande"))
385 return scaleEmToUnits(901);
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000386
ojan@chromium.org68b773d2010-02-13 03:20:20 +0000387 return RenderTextControl::getAvgCharWidth(family);
388}
389
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000390int RenderTextControlSingleLine::preferredContentWidth(float charWidth) const
391{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000392 int factor = inputElement()->size();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000393 if (factor <= 0)
394 factor = 20;
395
396 int result = static_cast<int>(ceilf(charWidth * factor));
397
ojan@chromium.org68b773d2010-02-13 03:20:20 +0000398 float maxCharWidth = 0.f;
399 AtomicString family = style()->font().family().family();
400 // Since Lucida Grande is the default font, we want this to match the width
401 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
402 // IE for some encodings (in IE, the default font is encoding specific).
403 // 4027 is the (xMax - xMin) value in the "head" font table for MS Shell Dlg.
404 if (family == AtomicString("Lucida Grande"))
405 maxCharWidth = scaleEmToUnits(4027);
406 else if (hasValidAvgCharWidth(family))
407 maxCharWidth = roundf(style()->font().primaryFont()->maxCharWidth());
408
ojan@chromium.org149606a2009-04-29 20:31:53 +0000409 // For text inputs, IE adds some extra width.
ojan@chromium.org68b773d2010-02-13 03:20:20 +0000410 if (maxCharWidth > 0.f)
411 result += maxCharWidth - charWidth;
ojan@chromium.org149606a2009-04-29 20:31:53 +0000412
hyatt@apple.com55fe6bd2009-01-23 07:14:49 +0000413 if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0)
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000414 result += resultsRenderer->borderLeft() + resultsRenderer->borderRight() +
415 resultsRenderer->paddingLeft() + resultsRenderer->paddingRight();
416
hyatt@apple.com55fe6bd2009-01-23 07:14:49 +0000417 if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0)
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000418 result += cancelRenderer->borderLeft() + cancelRenderer->borderRight() +
419 cancelRenderer->paddingLeft() + cancelRenderer->paddingRight();
420
421 return result;
422}
423
424void RenderTextControlSingleLine::adjustControlHeightBasedOnLineHeight(int lineHeight)
425{
hyatt@apple.comd885df72009-01-22 02:31:52 +0000426 if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) {
hyatt@apple.com801ed3f2009-01-30 18:16:03 +0000427 toRenderBlock(resultsRenderer)->calcHeight();
hyatt@apple.comd885df72009-01-22 02:31:52 +0000428 setHeight(max(height(),
429 resultsRenderer->borderTop() + resultsRenderer->borderBottom() +
430 resultsRenderer->paddingTop() + resultsRenderer->paddingBottom() +
431 resultsRenderer->marginTop() + resultsRenderer->marginBottom()));
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000432 lineHeight = max(lineHeight, resultsRenderer->height());
433 }
434
hyatt@apple.comd885df72009-01-22 02:31:52 +0000435 if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) {
hyatt@apple.com801ed3f2009-01-30 18:16:03 +0000436 toRenderBlock(cancelRenderer)->calcHeight();
hyatt@apple.comd885df72009-01-22 02:31:52 +0000437 setHeight(max(height(),
438 cancelRenderer->borderTop() + cancelRenderer->borderBottom() +
439 cancelRenderer->paddingTop() + cancelRenderer->paddingBottom() +
440 cancelRenderer->marginTop() + cancelRenderer->marginBottom()));
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000441 lineHeight = max(lineHeight, cancelRenderer->height());
442 }
443
hyatt@apple.comd885df72009-01-22 02:31:52 +0000444 setHeight(height() + lineHeight);
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000445}
446
447void RenderTextControlSingleLine::createSubtreeIfNeeded()
448{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000449 if (!inputElement()->isSearchField()) {
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000450 RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get());
451 return;
452 }
453
454 if (!m_innerBlock) {
455 // Create the inner block element
456 m_innerBlock = new TextControlInnerElement(document(), node());
457 m_innerBlock->attachInnerElement(node(), createInnerBlockStyle(style()), renderArena());
458 }
459
460 if (!m_resultsButton) {
461 // Create the search results button element
462 m_resultsButton = new SearchFieldResultsButtonElement(document());
463 m_resultsButton->attachInnerElement(m_innerBlock.get(), createResultsButtonStyle(m_innerBlock->renderer()->style()), renderArena());
464 }
465
466 // Create innerText element before adding the cancel button
467 RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get());
468
469 if (!m_cancelButton) {
470 // Create the cancel button element
471 m_cancelButton = new SearchFieldCancelButtonElement(document());
472 m_cancelButton->attachInnerElement(m_innerBlock.get(), createCancelButtonStyle(m_innerBlock->renderer()->style()), renderArena());
473 }
474}
475
476void RenderTextControlSingleLine::updateFromElement()
477{
478 createSubtreeIfNeeded();
479 RenderTextControl::updateFromElement();
480
simon.fraser@apple.comf71e81d2009-03-11 19:00:13 +0000481 if (m_cancelButton)
482 updateCancelButtonVisibility();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000483
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000484 if (m_placeholderVisible) {
485 ExceptionCode ec = 0;
eric@webkit.orge6cd79c2009-09-26 18:56:04 +0000486 innerTextElement()->setInnerText(static_cast<Element*>(node())->getAttribute(placeholderAttr), ec);
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000487 ASSERT(!ec);
eric@webkit.orgb6e62112009-12-16 18:43:27 +0000488 } else {
489 if (!inputElement()->suggestedValue().isNull())
490 setInnerTextValue(inputElement()->suggestedValue());
491 else
492 setInnerTextValue(inputElement()->value());
493 }
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000494
495 if (m_searchPopupIsVisible)
496 m_searchPopup->updateFromElement();
497}
498
499void RenderTextControlSingleLine::cacheSelection(int start, int end)
500{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000501 inputElement()->cacheSelection(start, end);
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000502}
503
504PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const
505{
506 RefPtr<RenderStyle> textBlockStyle;
eric@webkit.orgb05364f2009-08-24 08:28:51 +0000507 if (m_placeholderVisible) {
mitz@apple.com0b86b492009-05-28 05:25:53 +0000508 if (RenderStyle* pseudoStyle = getCachedPseudoStyle(INPUT_PLACEHOLDER))
509 textBlockStyle = RenderStyle::clone(pseudoStyle);
510 }
511 if (!textBlockStyle) {
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000512 textBlockStyle = RenderStyle::create();
513 textBlockStyle->inheritFrom(startStyle);
514 }
515
516 adjustInnerTextStyle(startStyle, textBlockStyle.get());
517
518 textBlockStyle->setWhiteSpace(PRE);
519 textBlockStyle->setWordWrap(NormalWordWrap);
520 textBlockStyle->setOverflowX(OHIDDEN);
521 textBlockStyle->setOverflowY(OHIDDEN);
522
523 // Do not allow line-height to be smaller than our default.
524 if (textBlockStyle->font().lineSpacing() > lineHeight(true, true))
525 textBlockStyle->setLineHeight(Length(-100.0f, Percent));
526
527 textBlockStyle->setDisplay(m_innerBlock ? INLINE_BLOCK : BLOCK);
528
529 // We're adding one extra pixel of padding to match WinIE.
530 textBlockStyle->setPaddingLeft(Length(1, Fixed));
531 textBlockStyle->setPaddingRight(Length(1, Fixed));
532
533 // When the placeholder is going to be displayed, temporarily override the text security to be "none".
534 // After this, updateFromElement will immediately update the text displayed.
535 // When the placeholder is no longer visible, updatePlaceholderVisiblity will reset the style,
536 // and the text security mode will be set back to the computed value correctly.
eric@webkit.orgb05364f2009-08-24 08:28:51 +0000537 if (m_placeholderVisible)
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000538 textBlockStyle->setTextSecurity(TSNONE);
539
540 return textBlockStyle.release();
541}
542
543PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerBlockStyle(const RenderStyle* startStyle) const
544{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000545 ASSERT(node()->isHTMLElement());
546
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000547 RefPtr<RenderStyle> innerBlockStyle = RenderStyle::create();
548 innerBlockStyle->inheritFrom(startStyle);
549
550 innerBlockStyle->setDisplay(BLOCK);
551 innerBlockStyle->setDirection(LTR);
552
553 // 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.
554 innerBlockStyle->setUserModify(READ_ONLY);
555
556 return innerBlockStyle.release();
557}
558
559PassRefPtr<RenderStyle> RenderTextControlSingleLine::createResultsButtonStyle(const RenderStyle* startStyle) const
560{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000561 ASSERT(node()->isHTMLElement());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000562 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
563
564 RefPtr<RenderStyle> resultsBlockStyle;
565 if (input->maxResults() < 0)
weinig@apple.comedad01d2009-02-04 21:02:02 +0000566 resultsBlockStyle = getCachedPseudoStyle(SEARCH_DECORATION);
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000567 else if (!input->maxResults())
weinig@apple.comedad01d2009-02-04 21:02:02 +0000568 resultsBlockStyle = getCachedPseudoStyle(SEARCH_RESULTS_DECORATION);
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000569 else
weinig@apple.comedad01d2009-02-04 21:02:02 +0000570 resultsBlockStyle = getCachedPseudoStyle(SEARCH_RESULTS_BUTTON);
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000571
572 if (!resultsBlockStyle)
573 resultsBlockStyle = RenderStyle::create();
574
575 if (startStyle)
576 resultsBlockStyle->inheritFrom(startStyle);
577
578 return resultsBlockStyle.release();
579}
580
581PassRefPtr<RenderStyle> RenderTextControlSingleLine::createCancelButtonStyle(const RenderStyle* startStyle) const
582{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000583 ASSERT(node()->isHTMLElement());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000584 RefPtr<RenderStyle> cancelBlockStyle;
585
weinig@apple.comedad01d2009-02-04 21:02:02 +0000586 if (RefPtr<RenderStyle> pseudoStyle = getCachedPseudoStyle(SEARCH_CANCEL_BUTTON))
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000587 // We may be sharing style with another search field, but we must not share the cancel button style.
588 cancelBlockStyle = RenderStyle::clone(pseudoStyle.get());
589 else
590 cancelBlockStyle = RenderStyle::create();
591
592 if (startStyle)
593 cancelBlockStyle->inheritFrom(startStyle);
594
simon.fraser@apple.comf71e81d2009-03-11 19:00:13 +0000595 cancelBlockStyle->setVisibility(visibilityForCancelButton());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000596 return cancelBlockStyle.release();
597}
598
simon.fraser@apple.comf71e81d2009-03-11 19:00:13 +0000599void RenderTextControlSingleLine::updateCancelButtonVisibility() const
600{
601 if (!m_cancelButton->renderer())
602 return;
603
604 const RenderStyle* curStyle = m_cancelButton->renderer()->style();
605 EVisibility buttonVisibility = visibilityForCancelButton();
606 if (curStyle->visibility() == buttonVisibility)
607 return;
608
609 RefPtr<RenderStyle> cancelButtonStyle = RenderStyle::clone(curStyle);
610 cancelButtonStyle->setVisibility(buttonVisibility);
611 m_cancelButton->renderer()->setStyle(cancelButtonStyle);
612}
613
614EVisibility RenderTextControlSingleLine::visibilityForCancelButton() const
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000615{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000616 ASSERT(node()->isHTMLElement());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000617 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
simon.fraser@apple.comf71e81d2009-03-11 19:00:13 +0000618 return input->value().isEmpty() ? HIDDEN : VISIBLE;
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000619}
620
621const AtomicString& RenderTextControlSingleLine::autosaveName() const
622{
623 return static_cast<Element*>(node())->getAttribute(autosaveAttr);
624}
625
626void RenderTextControlSingleLine::startSearchEventTimer()
627{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000628 ASSERT(node()->isHTMLElement());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000629 unsigned length = text().length();
630
631 // If there's no text, fire the event right away.
632 if (!length) {
633 stopSearchEventTimer();
634 static_cast<HTMLInputElement*>(node())->onSearch();
635 return;
636 }
637
638 // After typing the first key, we wait 0.5 seconds.
639 // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on.
640 m_searchEventTimer.startOneShot(max(0.2, 0.6 - 0.1 * length));
641}
642
643void RenderTextControlSingleLine::searchEventTimerFired(Timer<RenderTextControlSingleLine>*)
644{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000645 ASSERT(node()->isHTMLElement());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000646 static_cast<HTMLInputElement*>(node())->onSearch();
647}
648
649// PopupMenuClient methods
650void RenderTextControlSingleLine::valueChanged(unsigned listIndex, bool fireEvents)
651{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000652 ASSERT(node()->isHTMLElement());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000653 ASSERT(static_cast<int>(listIndex) < listSize());
654 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
655 if (static_cast<int>(listIndex) == (listSize() - 1)) {
656 if (fireEvents) {
657 m_recentSearches.clear();
658 const AtomicString& name = autosaveName();
659 if (!name.isEmpty()) {
660 if (!m_searchPopup)
661 m_searchPopup = SearchPopupMenu::create(this);
662 m_searchPopup->saveRecentSearches(name, m_recentSearches);
663 }
664 }
665 } else {
666 input->setValue(itemText(listIndex));
667 if (fireEvents)
668 input->onSearch();
669 input->select();
670 }
671}
672
673String RenderTextControlSingleLine::itemText(unsigned listIndex) const
674{
675 int size = listSize();
676 if (size == 1) {
677 ASSERT(!listIndex);
678 return searchMenuNoRecentSearchesText();
679 }
680 if (!listIndex)
681 return searchMenuRecentSearchesText();
682 if (itemIsSeparator(listIndex))
683 return String();
684 if (static_cast<int>(listIndex) == (size - 1))
685 return searchMenuClearRecentSearchesText();
686 return m_recentSearches[listIndex - 1];
687}
688
689bool RenderTextControlSingleLine::itemIsEnabled(unsigned listIndex) const
690{
691 if (!listIndex || itemIsSeparator(listIndex))
692 return false;
693 return true;
694}
695
darin@apple.com98a7ac62009-01-05 17:26:53 +0000696PopupMenuStyle RenderTextControlSingleLine::itemStyle(unsigned) const
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000697{
698 return menuStyle();
699}
700
701PopupMenuStyle RenderTextControlSingleLine::menuStyle() const
702{
adachan@apple.com2ad65132009-03-23 23:46:27 +0000703 return PopupMenuStyle(style()->color(), style()->backgroundColor(), style()->font(), style()->visibility() == VISIBLE, style()->textIndent(), style()->direction());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000704}
705
706int RenderTextControlSingleLine::clientInsetLeft() const
707{
708 // Inset the menu by the radius of the cap on the left so that
709 // it only runs along the straight part of the bezel.
710 return height() / 2;
711}
712
713int RenderTextControlSingleLine::clientInsetRight() const
714{
715 // Inset the menu by the radius of the cap on the right so that
716 // it only runs along the straight part of the bezel (unless it needs
717 // to be wider).
718 return height() / 2;
719}
720
721int RenderTextControlSingleLine::clientPaddingLeft() const
722{
adele@apple.com524122c2009-01-10 01:12:04 +0000723 int padding = paddingLeft();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000724
hyatt@apple.comd885df72009-01-22 02:31:52 +0000725 if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0)
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000726 padding += resultsRenderer->width();
727
728 return padding;
729}
730
731int RenderTextControlSingleLine::clientPaddingRight() const
732{
adele@apple.com524122c2009-01-10 01:12:04 +0000733 int padding = paddingRight();
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000734
hyatt@apple.comd885df72009-01-22 02:31:52 +0000735 if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0)
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000736 padding += cancelRenderer->width();
737
738 return padding;
739}
740
741int RenderTextControlSingleLine::listSize() const
742{
743 // If there are no recent searches, then our menu will have 1 "No recent searches" item.
744 if (!m_recentSearches.size())
745 return 1;
746 // Otherwise, leave room in the menu for a header, a separator, and the "Clear recent searches" item.
747 return m_recentSearches.size() + 3;
748}
749
750int RenderTextControlSingleLine::selectedIndex() const
751{
752 return -1;
753}
754
pkasting@chromium.org43ee8102010-01-22 22:05:40 +0000755void RenderTextControlSingleLine::popupDidHide()
andersca@apple.comf3f30062009-09-14 22:14:13 +0000756{
757 m_searchPopupIsVisible = false;
758}
759
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000760bool RenderTextControlSingleLine::itemIsSeparator(unsigned listIndex) const
761{
ddkilzer@apple.comdfdc3fd2009-07-08 13:55:55 +0000762 // The separator will be the second to last item in our list.
763 return static_cast<int>(listIndex) == (listSize() - 2);
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000764}
765
766bool RenderTextControlSingleLine::itemIsLabel(unsigned listIndex) const
767{
768 return listIndex == 0;
769}
770
771bool RenderTextControlSingleLine::itemIsSelected(unsigned) const
772{
773 return false;
774}
775
776void RenderTextControlSingleLine::setTextFromItem(unsigned listIndex)
777{
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000778 ASSERT(node()->isHTMLElement());
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000779 static_cast<HTMLInputElement*>(node())->setValue(itemText(listIndex));
780}
781
782FontSelector* RenderTextControlSingleLine::fontSelector() const
783{
784 return document()->styleSelector()->fontSelector();
785}
786
787HostWindow* RenderTextControlSingleLine::hostWindow() const
788{
789 return document()->view()->hostWindow();
790}
791
eric@webkit.orgd2bb5a02009-03-17 23:44:11 +0000792void RenderTextControlSingleLine::autoscroll()
793{
794 RenderLayer* layer = innerTextElement()->renderBox()->layer();
795 if (layer)
796 layer->autoscroll();
797}
798
799int RenderTextControlSingleLine::scrollWidth() const
800{
801 if (innerTextElement())
802 return innerTextElement()->scrollWidth();
803 return RenderBlock::scrollWidth();
804}
805
806int RenderTextControlSingleLine::scrollHeight() const
807{
808 if (innerTextElement())
809 return innerTextElement()->scrollHeight();
810 return RenderBlock::scrollHeight();
811}
812
813int RenderTextControlSingleLine::scrollLeft() const
814{
815 if (innerTextElement())
816 return innerTextElement()->scrollLeft();
817 return RenderBlock::scrollLeft();
818}
819
820int RenderTextControlSingleLine::scrollTop() const
821{
822 if (innerTextElement())
823 return innerTextElement()->scrollTop();
824 return RenderBlock::scrollTop();
825}
826
827void RenderTextControlSingleLine::setScrollLeft(int newLeft)
828{
829 if (innerTextElement())
830 innerTextElement()->setScrollLeft(newLeft);
831}
832
833void RenderTextControlSingleLine::setScrollTop(int newTop)
834{
835 if (innerTextElement())
836 innerTextElement()->setScrollTop(newTop);
837}
838
timothy@apple.com8af4f2e2009-10-01 13:18:03 +0000839bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
eric@webkit.orgd2bb5a02009-03-17 23:44:11 +0000840{
841 RenderLayer* layer = innerTextElement()->renderBox()->layer();
842 if (layer && layer->scroll(direction, granularity, multiplier))
843 return true;
timothy@apple.com8af4f2e2009-10-01 13:18:03 +0000844 return RenderBlock::scroll(direction, granularity, multiplier, stopNode);
eric@webkit.orgd2bb5a02009-03-17 23:44:11 +0000845}
846
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000847PassRefPtr<Scrollbar> RenderTextControlSingleLine::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize)
848{
849 RefPtr<Scrollbar> widget;
weinig@apple.comedad01d2009-02-04 21:02:02 +0000850 bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000851 if (hasCustomScrollbarStyle)
852 widget = RenderScrollbar::createCustomScrollbar(client, orientation, this);
853 else
854 widget = Scrollbar::createNativeScrollbar(client, orientation, controlSize);
855 return widget.release();
856}
857
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000858InputElement* RenderTextControlSingleLine::inputElement() const
859{
zimmermann@webkit.org6d4da352009-01-22 22:53:47 +0000860 return toInputElement(static_cast<Element*>(node()));
zimmermann@webkit.orgdceb5db2009-01-20 21:02:58 +0000861}
862
zimmermann@webkit.org9402bba2008-12-28 13:54:17 +0000863}