GestureTapHighlighter.cpp [plain text]
#include "config.h"
#include "GestureTapHighlighter.h"
#include "Element.h"
#include "Frame.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "GraphicsTypes.h"
#include "Node.h"
#include "Page.h"
#include "RenderBoxModelObject.h"
#include "RenderInline.h"
#include "RenderLayer.h"
#include "RenderObject.h"
namespace WebCore {
namespace {
inline LayoutPoint ownerFrameToMainFrameOffset(const RenderObject* o)
{
ASSERT(o->node());
Frame* containingFrame = o->frame();
if (!containingFrame)
return LayoutPoint();
Frame* mainFrame = containingFrame->page()->mainFrame();
LayoutPoint mainFramePoint = mainFrame->view()->windowToContents(containingFrame->view()->contentsToWindow(IntPoint()));
return mainFramePoint;
}
AffineTransform localToAbsoluteTransform(const RenderObject* o)
{
AffineTransform transform;
LayoutPoint referencePoint;
while (o) {
RenderObject* nextContainer = o->container();
if (!nextContainer)
break;
LayoutSize containerOffset = o->offsetFromContainer(nextContainer, referencePoint);
TransformationMatrix t;
o->getTransformFromContainer(nextContainer, containerOffset, t);
transform = t.toAffineTransform() * transform;
referencePoint.move(containerOffset);
o = nextContainer;
}
return transform;
}
inline bool contains(const LayoutRect& rect, int x)
{
return !rect.isEmpty() && x >= rect.x() && x <= rect.maxX();
}
inline bool strikes(const LayoutRect& a, const LayoutRect& b)
{
return !a.isEmpty() && !b.isEmpty()
&& a.x() <= b.maxX() && b.x() <= a.maxX()
&& a.y() <= b.maxY() && b.y() <= a.maxY();
}
inline void shiftXEdgesToContainIfStrikes(LayoutRect& rect, LayoutRect& other, bool isFirst)
{
if (rect.isEmpty())
return;
if (other.isEmpty() || !strikes(rect, other))
return;
LayoutUnit leftSide = std::min(rect.x(), other.x());
LayoutUnit rightSide = std::max(rect.maxX(), other.maxX());
rect.shiftXEdgeTo(leftSide);
rect.shiftMaxXEdgeTo(rightSide);
if (isFirst)
other.shiftMaxXEdgeTo(rightSide);
else
other.shiftXEdgeTo(leftSide);
}
inline void addHighlightRect(Path& path, const LayoutRect& rect, const LayoutRect& prev, const LayoutRect& next)
{
ASSERT(!rect.intersects(prev));
ASSERT(!rect.intersects(next));
if (rect.isEmpty())
return;
const int rounding = 4;
FloatRect copy(rect);
copy.inflateX(rounding);
copy.inflateY(rounding / 2);
FloatSize rounded(rounding * 1.8, rounding * 1.8);
FloatSize squared(0, 0);
path.addBeziersForRoundedRect(copy,
contains(prev, rect.x()) ? squared : rounded,
contains(prev, rect.maxX()) ? squared : rounded,
contains(next, rect.x()) ? squared : rounded,
contains(next, rect.maxX()) ? squared : rounded);
}
Path absolutePathForRenderer(RenderObject* const o)
{
ASSERT(o);
Vector<IntRect> rects;
LayoutPoint frameOffset = ownerFrameToMainFrameOffset(o);
o->addFocusRingRects(rects, frameOffset);
if (rects.isEmpty())
return Path();
LayoutRect mid;
int end = rects.size() - 1;
for (int i = 1; i < end; ++i)
mid.uniteIfNonZero(rects.at(i));
LayoutRect first;
LayoutRect last;
if (rects.size() && !rects.first().isEmpty()) {
if (mid.isEmpty() || mid.intersects(rects.first()))
mid.unite(rects.first());
else {
first = rects.first();
shiftXEdgesToContainIfStrikes(mid, first, true);
}
}
if (rects.size() > 1 && !rects.last().isEmpty()) {
if (mid.intersects(rects.last()))
mid.unite(rects.last());
else {
last = rects.last();
shiftXEdgesToContainIfStrikes(mid, last, false);
}
}
Vector<LayoutRect> drawableRects;
if (!first.isEmpty())
drawableRects.append(first);
if (!mid.isEmpty())
drawableRects.append(mid);
if (!last.isEmpty())
drawableRects.append(last);
for (int i = drawableRects.size() - 1; i >= 0; --i) {
LayoutRect& ringRect = drawableRects.at(i);
LayoutPoint ringRectLocation = ringRect.location();
ringRect.moveBy(-frameOffset);
RenderLayer* layer = o->enclosingLayer();
RenderObject* currentRenderer = o;
for (; layer; layer = layer->parent()) {
RenderLayerModelObject* layerRenderer = layer->renderer();
if (layerRenderer->hasOverflowClip() && layerRenderer != currentRenderer) {
bool containerSkipped = false;
currentRenderer->container(layerRenderer, &containerSkipped);
if (containerSkipped)
continue;
ringRect.move(currentRenderer->offsetFromAncestorContainer(layerRenderer));
currentRenderer = layerRenderer;
ASSERT(layerRenderer->isBox());
ringRect.intersect(toRenderBox(layerRenderer)->borderBoxRect());
if (ringRect.isEmpty())
break;
}
}
if (ringRect.isEmpty()) {
drawableRects.remove(i);
continue;
}
ringRect.setLocation(ringRectLocation);
}
Path path;
for (size_t i = 0; i < drawableRects.size(); ++i) {
LayoutRect prev = i ? drawableRects.at(i - 1) : LayoutRect();
LayoutRect next = i < (drawableRects.size() - 1) ? drawableRects.at(i + 1) : LayoutRect();
addHighlightRect(path, drawableRects.at(i), prev, next);
}
path.transform(localToAbsoluteTransform(o));
return path;
}
}
namespace GestureTapHighlighter {
Path pathForNodeHighlight(const Node* node)
{
RenderObject* renderer = node->renderer();
if (!renderer || (!renderer->isBox() && !renderer->isRenderInline()))
return Path();
return absolutePathForRenderer(renderer);
}
}
}