~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/html/shadow/TextControlInnerElements.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 "TextControlInnerElements.h"
 
29
 
 
30
#include "BeforeTextInsertedEvent.h"
 
31
#include "Document.h"
 
32
#include "EventHandler.h"
 
33
#include "EventNames.h"
 
34
#include "Frame.h"
 
35
#include "HTMLInputElement.h"
 
36
#include "HTMLNames.h"
 
37
#include "HTMLTextAreaElement.h"
 
38
#include "MouseEvent.h"
 
39
#include "Page.h"
 
40
#include "RenderSearchField.h"
 
41
#include "RenderView.h"
 
42
#include "ScriptController.h"
 
43
#include "SpeechInput.h"
 
44
#include "SpeechInputEvent.h"
 
45
#include "StyleInheritedData.h"
 
46
#include "TextEvent.h"
 
47
#include "TextEventInputType.h"
 
48
 
 
49
namespace WebCore {
 
50
 
 
51
using namespace HTMLNames;
 
52
 
 
53
TextControlInnerElement::TextControlInnerElement(Document* document)
 
54
    : HTMLDivElement(divTag, document)
 
55
{
 
56
    setHasCustomCallbacks();
 
57
}
 
58
 
 
59
PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(Document* document)
 
60
{
 
61
    return adoptRef(new TextControlInnerElement(document));
 
62
}
 
63
 
 
64
PassRefPtr<RenderStyle> TextControlInnerElement::customStyleForRenderer()
 
65
{
 
66
    RenderTextControlSingleLine* parentRenderer = toRenderTextControlSingleLine(shadowHost()->renderer());
 
67
    return parentRenderer->createInnerBlockStyle(parentRenderer->style());
 
68
}
 
69
 
 
70
// ---------------------------
 
71
 
 
72
inline TextControlInnerTextElement::TextControlInnerTextElement(Document* document)
 
73
    : HTMLDivElement(divTag, document)
 
74
{
 
75
    setHasCustomCallbacks();
 
76
}
 
77
 
 
78
PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document* document)
 
79
{
 
80
    return adoptRef(new TextControlInnerTextElement(document));
 
81
}
 
82
 
 
83
void TextControlInnerTextElement::defaultEventHandler(Event* event)
 
84
{
 
85
    // FIXME: In the future, we should add a way to have default event listeners.
 
86
    // Then we would add one to the text field's inner div, and we wouldn't need this subclass.
 
87
    // Or possibly we could just use a normal event listener.
 
88
    if (event->isBeforeTextInsertedEvent() || event->type() == eventNames().webkitEditableContentChangedEvent) {
 
89
        Element* shadowAncestor = shadowHost();
 
90
        // A TextControlInnerTextElement can have no host if its been detached,
 
91
        // but kept alive by an EditCommand. In this case, an undo/redo can
 
92
        // cause events to be sent to the TextControlInnerTextElement. To
 
93
        // prevent an infinite loop, we must check for this case before sending
 
94
        // the event up the chain.
 
95
        if (shadowAncestor)
 
96
            shadowAncestor->defaultEventHandler(event);
 
97
    }
 
98
    if (!event->defaultHandled())
 
99
        HTMLDivElement::defaultEventHandler(event);
 
100
}
 
101
 
 
102
RenderObject* TextControlInnerTextElement::createRenderer(RenderArena* arena, RenderStyle*)
 
103
{
 
104
    return new (arena) RenderTextControlInnerBlock(this);
 
105
}
 
106
 
 
107
PassRefPtr<RenderStyle> TextControlInnerTextElement::customStyleForRenderer()
 
108
{
 
109
    RenderTextControl* parentRenderer = toRenderTextControl(shadowHost()->renderer());
 
110
    return parentRenderer->createInnerTextStyle(parentRenderer->style());
 
111
}
 
112
 
 
113
// ----------------------------
 
114
 
 
115
inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* document)
 
116
    : HTMLDivElement(divTag, document)
 
117
{
 
118
}
 
119
 
 
120
PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document* document)
 
121
{
 
122
    return adoptRef(new SearchFieldResultsButtonElement(document));
 
123
}
 
124
 
 
125
const AtomicString& SearchFieldResultsButtonElement::shadowPseudoId() const
 
