summaryrefslogtreecommitdiffstats
path: root/third_party/WebKit/Source/core/layout/LayoutPart.cpp
blob: 0a83a6843dfb5ec2ff752fc4fdbc5e235042c7b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
/*
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 2000 Simon Hausmann <hausmann@kde.org>
 *           (C) 2000 Stefan Schimanski (1Stein@gmx.de)
 * Copyright (C) 2004, 2005, 2006, 2009 Apple Inc. All rights reserved.
 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#include "core/layout/LayoutPart.h"

#include "core/dom/AXObjectCache.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/html/HTMLFrameElementBase.h"
#include "core/layout/HitTestResult.h"
#include "core/layout/LayoutAnalyzer.h"
#include "core/layout/LayoutView.h"
#include "core/layout/svg/LayoutSVGRoot.h"
#include "core/paint/BoxPainter.h"
#include "core/paint/PaintLayer.h"
#include "core/paint/PartPainter.h"
#include "core/plugins/PluginView.h"

namespace blink {

LayoutPart::LayoutPart(Element* element)
    : LayoutReplaced(element)
    // Reference counting is used to prevent the part from being destroyed
    // while inside the Widget code, which might not be able to handle that.
    , m_refCount(1)
{
    ASSERT(element);
    frameView()->addPart(this);
    setInline(false);
}

void LayoutPart::deref()
{
    if (--m_refCount <= 0)
        delete this;
}

void LayoutPart::willBeDestroyed()
{
    frameView()->removePart(this);

    if (AXObjectCache* cache = document().existingAXObjectCache()) {
        cache->childrenChanged(this->parent());
        cache->remove(this);
    }

    Element* element = toElement(node());
    if (element && element->isFrameOwnerElement())
        toHTMLFrameOwnerElement(element)->setWidget(nullptr);

    LayoutReplaced::willBeDestroyed();
}

void LayoutPart::destroy()
{
    willBeDestroyed();
    // We call clearNode here because LayoutPart is ref counted. This call to destroy
    // may not actually destroy the layout object. We can keep it around because of
    // references from the FrameView class. (The actual destruction of the class happens
    // in postDestroy() which is called from deref()).
    //
    // But, we've told the system we've destroyed the layoutObject, which happens when
    // the DOM node is destroyed. So there is a good change the DOM node this object
    // points too is invalid, so we have to clear the node so we make sure we don't
    // access it in the future.
    clearNode();
    deref();
}

LayoutPart::~LayoutPart()
{
    ASSERT(m_refCount <= 0);
}

Widget* LayoutPart::widget() const
{
    // Plugin widgets are stored in their DOM node.
    Element* element = toElement(node());

    if (element && element->isFrameOwnerElement())
        return toHTMLFrameOwnerElement(element)->ownedWidget();

    return nullptr;
}

PaintLayerType LayoutPart::layerTypeRequired() const
{
    PaintLayerType type = LayoutReplaced::layerTypeRequired();
    if (type != NoPaintLayer)
        return type;
    return ForcedPaintLayer;
}

bool LayoutPart::requiresAcceleratedCompositing() const
{
    // There are two general cases in which we can return true. First, if this is a plugin
    // LayoutObject and the plugin has a layer, then we need a layer. Second, if this is
    // a LayoutObject with a contentDocument and that document needs a layer, then we need
    // a layer.
    if (widget() && widget()->isPluginView() && toPluginView(widget())->platformLayer())
        return true;

    if (!node() || !node()->isFrameOwnerElement())
        return false;

    HTMLFrameOwnerElement* element = toHTMLFrameOwnerElement(node());
    if (element->contentFrame() && element->contentFrame()->isRemoteFrame())
        return true;

    if (Document* contentDocument = element->contentDocument()) {
        if (LayoutView* view = contentDocument->layoutView())
            return view->usesCompositing();
    }

    return false;
}

bool LayoutPart::needsPreferredWidthsRecalculation() const
{
    if (LayoutReplaced::needsPreferredWidthsRecalculation())
        return true;
    return embeddedReplacedContent();
}

bool LayoutPart::nodeAtPointOverWidget(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
{
    bool hadResult = result.innerNode();
    bool inside = LayoutReplaced::nodeAtPoint(result, locationInContainer, accumulatedOffset, action);

    // Check to see if we are really over the widget itself (and not just in the border/padding area).
    if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == node())
        result.setIsOverWidget(contentBoxRect().contains(result.localPoint()));
    return inside;
}

bool LayoutPart::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
{
    if (!widget() || !widget()->isFrameView() || !result.hitTestRequest().allowsChildFrameContent())
        return nodeAtPointOverWidget(result, locationInContainer, accumulatedOffset, action);

    // A hit test can never hit an off-screen element; only off-screen iframes are throttled;
    // therefore, hit tests can skip descending into throttled iframes.
    if (toFrameView(widget())->shouldThrottleRendering())
        return nodeAtPointOverWidget(result, locationInContainer, accumulatedOffset, action);

    ASSERT(document().lifecycle().state() >= DocumentLifecycle::CompositingClean);

    if (action == HitTestForeground) {
        FrameView* childFrameView = toFrameView(widget());
        LayoutView* childRoot = childFrameView->layoutView();

        if (visibleToHitTestRequest(result.hitTestRequest()) && childRoot) {
            LayoutPoint adjustedLocation = accumulatedOffset + location();
            LayoutPoint contentOffset = LayoutPoint(borderLeft() + paddingLeft(), borderTop() + paddingTop()) - LayoutSize(childFrameView->scrollOffset());
            HitTestLocation newHitTestLocation(locationInContainer, -adjustedLocation - contentOffset);
            HitTestRequest newHitTestRequest(result.hitTestRequest().type() | HitTestRequest::ChildFrameHitTest);
            HitTestResult childFrameResult(newHitTestRequest, newHitTestLocation);

            // The frame's layout and style must be up-to-date if we reach here.
            bool isInsideChildFrame = childRoot->hitTestNoLifecycleUpdate(childFrameResult);

            if (result.hitTestRequest().listBased()) {
                result.append(childFrameResult);
            } else if (isInsideChildFrame) {
                // Force the result not to be cacheable because the parent
                // frame should not cache this result; as it won't be notified of
                // changes in the child.
                childFrameResult.setCacheable(false);
                result = childFrameResult;
            }

            // Don't trust |isInsideChildFrame|. For rect-based hit-test, returns
            // true only when the hit test rect is totally within the iframe,
            // i.e. nodeAtPointOverWidget() also returns true.
            // Use a temporary HitTestResult because we don't want to collect the
            // iframe element itself if the hit-test rect is totally within the iframe.
            if (isInsideChildFrame) {
                if (!locationInContainer.isRectBasedTest())
                    return true;
                HitTestResult pointOverWidgetResult = result;
                bool pointOverWidget = nodeAtPointOverWidget(pointOverWidgetResult, locationInContainer, accumulatedOffset, action);
                if (pointOverWidget)
                    return true;
                result = pointOverWidgetResult;
                return false;
            }
        }
    }

    return nodeAtPointOverWidget(result, locationInContainer, accumulatedOffset, action);
}

CompositingReasons LayoutPart::additionalCompositingReasons() const
{
    if (requiresAcceleratedCompositing())
        return CompositingReasonIFrame;
    return CompositingReasonNone;
}

void LayoutPart::styleDidChange(StyleDifference diff, const ComputedStyle* oldStyle)
{
    LayoutReplaced::styleDidChange(diff, oldStyle);
    Widget* widget = this->widget();

    if (!widget)
        return;

    // If the iframe has custom scrollbars, recalculate their style.
    if (widget && widget->isFrameView())
        toFrameView(widget)->recalculateCustomScrollbarStyle();

    if (style()->visibility() != VISIBLE) {
        widget->hide();
    } else {
        widget->show();
    }
}

void LayoutPart::layout()
{
    ASSERT(needsLayout());
    LayoutAnalyzer::Scope analyzer(*this);
    clearNeedsLayout();
}

void LayoutPart::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) const
{
    PartPainter(*this).paint(paintInfo, paintOffset);
}

void LayoutPart::paintContents(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) const
{
    PartPainter(*this).paintContents(paintInfo, paintOffset);
}

CursorDirective LayoutPart::getCursor(const LayoutPoint& point, Cursor& cursor) const
{
    if (widget() && widget()->isPluginView()) {
        // A plugin is responsible for setting the cursor when the pointer is over it.
        return DoNotSetCursor;
    }
    return LayoutReplaced::getCursor(point, cursor);
}

void LayoutPart::updateOnWidgetChange()
{
    Widget* widget = this->widget();
    if (!widget)
        return;

    if (!style())
        return;

    if (!needsLayout())
        updateWidgetGeometryInternal();

    if (style()->visibility() != VISIBLE) {
        widget->hide();
    } else {
        widget->show();
        // FIXME: Why do we issue a full paint invalidation in this case, but not the other?
        setShouldDoFullPaintInvalidation();
    }
}

void LayoutPart::updateWidgetGeometry()
{
    Widget* widget = this->widget();
    if (!widget || !node()) // Check the node in case destroy() has been called.
        return;

    bool boundsChanged = updateWidgetGeometryInternal();

    // If the frame bounds got changed, or if view needs layout (possibly indicating
    // content size is wrong) we have to do a layout to set the right widget size.
    if (widget && widget->isFrameView()) {
        FrameView* frameView = toFrameView(widget);
        // Check the frame's page to make sure that the frame isn't in the process of being destroyed.
        if ((boundsChanged || frameView->needsLayout()) && frameView->frame().page())
            frameView->layout();
    }

    widget->widgetGeometryMayHaveChanged();
}

bool LayoutPart::updateWidgetGeometryInternal()
{
    Widget* widget = this->widget();
    ASSERT(widget);

    LayoutRect contentBox = contentBoxRect();
    LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(FloatRect(contentBox))).boundingBox());
    if (widget->isFrameView()) {
        if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled())
            contentBox.setLocation(absoluteContentBox.location());
        return setWidgetGeometry(contentBox);
    }
    // TODO(chrishtr): why are these widgets using an absolute rect for their frameRect?
    return setWidgetGeometry(absoluteContentBox);
}

// Widgets are always placed on integer boundaries, so rounding the size is actually
// the desired behavior. This function is here because it's otherwise seldom what we
// want to do with a LayoutRect.
static inline IntRect roundedIntRect(const LayoutRect& rect)
{
    return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size()));
}

bool LayoutPart::setWidgetGeometry(const LayoutRect& frame)
{
    if (!node())
        return false;

    Widget* widget = this->widget();
    ASSERT(widget);

    IntRect newFrame = roundedIntRect(frame);

    if (widget->frameRect() == newFrame)
        return false;

    RefPtr<LayoutPart> protector(this);
    RefPtrWillBeRawPtr<Node> protectedNode(node());
    widget->setFrameRect(newFrame);
    return widget->frameRect().size() != newFrame.size();
}

void LayoutPart::invalidatePaintOfSubtreesIfNeeded(const PaintInvalidationState& paintInvalidationState)
{
    if (widget() && widget()->isFrameView()) {
        FrameView* childFrameView = toFrameView(widget());
        // |childFrameView| is in another document, which could be
        // missing its LayoutView. TODO(jchaffraix): Ideally we should
        // not need this code.
        if (LayoutView* childLayoutView = childFrameView->layoutView()) {
            PaintInvalidationState childViewPaintInvalidationState(*childLayoutView, paintInvalidationState);
            childFrameView->invalidateTreeIfNeeded(childViewPaintInvalidationState);
        }
    }

    LayoutReplaced::invalidatePaintOfSubtreesIfNeeded(paintInvalidationState);
}

bool LayoutPart::isThrottledFrameView() const
{
    if (!widget() || !widget()->isFrameView())
        return false;
    const FrameView* frameView = toFrameView(widget());
    return frameView->shouldThrottleRendering();
}

} // namespace blink