File: LayoutPart.cpp

package info (click to toggle)
chromium-browser 57.0.2987.98-1~deb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 2,637,852 kB
  • ctags: 2,544,394
  • sloc: cpp: 12,815,961; ansic: 3,676,222; python: 1,147,112; asm: 526,608; java: 523,212; xml: 286,794; perl: 92,654; sh: 86,408; objc: 73,271; makefile: 27,698; cs: 18,487; yacc: 13,031; tcl: 12,957; pascal: 4,875; ml: 4,716; lex: 3,904; sql: 3,862; ruby: 1,982; lisp: 1,508; php: 1,368; exp: 404; awk: 325; csh: 117; jsp: 39; sed: 37
file content (398 lines) | stat: -rw-r--r-- 14,853 bytes parent folder | download
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
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
/*
 * 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/api/LayoutAPIShim.h"
#include "core/layout/api/LayoutViewItem.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()) {
    LayoutViewItem viewItem = contentDocument->layoutViewItem();
    if (!viewItem.isNull())
      return viewItem.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());
    LayoutViewItem childRootItem = childFrameView->layoutViewItem();

    if (visibleToHitTestRequest(result.hitTestRequest()) &&
        !childRootItem.isNull()) {
      LayoutPoint adjustedLocation = accumulatedOffset + location();
      LayoutPoint contentOffset = LayoutPoint(borderLeft() + paddingLeft(),
                                              borderTop() + paddingTop()) -
                                  LayoutSize(childFrameView->scrollOffsetInt());
      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 =
          childRootItem.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() != EVisibility::kVisible) {
    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);
}

LayoutRect LayoutPart::replacedContentRect() const {
  // We don't propagate sub-pixel into sub-frame layout, in other words, the
  // rect is snapped at the document boundary, and sub-pixel movement could
  // cause the sub-frame to layout due to the 1px snap difference. In order to
  // avoid that, the size of sub-frame is rounded in advance.
  LayoutRect sizeRoundedRect = contentBoxRect();
  sizeRoundedRect.setSize(LayoutSize(roundedIntSize(sizeRoundedRect.size())));
  return sizeRoundedRect;
}

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

  if (!style())
    return;

  if (!needsLayout())
    updateWidgetGeometryInternal();

  if (style()->visibility() != EVisibility::kVisible) {
    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;

  LayoutRect newFrame = replacedContentRect();
  DCHECK(newFrame.size() == roundedIntSize(newFrame.size()));
  bool boundsWillChange =
      LayoutSize(widget->frameRect().size()) != newFrame.size();

  FrameView* frameView = widget->isFrameView() ? toFrameView(widget) : nullptr;

  // If frame bounds are changing mark the view for layout. Also check the
  // frame's page to make sure that the frame isn't in the process of being
  // destroyed. If iframe scrollbars needs reconstruction from native to custom
  // scrollbar, then also we need to layout the frameview.
  if (frameView && frameView->frame().page() &&
      (boundsWillChange || frameView->needsScrollbarReconstruction()))
    frameView->setNeedsLayout();

  updateWidgetGeometryInternal();

  // If view needs layout, either because bounds have changed or possibly
  // indicating content size is wrong, we have to do a layout to set the right
  // widget size.
  if (frameView && frameView->needsLayout() && frameView->frame().page())
    frameView->layout();

  widget->widgetGeometryMayHaveChanged();
}

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

  // Ignore transform here, as we only care about the sub-pixel accumulation.
  // TODO(trchen): What about multicol? Need a LayoutBox function to query
  // sub-pixel accumulation.
  LayoutPoint absoluteLocation(localToAbsolute(FloatPoint()));
  LayoutRect absoluteReplacedRect = replacedContentRect();
  absoluteReplacedRect.moveBy(absoluteLocation);

  IntRect frameRect(IntPoint(),
                    pixelSnappedIntRect(absoluteReplacedRect).size());
  // Normally the location of the frame rect is ignored by the painter, but
  // currently it is still used by a family of coordinate conversion function in
  // Widget/FrameView. This is incorrect because coordinate conversion needs to
  // take transform and into account.
  // A few callers still use the family of conversion function, including but
  // not exhaustive:
  // FrameView::updateViewportIntersectionIfNeeded()
  // RemoteFrameView::frameRectsChanged().
  // WebPluginContainerImpl::reportGeometry()
  // TODO(trchen): Remove this hack once we fixed all callers.
  FloatRect absoluteBoundingBox =
      localToAbsoluteQuad(FloatRect(replacedContentRect())).boundingBox();
  frameRect.setLocation(roundedIntPoint(absoluteBoundingBox.location()));

  // Why is the protector needed?
  RefPtr<LayoutPart> protector(this);
  widget->setFrameRect(frameRect);
}

void LayoutPart::invalidatePaintOfSubtreesIfNeeded(
    const PaintInvalidationState& paintInvalidationState) {
  if (widget() && widget()->isFrameView() && !isThrottledFrameView()) {
    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 =
            toLayoutView(LayoutAPIShim::layoutObjectFrom(
                childFrameView->layoutViewItem()))) {
      PaintInvalidationState childViewPaintInvalidationState(
          paintInvalidationState, *childLayoutView);
      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