126
{
 
127
    DEFINE_STATIC_LOCAL(AtomicString, resultsId, ("-webkit-search-results-button", AtomicString::ConstructFromLiteral));
 
128
    DEFINE_STATIC_LOCAL(AtomicString, resultsDecorationId, ("-webkit-search-results-decoration", AtomicString::ConstructFromLiteral));
 
129
    DEFINE_STATIC_LOCAL(AtomicString, decorationId, ("-webkit-search-decoration", AtomicString::ConstructFromLiteral));
 
130
    Element* host = shadowHost();
 
131
    if (!host)
 
132
        return resultsId;
 
133
    if (HTMLInputElement* input = host->toInputElement()) {
 
134
        if (input->maxResults() < 0)
 
135
            return decorationId;
 
136
        if (input->maxResults() > 0)
 
137
            return resultsId;
 
138
        return resultsDecorationId;
 
139
    }
 
140
    return resultsId;
 
141
}
 
142
 
 
143
void SearchFieldResultsButtonElement::defaultEventHandler(Event* event)
 
144
{
 
145
    // On mousedown, bring up a menu, if needed
 
146
    HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
 
147
    if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
 
148
        input->focus();
 
149
        input->select();
 
150
        RenderSearchField* renderer = toRenderSearchField(input->renderer());
 
151
        if (renderer->popupIsVisible())
 
152
            renderer->hidePopup();
 
153
        else if (input->maxResults() > 0)
 
154
            renderer->showPopup();
 
155
        event->setDefaultHandled();
 
156
    }
 
157
 
 
158
    if (!event->defaultHandled())
 
159
        HTMLDivElement::defaultEventHandler(event);
 
160
}
 
161
 
 
162
bool SearchFieldResultsButtonElement::willRespondToMouseClickEvents()
 
163
{
 
164
    return true;
 
165
}
 
166
 
 
167
// ----------------------------
 
168
 
 
169
inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* document)
 
170
    : HTMLDivElement(divTag, document)
 
171
    , m_capturing(false)
 
172
{
 
173
}
 
174
 
 
175
PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document* document)
 
176
{
 
177
    return adoptRef(new SearchFieldCancelButtonElement(document));
 
178
}
 
179
 
 
180
const AtomicString& SearchFieldCancelButtonElement::shadowPseudoId() const
 
181
{
 
182
    DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-search-cancel-button", AtomicString::ConstructFromLiteral));
 
183
    return pseudoId;
 
184
}
 
185
 
 
186
void SearchFieldCancelButtonElement::detach()
 
187
{
 
188
    if (m_capturing) {
 
189
        if (Frame* frame = document()->frame())
 
190
            frame->eventHandler()->setCapturingMouseEventsNode(0);
 
191
    }
 
192
    HTMLDivElement::detach();
 
193
}
 
194
 
 
195
 
 
196
void SearchFieldCancelButtonElement::defaultEventHandler(Event* event)
 
197
{
 
198
    // If the element is visible, on mouseup, clear the value, and set selection
 
199
    RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
 
200
    if (input->disabled() || input->readOnly()) {
 
201
        if (!event->defaultHandled())
 
202
            HTMLDivElement::defaultEventHandler(event);
 
203
        return;
 
204
    }
 
205
 
 
206
    if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
 
207
        if (renderer() && renderer()->visibleToHitTesting()) {
 
208
            if (Frame* frame = document()->frame()) {
 
209
                frame->eventHandler()->setCapturingMouseEventsNode(this);
 
210
                m_capturing = true;
 
211
            }
 
212
        }
 
213
        input->focus();
 
214
        input->select();
 
215
        event->setDefaultHandled();
 
216
    }
 
217
    if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
 
218
        if (m_capturing) {
 
219
            if (Frame* frame = document()->frame()) {
 
220
                frame->eventHandler()->setCapturingMouseEventsNode(0);
 
221
                m_capturing = false;
 
222
            }
 
223
            if (hovered()) {
 
224
                String oldValue = input->value();
 
225
                input->setValueForUser("");
 
226
                input->onSearch();
 
227
                event->setDefaultHandled();
 
228
            }
 
229
        }
 
230
    }
 
231
 
 
232
    if (!event->defaultHandled())
 
233
        HTMLDivElement::defaultEventHandler(event);
 
234
}
 
