void RenderNamedFlowThread::checkInvalidRegions() { Vector<RenderNamedFlowFragment*> newValidFragments; for (auto& region : m_invalidRegionList) { RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(region); // The only reason a region would be invalid is because it has a parent flow thread. ASSERT(!namedFlowFragment->isValid() && namedFlowFragment->parentNamedFlowThread()); if (namedFlowFragment->parentNamedFlowThread()->dependsOn(this)) continue; newValidFragments.append(namedFlowFragment); } for (auto& namedFlowFragment : newValidFragments) { m_invalidRegionList.remove(namedFlowFragment); namedFlowFragment->parentNamedFlowThread()->m_observerThreadsSet.remove(this); addFragmentToNamedFlowThread(namedFlowFragment); } if (!newValidFragments.isEmpty()) invalidateRegions(); if (m_observerThreadsSet.isEmpty()) return; // Notify all the flow threads that were dependent on this flow. // Create a copy of the list first. That's because observers might change the list when calling checkInvalidRegions. Vector<RenderNamedFlowThread*> observers; copyToVector(m_observerThreadsSet, observers); for (auto& flowThread : observers) flowThread->checkInvalidRegions(); }
void RenderNamedFlowThread::removeRegionFromThread(RenderRegion* renderRegion) { ASSERT(renderRegion); RenderNamedFlowFragment* renderNamedFlowFragment = toRenderNamedFlowFragment(renderRegion); if (renderNamedFlowFragment->parentNamedFlowThread()) { if (!renderNamedFlowFragment->isValid()) { ASSERT(m_invalidRegionList.contains(renderNamedFlowFragment)); m_invalidRegionList.remove(renderNamedFlowFragment); renderNamedFlowFragment->parentNamedFlowThread()->m_observerThreadsSet.remove(this); // No need to invalidate the regions rectangles. The removed region // was not taken into account. Just return here. return; } removeDependencyOnFlowThread(renderNamedFlowFragment->parentNamedFlowThread()); } ASSERT(m_regionList.contains(renderNamedFlowFragment)); bool wasFirst = m_regionList.first() == renderNamedFlowFragment; m_regionList.remove(renderNamedFlowFragment); if (canBeDestroyed()) setMarkForDestruction(); if (!m_regionList.isEmpty() && wasFirst) updateWritingMode(); invalidateRegions(); }
void RenderNamedFlowThread::updateWritingMode() { RenderNamedFlowFragment* firstFragment = toRenderNamedFlowFragment(m_regionList.first()); if (!firstFragment) return; if (style().writingMode() == firstFragment->style().writingMode()) return; // The first region defines the principal writing mode for the entire flow. auto newStyle = RenderStyle::clone(&style()); newStyle.get().setWritingMode(firstFragment->style().writingMode()); setStyle(WTF::move(newStyle)); }
RenderNamedFlowFragment* RenderNamedFlowThread::fragmentFromAbsolutePointAndBox(const IntPoint& absolutePoint, const RenderBox& flowedBox) { RenderRegion* startRegion = nullptr; RenderRegion* endRegion = nullptr; if (!getRegionRangeForBox(&flowedBox, startRegion, endRegion)) return nullptr; for (auto iter = m_regionList.find(startRegion), end = m_regionList.end(); iter != end; ++iter) { RenderNamedFlowFragment* fragment = toRenderNamedFlowFragment(*iter); RenderBlockFlow& fragmentContainer = fragment->fragmentContainer(); IntRect fragmentAbsoluteRect(roundedIntPoint(fragmentContainer.localToAbsolute()), roundedIntSize(fragmentContainer.paddingBoxRect().size())); if (fragmentAbsoluteRect.contains(absolutePoint)) return fragment; if (fragment == endRegion) break; } return nullptr; }
void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion) { ASSERT(renderRegion); ASSERT(!renderRegion->isValid()); RenderNamedFlowFragment* renderNamedFlowFragment = toRenderNamedFlowFragment(renderRegion); resetMarkForDestruction(); if (renderNamedFlowFragment->parentNamedFlowThread() && renderNamedFlowFragment->parentNamedFlowThread()->dependsOn(this)) { // The order of invalid regions is irrelevant. m_invalidRegionList.add(renderNamedFlowFragment); // Register ourself to get a notification when the state changes. renderNamedFlowFragment->parentNamedFlowThread()->m_observerThreadsSet.add(this); return; } addFragmentToNamedFlowThread(renderNamedFlowFragment); invalidateRegions(); }
bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseMask) return false; if (!paintInfo.shouldPaintWithinRoot(*this)) return false; // if we're invisible or haven't received a layout yet, then just bail. if (style().visibility() != VISIBLE) return false; RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); // Check our region range to make sure we need to be painting in this region. if (namedFlowFragment && !namedFlowFragment->flowThread()->objectShouldFragmentInFlowRegion(this, namedFlowFragment)) return false; LayoutPoint adjustedPaintOffset = paintOffset + location(); // Early exit if the element touches the edges. LayoutUnit top = adjustedPaintOffset.y() + visualOverflowRect().y(); LayoutUnit bottom = adjustedPaintOffset.y() + visualOverflowRect().maxY(); if (isSelected() && m_inlineBoxWrapper) { const RootInlineBox& rootBox = m_inlineBoxWrapper->root(); LayoutUnit selTop = paintOffset.y() + rootBox.selectionTop(); LayoutUnit selBottom = paintOffset.y() + selTop + rootBox.selectionHeight(); top = std::min(selTop, top); bottom = std::max(selBottom, bottom); } LayoutRect localRepaintRect = paintInfo.rect; adjustRectWithMaximumOutline(paintInfo.phase, localRepaintRect); if (adjustedPaintOffset.x() + visualOverflowRect().x() >= localRepaintRect.maxX() || adjustedPaintOffset.x() + visualOverflowRect().maxX() <= localRepaintRect.x()) return false; if (top >= localRepaintRect.maxY() || bottom <= localRepaintRect.y()) return false; return true; }
static PassRefPtr<InspectorObject> buildObjectForElementInfo(Node* node) { if (!node->isElementNode() || !node->document().frame()) return nullptr; RefPtr<InspectorObject> elementInfo = InspectorObject::create(); Element* element = toElement(node); bool isXHTML = element->document().isXHTMLDocument(); elementInfo->setString("tagName", isXHTML ? element->nodeName() : element->nodeName().lower()); elementInfo->setString("idValue", element->getIdAttribute()); HashSet<AtomicString> usedClassNames; if (element->hasClass() && element->isStyledElement()) { StringBuilder classNames; const SpaceSplitString& classNamesString = toStyledElement(element)->classNames(); size_t classNameCount = classNamesString.size(); for (size_t i = 0; i < classNameCount; ++i) { const AtomicString& className = classNamesString[i]; if (usedClassNames.contains(className)) continue; usedClassNames.add(className); classNames.append('.'); classNames.append(className); } elementInfo->setString("className", classNames.toString()); } RenderElement* renderer = element->renderer(); Frame* containingFrame = node->document().frame(); FrameView* containingView = containingFrame->view(); IntRect boundingBox = pixelSnappedIntRect(containingView->contentsToRootView(renderer->absoluteBoundingBoxRect())); RenderBoxModelObject* modelObject = renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : nullptr; elementInfo->setString("nodeWidth", String::number(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), *modelObject) : boundingBox.width())); elementInfo->setString("nodeHeight", String::number(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), *modelObject) : boundingBox.height())); if (renderer->isRenderNamedFlowFragmentContainer()) { RenderNamedFlowFragment* region = toRenderBlockFlow(renderer)->renderNamedFlowFragment(); if (region->isValid()) { RenderFlowThread* flowThread = region->flowThread(); ASSERT(flowThread && flowThread->isRenderNamedFlowThread()); RefPtr<InspectorObject> regionFlowInfo = InspectorObject::create(); regionFlowInfo->setString("name", toRenderNamedFlowThread(flowThread)->flowThreadName()); regionFlowInfo->setArray("regions", buildObjectForCSSRegionsHighlight(region, flowThread)); elementInfo->setObject("regionFlowInfo", regionFlowInfo.release()); } } RenderFlowThread* containingFlowThread = renderer->flowThreadContainingBlock(); if (containingFlowThread && containingFlowThread->isRenderNamedFlowThread()) { RefPtr<InspectorObject> contentFlowInfo = InspectorObject::create(); contentFlowInfo->setString("name", toRenderNamedFlowThread(containingFlowThread)->flowThreadName()); elementInfo->setObject("contentFlowInfo", contentFlowInfo.release()); } #if ENABLE(CSS_SHAPES) if (renderer->isBox()) { RenderBox* renderBox = toRenderBox(renderer); if (RefPtr<InspectorObject> shapeObject = buildObjectForShapeOutside(containingFrame, renderBox)) elementInfo->setObject("shapeOutsideInfo", shapeObject.release()); } #endif // Need to enable AX to get the computed role. if (!WebCore::AXObjectCache::accessibilityEnabled()) WebCore::AXObjectCache::enableAccessibility(); if (AXObjectCache* axObjectCache = node->document().axObjectCache()) { if (AccessibilityObject* axObject = axObjectCache->getOrCreate(node)) elementInfo->setString("role", axObject->computedRoleString()); } return elementInfo.release(); }
LayoutRect RenderNamedFlowThread::decorationsClipRectForBoxInNamedFlowFragment(const RenderBox& box, RenderNamedFlowFragment& fragment) const { LayoutRect visualOverflowRect = fragment.visualOverflowRectForBox(&box); LayoutUnit initialLogicalX = style().isHorizontalWritingMode() ? visualOverflowRect.x() : visualOverflowRect.y(); // The visual overflow rect returned by visualOverflowRectForBox is already flipped but the // RenderRegion::rectFlowPortionForBox method expects it unflipped. flipForWritingModeLocalCoordinates(visualOverflowRect); visualOverflowRect = fragment.rectFlowPortionForBox(&box, visualOverflowRect); // Now flip it again. flipForWritingModeLocalCoordinates(visualOverflowRect); // Take the scrolled offset of this object's parents into consideration. IntSize scrolledContentOffset; RenderBlock* containingBlock = box.containingBlock(); while (containingBlock) { if (containingBlock->isRenderNamedFlowThread()) { // We've reached the flow thread, take the scrolled offset of the region into consideration. ASSERT(containingBlock == this); scrolledContentOffset += fragment.fragmentContainer().scrolledContentOffset(); break; } scrolledContentOffset += containingBlock->scrolledContentOffset(); containingBlock = containingBlock->containingBlock(); } if (!scrolledContentOffset.isZero()) { if (style().isFlippedBlocksWritingMode()) scrolledContentOffset = -scrolledContentOffset; visualOverflowRect.inflateX(scrolledContentOffset.width()); visualOverflowRect.inflateY(scrolledContentOffset.height()); } // Layers are in physical coordinates so the origin must be moved to the physical top-left of the flowthread. if (style().isFlippedBlocksWritingMode()) { if (style().isHorizontalWritingMode()) visualOverflowRect.moveBy(LayoutPoint(0, height())); else visualOverflowRect.moveBy(LayoutPoint(width(), 0)); } const RenderBox* iterBox = &box; while (iterBox && iterBox != this) { RenderBlock* containerBlock = iterBox->containingBlock(); // FIXME: This doesn't work properly with flipped writing modes. // https://bugs.webkit.org/show_bug.cgi?id=125149 if (iterBox->isPositioned()) { // For positioned elements, just use the layer's absolute bounding box. visualOverflowRect.moveBy(iterBox->layer()->absoluteBoundingBox().location()); break; } LayoutRect currentBoxRect = iterBox->frameRect(); if (iterBox->style().isFlippedBlocksWritingMode()) { if (iterBox->style().isHorizontalWritingMode()) currentBoxRect.setY(currentBoxRect.height() - currentBoxRect.maxY()); else currentBoxRect.setX(currentBoxRect.width() - currentBoxRect.maxX()); } if (containerBlock->style().writingMode() != iterBox->style().writingMode()) iterBox->flipForWritingMode(currentBoxRect); visualOverflowRect.moveBy(currentBoxRect.location()); iterBox = containerBlock; } // Since the purpose of this method is to make sure the borders of a fragmented // element don't overflow the region in the fragmentation direction, there's no // point in restricting the clipping rect on the logical X axis. // This also saves us the trouble of handling percent-based widths and margins // since the absolute bounding box of a positioned element would not contain // the correct coordinates relative to the region we're interested in, but rather // relative to the actual flow thread. if (style().isHorizontalWritingMode()) { if (initialLogicalX < visualOverflowRect.x()) visualOverflowRect.shiftXEdgeTo(initialLogicalX); if (visualOverflowRect.width() < frameRect().width()) visualOverflowRect.setWidth(frameRect().width()); } else { if (initialLogicalX < visualOverflowRect.y()) visualOverflowRect.shiftYEdgeTo(initialLogicalX); if (visualOverflowRect.height() < frameRect().height()) visualOverflowRect.setHeight(frameRect().height()); } return visualOverflowRect; }