void BlockPainter::paintContinuationOutlines(const PaintInfo& info, const LayoutPoint& paintOffset) { LayoutInline* inlineCont = m_layoutBlock.inlineElementContinuation(); if (inlineCont && inlineCont->style()->hasOutline() && inlineCont->style()->visibility() == VISIBLE) { LayoutInline* inlineLayoutObject = toLayoutInline(inlineCont->node()->layoutObject()); LayoutBlock* cb = m_layoutBlock.containingBlock(); bool inlineEnclosedInSelfPaintingLayer = false; for (LayoutBoxModelObject* box = inlineLayoutObject; box != cb; box = box->parent()->enclosingBoxModelObject()) { if (box->hasSelfPaintingLayer()) { inlineEnclosedInSelfPaintingLayer = true; break; } } // Do not add continuations for outline painting by our containing block if we are a relative positioned // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on layoutObjects in its continuation table being // in the same layer. if (!inlineEnclosedInSelfPaintingLayer && !m_layoutBlock.hasLayer()) { cb->addContinuationWithOutline(inlineLayoutObject); } else if (!inlineLayoutObject->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && m_layoutBlock.hasLayer())) { // The outline might be painted multiple times if multiple blocks have the same inline element continuation, and the inline has a self-painting layer. ScopeRecorder scopeRecorder(*info.context); InlinePainter(*inlineLayoutObject).paintOutline(info, paintOffset - m_layoutBlock.locationOffset() + inlineLayoutObject->containingBlock()->location()); } } ContinuationOutlineTableMap* table = continuationOutlineTable(); if (table->isEmpty()) return; OwnPtr<ListHashSet<LayoutInline*>> continuations = table->take(&m_layoutBlock); if (!continuations) return; LayoutPoint accumulatedPaintOffset = paintOffset; // Paint each continuation outline. ListHashSet<LayoutInline*>::iterator end = continuations->end(); for (ListHashSet<LayoutInline*>::iterator it = continuations->begin(); it != end; ++it) { // Need to add in the coordinates of the intervening blocks. LayoutInline* flow = *it; LayoutBlock* block = flow->containingBlock(); for ( ; block && block != &m_layoutBlock; block = block->containingBlock()) accumulatedPaintOffset.moveBy(block->location()); ASSERT(block); InlinePainter(*flow).paintOutline(info, accumulatedPaintOffset); } }
void findGoodTouchTargets(const IntRect& touchBoxInRootFrame, LocalFrame* mainFrame, Vector<IntRect>& goodTargets, WillBeHeapVector<RawPtrWillBeMember<Node>>& highlightNodes) { goodTargets.clear(); int touchPointPadding = ceil(std::max(touchBoxInRootFrame.width(), touchBoxInRootFrame.height()) * 0.5); IntPoint touchPoint = touchBoxInRootFrame.center(); IntPoint contentsPoint = mainFrame->view()->rootFrameToContents(touchPoint); HitTestResult result = mainFrame->eventHandler().hitTestResultAtPoint(contentsPoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ListBased, LayoutSize(touchPointPadding, touchPointPadding)); const WillBeHeapListHashSet<RefPtrWillBeMember<Node>>& hitResults = result.listBasedTestResult(); // 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 (const auto& hitResult : hitResults) { // Ignore any Nodes that can't be clicked on. LayoutObject* layoutObject = hitResult.get()->layoutObject(); if (!layoutObject || !hitResult.get()->willRespondToMouseClickEvents()) continue; // Blacklist all of the Node's containers. for (LayoutBlock* container = layoutObject->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 (const auto& hitResult : hitResults) { for (Node* node = hitResult.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 (const auto& touchTarget : touchTargets) { // 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 (touchTarget.value.score < bestScore * 0.5) continue; goodTargets.append(touchTarget.value.windowBoundingBox); highlightNodes.append(touchTarget.key); } }
void InlineFlowBoxPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutUnit lineTop, const LayoutUnit lineBottom) { LayoutRect overflowRect(m_inlineFlowBox.visualOverflowRect(lineTop, lineBottom)); m_inlineFlowBox.flipForWritingMode(overflowRect); overflowRect.moveBy(paintOffset); if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect))) return; if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) { // Add ourselves to the paint info struct's list of inlines that need to paint their // outlines. if (m_inlineFlowBox.layoutObject().style()->visibility() == VISIBLE && m_inlineFlowBox.layoutObject().style()->hasOutline() && !m_inlineFlowBox.isRootInlineBox()) { LayoutInline& inlineFlow = toLayoutInline(m_inlineFlowBox.layoutObject()); LayoutBlock* cb = 0; bool containingBlockPaintsContinuationOutline = inlineFlow.continuation() || inlineFlow.isInlineElementContinuation(); if (containingBlockPaintsContinuationOutline) { // FIXME: See https://bugs.webkit.org/show_bug.cgi?id=54690. We currently don't reconnect inline continuations // after a child removal. As a result, those merged inlines do not get seperated and hence not get enclosed by // anonymous blocks. In this case, it is better to bail out and paint it ourself. LayoutBlock* enclosingAnonymousBlock = m_inlineFlowBox.layoutObject().containingBlock(); if (!enclosingAnonymousBlock->isAnonymousBlock()) { containingBlockPaintsContinuationOutline = false; } else { cb = enclosingAnonymousBlock->containingBlock(); for (LayoutBoxModelObject* box = m_inlineFlowBox.boxModelObject(); box != cb; box = box->parent()->enclosingBoxModelObject()) { if (box->hasSelfPaintingLayer()) { containingBlockPaintsContinuationOutline = false; break; } } } } if (containingBlockPaintsContinuationOutline) { // Add ourselves to the containing block of the entire continuation so that it can // paint us atomically. cb->addContinuationWithOutline(toLayoutInline(m_inlineFlowBox.layoutObject().node()->layoutObject())); } else if (!inlineFlow.isInlineElementContinuation()) { paintInfo.outlineObjects()->add(&inlineFlow); } } } else if (paintInfo.phase == PaintPhaseMask) { DrawingRecorder recorder(*paintInfo.context, m_inlineFlowBox, DisplayItem::paintPhaseToDrawingType(paintInfo.phase), pixelSnappedIntRect(overflowRect)); if (!recorder.canUseCachedDrawing()) paintMask(paintInfo, paintOffset); return; } else if (paintInfo.phase == PaintPhaseForeground) { // Paint our background, border and box-shadow. paintBoxDecorationBackground(paintInfo, paintOffset); } // Paint our children. if (paintInfo.phase != PaintPhaseSelfOutline) { PaintInfo childInfo(paintInfo); childInfo.phase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase; if (childInfo.paintingRoot && childInfo.paintingRoot->isDescendantOf(&m_inlineFlowBox.layoutObject())) childInfo.paintingRoot = 0; else childInfo.updatePaintingRootForChildren(&m_inlineFlowBox.layoutObject()); for (InlineBox* curr = m_inlineFlowBox.firstChild(); curr; curr = curr->nextOnLine()) { if (curr->layoutObject().isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) curr->paint(childInfo, paintOffset, lineTop, lineBottom); } } }