235
 
 
236
bool SearchFieldCancelButtonElement::willRespondToMouseClickEvents()
 
237
{
 
238
    const HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
 
239
    if (!input->disabled() && !input->readOnly())
 
240
        return true;
 
241
 
 
242
    return HTMLDivElement::willRespondToMouseClickEvents();
 
243
}
 
244
 
 
245
// ----------------------------
 
246
 
 
247
#if ENABLE(INPUT_SPEECH)
 
248
 
 
249
inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(Document* document)
 
250
    : HTMLDivElement(divTag, document)
 
251
    , m_capturing(false)
 
252
    , m_state(Idle)
 
253
    , m_listenerId(0)
 
254
{
 
255
}
 
256
 
 
257
InputFieldSpeechButtonElement::~InputFieldSpeechButtonElement()
 
258
{
 
259
    SpeechInput* speech = speechInput();
 
260
    if (speech && m_listenerId)  { // Could be null when page is unloading.
 
261
        if (m_state != Idle)
 
262
            speech->cancelRecognition(m_listenerId);
 
263
        speech->unregisterListener(m_listenerId);
 
264
    }
 
265
}
 
266
 
 
267
PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(Document* document)
 
268
{
 
269
    return adoptRef(new InputFieldSpeechButtonElement(document));
 
270
}
 
271
 
 
272
void InputFieldSpeechButtonElement::defaultEventHandler(Event* event)
 
273
{
 
274
    // For privacy reasons, only allow clicks directly coming from the user.
 
275
    if (!ScriptController::processingUserGesture()) {
 
276
        HTMLDivElement::defaultEventHandler(event);
 
277
        return;
 
278
    }
 
279
 
 
280
    // The call to focus() below dispatches a focus event, and an event handler in the page might
 
281
    // remove the input element from DOM. To make sure it remains valid until we finish our work
 
282
    // here, we take a temporary reference.
 
283
    RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
 
284
 
 
285
    if (input->disabled() || input->readOnly()) {
 
286
        if (!event->defaultHandled())
 
287
            HTMLDivElement::defaultEventHandler(event);
 
288
        return;
 
289
    }
 
290
 
 
291
    // On mouse down, select the text and set focus.
 
292
    if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
 
293
        if (renderer() && renderer()->visibleToHitTesting()) {
 
294
            if (Frame* frame = document()->frame()) {
 
295
                frame->eventHandler()->setCapturingMouseEventsNode(this);
 
296
                m_capturing = true;
 
297
            }
 
298
        }
 
299
        RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
 
300
        input->focus();
 
301
        input->select();
 
302
        event->setDefaultHandled();
 
303
    }
 
304
    // On mouse up, release capture cleanly.
 
305
    if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
 
306
        if (m_capturing && renderer() && renderer()->visibleToHitTesting()) {
 
307
            if (Frame* frame = document()->frame()) {
 
308
                frame->eventHandler()->setCapturingMouseEventsNode(0);
 
309
                m_capturing = false;
 
310
            }
 
311
        }
 
312
    }
 
313
 
 
314
    if (event->type() == eventNames().clickEvent && m_listenerId) {
 
315
        switch (m_state) {
 
316
        case Idle:
 
317
            startSpeechInput();
 
318
            break;
 
319
        case Recording:
 
320
            stopSpeechInput();
 
321
            break;
 
322
        case Recognizing:
 
323
            // Nothing to do here, we will continue to wait for results.
 
324
            break;
 
325
        }
 
326
        event->setDefaultHandled();
 
327
    }
 
328
 
 
329
    if (!event->defaultHandled())
 
330
        HTMLDivElement::defaultEventHandler(event);
 
331
}
 
332
 
 
333
bool InputFieldSpeechButtonElement::willRespondToMouseClickEvents()
 
334
{
 
335
    const HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
 
336
    if (!input->disabled() && !input->readOnly())
 
337
        return true;
 
338
 
 
339
    return HTMLDivElement::willRespondToMouseClickEvents();
 
340
}
 
341
 
 
342
void InputFieldSpeechButtonElement::setState(SpeechInputState state)
 
343
{
 
344
    if (m_state != state) {
 
345
        m_state = state;
 
346
        shadowHost()->renderer()->repaint();
 
347
    }
 
348
}
 
