1 /*
2 * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include "core/html/shadow/TextControlInnerElements.h"
29
30 #include "HTMLNames.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/NodeRenderStyle.h"
33 #include "core/events/MouseEvent.h"
34 #include "core/events/TextEvent.h"
35 #include "core/events/TextEventInputType.h"
36 #include "core/events/ThreadLocalEventNames.h"
37 #include "core/html/HTMLInputElement.h"
38 #include "core/html/shadow/ShadowElementNames.h"
39 #include "core/page/EventHandler.h"
40 #include "core/frame/Frame.h"
41 #include "core/rendering/RenderTextControlSingleLine.h"
42 #include "core/rendering/RenderView.h"
43 #include "core/speech/SpeechInput.h"
44 #include "core/speech/SpeechInputEvent.h"
45 #include "platform/UserGestureIndicator.h"
46
47 namespace WebCore {
48
49 using namespace HTMLNames;
50
TextControlInnerContainer(Document & document)51 TextControlInnerContainer::TextControlInnerContainer(Document& document)
52 : HTMLDivElement(document)
53 {
54 }
55
create(Document & document)56 PassRefPtr<TextControlInnerContainer> TextControlInnerContainer::create(Document& document)
57 {
58 RefPtr<TextControlInnerContainer> element = adoptRef(new TextControlInnerContainer(document));
59 element->setAttribute(idAttr, ShadowElementNames::textFieldContainer());
60 return element.release();
61 }
62
createRenderer(RenderStyle *)63 RenderObject* TextControlInnerContainer::createRenderer(RenderStyle*)
64 {
65 return new RenderTextControlInnerContainer(this);
66 }
67
68 // ---------------------------
69
EditingViewPortElement(Document & document)70 EditingViewPortElement::EditingViewPortElement(Document& document)
71 : HTMLDivElement(document)
72 {
73 setHasCustomStyleCallbacks();
74 }
75
create(Document & document)76 PassRefPtr<EditingViewPortElement> EditingViewPortElement::create(Document& document)
77 {
78 RefPtr<EditingViewPortElement> element = adoptRef(new EditingViewPortElement(document));
79 element->setAttribute(idAttr, ShadowElementNames::editingViewPort());
80 return element.release();
81 }
82
customStyleForRenderer()83 PassRefPtr<RenderStyle> EditingViewPortElement::customStyleForRenderer()
84 {
85 // FXIME: Move these styles to html.css.
86
87 RefPtr<RenderStyle> style = RenderStyle::create();
88 style->inheritFrom(shadowHost()->renderStyle());
89
90 style->setFlexGrow(1);
91 // min-width: 0; is needed for correct shrinking.
92 // FIXME: Remove this line when https://bugs.webkit.org/show_bug.cgi?id=111790 is fixed.
93 style->setMinWidth(Length(0, Fixed));
94 style->setDisplay(BLOCK);
95 style->setDirection(LTR);
96
97 // We don't want the shadow dom to be editable, so we set this block to
98 // read-only in case the input itself is editable.
99 style->setUserModify(READ_ONLY);
100 style->setUnique();
101
102 return style.release();
103 }
104
105 // ---------------------------
106
TextControlInnerTextElement(Document & document)107 inline TextControlInnerTextElement::TextControlInnerTextElement(Document& document)
108 : HTMLDivElement(document)
109 {
110 setHasCustomStyleCallbacks();
111 }
112
create(Document & document)113 PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document& document)
114 {
115 RefPtr<TextControlInnerTextElement> element = adoptRef(new TextControlInnerTextElement(document));
116 element->setAttribute(idAttr, ShadowElementNames::innerEditor());
117 return element.release();
118 }
119
defaultEventHandler(Event * event)120 void TextControlInnerTextElement::defaultEventHandler(Event* event)
121 {
122 // FIXME: In the future, we should add a way to have default event listeners.
123 // Then we would add one to the text field's inner div, and we wouldn't need this subclass.
124 // Or possibly we could just use a normal event listener.
125 if (event->isBeforeTextInsertedEvent() || event->type() == EventTypeNames::webkitEditableContentChanged) {
126 Element* shadowAncestor = shadowHost();
127 // A TextControlInnerTextElement can have no host if its been detached,
128 // but kept alive by an EditCommand. In this case, an undo/redo can
129 // cause events to be sent to the TextControlInnerTextElement. To
130 // prevent an infinite loop, we must check for this case before sending
131 // the event up the chain.
132 if (shadowAncestor)
133 shadowAncestor->defaultEventHandler(event);
134 }
135 if (!event->defaultHandled())
136 HTMLDivElement::defaultEventHandler(event);
137 }
138
createRenderer(RenderStyle *)139 RenderObject* TextControlInnerTextElement::createRenderer(RenderStyle*)
140 {
141 return new RenderTextControlInnerBlock(this);
142 }
143
customStyleForRenderer()144 PassRefPtr<RenderStyle> TextControlInnerTextElement::customStyleForRenderer()
145 {
146 RenderObject* parentRenderer = shadowHost()->renderer();
147 if (!parentRenderer || !parentRenderer->isTextControl())
148 return originalStyleForRenderer();
149 RenderTextControl* textControlRenderer = toRenderTextControl(parentRenderer);
150 return textControlRenderer->createInnerTextStyle(textControlRenderer->style());
151 }
152
153 // ----------------------------
154
SearchFieldDecorationElement(Document & document)155 inline SearchFieldDecorationElement::SearchFieldDecorationElement(Document& document)
156 : HTMLDivElement(document)
157 {
158 }
159
create(Document & document)160 PassRefPtr<SearchFieldDecorationElement> SearchFieldDecorationElement::create(Document& document)
161 {
162 RefPtr<SearchFieldDecorationElement> element = adoptRef(new SearchFieldDecorationElement(document));
163 element->setAttribute(idAttr, ShadowElementNames::searchDecoration());
164 return element.release();
165 }
166
pseudo() const167 const AtomicString& SearchFieldDecorationElement::pseudo() const
168 {
169 DEFINE_STATIC_LOCAL(AtomicString, resultsDecorationId, ("-webkit-search-results-decoration", AtomicString::ConstructFromLiteral));
170 DEFINE_STATIC_LOCAL(AtomicString, decorationId, ("-webkit-search-decoration", AtomicString::ConstructFromLiteral));
171 Element* host = shadowHost();
172 if (!host)
173 return resultsDecorationId;
174 if (host->hasTagName(inputTag)) {
175 if (toHTMLInputElement(host)->maxResults() < 0)
176 return decorationId;
177 return resultsDecorationId;
178 }
179 return resultsDecorationId;
180 }
181
defaultEventHandler(Event * event)182 void SearchFieldDecorationElement::defaultEventHandler(Event* event)
183 {
184 // On mousedown, focus the search field
185 HTMLInputElement* input = toHTMLInputElement(shadowHost());
186 if (input && event->type() == EventTypeNames::mousedown && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
187 input->focus();
188 input->select();
189 event->setDefaultHandled();
190 }
191
192 if (!event->defaultHandled())
193 HTMLDivElement::defaultEventHandler(event);
194 }
195
willRespondToMouseClickEvents()196 bool SearchFieldDecorationElement::willRespondToMouseClickEvents()
197 {
198 return true;
199 }
200
201 // ----------------------------
202
SearchFieldCancelButtonElement(Document & document)203 inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document& document)
204 : HTMLDivElement(document)
205 , m_capturing(false)
206 {
207 }
208
create(Document & document)209 PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document& document)
210 {
211 RefPtr<SearchFieldCancelButtonElement> element = adoptRef(new SearchFieldCancelButtonElement(document));
212 element->setPseudo(AtomicString("-webkit-search-cancel-button", AtomicString::ConstructFromLiteral));
213 element->setAttribute(idAttr, ShadowElementNames::clearButton());
214 return element.release();
215 }
216
detach(const AttachContext & context)217 void SearchFieldCancelButtonElement::detach(const AttachContext& context)
218 {
219 if (m_capturing) {
220 if (Frame* frame = document().frame())
221 frame->eventHandler().setCapturingMouseEventsNode(0);
222 }
223 HTMLDivElement::detach(context);
224 }
225
226
defaultEventHandler(Event * event)227 void SearchFieldCancelButtonElement::defaultEventHandler(Event* event)
228 {
229 // If the element is visible, on mouseup, clear the value, and set selection
230 RefPtr<HTMLInputElement> input(toHTMLInputElement(shadowHost()));
231 if (!input || input->isDisabledOrReadOnly()) {
232 if (!event->defaultHandled())
233 HTMLDivElement::defaultEventHandler(event);
234 return;
235 }
236
237 if (event->type() == EventTypeNames::mousedown && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
238 if (renderer() && renderer()->visibleToHitTesting()) {
239 if (Frame* frame = document().frame()) {
240 frame->eventHandler().setCapturingMouseEventsNode(this);
241 m_capturing = true;
242 }
243 }
244 input->focus();
245 input->select();
246 event->setDefaultHandled();
247 }
248 if (event->type() == EventTypeNames::mouseup && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
249 if (m_capturing) {
250 if (Frame* frame = document().frame()) {
251 frame->eventHandler().setCapturingMouseEventsNode(0);
252 m_capturing = false;
253 }
254 if (hovered()) {
255 String oldValue = input->value();
256 input->setValueForUser("");
257 input->onSearch();
258 event->setDefaultHandled();
259 }
260 }
261 }
262
263 if (!event->defaultHandled())
264 HTMLDivElement::defaultEventHandler(event);
265 }
266
willRespondToMouseClickEvents()267 bool SearchFieldCancelButtonElement::willRespondToMouseClickEvents()
268 {
269 const HTMLInputElement* input = toHTMLInputElement(shadowHost());
270 if (input && !input->isDisabledOrReadOnly())
271 return true;
272
273 return HTMLDivElement::willRespondToMouseClickEvents();
274 }
275
276 // ----------------------------
277
278 #if ENABLE(INPUT_SPEECH)
279
InputFieldSpeechButtonElement(Document & document)280 inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(Document& document)
281 : HTMLDivElement(document)
282 , m_capturing(false)
283 , m_state(Idle)
284 , m_listenerId(0)
285 {
286 }
287
~InputFieldSpeechButtonElement()288 InputFieldSpeechButtonElement::~InputFieldSpeechButtonElement()
289 {
290 SpeechInput* speech = speechInput();
291 if (speech && m_listenerId) { // Could be null when page is unloading.
292 if (m_state != Idle)
293 speech->cancelRecognition(m_listenerId);
294 speech->unregisterListener(m_listenerId);
295 }
296 }
297
create(Document & document)298 PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(Document& document)
299 {
300 RefPtr<InputFieldSpeechButtonElement> element = adoptRef(new InputFieldSpeechButtonElement(document));
301 element->setPseudo(AtomicString("-webkit-input-speech-button", AtomicString::ConstructFromLiteral));
302 element->setAttribute(idAttr, ShadowElementNames::speechButton());
303 return element.release();
304 }
305
defaultEventHandler(Event * event)306 void InputFieldSpeechButtonElement::defaultEventHandler(Event* event)
307 {
308 // For privacy reasons, only allow clicks directly coming from the user.
309 if (!UserGestureIndicator::processingUserGesture()) {
310 HTMLDivElement::defaultEventHandler(event);
311 return;
312 }
313
314 // The call to focus() below dispatches a focus event, and an event handler in the page might
315 // remove the input element from DOM. To make sure it remains valid until we finish our work
316 // here, we take a temporary reference.
317 RefPtr<HTMLInputElement> input(toHTMLInputElement(shadowHost()));
318
319 if (!input || input->isDisabledOrReadOnly()) {
320 if (!event->defaultHandled())
321 HTMLDivElement::defaultEventHandler(event);
322 return;
323 }
324
325 // On mouse down, select the text and set focus.
326 if (event->type() == EventTypeNames::mousedown && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
327 if (renderer() && renderer()->visibleToHitTesting()) {
328 if (Frame* frame = document().frame()) {
329 frame->eventHandler().setCapturingMouseEventsNode(this);
330 m_capturing = true;
331 }
332 }
333 RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
334 input->focus();
335 input->select();
336 event->setDefaultHandled();
337 }
338 // On mouse up, release capture cleanly.
339 if (event->type() == EventTypeNames::mouseup && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
340 if (m_capturing && renderer() && renderer()->visibleToHitTesting()) {
341 if (Frame* frame = document().frame()) {
342 frame->eventHandler().setCapturingMouseEventsNode(0);
343 m_capturing = false;
344 }
345 }
346 }
347
348 if (event->type() == EventTypeNames::click && m_listenerId) {
349 switch (m_state) {
350 case Idle:
351 startSpeechInput();
352 break;
353 case Recording:
354 stopSpeechInput();
355 break;
356 case Recognizing:
357 // Nothing to do here, we will continue to wait for results.
358 break;
359 }
360 event->setDefaultHandled();
361 }
362
363 if (!event->defaultHandled())
364 HTMLDivElement::defaultEventHandler(event);
365 }
366
willRespondToMouseClickEvents()367 bool InputFieldSpeechButtonElement::willRespondToMouseClickEvents()
368 {
369 const HTMLInputElement* input = toHTMLInputElement(shadowHost());
370 if (input && !input->isDisabledOrReadOnly())
371 return true;
372
373 return HTMLDivElement::willRespondToMouseClickEvents();
374 }
375
setState(SpeechInputState state)376 void InputFieldSpeechButtonElement::setState(SpeechInputState state)
377 {
378 if (m_state != state) {
379 m_state = state;
380 shadowHost()->renderer()->repaint();
381 }
382 }
383
speechInput()384 SpeechInput* InputFieldSpeechButtonElement::speechInput()
385 {
386 return SpeechInput::from(document().page());
387 }
388
didCompleteRecording(int)389 void InputFieldSpeechButtonElement::didCompleteRecording(int)
390 {
391 setState(Recognizing);
392 }
393
didCompleteRecognition(int)394 void InputFieldSpeechButtonElement::didCompleteRecognition(int)
395 {
396 setState(Idle);
397 }
398
setRecognitionResult(int,const SpeechInputResultArray & results)399 void InputFieldSpeechButtonElement::setRecognitionResult(int, const SpeechInputResultArray& results)
400 {
401 m_results = results;
402
403 // The call to setValue() below dispatches an event, and an event handler in the page might
404 // remove the input element from DOM. To make sure it remains valid until we finish our work
405 // here, we take a temporary reference.
406 RefPtr<HTMLInputElement> input(toHTMLInputElement(shadowHost()));
407 if (!input || input->isDisabledOrReadOnly())
408 return;
409
410 RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
411 if (document().domWindow()) {
412 // Call selectionChanged, causing the element to cache the selection,
413 // so that the text event inserts the text in this element even if
414 // focus has moved away from it.
415 input->selectionChanged(false);
416 input->dispatchEvent(TextEvent::create(document().domWindow(), results.isEmpty() ? "" : results[0]->utterance(), TextEventInputOther));
417 }
418
419 // This event is sent after the text event so the website can perform actions using the input field content immediately.
420 // It provides alternative recognition hypotheses and notifies that the results come from speech input.
421 input->dispatchEvent(SpeechInputEvent::create(EventTypeNames::webkitspeechchange, results));
422
423 // Check before accessing the renderer as the above event could have potentially turned off
424 // speech in the input element, hence removing this button and renderer from the hierarchy.
425 if (renderer())
426 renderer()->repaint();
427 }
428
attach(const AttachContext & context)429 void InputFieldSpeechButtonElement::attach(const AttachContext& context)
430 {
431 ASSERT(!m_listenerId);
432 if (SpeechInput* input = SpeechInput::from(document().page()))
433 m_listenerId = input->registerListener(this);
434 HTMLDivElement::attach(context);
435 }
436
detach(const AttachContext & context)437 void InputFieldSpeechButtonElement::detach(const AttachContext& context)
438 {
439 if (m_capturing) {
440 if (Frame* frame = document().frame())
441 frame->eventHandler().setCapturingMouseEventsNode(0);
442 }
443
444 if (m_listenerId) {
445 if (m_state != Idle)
446 speechInput()->cancelRecognition(m_listenerId);
447 speechInput()->unregisterListener(m_listenerId);
448 m_listenerId = 0;
449 }
450
451 HTMLDivElement::detach(context);
452 }
453
startSpeechInput()454 void InputFieldSpeechButtonElement::startSpeechInput()
455 {
456 if (m_state != Idle)
457 return;
458
459 RefPtr<HTMLInputElement> input = toHTMLInputElement(shadowHost());
460 AtomicString language = input->computeInheritedLanguage();
461 String grammar = input->getAttribute(webkitgrammarAttr);
462 IntRect rect = document().view()->contentsToRootView(pixelSnappedBoundingBox());
463 if (speechInput()->startRecognition(m_listenerId, rect, language, grammar, document().securityOrigin()))
464 setState(Recording);
465 }
466
stopSpeechInput()467 void InputFieldSpeechButtonElement::stopSpeechInput()
468 {
469 if (m_state == Recording)
470 speechInput()->stopRecording(m_listenerId);
471 }
472 #endif // ENABLE(INPUT_SPEECH)
473
474 }
475