blob: 0cb694fec9d7d3142a41090f714051c32c4fd45d [file] [log] [blame]
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
* Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#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 "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, const LayoutRect& other)
{
if (rect.isEmpty())
return;
LayoutUnit leftSide = rect.x();
LayoutUnit rightSide = rect.maxX();
if (!other.isEmpty() && strikes(rect, other)) {
leftSide = std::min(leftSide, other.x());
rightSide = std::max(rightSide, other.maxX());
}
rect.setX(leftSide);
rect.setWidth(rightSide - leftSide);
}
inline void addHighlightRect(Path& path, const LayoutRect& rect, const LayoutRect& prev, const LayoutRect& next)
{
// The rounding check depends on the rects not intersecting eachother,
// or being contained for that matter.
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 pathForRenderer(RenderObject* o)
{
ASSERT(o);
Path path;
Vector<IntRect> rects;
o->addFocusRingRects(rects, /* acc. offset */ ownerFrameToMainFrameOffset(o));
if (rects.isEmpty())
return path;
// The basic idea is to allow up to three different boxes in order to highlight
// text with line breaks more nicer than using a bounding box.
// Merge all center boxes (all but the first and the last).
LayoutRect mid;
// Set the end value to integer. It ensures that no unsigned int overflow occurs
// in the test expression, in case of empty rects vector.
int end = rects.size() - 1;
for (int i = 1; i < end; ++i)
mid.uniteIfNonZero(rects.at(i));
Vector<LayoutRect> drawableRects;
if (!mid.isEmpty())
drawableRects.append(mid);
// Add the first box, but merge it with the center boxes if it intersects.
if (rects.size() && !rects.first().isEmpty()) {
// Adjust center boxes to boundary of first
if (drawableRects.size())
shiftXEdgesToContainIfStrikes(drawableRects.last(), rects.first());
if (drawableRects.size() && drawableRects.last().intersects(rects.first()))
drawableRects.last().unite(rects.first());
else
drawableRects.prepend(rects.first());
}
// Add the last box, but merge it with the center boxes if it intersects.
if (rects.size() > 1 && !rects.last().isEmpty()) {
// Adjust center boxes to boundary of last
if (drawableRects.size())
shiftXEdgesToContainIfStrikes(drawableRects.last(), rects.last());
if (drawableRects.size() && drawableRects.last().intersects(rects.last()))
drawableRects.last().unite(rects.last());
else
drawableRects.append(rects.last());
}
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);
}
return path;
}
} // anonymous namespace
namespace GestureTapHighlighter {
Path pathForNodeHighlight(const Node* node)
{
RenderObject* renderer = node->renderer();
Path path;
if (!renderer || (!renderer->isBox() && !renderer->isRenderInline()))
return path;
path = pathForRenderer(renderer);
path.transform(localToAbsoluteTransform(renderer));
return path;
}
} // namespace GestureTapHighlighter
} // namespace WebCore