349
 
 
350
SpeechInput* InputFieldSpeechButtonElement::speechInput()
 
351
{
 
352
    return SpeechInput::from(document()->page());
 
353
}
 
354
 
 
355
void InputFieldSpeechButtonElement::didCompleteRecording(int)
 
356
{
 
357
    setState(Recognizing);
 
358
}
 
359
 
 
360
void InputFieldSpeechButtonElement::didCompleteRecognition(int)
 
361
{
 
362
    setState(Idle);
 
363
}
 
364
 
 
365
void InputFieldSpeechButtonElement::setRecognitionResult(int, const SpeechInputResultArray& results)
 
366
{
 
367
    m_results = results;
 
368
 
 
369
    // The call to setValue() below dispatches an event, and an event handler in the page might
 
370
    // remove the input element from DOM. To make sure it remains valid until we finish our work
 
371
    // here, we take a temporary reference.
 
372
    RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
 
373
    if (input->disabled() || input->readOnly())
 
374
        return;
 
375
 
 
376
    RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
 
377
    if (document() && document()->domWindow()) {
 
378
        // Call selectionChanged, causing the element to cache the selection,
 
379
        // so that the text event inserts the text in this element even if
 
380
        // focus has moved away from it.
 
381
        input->selectionChanged(false);
 
382
        input->dispatchEvent(TextEvent::create(document()->domWindow(), results.isEmpty() ? "" : results[0]->utterance(), TextEventInputOther));
 
383
    }
 
384
 
 
385
    // This event is sent after the text event so the website can perform actions using the input field content immediately.
 
386
    // It provides alternative recognition hypotheses and notifies that the results come from speech input.
 
387
    input->dispatchEvent(SpeechInputEvent::create(eventNames().webkitspeechchangeEvent, results));
 
388
 
 
389
    // Check before accessing the renderer as the above event could have potentially turned off
 
390
    // speech in the input element, hence removing this button and renderer from the hierarchy.
 
391
    if (renderer())
 
392
        renderer()->repaint();
 
393
}
 
394
 
 
395
void InputFieldSpeechButtonElement::attach()
 
396
{
 
397
    ASSERT(!m_listenerId);
 
398
    if (SpeechInput* input = SpeechInput::from(document()->page()))
 
399
        m_listenerId = input->registerListener(this);
 
400
    HTMLDivElement::attach();
 
401
}
 
402
 
 
403
void InputFieldSpeechButtonElement::detach()
 
404
{
 
405
    if (m_capturing) {
 
406
        if (Frame* frame = document()->frame())
 
407
            frame->eventHandler()->setCapturingMouseEventsNode(0);
 
408
    }
 
409
 
 
410
    if (m_listenerId) {
 
411
        if (m_state != Idle)
 
412
            speechInput()->cancelRecognition(m_listenerId);
 
413
        speechInput()->unregisterListener(m_listenerId);
 
414
        m_listenerId = 0;
 
415
    }
 
416
 
 
417
    HTMLDivElement::detach();
 
418
}
 
419
 
 
420
void InputFieldSpeechButtonElement::startSpeechInput()
 
421
{
 
422
    if (m_state != Idle)
 
423
        return;
 
424
 
 
425
    RefPtr<HTMLInputElement> input = static_cast<HTMLInputElement*>(shadowHost());
 
426
    AtomicString language = input->computeInheritedLanguage();
 
427
    String grammar = input->getAttribute(webkitgrammarAttr);
 
428
    IntRect rect = document()->view()->contentsToRootView(pixelSnappedBoundingBox());
 
429
    if (speechInput()->startRecognition(m_listenerId, rect, language, grammar, document()->securityOrigin()))
 
430
        setState(Recording);
 
431
}
 
432
 
 
433
void InputFieldSpeechButtonElement::stopSpeechInput()
 
434
{
 
435
    if (m_state == Recording)
 
436
        speechInput()->stopRecording(m_listenerId);
 
437
}
 
438
 
 
439
const AtomicString& InputFieldSpeechButtonElement::shadowPseudoId() const
 
440
{
 
441
    DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-input-speech-button", AtomicString::ConstructFromLiteral));
 
442
    return pseudoId;
 
443
}
 
444
 
 
445
#endif // ENABLE(INPUT_SPEECH)
 
446
 
 
447
}