2
* Copyright (C) 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Lesser General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public
15
* License along with this library; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
#include "TouchEventHandler.h"
22
#include "BlackBerryPlatformSystemSound.h"
23
#include "DOMSupport.h"
25
#include "DocumentMarkerController.h"
26
#include "FocusController.h"
28
#include "FrameView.h"
29
#include "HTMLAnchorElement.h"
30
#include "HTMLAreaElement.h"
31
#include "HTMLImageElement.h"
32
#include "HTMLInputElement.h"
33
#include "HTMLNames.h"
34
#include "HTMLPlugInElement.h"
35
#include "InRegionScroller_p.h"
36
#include "InputHandler.h"
41
#include "PlatformMouseEvent.h"
42
#include "PlatformTouchEvent.h"
43
#include "RenderLayer.h"
44
#include "RenderTheme.h"
45
#include "RenderView.h"
46
#include "SelectionHandler.h"
47
#include "WebPage_p.h"
48
#include "WebTapHighlight.h"
50
#include <wtf/MathExtras.h>
52
using namespace WebCore;
55
namespace BlackBerry {
58
TouchEventHandler::TouchEventHandler(WebPagePrivate* webpage)
60
, m_existingTouchMode(ProcessedTouchEvents)
61
, m_shouldRequestSpellCheckOptions(false)
65
TouchEventHandler::~TouchEventHandler()
69
void TouchEventHandler::doFatFingers(Platform::TouchPoint& point)
71
m_lastScreenPoint = point.m_screenPos;
72
m_lastFatFingersResult.reset(); // Theoretically this shouldn't be required. Keep it just in case states get mangled.
73
IntPoint contentPos(m_webPage->mapFromViewportToContents(point.m_pos));
74
m_webPage->postponeDocumentStyleRecalc();
75
m_lastFatFingersResult = FatFingers(m_webPage, contentPos, FatFingers::ClickableElement).findBestPoint();
76
m_webPage->resumeDocumentStyleRecalc();
79
void TouchEventHandler::sendClickAtFatFingersPoint()
81
handleFatFingerPressed();
82
PlatformMouseEvent mouseRelease(m_webPage->mapFromContentsToViewport(m_lastFatFingersResult.adjustedPosition()), m_lastScreenPoint, PlatformEvent::MouseReleased, 1, LeftButton, TouchScreen);
83
m_webPage->handleMouseEvent(mouseRelease);
86
void TouchEventHandler::handleTouchPoint(Platform::TouchPoint& point)
88
// Enable input mode on any touch event.
89
m_webPage->m_inputHandler->setInputModeEnabled();
90
switch (point.m_state) {
91
case Platform::TouchPoint::TouchPressed:
95
// FIXME Draw tap highlight as soon as possible if we can
96
Element* elementUnderFatFinger = m_lastFatFingersResult.nodeAsElementIfApplicable();
97
if (elementUnderFatFinger)
100
// Check for text selection
101
if (m_lastFatFingersResult.isTextInput()) {
102
elementUnderFatFinger = m_lastFatFingersResult.nodeAsElementIfApplicable(FatFingersResult::ShadowContentNotAllowed, true /* shouldUseRootEditableElement */);
103
m_shouldRequestSpellCheckOptions = m_webPage->m_inputHandler->shouldRequestSpellCheckingOptionsForPoint(point.m_pos, elementUnderFatFinger, m_spellCheckOptionRequest);
106
handleFatFingerPressed();
109
case Platform::TouchPoint::TouchReleased:
112
if (!m_shouldRequestSpellCheckOptions)
113
m_webPage->m_inputHandler->processPendingKeyboardVisibilityChange();
115
// The rebase has eliminated a necessary event when the mouse does not
116
// trigger an actual selection change preventing re-showing of the
117
// keyboard. If input mode is active, call showVirtualKeyboard which
118
// will update the state and display keyboard if needed.
119
if (m_webPage->m_inputHandler->isInputMode())
120
m_webPage->m_inputHandler->notifyClientOfKeyboardVisibilityChange(true);
122
m_webPage->m_tapHighlight->hide();
124
IntPoint adjustedPoint = m_webPage->mapFromContentsToViewport(m_lastFatFingersResult.adjustedPosition());
125
PlatformMouseEvent mouseEvent(adjustedPoint, m_lastScreenPoint, PlatformEvent::MouseReleased, 1, LeftButton, TouchScreen);
126
m_webPage->handleMouseEvent(mouseEvent);
128
if (m_shouldRequestSpellCheckOptions) {
129
IntPoint pixelPositionRelativeToViewport = m_webPage->mapToTransformed(adjustedPoint);
130
m_webPage->m_inputHandler->requestSpellingCheckingOptions(m_spellCheckOptionRequest, IntSize(m_lastScreenPoint - pixelPositionRelativeToViewport));
133
m_lastFatFingersResult.reset(); // Reset the fat finger result as its no longer valid when a user's finger is not on the screen.
136
case Platform::TouchPoint::TouchMoved:
138
// You can still send mouse move events
139
PlatformMouseEvent mouseEvent(point.m_pos, m_lastScreenPoint, PlatformEvent::MouseMoved, 1, LeftButton, TouchScreen);
140
m_lastScreenPoint = point.m_screenPos;
141
m_webPage->handleMouseEvent(mouseEvent);
149
void TouchEventHandler::handleFatFingerPressed()
151
// First update the mouse position with a MouseMoved event.
152
PlatformMouseEvent mouseMoveEvent(m_webPage->mapFromContentsToViewport(m_lastFatFingersResult.adjustedPosition()), m_lastScreenPoint, PlatformEvent::MouseMoved, 0, LeftButton, TouchScreen);
153
m_webPage->handleMouseEvent(mouseMoveEvent);
155
// Then send the MousePressed event.
156
PlatformMouseEvent mousePressedEvent(m_webPage->mapFromContentsToViewport(m_lastFatFingersResult.adjustedPosition()), m_lastScreenPoint, PlatformEvent::MousePressed, 1, LeftButton, TouchScreen);
157
m_webPage->handleMouseEvent(mousePressedEvent);
160
// This method filters what element will get tap-highlight'ed or not. To start with,
161
// we are going to highlight links (anchors with a valid href element), and elements
162
// whose tap highlight color value is different than the default value.
163
static Element* elementForTapHighlight(Element* elementUnderFatFinger)
165
// Do not bail out right way here if there element does not have a renderer.
166
// It is the casefor <map> (descendent of <area>) elements. The associated <image>
167
// element actually has the renderer.
168
if (elementUnderFatFinger->renderer()) {
169
Color tapHighlightColor = elementUnderFatFinger->renderStyle()->tapHighlightColor();
170
if (tapHighlightColor != RenderTheme::defaultTheme()->platformTapHighlightColor())
171
return elementUnderFatFinger;
174
bool isArea = elementUnderFatFinger->hasTagName(HTMLNames::areaTag);
175
Node* linkNode = elementUnderFatFinger->enclosingLinkEventParentOrSelf();
176
if (!linkNode || !linkNode->isHTMLElement() || (!linkNode->renderer() && !isArea))
179
ASSERT(linkNode->isLink());
181
// FatFingers class selector ensure only anchor with valid href attr value get here.
182
// It includes empty hrefs.
183
Element* highlightCandidateElement = static_cast<Element*>(linkNode);
186
return highlightCandidateElement;
188
HTMLAreaElement* area = static_cast<HTMLAreaElement*>(highlightCandidateElement);
189
HTMLImageElement* image = area->imageElement();
190
if (image && image->renderer())
196
void TouchEventHandler::drawTapHighlight()
198
Element* elementUnderFatFinger = m_lastFatFingersResult.nodeAsElementIfApplicable();
199
if (!elementUnderFatFinger)
202
Element* element = elementForTapHighlight(elementUnderFatFinger);
206
// Get the element bounding rect in transformed coordinates so we can extract
207
// the focus ring relative position each rect.
208
RenderObject* renderer = element->renderer();
211
Frame* elementFrame = element->document()->frame();
212
ASSERT(elementFrame);
214
FrameView* elementFrameView = elementFrame->view();
215
if (!elementFrameView)
218
// Tell the client if the element is either in a scrollable container or in a fixed positioned container.
219
// On the client side, this info is being used to hide the tap highlight window on scroll.
220
RenderLayer* layer = m_webPage->enclosingFixedPositionedAncestorOrSelfIfFixedPositioned(renderer->enclosingLayer());
221
bool shouldHideTapHighlightRightAfterScrolling = !layer->renderer()->isRenderView();
222
shouldHideTapHighlightRightAfterScrolling |= !!m_webPage->m_inRegionScroller->d->isActive();
224
IntPoint framePos(m_webPage->frameOffset(elementFrame));
226
// FIXME: We can get more precise on the <map> case by calculating the rect with HTMLAreaElement::computeRect().
227
IntRect absoluteRect(renderer->absoluteClippedOverflowRect());
228
absoluteRect.move(framePos.x(), framePos.y());
230
IntRect clippingRect;
231
if (elementFrame == m_webPage->mainFrame())
232
clippingRect = IntRect(IntPoint(0, 0), elementFrameView->contentsSize());
234
clippingRect = m_webPage->mainFrame()->view()->windowToContents(m_webPage->getRecursiveVisibleWindowRect(elementFrameView, true /*noClipToMainFrame*/));
235
clippingRect = intersection(absoluteRect, clippingRect);
237
Vector<FloatQuad> focusRingQuads;
238
renderer->absoluteFocusRingQuads(focusRingQuads);
240
Platform::IntRectRegion region;
241
for (size_t i = 0; i < focusRingQuads.size(); ++i) {
242
IntRect rect = focusRingQuads[i].enclosingBoundingBox();
243
rect.move(framePos.x(), framePos.y());
244
IntRect clippedRect = intersection(clippingRect, rect);
245
// FIXME we shouldn't have any empty rects here PR 246960
246
if (clippedRect.isEmpty())
248
clippedRect.inflate(2);
249
region = unionRegions(region, Platform::IntRect(clippedRect));
252
Color highlightColor = element->renderStyle()->tapHighlightColor();
254
m_webPage->m_tapHighlight->draw(region,
255
highlightColor.red(), highlightColor.green(), highlightColor.blue(), highlightColor.alpha(),
256
shouldHideTapHighlightRightAfterScrolling);
259
void TouchEventHandler::playSoundIfAnchorIsTarget() const
261
if (m_lastFatFingersResult.node() && m_lastFatFingersResult.node()->isLink())
262
BlackBerry::Platform::SystemSound::instance()->playSound(BlackBerry::Platform::SystemSoundType::InputKeypress);