1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2000 Simon Hausmann <hausmann@kde.org>
4 * (C) 2000 Stefan Schimanski (1Stein@gmx.de)
5 * Copyright (C) 2004, 2005, 2006, 2009 Apple Inc. All rights reserved.
6 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
26
27 #include "third_party/blink/public/common/features.h"
28 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
29 #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
30 #include "third_party/blink/renderer/core/frame/embedded_content_view.h"
31 #include "third_party/blink/renderer/core/frame/local_frame.h"
32 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
33 #include "third_party/blink/renderer/core/frame/remote_frame.h"
34 #include "third_party/blink/renderer/core/frame/remote_frame_view.h"
35 #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
36 #include "third_party/blink/renderer/core/html/html_plugin_element.h"
37 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
38 #include "third_party/blink/renderer/core/layout/layout_analyzer.h"
39 #include "third_party/blink/renderer/core/layout/layout_view.h"
40 #include "third_party/blink/renderer/core/paint/embedded_content_painter.h"
41 #include "third_party/blink/renderer/core/paint/paint_layer.h"
42
43 namespace blink {
44
LayoutEmbeddedContent(HTMLFrameOwnerElement * element)45 LayoutEmbeddedContent::LayoutEmbeddedContent(HTMLFrameOwnerElement* element)
46 : LayoutReplaced(element),
47 // Reference counting is used to prevent the part from being destroyed
48 // while inside the EmbeddedContentView code, which might not be able to
49 // handle that.
50 ref_count_(1) {
51 DCHECK(element);
52 SetInline(false);
53 }
54
Release()55 void LayoutEmbeddedContent::Release() {
56 if (--ref_count_ <= 0)
57 delete this;
58 }
59
WillBeDestroyed()60 void LayoutEmbeddedContent::WillBeDestroyed() {
61 if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) {
62 cache->ChildrenChanged(Parent());
63 cache->Remove(this);
64 }
65
66 if (auto* frame_owner = GetFrameOwnerElement())
67 frame_owner->SetEmbeddedContentView(nullptr);
68
69 LayoutReplaced::WillBeDestroyed();
70 }
71
DeleteThis()72 void LayoutEmbeddedContent::DeleteThis() {
73 // We call clearNode here because LayoutEmbeddedContent is ref counted. This
74 // call to destroy may not actually destroy the layout object. We can keep it
75 // around because of references from the LocalFrameView class. (The actual
76 // destruction of the class happens in PostDestroy() which is called from
77 // Release()).
78 //
79 // But, we've told the system we've destroyed the layoutObject, which happens
80 // when the DOM node is destroyed. So there is a good chance the DOM node this
81 // object points too is invalid, so we have to clear the node so we make sure
82 // we don't access it in the future.
83 ClearNode();
84 Release();
85 }
86
~LayoutEmbeddedContent()87 LayoutEmbeddedContent::~LayoutEmbeddedContent() {
88 DCHECK_LE(ref_count_, 0);
89 }
90
ChildFrameView() const91 FrameView* LayoutEmbeddedContent::ChildFrameView() const {
92 return DynamicTo<FrameView>(GetEmbeddedContentView());
93 }
94
Plugin() const95 WebPluginContainerImpl* LayoutEmbeddedContent::Plugin() const {
96 EmbeddedContentView* embedded_content_view = GetEmbeddedContentView();
97 if (embedded_content_view && embedded_content_view->IsPluginView())
98 return To<WebPluginContainerImpl>(embedded_content_view);
99 return nullptr;
100 }
101
GetEmbeddedContentView() const102 EmbeddedContentView* LayoutEmbeddedContent::GetEmbeddedContentView() const {
103 if (auto* frame_owner = GetFrameOwnerElement())
104 return frame_owner->OwnedEmbeddedContentView();
105 return nullptr;
106 }
107
LayerTypeRequired() const108 PaintLayerType LayoutEmbeddedContent::LayerTypeRequired() const {
109 if (RequiresAcceleratedCompositing())
110 return kNormalPaintLayer;
111
112 PaintLayerType type = LayoutReplaced::LayerTypeRequired();
113 if (type != kNoPaintLayer)
114 return type;
115 return kForcedPaintLayer;
116 }
117
RequiresAcceleratedCompositing() const118 bool LayoutEmbeddedContent::RequiresAcceleratedCompositing() const {
119 // There are two general cases in which we can return true. First, if this is
120 // a plugin LayoutObject and the plugin has a layer, then we need a layer.
121 // Second, if this is a LayoutObject with a contentDocument and that document
122 // needs a layer, then we need a layer.
123 WebPluginContainerImpl* plugin_view = Plugin();
124 if (plugin_view && plugin_view->CcLayer())
125 return true;
126
127 auto* element = GetFrameOwnerElement();
128 if (!element)
129 return false;
130
131 if (Frame* content_frame = element->ContentFrame()) {
132 if (content_frame->IsRemoteFrame())
133 return true;
134 if (base::FeatureList::IsEnabled(
135 blink::features::kCompositeCrossOriginIframes) &&
136 content_frame->IsCrossOriginToParentFrame()) {
137 return true;
138 }
139 }
140
141 if (Document* content_document = element->contentDocument()) {
142 auto* layout_view = content_document->GetLayoutView();
143 if (layout_view)
144 return layout_view->UsesCompositing();
145 }
146
147 return false;
148 }
149
NodeAtPointOverEmbeddedContentView(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestAction action)150 bool LayoutEmbeddedContent::NodeAtPointOverEmbeddedContentView(
151 HitTestResult& result,
152 const HitTestLocation& hit_test_location,
153 const PhysicalOffset& accumulated_offset,
154 HitTestAction action) {
155 bool had_result = result.InnerNode();
156 bool inside = LayoutReplaced::NodeAtPoint(result, hit_test_location,
157 accumulated_offset, action);
158
159 // Check to see if we are really over the EmbeddedContentView itself (and not
160 // just in the border/padding area).
161 if ((inside || hit_test_location.IsRectBasedTest()) && !had_result &&
162 result.InnerNode() == GetNode()) {
163 result.SetIsOverEmbeddedContentView(
164 PhysicalContentBoxRect().Contains(result.LocalPoint()));
165 }
166 return inside;
167 }
168
NodeAtPoint(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestAction action)169 bool LayoutEmbeddedContent::NodeAtPoint(
170 HitTestResult& result,
171 const HitTestLocation& hit_test_location,
172 const PhysicalOffset& accumulated_offset,
173 HitTestAction action) {
174 auto* local_frame_view = DynamicTo<LocalFrameView>(ChildFrameView());
175 bool skip_contents = (result.GetHitTestRequest().GetStopNode() == this ||
176 !result.GetHitTestRequest().AllowsChildFrameContent());
177 if (!local_frame_view || skip_contents) {
178 return NodeAtPointOverEmbeddedContentView(result, hit_test_location,
179 accumulated_offset, action);
180 }
181
182 // A hit test can never hit an off-screen element; only off-screen iframes are
183 // throttled; therefore, hit tests can skip descending into throttled iframes.
184 // We also check the document lifecycle state because the frame may have been
185 // throttled at the time lifecycle updates happened, in which case it will not
186 // be up-to-date and we can't hit test it.
187 if (local_frame_view->ShouldThrottleRendering() ||
188 !local_frame_view->GetFrame().GetDocument() ||
189 local_frame_view->GetFrame().GetDocument()->Lifecycle().GetState() <
190 DocumentLifecycle::kCompositingClean) {
191 return NodeAtPointOverEmbeddedContentView(result, hit_test_location,
192 accumulated_offset, action);
193 }
194
195 DCHECK_GE(GetDocument().Lifecycle().GetState(),
196 DocumentLifecycle::kCompositingClean);
197
198 if (action == kHitTestForeground) {
199 auto* child_layout_view = local_frame_view->GetLayoutView();
200
201 if (VisibleToHitTestRequest(result.GetHitTestRequest()) &&
202 child_layout_view) {
203 PhysicalOffset content_offset(BorderLeft() + PaddingLeft(),
204 BorderTop() + PaddingTop());
205 HitTestLocation new_hit_test_location(
206 hit_test_location, -accumulated_offset - content_offset);
207 HitTestRequest new_hit_test_request(
208 result.GetHitTestRequest().GetType() |
209 HitTestRequest::kChildFrameHitTest,
210 result.GetHitTestRequest().GetStopNode());
211 HitTestResult child_frame_result(new_hit_test_request,
212 new_hit_test_location);
213 child_frame_result.SetInertNode(result.InertNode());
214
215 // The frame's layout and style must be up to date if we reach here.
216 bool is_inside_child_frame = child_layout_view->HitTestNoLifecycleUpdate(
217 new_hit_test_location, child_frame_result);
218
219 if (result.GetHitTestRequest().ListBased()) {
220 result.Append(child_frame_result);
221 } else if (is_inside_child_frame) {
222 // Force the result not to be cacheable because the parent frame should
223 // not cache this result; as it won't be notified of changes in the
224 // child.
225 child_frame_result.SetCacheable(false);
226 result = child_frame_result;
227 }
228
229 // Don't trust |isInsideChildFrame|. For rect-based hit-test, returns
230 // true only when the hit test rect is totally within the iframe,
231 // i.e. nodeAtPointOverEmbeddedContentView() also returns true.
232 // Use a temporary HitTestResult because we don't want to collect the
233 // iframe element itself if the hit-test rect is totally within the
234 // iframe.
235 if (is_inside_child_frame) {
236 if (!hit_test_location.IsRectBasedTest())
237 return true;
238 HitTestResult point_over_embedded_content_view_result = result;
239 bool point_over_embedded_content_view =
240 NodeAtPointOverEmbeddedContentView(
241 point_over_embedded_content_view_result, hit_test_location,
242 accumulated_offset, action);
243 if (point_over_embedded_content_view)
244 return true;
245 result = point_over_embedded_content_view_result;
246 return false;
247 }
248 }
249 }
250
251 return NodeAtPointOverEmbeddedContentView(result, hit_test_location,
252 accumulated_offset, action);
253 }
254
AdditionalCompositingReasons() const255 CompositingReasons LayoutEmbeddedContent::AdditionalCompositingReasons() const {
256 if (RequiresAcceleratedCompositing())
257 return CompositingReason::kIFrame;
258 return CompositingReason::kNone;
259 }
260
StyleDidChange(StyleDifference diff,const ComputedStyle * old_style)261 void LayoutEmbeddedContent::StyleDidChange(StyleDifference diff,
262 const ComputedStyle* old_style) {
263 LayoutReplaced::StyleDidChange(diff, old_style);
264
265 if (EmbeddedContentView* embedded_content_view = GetEmbeddedContentView()) {
266 if (StyleRef().Visibility() != EVisibility::kVisible) {
267 embedded_content_view->Hide();
268 } else {
269 embedded_content_view->Show();
270 }
271 }
272
273 if (old_style &&
274 StyleRef().VisibleToHitTesting() == old_style->VisibleToHitTesting()) {
275 return;
276 }
277
278 auto* frame_owner = GetFrameOwnerElement();
279 if (!frame_owner)
280 return;
281
282 auto* frame = frame_owner->ContentFrame();
283 if (!frame)
284 return;
285
286 frame->UpdateVisibleToHitTesting();
287 }
288
UpdateLayout()289 void LayoutEmbeddedContent::UpdateLayout() {
290 DCHECK(NeedsLayout());
291 LayoutAnalyzer::Scope analyzer(*this);
292 UpdateAfterLayout();
293 ClearNeedsLayout();
294 }
295
PaintReplaced(const PaintInfo & paint_info,const PhysicalOffset & paint_offset) const296 void LayoutEmbeddedContent::PaintReplaced(
297 const PaintInfo& paint_info,
298 const PhysicalOffset& paint_offset) const {
299 if (PaintBlockedByDisplayLock(DisplayLockLifecycleTarget::kChildren))
300 return;
301 EmbeddedContentPainter(*this).PaintReplaced(paint_info, paint_offset);
302 }
303
InvalidatePaint(const PaintInvalidatorContext & context) const304 void LayoutEmbeddedContent::InvalidatePaint(
305 const PaintInvalidatorContext& context) const {
306 LayoutReplaced::InvalidatePaint(context);
307 if (auto* plugin = Plugin())
308 plugin->InvalidatePaint();
309 }
310
GetCursor(const PhysicalOffset & point,ui::Cursor & cursor) const311 CursorDirective LayoutEmbeddedContent::GetCursor(const PhysicalOffset& point,
312 ui::Cursor& cursor) const {
313 if (Plugin()) {
314 // A plugin is responsible for setting the cursor when the pointer is over
315 // it.
316 return kDoNotSetCursor;
317 }
318 return LayoutReplaced::GetCursor(point, cursor);
319 }
320
ReplacedContentRect() const321 PhysicalRect LayoutEmbeddedContent::ReplacedContentRect() const {
322 PhysicalRect content_rect = PhysicalContentBoxRect();
323 // IFrames set as the root scroller should get their size from their parent.
324 if (ChildFrameView() && View() && IsEffectiveRootScroller()) {
325 content_rect.offset = PhysicalOffset();
326 content_rect.size = View()->ViewRect().size;
327 }
328
329 // We don't propagate sub-pixel into sub-frame layout, in other words, the
330 // rect is snapped at the document boundary, and sub-pixel movement could
331 // cause the sub-frame to layout due to the 1px snap difference. In order to
332 // avoid that, the size of sub-frame is rounded in advance.
333 return PreSnappedRectForPersistentSizing(content_rect);
334 }
335
UpdateOnEmbeddedContentViewChange()336 void LayoutEmbeddedContent::UpdateOnEmbeddedContentViewChange() {
337 if (!Style())
338 return;
339
340 if (EmbeddedContentView* embedded_content_view = GetEmbeddedContentView()) {
341 if (!NeedsLayout())
342 UpdateGeometry(*embedded_content_view);
343
344 if (StyleRef().Visibility() != EVisibility::kVisible)
345 embedded_content_view->Hide();
346 else
347 embedded_content_view->Show();
348 }
349
350 // One of the reasons of the following is that the layout tree in the new
351 // embedded content view may have already had some paint property and paint
352 // invalidation flags set, and we need to propagate the flags into the host
353 // view. Adding, changing and removing are also significant changes to the
354 // tree so setting the flags ensures the required updates.
355 SetNeedsPaintPropertyUpdate();
356 SetShouldDoFullPaintInvalidation();
357 // Showing/hiding the embedded content view and changing the view between null
358 // and non-null affect compositing (see: PaintLayerCompositor::CanBeComposited
359 // and RootShouldAlwaysComposite).
360 if (HasLayer())
361 Layer()->SetNeedsCompositingInputsUpdate();
362 }
363
UpdateGeometry(EmbeddedContentView & embedded_content_view)364 void LayoutEmbeddedContent::UpdateGeometry(
365 EmbeddedContentView& embedded_content_view) {
366 // TODO(wangxianzhu): We reset subpixel accumulation at some boundaries, so
367 // the following code is incorrect when some ancestors are such boundaries.
368 // What about multicol? Need a LayoutBox function to query sub-pixel
369 // accumulation.
370 PhysicalRect replaced_rect = ReplacedContentRect();
371 TransformState transform_state(TransformState::kApplyTransformDirection,
372 FloatPoint(),
373 FloatQuad(FloatRect(replaced_rect)));
374 MapLocalToAncestor(nullptr, transform_state, 0);
375 transform_state.Flatten();
376 PhysicalOffset absolute_location =
377 PhysicalOffset::FromFloatPointRound(transform_state.LastPlanarPoint());
378 PhysicalRect absolute_replaced_rect = replaced_rect;
379 absolute_replaced_rect.Move(absolute_location);
380 FloatRect absolute_bounding_box =
381 transform_state.LastPlanarQuad().BoundingBox();
382 IntRect frame_rect(IntPoint(),
383 PixelSnappedIntRect(absolute_replaced_rect).Size());
384 // Normally the location of the frame rect is ignored by the painter, but
385 // currently it is still used by a family of coordinate conversion function in
386 // LocalFrameView. This is incorrect because coordinate conversion
387 // needs to take transform and into account. A few callers still use the
388 // family of conversion function, including but not exhaustive:
389 // LocalFrameView::updateViewportIntersectionIfNeeded()
390 // RemoteFrameView::frameRectsChanged().
391 // WebPluginContainerImpl::reportGeometry()
392 // TODO(trchen): Remove this hack once we fixed all callers.
393 frame_rect.SetLocation(RoundedIntPoint(absolute_bounding_box.Location()));
394
395 // As an optimization, we don't include the root layer's scroll offset in the
396 // frame rect. As a result, we don't need to recalculate the frame rect every
397 // time the root layer scrolls; however, each implementation of
398 // EmbeddedContentView::FrameRect() must add the root layer's scroll offset
399 // into its position.
400 // TODO(szager): Refactor this functionality into EmbeddedContentView, rather
401 // than reimplementing in each concrete subclass.
402 LayoutView* layout_view = View();
403 if (layout_view && layout_view->HasOverflowClip()) {
404 // Floored because the PixelSnappedScrollOffset returns a ScrollOffset
405 // which is a float-type but frame_rect in a content view is an IntRect. We
406 // may want to reevaluate the use of pixel snapping that since scroll
407 // offsets/layout can be fractional.
408 frame_rect.Move(
409 FlooredIntSize(layout_view->PixelSnappedScrolledContentOffset()));
410 }
411
412 embedded_content_view.SetFrameRect(frame_rect);
413 }
414
IsThrottledFrameView() const415 bool LayoutEmbeddedContent::IsThrottledFrameView() const {
416 if (auto* local_frame_view = DynamicTo<LocalFrameView>(ChildFrameView()))
417 return local_frame_view->ShouldThrottleRendering();
418 return false;
419 }
420
421 } // namespace blink
422