FatFingers.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /*
  2. * Copyright (C) 2010, 2011, 2012, 2013 Research In Motion Limited. All rights reserved.
  3. *
  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.
  8. *
  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.
  13. *
  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
  17. */
  18. #include "config.h"
  19. #include "FatFingers.h"
  20. #include "BlackBerryPlatformLog.h"
  21. #include "BlackBerryPlatformScreen.h"
  22. #include "BlackBerryPlatformSettings.h"
  23. #include "CSSComputedStyleDeclaration.h"
  24. #include "CSSParser.h"
  25. #include "DOMSupport.h"
  26. #include "Document.h"
  27. #include "Element.h"
  28. #include "EventNames.h"
  29. #include "ExceptionCode.h"
  30. #include "FloatQuad.h"
  31. #include "Frame.h"
  32. #include "FrameView.h"
  33. #include "HTMLFrameOwnerElement.h"
  34. #include "HTMLInputElement.h"
  35. #include "HTMLNames.h"
  36. #include "HTMLTextAreaElement.h"
  37. #include "Range.h"
  38. #include "RenderObject.h"
  39. #include "RenderView.h"
  40. #include "Text.h"
  41. #include "TextBreakIterator.h"
  42. #include "WebKitThreadViewportAccessor.h"
  43. #include "WebPage_p.h"
  44. #if DEBUG_FAT_FINGERS
  45. #include "BackingStore.h"
  46. #endif
  47. using BlackBerry::Platform::IntRectRegion;
  48. using namespace WebCore;
  49. // Lets make the top padding bigger than other directions, since it gets us more
  50. // accurate clicking results.
  51. namespace BlackBerry {
  52. namespace WebKit {
  53. #if DEBUG_FAT_FINGERS
  54. IntRect FatFingers::m_debugFatFingerRect;
  55. IntPoint FatFingers::m_debugFatFingerClickPosition;
  56. IntPoint FatFingers::m_debugFatFingerAdjustedPosition;
  57. #endif
  58. IntRect FatFingers::fingerRectForPoint(const IntPoint& point) const
  59. {
  60. const Platform::ViewportAccessor* viewportAccessor = m_webPage->m_webkitThreadViewportAccessor;
  61. unsigned topPadding, rightPadding, bottomPadding, leftPadding;
  62. IntPoint contentViewportPos = viewportAccessor->documentViewportFromContents(point);
  63. getAdjustedPaddings(contentViewportPos, topPadding, rightPadding, bottomPadding, leftPadding);
  64. return HitTestLocation::rectForPoint(point, topPadding, rightPadding, bottomPadding, leftPadding);
  65. }
  66. static bool hasMousePressListener(Element* element)
  67. {
  68. ASSERT(element);
  69. return element->hasEventListeners(eventNames().clickEvent)
  70. || element->hasEventListeners(eventNames().mousedownEvent)
  71. || element->hasEventListeners(eventNames().mouseupEvent);
  72. }
  73. bool FatFingers::isElementClickable(Element* element) const
  74. {
  75. ASSERT(element);
  76. ASSERT(m_targetType == ClickableElement);
  77. ExceptionCode ec = 0;
  78. if (element->webkitMatchesSelector("a[href],*:link,*:visited,*[role=button],button,input,select,label[for],area[href],textarea,embed,object", ec)
  79. || element->isMediaControlElement()
  80. || element->isContentEditable()
  81. || (element->hasTagName(HTMLNames::imgTag) && element->parentNode() && element->parentNode()->hasTagName(HTMLNames::aTag)))
  82. return true;
  83. return hasMousePressListener(element)
  84. || CSSComputedStyleDeclaration::create(element)->getPropertyValue(cssPropertyID("cursor")) == "pointer";
  85. }
  86. // FIXME: Handle content editable nodes here too.
  87. static inline bool isFieldWithText(Node* node)
  88. {
  89. ASSERT(node);
  90. if (!node || !node->isElementNode())
  91. return false;
  92. Element* element = toElement(node);
  93. return !DOMSupport::inputElementText(element).isEmpty();
  94. }
  95. static inline int distanceBetweenPoints(const IntPoint& p1, const IntPoint& p2)
  96. {
  97. int dx = p1.x() - p2.x();
  98. int dy = p1.y() - p2.y();
  99. return sqrt((double)((dx * dx) + (dy * dy)));
  100. }
  101. static bool compareDistanceBetweenPoints(const Platform::IntPoint& p, const IntRectRegion& r1, const IntRectRegion& r2)
  102. {
  103. return distanceBetweenPoints(p, r1.extents().center()) > distanceBetweenPoints(p, r2.extents().center());
  104. }
  105. static bool isValidFrameOwner(WebCore::Element* element)
  106. {
  107. ASSERT(element);
  108. return element->isFrameOwnerElement() && static_cast<HTMLFrameOwnerElement*>(element)->contentFrame();
  109. }
  110. // NOTE: 'contentPos' is in main frame contents coordinates.
  111. FatFingers::FatFingers(WebPagePrivate* webPage, const WebCore::IntPoint& contentPos, TargetType targetType)
  112. : m_webPage(webPage)
  113. , m_contentPos(contentPos)
  114. , m_targetType(targetType)
  115. {
  116. ASSERT(webPage);
  117. #if DEBUG_FAT_FINGERS
  118. const Platform::ViewportAccessor* viewportAccessor = m_webPage->m_webkitThreadViewportAccessor;
  119. m_debugFatFingerRect = IntRect(0, 0, 0, 0);
  120. m_debugFatFingerClickPosition = viewportAccessor->pixelViewportFromContents(viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatPoint(contentPos)));
  121. m_debugFatFingerAdjustedPosition = m_debugFatFingerClickPosition;
  122. #endif
  123. }
  124. FatFingers::~FatFingers()
  125. {
  126. }
  127. const FatFingersResult FatFingers::findBestPoint()
  128. {
  129. ASSERT(m_webPage);
  130. ASSERT(m_webPage->m_mainFrame);
  131. // Even though we have clamped the point in libwebview to viewport, but there might be a rounding difference for viewport rect.
  132. // Clamp position to viewport to ensure we are inside viewport.
  133. IntRect viewportRect = m_webPage->mainFrame()->view()->visibleContentRect();
  134. m_contentPos = Platform::pointClampedToRect(m_contentPos, viewportRect);
  135. FatFingersResult result(m_contentPos);
  136. // Lets set nodeUnderFatFinger to the result of a point based hit test here. If something
  137. // targable is actually found by ::findIntersectingRegions, then we might replace what we just set below later on.
  138. const HitTestResult& hitResult = m_webPage->hitTestResult(m_contentPos);
  139. Node* node = hitResult.innerNode();
  140. while (node && !node->isElementNode())
  141. node = node->parentNode();
  142. Element* elementUnderPoint = toElement(node);
  143. if (elementUnderPoint) {
  144. result.m_nodeUnderFatFinger = elementUnderPoint;
  145. // If we are looking for a Clickable Element and we found one, we can quit early.
  146. if (m_targetType == ClickableElement) {
  147. if (isElementClickable(elementUnderPoint)) {
  148. setSuccessfulFatFingersResult(result, elementUnderPoint, m_contentPos /*adjustedPosition*/);
  149. return result;
  150. }
  151. if (hitResult.URLElement()) {
  152. setSuccessfulFatFingersResult(result, hitResult.URLElement(), m_contentPos /*adjustedPosition*/);
  153. return result;
  154. }
  155. }
  156. }
  157. #if DEBUG_FAT_FINGERS
  158. // Force blit to make the fat fingers rects show up.
  159. if (!m_debugFatFingerRect.isEmpty())
  160. m_webPage->m_backingStore->repaint(0, 0, m_webPage->transformedViewportSize().width(), m_webPage->transformedViewportSize().height(), true, true);
  161. #endif
  162. Vector<IntersectingRegion> intersectingRegions;
  163. IntRectRegion remainingFingerRegion = IntRectRegion(fingerRectForPoint(m_contentPos));
  164. bool foundOne = findIntersectingRegions(m_webPage->m_mainFrame->document(), intersectingRegions, remainingFingerRegion);
  165. if (!foundOne)
  166. return result;
  167. Node* bestNode = 0;
  168. IntRectRegion largestIntersectionRegion;
  169. int largestIntersectionRegionArea = 0;
  170. Vector<IntersectingRegion>::const_iterator endIt = intersectingRegions.end();
  171. for (Vector<IntersectingRegion>::const_iterator it = intersectingRegions.begin(); it != endIt; ++it) {
  172. Node* currentNode = it->first;
  173. IntRectRegion currentIntersectionRegion = it->second;
  174. int currentIntersectionRegionArea = currentIntersectionRegion.area();
  175. if (currentIntersectionRegionArea > largestIntersectionRegionArea
  176. || (currentIntersectionRegionArea == largestIntersectionRegionArea
  177. && compareDistanceBetweenPoints(m_contentPos, currentIntersectionRegion, largestIntersectionRegion))) {
  178. bestNode = currentNode;
  179. largestIntersectionRegion = currentIntersectionRegion;
  180. largestIntersectionRegionArea = currentIntersectionRegionArea;
  181. }
  182. }
  183. if (!bestNode || largestIntersectionRegion.isEmpty())
  184. return result;
  185. #if DEBUG_FAT_FINGERS
  186. const Platform::ViewportAccessor* viewportAccessor = m_webPage->m_webkitThreadViewportAccessor;
  187. m_debugFatFingerAdjustedPosition = viewportAccessor->pixelViewportFromContents(
  188. viewportAccessor->roundToPixelFromDocumentContents(largestIntersectionRegion.rects()[0].center()));
  189. #endif
  190. setSuccessfulFatFingersResult(result, bestNode, largestIntersectionRegion.rects()[0].center() /*adjustedPosition*/);
  191. return result;
  192. }
  193. // 'region' is in contents coordinates relative to the frame containing 'node'
  194. // 'remainingFingerRegion' and 'intersectingRegions' will always be in main frame contents
  195. // coordinates.
  196. // Thus, before comparing, we need to map the former to main frame contents coordinates.
  197. bool FatFingers::checkFingerIntersection(const IntRectRegion& region, const IntRectRegion& remainingFingerRegion, Node* node, Vector<IntersectingRegion>& intersectingRegions)
  198. {
  199. ASSERT(node);
  200. IntRectRegion regionCopy(region);
  201. WebCore::IntPoint framePos(m_webPage->frameOffset(node->document()->frame()));
  202. regionCopy.move(framePos.x(), framePos.y());
  203. IntRectRegion intersection = intersectRegions(regionCopy, remainingFingerRegion);
  204. if (intersection.isEmpty())
  205. return false;
  206. #if DEBUG_FAT_FINGERS
  207. String nodeName;
  208. if (node->isTextNode())
  209. nodeName = "text node";
  210. else if (node->isElementNode())
  211. nodeName = String::format("%s node", toElement(node)->tagName().latin1().data());
  212. else
  213. nodeName = "unknown node";
  214. if (node->isInShadowTree()) {
  215. nodeName = nodeName + "(in shadow tree";
  216. if (node->isElementNode() && !toElement(node)->shadowPseudoId().isEmpty())
  217. nodeName = nodeName + ", pseudo id " + toElement(node)->shadowPseudoId();
  218. nodeName = nodeName + ")";
  219. }
  220. Platform::logAlways(Platform::LogLevelInfo,
  221. "%s has region %s, intersecting at %s (area %d)", nodeName.latin1().data(),
  222. regionCopy.toString().c_str(), intersection.toString().c_str(), intersection.area());
  223. #endif
  224. intersectingRegions.append(std::make_pair(node, intersection));
  225. return true;
  226. }
  227. // intersectingRegions and remainingFingerRegion are all in main frame contents coordinates,
  228. // even on recursive calls of ::findIntersectingRegions.
  229. bool FatFingers::findIntersectingRegions(Document* document, Vector<IntersectingRegion>& intersectingRegions, IntRectRegion& remainingFingerRegion)
  230. {
  231. if (!document || !document->frame()->view())
  232. return false;
  233. // The layout needs to be up-to-date to determine if a node is focusable.
  234. document->updateLayoutIgnorePendingStylesheets();
  235. // Create fingerRect.
  236. IntPoint frameContentPos(document->frame()->view()->windowToContents(m_webPage->m_mainFrame->view()->contentsToWindow(m_contentPos)));
  237. IntRect viewportRect = m_webPage->mainFrame()->view()->visibleContentRect();
  238. // Ensure the frameContentPos is inside the viewport.
  239. frameContentPos = Platform::pointClampedToRect(frameContentPos, viewportRect);
  240. #if DEBUG_FAT_FINGERS
  241. const Platform::ViewportAccessor* viewportAccessor = m_webPage->m_webkitThreadViewportAccessor;
  242. Platform::IntRect fingerRect(fingerRectForPoint(frameContentPos));
  243. Platform::IntRect screenFingerRect = viewportAccessor->roundToPixelFromDocumentContents(fingerRect);
  244. Platform::logAlways(Platform::LogLevelInfo, "fat finger rect now %s", screenFingerRect.toString().c_str());
  245. // only record the first finger rect
  246. if (document == m_webPage->m_mainFrame->document())
  247. m_debugFatFingerRect = viewportAccessor->pixelViewportFromContents(screenFingerRect);
  248. #endif
  249. bool foundOne = false;
  250. RenderLayer* lowestPositionedEnclosingLayerSoFar = 0;
  251. // Iterate over the list of nodes (and subrects of nodes where possible), for each saving the
  252. // intersection of the bounding box with the finger rect.
  253. ListHashSet<RefPtr<Node> > intersectedNodes;
  254. if (m_webPage->m_cachedRectHitTestResults.contains(document))
  255. intersectedNodes = m_webPage->m_cachedRectHitTestResults.get(document);
  256. else
  257. getNodesFromRect(document, frameContentPos, intersectedNodes);
  258. ListHashSet<RefPtr<Node> >::const_iterator it = intersectedNodes.begin();
  259. ListHashSet<RefPtr<Node> >::const_iterator end = intersectedNodes.end();
  260. for ( ; it != end; ++it) {
  261. Node* curNode = (*it).get();
  262. if (!curNode || !curNode->renderer())
  263. continue;
  264. if (remainingFingerRegion.isEmpty())
  265. break;
  266. bool isElement = curNode->isElementNode();
  267. if (isElement && isValidFrameOwner(toElement(curNode))) {
  268. HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(curNode);
  269. Document* childDocument = owner && owner->contentFrame() ? owner->contentFrame()->document() : 0;
  270. if (!childDocument)
  271. continue;
  272. ASSERT(childDocument->frame()->view());
  273. foundOne |= findIntersectingRegions(childDocument, intersectingRegions, remainingFingerRegion);
  274. } else if (isElement && m_targetType == ClickableElement) {
  275. foundOne |= checkForClickableElement(toElement(curNode), intersectingRegions, remainingFingerRegion, lowestPositionedEnclosingLayerSoFar);
  276. } else if (m_targetType == Text)
  277. foundOne |= checkForText(curNode, intersectingRegions, remainingFingerRegion);
  278. }
  279. return foundOne;
  280. }
  281. bool FatFingers::checkForClickableElement(Element* curElement, Vector<IntersectingRegion>& intersectingRegions, IntRectRegion& remainingFingerRegion, RenderLayer*& lowestPositionedEnclosingLayerSoFar)
  282. {
  283. ASSERT(curElement);
  284. bool intersects = false;
  285. IntRectRegion elementRegion;
  286. bool isClickableElement = isElementClickable(curElement);
  287. if (isClickableElement) {
  288. if (curElement->isLink()) {
  289. // Links can wrap lines, and in such cases Node::boundingBox() can give us
  290. // not accurate rects, since it unites all InlineBox's rects. In these
  291. // cases, we can process each line of the link separately with our
  292. // intersection rect, getting a more accurate clicking.
  293. Vector<FloatQuad> quads;
  294. curElement->renderer()->absoluteFocusRingQuads(quads);
  295. size_t n = quads.size();
  296. ASSERT(n);
  297. for (size_t i = 0; i < n; ++i)
  298. elementRegion = unionRegions(elementRegion, Platform::IntRect(quads[i].enclosingBoundingBox()));
  299. } else
  300. elementRegion = IntRectRegion(curElement->renderer()->absoluteBoundingBoxRect(true /*use transforms*/));
  301. } else
  302. elementRegion = IntRectRegion(curElement->renderer()->absoluteBoundingBoxRect(true /*use transforms*/));
  303. if (lowestPositionedEnclosingLayerSoFar) {
  304. RenderLayer* curElementRenderLayer = m_webPage->enclosingPositionedAncestorOrSelfIfPositioned(curElement->renderer()->enclosingLayer());
  305. if (curElementRenderLayer != lowestPositionedEnclosingLayerSoFar) {
  306. // elementRegion will always be in contents coordinates of its container frame. It needs to be
  307. // mapped to main frame contents coordinates in order to intersect the fingerRegion, then.
  308. WebCore::IntPoint framePos(m_webPage->frameOffset(curElement->document()->frame()));
  309. IntRectRegion layerRegion(Platform::IntRect(lowestPositionedEnclosingLayerSoFar->renderer()->absoluteBoundingBoxRect(true/*use transforms*/)));
  310. layerRegion.move(framePos.x(), framePos.y());
  311. remainingFingerRegion = intersectRegions(remainingFingerRegion, layerRegion);
  312. lowestPositionedEnclosingLayerSoFar = curElementRenderLayer;
  313. }
  314. } else
  315. lowestPositionedEnclosingLayerSoFar = m_webPage->enclosingPositionedAncestorOrSelfIfPositioned(curElement->renderer()->enclosingLayer());
  316. if (isClickableElement)
  317. intersects = checkFingerIntersection(elementRegion, remainingFingerRegion, curElement, intersectingRegions);
  318. return intersects;
  319. }
  320. bool FatFingers::checkForText(Node* curNode, Vector<IntersectingRegion>& intersectingRegions, IntRectRegion& fingerRegion)
  321. {
  322. ASSERT(curNode);
  323. if (isFieldWithText(curNode)) {
  324. // FIXME: Find all text in the field and find the best word.
  325. // For now, we will just select the whole field.
  326. IntRect boundingRect = curNode->renderer()->absoluteBoundingBoxRect(true /*use transforms*/);
  327. IntRectRegion nodeRegion(boundingRect);
  328. return checkFingerIntersection(nodeRegion, fingerRegion, curNode, intersectingRegions);
  329. }
  330. if (curNode->isTextNode()) {
  331. WebCore::Text* curText = static_cast<WebCore::Text*>(curNode);
  332. String allText = curText->wholeText();
  333. // Iterate through all words, breaking at whitespace, to find the bounding box of each word.
  334. TextBreakIterator* wordIterator = wordBreakIterator(allText.characters(), allText.length());
  335. int lastOffset = textBreakFirst(wordIterator);
  336. if (lastOffset == -1)
  337. return false;
  338. bool foundOne = false;
  339. int offset;
  340. Document* document = curNode->document();
  341. while ((offset = textBreakNext(wordIterator)) != -1) {
  342. RefPtr<Range> range = Range::create(document, curText, lastOffset, curText, offset);
  343. if (!range->text().stripWhiteSpace().isEmpty()) {
  344. #if DEBUG_FAT_FINGERS
  345. Platform::logAlways(Platform::LogLevelInfo, "Checking word '%s'", range->text().latin1().data());
  346. #endif
  347. IntRectRegion rangeRegion(DOMSupport::transformedBoundingBoxForRange(*range));
  348. foundOne |= checkFingerIntersection(rangeRegion, fingerRegion, curNode, intersectingRegions);
  349. }
  350. lastOffset = offset;
  351. }
  352. return foundOne;
  353. }
  354. return false;
  355. }
  356. void FatFingers::getAdjustedPaddings(const IntPoint& contentViewportPos, unsigned& top, unsigned& right, unsigned& bottom, unsigned& left) const
  357. {
  358. static unsigned topPadding = Platform::Settings::instance()->topFatFingerPadding();
  359. static unsigned rightPadding = Platform::Settings::instance()->rightFatFingerPadding();
  360. static unsigned bottomPadding = Platform::Settings::instance()->bottomFatFingerPadding();
  361. static unsigned leftPadding = Platform::Settings::instance()->leftFatFingerPadding();
  362. double currentScale = m_webPage->currentScale();
  363. top = topPadding / currentScale;
  364. right = rightPadding / currentScale;
  365. bottom = bottomPadding / currentScale;
  366. left = leftPadding / currentScale;
  367. IntRect viewportRect = m_webPage->mainFrame()->view()->visibleContentRect();
  368. // We clamp the event position inside the viewport. We should not expand the fat finger rect to the edge again.
  369. top = std::min(unsigned(std::max(contentViewportPos.y() - 1, 0)), top);
  370. left = std::min(unsigned(std::max(contentViewportPos.x() - 1, 0)), left);
  371. bottom = std::min(unsigned(std::max(viewportRect.height() - contentViewportPos.y() - 1, 0)), bottom);
  372. right = std::min(unsigned(std::max(viewportRect.width() - contentViewportPos.x() - 1, 0)), right);
  373. }
  374. void FatFingers::getNodesFromRect(Document* document, const IntPoint& contentPos, ListHashSet<RefPtr<Node> >& intersectedNodes)
  375. {
  376. const Platform::ViewportAccessor* viewportAccessor = m_webPage->m_webkitThreadViewportAccessor;
  377. unsigned topPadding, rightPadding, bottomPadding, leftPadding;
  378. IntPoint contentViewportPos = viewportAccessor->documentViewportFromContents(m_contentPos);
  379. // Do not allow fat fingers detect anything not visible(ie outside of the viewport)
  380. getAdjustedPaddings(contentViewportPos, topPadding, rightPadding, bottomPadding, leftPadding);
  381. // The user functions checkForText() and findIntersectingRegions() uses the Node.wholeText() to checkFingerIntersection()
  382. // not the text in its shadow tree.
  383. HitTestRequest::HitTestRequestType requestType = HitTestRequest::ReadOnly | HitTestRequest::Active;
  384. if (m_targetType != Text)
  385. requestType |= HitTestRequest::DisallowShadowContent;
  386. HitTestResult result(contentPos, topPadding, rightPadding, bottomPadding, leftPadding);
  387. document->renderView()->layer()->hitTest(requestType, result);
  388. intersectedNodes = result.rectBasedTestResult();
  389. m_webPage->m_cachedRectHitTestResults.add(document, intersectedNodes);
  390. }
  391. void FatFingers::setSuccessfulFatFingersResult(FatFingersResult& result, Node* bestNode, const WebCore::IntPoint& adjustedPoint)
  392. {
  393. result.m_nodeUnderFatFinger = bestNode;
  394. result.m_adjustedPosition = adjustedPoint;
  395. result.m_positionWasAdjusted = true;
  396. result.m_isValid = true;
  397. bool isTextInputElement = false;
  398. if (m_targetType == ClickableElement) {
  399. ASSERT_WITH_SECURITY_IMPLICATION(bestNode->isElementNode());
  400. Element* bestElement = toElement(bestNode);
  401. isTextInputElement = DOMSupport::isTextInputElement(bestElement);
  402. }
  403. result.m_isTextInput = isTextInputElement;
  404. }
  405. }
  406. }