bool MediaControlsPainter::paintMediaOverlayPlayButton(const LayoutObject& object, const PaintInfo& paintInfo, const IntRect& rect) { const HTMLMediaElement* mediaElement = toParentMediaElement(object); if (!mediaElement) return false; if (!hasSource(mediaElement) || !mediaElement->paused()) return false; static Image* mediaOverlayPlay = platformResource("mediaplayerOverlayPlay", "mediaplayerOverlayPlayNew"); IntRect buttonRect(rect); if (RuntimeEnabledFeatures::newMediaPlaybackUiEnabled()) { // Overlay play button covers the entire player, so center and draw a // smaller button. Center in the entire element. const LayoutBox* box = mediaElement->layoutObject()->enclosingBox(); if (!box) return false; int mediaHeight = box->pixelSnappedHeight(); buttonRect.setX(rect.center().x() - mediaOverlayPlayButtonWidthNew / 2); buttonRect.setY(rect.center().y() - mediaOverlayPlayButtonHeightNew / 2 + (mediaHeight - rect.height()) / 2); buttonRect.setWidth(mediaOverlayPlayButtonWidthNew); buttonRect.setHeight(mediaOverlayPlayButtonHeightNew); } return paintMediaButton(paintInfo.context, buttonRect, mediaOverlayPlay); }
void findGoodTouchTargets(const IntRect& touchBox, LocalFrame* mainFrame, Vector<IntRect>& goodTargets, WillBeHeapVector<RawPtrWillBeMember<Node> >& highlightNodes) { goodTargets.clear(); int touchPointPadding = ceil(std::max(touchBox.width(), touchBox.height()) * 0.5); IntPoint touchPoint = touchBox.center(); IntPoint contentsPoint = mainFrame->view()->windowToContents(touchPoint); HitTestResult result = mainFrame->eventHandler().hitTestResultAtPoint(contentsPoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent, IntSize(touchPointPadding, touchPointPadding)); const WillBeHeapListHashSet<RefPtrWillBeMember<Node> >& hitResults = result.rectBasedTestResult(); // Blacklist nodes that are container of disambiguated nodes. // It is not uncommon to have a clickable <div> that contains other clickable objects. // This heuristic avoids excessive disambiguation in that case. WillBeHeapHashSet<RawPtrWillBeMember<Node> > blackList; for (WillBeHeapListHashSet<RefPtrWillBeMember<Node> >::const_iterator it = hitResults.begin(); it != hitResults.end(); ++it) { // Ignore any Nodes that can't be clicked on. RenderObject* renderer = it->get()->renderer(); if (!renderer || !it->get()->willRespondToMouseClickEvents()) continue; // Blacklist all of the Node's containers. for (RenderBlock* container = renderer->containingBlock(); container; container = container->containingBlock()) { Node* containerNode = container->node(); if (!containerNode) continue; if (!blackList.add(containerNode).isNewEntry) break; } } WillBeHeapHashMap<RawPtrWillBeMember<Node>, TouchTargetData> touchTargets; float bestScore = 0; for (WillBeHeapListHashSet<RefPtrWillBeMember<Node> >::const_iterator it = hitResults.begin(); it != hitResults.end(); ++it) { for (Node* node = it->get(); node; node = node->parentNode()) { if (blackList.contains(node)) continue; if (node->isDocumentNode() || isHTMLHtmlElement(*node) || isHTMLBodyElement(*node)) break; if (node->willRespondToMouseClickEvents()) { TouchTargetData& targetData = touchTargets.add(node, TouchTargetData()).storedValue->value; targetData.windowBoundingBox = boundingBoxForEventNodes(node); targetData.score = scoreTouchTarget(touchPoint, touchPointPadding, targetData.windowBoundingBox); bestScore = std::max(bestScore, targetData.score); break; } } } for (WillBeHeapHashMap<RawPtrWillBeMember<Node>, TouchTargetData>::iterator it = touchTargets.begin(); it != touchTargets.end(); ++it) { // Currently the scoring function uses the overlap area with the fat point as the score. // We ignore the candidates that has less than 1/2 overlap (we consider not really ambiguous enough) than the best candidate to avoid excessive popups. if (it->value.score < bestScore * 0.5) continue; goodTargets.append(it->value.windowBoundingBox); highlightNodes.append(it->key); } }
bool snapTo(const SubtargetGeometry& geom, const IntPoint& touchPoint, const IntRect& touchArea, IntPoint& adjustedPoint) { FrameView* view = geom.node()->document()->view(); FloatQuad quad = geom.quad(); if (quad.isRectilinear()) { IntRect contentBounds = geom.boundingBox(); // Convert from frame coordinates to window coordinates. IntRect bounds = view->contentsToWindow(contentBounds); if (bounds.contains(touchPoint)) { adjustedPoint = touchPoint; return true; } if (bounds.intersects(touchArea)) { bounds.intersect(touchArea); adjustedPoint = bounds.center(); return true; } return false; } // The following code tries to adjust the point to place inside a both the touchArea and the non-rectilinear quad. // FIXME: This will return the point inside the touch area that is the closest to the quad center, but does not // guarantee that the point will be inside the quad. Corner-cases exist where the quad will intersect but this // will fail to adjust the point to somewhere in the intersection. // Convert quad from content to window coordinates. FloatPoint p1 = contentsToWindow(view, quad.p1()); FloatPoint p2 = contentsToWindow(view, quad.p2()); FloatPoint p3 = contentsToWindow(view, quad.p3()); FloatPoint p4 = contentsToWindow(view, quad.p4()); quad = FloatQuad(p1, p2, p3, p4); if (quad.containsPoint(touchPoint)) { adjustedPoint = touchPoint; return true; } // Pull point towards the center of the element. FloatPoint center = quad.center(); adjustPointToRect(center, touchArea); adjustedPoint = roundedIntPoint(center); return quad.containsPoint(adjustedPoint); }
static bool isRectInDirection(FocusDirection direction, const IntRect& curRect, const IntRect& targetRect) { IntPoint center(targetRect.center()); int targetMiddle = isHorizontalMove(direction) ? center.x() : center.y(); switch (direction) { case FocusDirectionLeft: return targetMiddle < curRect.x(); case FocusDirectionRight: return targetMiddle > curRect.right(); case FocusDirectionUp: return targetMiddle < curRect.y(); case FocusDirectionDown: return targetMiddle > curRect.bottom(); default: ASSERT_NOT_REACHED(); } return false; }
HRESULT AccessibleText::scrollSubstringTo(long startIndex, long endIndex, enum IA2ScrollType scrollType) { if (initialCheck() == E_POINTER) return E_POINTER; startIndex = convertSpecialOffset(startIndex); endIndex = convertSpecialOffset(endIndex); VisiblePositionRange textRange = m_object->visiblePositionRangeForRange(PlainTextRange(startIndex, endIndex-startIndex)); if (textRange.start.isNull() || textRange.end.isNull()) return S_FALSE; IntRect boundingBox = makeRange(textRange.start, textRange.end)->absoluteBoundingBox(); switch (scrollType) { case IA2_SCROLL_TYPE_TOP_LEFT: m_object->scrollToGlobalPoint(boundingBox.minXMinYCorner()); break; case IA2_SCROLL_TYPE_BOTTOM_RIGHT: m_object->scrollToGlobalPoint(boundingBox.maxXMaxYCorner()); break; case IA2_SCROLL_TYPE_TOP_EDGE: m_object->scrollToGlobalPoint(IntPoint((boundingBox.x() + boundingBox.maxX()) / 2, boundingBox.y())); break; case IA2_SCROLL_TYPE_BOTTOM_EDGE: m_object->scrollToGlobalPoint(IntPoint((boundingBox.x() + boundingBox.maxX()) / 2, boundingBox.maxY())); break; case IA2_SCROLL_TYPE_LEFT_EDGE: m_object->scrollToGlobalPoint(IntPoint(boundingBox.x(), (boundingBox.y() + boundingBox.maxY()) / 2)); break; case IA2_SCROLL_TYPE_RIGHT_EDGE: m_object->scrollToGlobalPoint(IntPoint(boundingBox.maxX(), (boundingBox.y() + boundingBox.maxY()) / 2)); break; case IA2_SCROLL_TYPE_ANYWHERE: m_object->scrollToGlobalPoint(boundingBox.center()); break; default: return E_INVALIDARG; } return S_OK; }
void MediaControlsPainter::adjustMediaSliderThumbPaintSize(const IntRect& rect, const ComputedStyle& style, IntRect& rectOut) { // Adjust the rectangle to be centered, the right size for the image. // We do this because it's quite hard to get the thumb touch target // to match. So, we provide the touch target size with // adjustMediaSliderThumbSize(), and scale it back when we paint. rectOut = rect; if (!RuntimeEnabledFeatures::newMediaPlaybackUiEnabled()) { // ...except for the old UI. return; } float zoomLevel = style.effectiveZoom(); float zoomedPaintWidth = mediaSliderThumbPaintWidthNew * zoomLevel; float zoomedPaintHeight = mediaSliderThumbPaintHeightNew * zoomLevel; rectOut.setX(rect.center().x() - zoomedPaintWidth / 2); rectOut.setY(rect.center().y() - zoomedPaintHeight / 2); rectOut.setWidth(zoomedPaintWidth); rectOut.setHeight(zoomedPaintHeight); }
static inline int middle(FocusDirection direction, const IntRect& rect) { IntPoint center(rect.center()); return isHorizontalMove(direction) ? center.y(): center.x(); }
void WKViewFindZoomableAreaForRect(WKViewRef viewRef, WKRect wkRect) { IntRect rect = toIntRect(wkRect); toImpl(viewRef)->findZoomableAreaForPoint(rect.center(), rect.size()); }
void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, MediaButton buttonType, bool translucent, bool drawBackground, const IntRect& thumb)//4.2 Merge { if (!gDecoded) { Decode(); } if (!canvas) return; // If we failed to decode, do nothing. This way the browser still works, // and webkit will still draw the label and layout space for us. if (gDecodingFailed) return; bool drawsNinePatch = false; bool drawsImage = true; int ninePatchIndex = 0; int imageIndex = 0; SkRect bounds(r); SkScalar imageMargin = 8; SkPaint paint; int alpha = 255; if (translucent) alpha = 190; SkColor backgroundColor = SkColorSetARGB(alpha, 34, 34, 34); SkColor trackBackgroundColor = SkColorSetARGB(255, 100, 100, 100); paint.setColor(backgroundColor); //Android KITKAT Merge - START // paint.setFlags(SkPaint::kFilterBitmap_Flag); //P140210-04273 : // In D2 Device if we pass kMedium_FilterLevel image gets corrupted while drawing the image into the canvas. So loading icons was corrupting // kLow_FilterLevel works for other kitkat devices. //WAS paint.setFilterLevel(SkPaint::kMedium_FilterLevel); paint.setFilterLevel(SkPaint::kLow_FilterLevel); //Android KITKAT Merge - END switch (buttonType) { case PAUSE: case PLAY: case MUTE: case REWIND: case FORWARD: case FULLSCREEN: { imageIndex = buttonType + 1; paint.setColor(backgroundColor); break; } case SPINNER_OUTER: case SPINNER_INNER: case VIDEO: { imageIndex = buttonType + 1; break; } case BACKGROUND_SLIDER: { drawsImage = false; break; } case SLIDER_TRACK: { drawsNinePatch = true; drawsImage = false; ninePatchIndex = buttonType + 1; break; } case SLIDER_THUMB: { imageMargin = 0; imageIndex = buttonType + 1; break; } default: return; } if (drawBackground) { canvas->drawRect(r, paint); } if (drawsNinePatch) { const PatchData& pd = gFiles[ninePatchIndex]; int marginValue = pd.margin + pd.outset; SkIRect margin; margin.set(marginValue, marginValue, marginValue, marginValue); if (buttonType == SLIDER_TRACK) { // Cut the height in half (with some extra slop determined by trial // and error to get the placement just right. SkScalar quarterHeight = SkScalarHalf(SkScalarHalf(bounds.height())); bounds.fTop += quarterHeight + SkScalarHalf(3); bounds.fBottom += -quarterHeight + SK_ScalarHalf; if (!thumb.isEmpty()) {//4.2 Merge // Inset the track by half the width of the thumb, so the track // does not appear to go beyond the space where the thumb can // be. SkScalar thumbHalfWidth = SkIntToScalar(thumb.width()/2); bounds.fLeft += thumbHalfWidth; bounds.fRight -= thumbHalfWidth; if (thumb.x() > 0) { // The video is past the starting point. Show the area to // left of the thumb as having been played. SkScalar alreadyPlayed = SkIntToScalar(thumb.center().x() + r.x()); SkRect playedRect(bounds); playedRect.fRight = alreadyPlayed; SkNinePatch::DrawNine(canvas, playedRect, gButton[0], margin); bounds.fLeft = alreadyPlayed; } } } SkNinePatch::DrawNine(canvas, bounds, gButton[ninePatchIndex], margin); } if (drawsImage) { SkScalar SIZE = gButton[imageIndex].width(); SkScalar width = r.width(); SkScalar scale = SkScalarDiv(width - 2*imageMargin, SIZE); int saveScaleCount = canvas->save(); canvas->translate(bounds.fLeft + imageMargin, bounds.fTop + imageMargin); canvas->scale(scale, scale); canvas->drawBitmap(gButton[imageIndex], 0, 0, &paint); canvas->restoreToCount(saveScaleCount); } }
void TouchActionTest::runTestOnTree(ContainerNode* root, WebView* webView, TouchActionTrackingWebViewClient& client) { // Find all elements to test the touch-action of in the document. TrackExceptionState es; // Oilpan: see runTouchActionTest() comment why these are persistent // references. Persistent<StaticElementList> elements = root->querySelectorAll("[expected-action]", es); ASSERT_FALSE(es.hadException()); for (unsigned index = 0; index < elements->length(); index++) { Element* element = elements->item(index); element->scrollIntoViewIfNeeded(); std::string failureContext("Test case: "); if (element->hasID()) { failureContext.append(element->getIdAttribute().ascii().data()); } else if (element->firstChild()) { failureContext.append("\""); failureContext.append(element->firstChild() ->textContent(false) .stripWhiteSpace() .ascii() .data()); failureContext.append("\""); } else { failureContext += "<missing ID>"; } // Run each test three times at different positions in the element. // Note that we don't want the bounding box because our tests sometimes have // elements with multiple border boxes with other elements in between. Use // the first border box (which we can easily visualize in a browser for // debugging). Persistent<ClientRectList> rects = element->getClientRects(); ASSERT_GE(rects->length(), 0u) << failureContext; Persistent<ClientRect> r = rects->item(0); FloatRect clientFloatRect = FloatRect(r->left(), r->top(), r->width(), r->height()); IntRect clientRect = enclosedIntRect(clientFloatRect); for (int locIdx = 0; locIdx < 3; locIdx++) { IntPoint framePoint; std::stringstream contextStream; contextStream << failureContext << " ("; switch (locIdx) { case 0: framePoint = clientRect.center(); contextStream << "center"; break; case 1: framePoint = clientRect.location(); contextStream << "top-left"; break; case 2: framePoint = clientRect.maxXMaxYCorner(); framePoint.move(-1, -1); contextStream << "bottom-right"; break; default: FAIL() << "Invalid location index."; } IntPoint windowPoint = root->document().frame()->view()->convertToRootFrame(framePoint); contextStream << "=" << windowPoint.x() << "," << windowPoint.y() << ")."; std::string failureContextPos = contextStream.str(); LocalFrame* mainFrame = static_cast<LocalFrame*>(webView->mainFrame()->toImplBase()->frame()); FrameView* mainFrameView = mainFrame->view(); IntRect visibleRect = windowClipRect(*mainFrameView); ASSERT_TRUE(visibleRect.contains(windowPoint)) << failureContextPos << " Test point not contained in visible area: " << visibleRect.x() << "," << visibleRect.y() << "-" << visibleRect.maxX() << "," << visibleRect.maxY(); // First validate that a hit test at this point will really hit the // element we intended. This is the easiest way for a test to be broken, // but has nothing really to do with touch action. Note that we can't use // WebView's hit test API because it doesn't look into shadow DOM. IntPoint docPoint(mainFrameView->frameToContents(windowPoint)); HitTestResult result = mainFrame->eventHandler().hitTestResultAtPoint( docPoint, HitTestRequest::ReadOnly | HitTestRequest::Active); ASSERT_EQ(element, result.innerElement()) << "Unexpected hit test result " << failureContextPos << " Got element: \"" << result.innerElement() ->outerHTML() .stripWhiteSpace() .left(80) .ascii() .data() << "\"" << std::endl << "Document render tree:" << std::endl << externalRepresentation(root->document().frame()).utf8().data(); // Now send the touch event and check any touch action result. sendTouchEvent(webView, WebInputEvent::TouchStart, windowPoint); AtomicString expectedAction = element->getAttribute("expected-action"); if (expectedAction == "auto") { // Auto is the default - no action set. EXPECT_EQ(0, client.touchActionSetCount()) << failureContextPos; EXPECT_EQ(WebTouchActionAuto, client.lastTouchAction()) << failureContextPos; } else { // Should have received exactly one touch action. EXPECT_EQ(1, client.touchActionSetCount()) << failureContextPos; if (client.touchActionSetCount()) { if (expectedAction == "none") { EXPECT_EQ(WebTouchActionNone, client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "pan-x") { EXPECT_EQ(WebTouchActionPanX, client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "pan-y") { EXPECT_EQ(WebTouchActionPanY, client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "pan-x-y") { EXPECT_EQ((WebTouchActionPan), client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "manipulation") { EXPECT_EQ((WebTouchActionManipulation), client.lastTouchAction()) << failureContextPos; } else { FAIL() << "Unrecognized expected-action \"" << expectedAction.ascii().data() << "\" " << failureContextPos; } } } // Reset webview touch state. client.reset(); sendTouchEvent(webView, WebInputEvent::TouchCancel, windowPoint); EXPECT_EQ(0, client.touchActionSetCount()); } } }