1/*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "SimpleLineLayoutFunctions.h"
28
29#include "FontCache.h"
30#include "Frame.h"
31#include "GraphicsContext.h"
32#include "HitTestLocation.h"
33#include "HitTestRequest.h"
34#include "HitTestResult.h"
35#include "InlineTextBox.h"
36#include "PaintInfo.h"
37#include "RenderBlockFlow.h"
38#include "RenderStyle.h"
39#include "RenderText.h"
40#include "RenderView.h"
41#include "Settings.h"
42#include "SimpleLineLayoutResolver.h"
43#include "Text.h"
44#include "TextPaintStyle.h"
45
46namespace WebCore {
47namespace SimpleLineLayout {
48
49static void paintDebugBorders(GraphicsContext& context, LayoutRect borderRect, const LayoutPoint& paintOffset)
50{
51    borderRect.moveBy(paintOffset);
52    IntRect snappedRect = pixelSnappedIntRect(borderRect);
53    if (snappedRect.isEmpty())
54        return;
55    GraphicsContextStateSaver stateSaver(context);
56    context.setStrokeColor(Color(0, 255, 0), ColorSpaceDeviceRGB);
57    context.setFillColor(Color::transparent, ColorSpaceDeviceRGB);
58    context.drawRect(snappedRect);
59}
60
61void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
62{
63    if (paintInfo.phase != PaintPhaseForeground)
64        return;
65
66    RenderStyle& style = flow.style();
67    if (style.visibility() != VISIBLE)
68        return;
69
70    RenderText& textRenderer = toRenderText(*flow.firstChild());
71    ASSERT(!textRenderer.firstTextBox());
72
73    bool debugBordersEnabled = flow.frame().settings().simpleLineLayoutDebugBordersEnabled();
74
75    GraphicsContext& context = *paintInfo.context;
76
77    const Font& font = style.font();
78    TextPaintStyle textPaintStyle = computeTextPaintStyle(textRenderer, style, paintInfo);
79    GraphicsContextStateSaver stateSaver(context, textPaintStyle.strokeWidth > 0);
80
81    updateGraphicsContext(context, textPaintStyle);
82    LayoutPoint adjustedPaintOffset = LayoutPoint(roundedForPainting(paintOffset, flow.document().deviceScaleFactor()));
83
84    LayoutRect paintRect = paintInfo.rect;
85    paintRect.moveBy(-adjustedPaintOffset);
86
87    auto resolver = runResolver(flow, layout);
88    auto range = resolver.rangeForRect(paintRect);
89    for (auto it = range.begin(), end = range.end(); it != end; ++it) {
90        const auto& run = *it;
91        if (!run.rect().intersects(paintRect))
92            continue;
93        TextRun textRun(run.text());
94        textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
95        context.drawText(font, textRun, run.baseline() + adjustedPaintOffset);
96        if (debugBordersEnabled)
97            paintDebugBorders(context, run.rect(), adjustedPaintOffset);
98    }
99}
100
101bool hitTestFlow(const RenderBlockFlow& flow, const Layout& layout, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
102{
103    if (hitTestAction != HitTestForeground)
104        return false;
105
106    if (!layout.runCount())
107        return false;
108
109    RenderStyle& style = flow.style();
110    if (style.visibility() != VISIBLE || style.pointerEvents() == PE_NONE)
111        return false;
112
113    RenderText& textRenderer = toRenderText(*flow.firstChild());
114
115    LayoutRect rangeRect = locationInContainer.boundingBox();
116    rangeRect.moveBy(-accumulatedOffset);
117
118    auto resolver = lineResolver(flow, layout);
119    auto range = resolver.rangeForRect(rangeRect);
120    for (auto it = range.begin(), end = range.end(); it != end; ++it) {
121        auto lineRect = *it;
122        lineRect.moveBy(accumulatedOffset);
123        if (!locationInContainer.intersects(lineRect))
124            continue;
125        textRenderer.updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
126        if (!result.addNodeToRectBasedTestResult(textRenderer.textNode(), request, locationInContainer, lineRect))
127            return true;
128    }
129
130    return false;
131}
132
133void collectFlowOverflow(RenderBlockFlow& flow, const Layout& layout)
134{
135    auto resolver = lineResolver(flow, layout);
136    for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
137        auto rect = *it;
138        flow.addLayoutOverflow(rect);
139        flow.addVisualOverflow(rect);
140    }
141}
142
143IntRect computeTextBoundingBox(const RenderText& textRenderer, const Layout& layout)
144{
145    auto resolver = lineResolver(toRenderBlockFlow(*textRenderer.parent()), layout);
146    auto it = resolver.begin();
147    auto end = resolver.end();
148    if (it == end)
149        return IntRect();
150    auto firstLineRect = *it;
151    float left = firstLineRect.x();
152    float right = firstLineRect.maxX();
153    float bottom = firstLineRect.maxY();
154    for (++it; it != end; ++it) {
155        auto rect = *it;
156        if (rect.x() < left)
157            left = rect.x();
158        if (rect.maxX() > right)
159            right = rect.maxX();
160        if (rect.maxY() > bottom)
161            bottom = rect.maxY();
162    }
163    float x = left;
164    float y = firstLineRect.y();
165    float width = right - left;
166    float height = bottom - y;
167    return enclosingIntRect(FloatRect(x, y, width, height));
168}
169
170IntPoint computeTextFirstRunLocation(const RenderText& textRenderer, const Layout& layout)
171{
172    auto resolver = runResolver(toRenderBlockFlow(*textRenderer.parent()), layout);
173    auto begin = resolver.begin();
174    if (begin == resolver.end())
175        return IntPoint();
176    return flooredIntPoint((*begin).rect().location());
177}
178
179Vector<IntRect> collectTextAbsoluteRects(const RenderText& textRenderer, const Layout& layout, const LayoutPoint& accumulatedOffset)
180{
181    Vector<IntRect> rects;
182    auto resolver = runResolver(toRenderBlockFlow(*textRenderer.parent()), layout);
183    for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
184        const auto& run = *it;
185        auto rect = run.rect();
186        rects.append(enclosingIntRect(FloatRect(accumulatedOffset + rect.location(), rect.size())));
187    }
188    return rects;
189}
190
191Vector<FloatQuad> collectTextAbsoluteQuads(const RenderText& textRenderer, const Layout& layout, bool* wasFixed)
192{
193    Vector<FloatQuad> quads;
194    auto resolver = runResolver(toRenderBlockFlow(*textRenderer.parent()), layout);
195    for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
196        const auto& run = *it;
197        auto rect = run.rect();
198        quads.append(textRenderer.localToAbsoluteQuad(FloatQuad(rect), 0, wasFixed));
199    }
200    return quads;
201}
202
203}
204}
205