• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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