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