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/compositing/paint_layer_compositor.h"
41 #include "third_party/blink/renderer/core/paint/embedded_content_painter.h"
42 #include "third_party/blink/renderer/core/paint/paint_layer.h"
43 
44 namespace blink {
45 
LayoutEmbeddedContent(HTMLFrameOwnerElement * element)46 LayoutEmbeddedContent::LayoutEmbeddedContent(HTMLFrameOwnerElement* element)
47     : LayoutReplaced(element),
48       // Reference counting is used to prevent the part from being destroyed
49       // while inside the EmbeddedContentView code, which might not be able to
50       // handle that.
51       ref_count_(1) {
52   DCHECK(element);
53   SetInline(false);
54 }
55 
Release()56 void LayoutEmbeddedContent::Release() {
57   NOT_DESTROYED();
58   if (--ref_count_ <= 0)
59     delete this;
60 }
61 
WillBeDestroyed()62 void LayoutEmbeddedContent::WillBeDestroyed() {
63   NOT_DESTROYED();
64   if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) {
65     cache->ChildrenChanged(Parent());
66     cache->Remove(this);
67   }
68 
69   if (auto* frame_owner = GetFrameOwnerElement())
70     frame_owner->SetEmbeddedContentView(nullptr);
71 
72   LayoutReplaced::WillBeDestroyed();
73 }
74 
DeleteThis()75 void LayoutEmbeddedContent::DeleteThis() {
76   NOT_DESTROYED();
77   // We call clearNode here because LayoutEmbeddedContent is ref counted. This
78   // call to destroy may not actually destroy the layout object. We can keep it
79   // around because of references from the LocalFrameView class. (The actual
80   // destruction of the class happens in PostDestroy() which is called from
81   // Release()).
82   //
83   // But, we've told the system we've destroyed the layoutObject, which happens
84   // when the DOM node is destroyed. So there is a good chance the DOM node this
85   // object points too is invalid, so we have to clear the node so we make sure
86   // we don't access it in the future.
87   ClearNode();
88   Release();
89 }
90 
~LayoutEmbeddedContent()91 LayoutEmbeddedContent::~LayoutEmbeddedContent() {
92   DCHECK_LE(ref_count_, 0);
93 }
94 
ChildFrameView() const95 FrameView* LayoutEmbeddedContent::ChildFrameView() const {
96   NOT_DESTROYED();
97   return DynamicTo<FrameView>(GetEmbeddedContentView());
98 }
99 
ChildLayoutView() const100 LayoutView* LayoutEmbeddedContent::ChildLayoutView() const {
101   NOT_DESTROYED();
102   if (HTMLFrameOwnerElement* owner_element = GetFrameOwnerElement()) {
103     if (Document* content_document = owner_element->contentDocument())
104       return content_document->GetLayoutView();
105   }
106   return nullptr;
107 }
108 
Plugin() const109 WebPluginContainerImpl* LayoutEmbeddedContent::Plugin() const {
110   NOT_DESTROYED();
111   EmbeddedContentView* embedded_content_view = GetEmbeddedContentView();
112   if (embedded_content_view && embedded_content_view->IsPluginView())
113     return To<WebPluginContainerImpl>(embedded_content_view);
114   return nullptr;
115 }
116 
GetEmbeddedContentView() const117 EmbeddedContentView* LayoutEmbeddedContent::GetEmbeddedContentView() const {
118   NOT_DESTROYED();
119   if (auto* frame_owner = GetFrameOwnerElement())
120     return frame_owner->OwnedEmbeddedContentView();
121   return nullptr;
122 }
123 
LayerTypeRequired() const124 PaintLayerType LayoutEmbeddedContent::LayerTypeRequired() const {
125   NOT_DESTROYED();
126   if (AdditionalCompositingReasons())
127     return kNormalPaintLayer;
128 
129   PaintLayerType type = LayoutReplaced::LayerTypeRequired();
130   if (type != kNoPaintLayer)
131     return type;
132 
133   // We can't check layout_view->Layer()->GetCompositingReasons() here because
134   // we're only in style update, so haven't run compositing update yet.
135   if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
136     if (LayoutView* child_layout_view = ChildLayoutView()) {
137       if (child_layout_view->AdditionalCompositingReasons())
138         return kNormalPaintLayer;
139     }
140   }
141 
142   return kForcedPaintLayer;
143 }
144 
ContentDocumentIsCompositing() const145 bool LayoutEmbeddedContent::ContentDocumentIsCompositing() const {
146   NOT_DESTROYED();
147   if (PaintLayerCompositor* inner_compositor =
148           PaintLayerCompositor::FrameContentsCompositor(*this)) {
149     return inner_compositor->StaleInCompositingMode();
150   }
151   return false;
152 }
153 
NodeAtPointOverEmbeddedContentView(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestAction action)154 bool LayoutEmbeddedContent::NodeAtPointOverEmbeddedContentView(
155     HitTestResult& result,
156     const HitTestLocation& hit_test_location,
157     const PhysicalOffset& accumulated_offset,
158     HitTestAction action) {
159   NOT_DESTROYED();
160   bool had_result = result.InnerNode();
161   bool inside = LayoutReplaced::NodeAtPoint(result, hit_test_location,
162                                             accumulated_offset, action);
163 
164   // Check to see if we are really over the EmbeddedContentView itself (and not
165   // just in the border/padding area).
166   if ((inside || hit_test_location.IsRectBasedTest()) && !had_result &&
167       result.InnerNode() == GetNode()) {
168     result.SetIsOverEmbeddedContentView(
169         PhysicalContentBoxRect().Contains(result.LocalPoint()));
170   }
171   return inside;
172 }
173 
NodeAtPoint(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestAction action)174 bool LayoutEmbeddedContent::NodeAtPoint(
175     HitTestResult& result,
176     const HitTestLocation& hit_test_location,
177     const PhysicalOffset& accumulated_offset,
178     HitTestAction action) {
179   NOT_DESTROYED();
180   auto* local_frame_view = DynamicTo<LocalFrameView>(ChildFrameView());
181   bool skip_contents = (result.GetHitTestRequest().GetStopNode() == this ||
182                         !result.GetHitTestRequest().AllowsChildFrameContent());
183   if (!local_frame_view || skip_contents) {
184     return NodeAtPointOverEmbeddedContentView(result, hit_test_location,
185                                               accumulated_offset, action);
186   }
187 
188   // A hit test can never hit an off-screen element; only off-screen iframes are
189   // throttled; therefore, hit tests can skip descending into throttled iframes.
190   // We also check the document lifecycle state because the frame may have been
191   // throttled at the time lifecycle updates happened, in which case it will not
192   // be up-to-date and we can't hit test it.
193   if (local_frame_view->ShouldThrottleRendering() ||
194       !local_frame_view->GetFrame().GetDocument() ||
195       local_frame_view->GetFrame().GetDocument()->Lifecycle().GetState() <
196           DocumentLifecycle::kPrePaintClean) {
197     return NodeAtPointOverEmbeddedContentView(result, hit_test_location,
198                                               accumulated_offset, action);
199   }
200 
201   DCHECK_GE(GetDocument().Lifecycle().GetState(),
202             DocumentLifecycle::kPrePaintClean);
203 
204   if (action == kHitTestForeground) {
205     auto* child_layout_view = local_frame_view->GetLayoutView();
206 
207     if (VisibleToHitTestRequest(result.GetHitTestRequest()) &&
208         child_layout_view) {
209       PhysicalOffset content_offset(BorderLeft() + PaddingLeft(),
210                                     BorderTop() + PaddingTop());
211       HitTestLocation new_hit_test_location(
212           hit_test_location, -accumulated_offset - content_offset);
213       HitTestRequest new_hit_test_request(
214           result.GetHitTestRequest().GetType() |
215               HitTestRequest::kChildFrameHitTest,
216           result.GetHitTestRequest().GetStopNode());
217       HitTestResult child_frame_result(new_hit_test_request,
218                                        new_hit_test_location);
219       child_frame_result.SetInertNode(result.InertNode());
220 
221       // The frame's layout and style must be up to date if we reach here.
222       bool is_inside_child_frame = child_layout_view->HitTestNoLifecycleUpdate(
223           new_hit_test_location, child_frame_result);
224 
225       if (result.GetHitTestRequest().ListBased()) {
226         result.Append(child_frame_result);
227       } else if (is_inside_child_frame) {
228         // Force the result not to be cacheable because the parent frame should
229         // not cache this result; as it won't be notified of changes in the
230         // child.
231         child_frame_result.SetCacheable(false);
232         result = child_frame_result;
233       }
234 
235       // Don't trust |isInsideChildFrame|. For rect-based hit-test, returns
236       // true only when the hit test rect is totally within the iframe,
237       // i.e. nodeAtPointOverEmbeddedContentView() also returns true.
238       // Use a temporary HitTestResult because we don't want to collect the
239       // iframe element itself if the hit-test rect is totally within the
240       // iframe.
241       if (is_inside_child_frame) {
242         if (!hit_test_location.IsRectBasedTest())
243           return true;
244         HitTestResult point_over_embedded_content_view_result = result;
245         bool point_over_embedded_content_view =
246             NodeAtPointOverEmbeddedContentView(
247                 point_over_embedded_content_view_result, hit_test_location,
248                 accumulated_offset, action);
249         if (point_over_embedded_content_view)
250           return true;
251         result = point_over_embedded_content_view_result;
252         return false;
253       }
254     }
255   }
256 
257   return NodeAtPointOverEmbeddedContentView(result, hit_test_location,
258                                             accumulated_offset, action);
259 }
260 
AdditionalCompositingReasons() const261 CompositingReasons LayoutEmbeddedContent::AdditionalCompositingReasons() const {
262   NOT_DESTROYED();
263   WebPluginContainerImpl* plugin_view = Plugin();
264   if (plugin_view && plugin_view->CcLayer())
265     return CompositingReason::kPlugin;
266   if (auto* element = GetFrameOwnerElement()) {
267     if (Frame* content_frame = element->ContentFrame()) {
268       if (content_frame->IsRemoteFrame())
269         return CompositingReason::kIFrame;
270     }
271   }
272   return CompositingReason::kNone;
273 }
274 
StyleDidChange(StyleDifference diff,const ComputedStyle * old_style)275 void LayoutEmbeddedContent::StyleDidChange(StyleDifference diff,
276                                            const ComputedStyle* old_style) {
277   NOT_DESTROYED();
278   LayoutReplaced::StyleDidChange(diff, old_style);
279 
280   if (EmbeddedContentView* embedded_content_view = GetEmbeddedContentView()) {
281     if (StyleRef().Visibility() != EVisibility::kVisible) {
282       embedded_content_view->Hide();
283     } else {
284       embedded_content_view->Show();
285     }
286   }
287 
288   if (old_style &&
289       StyleRef().VisibleToHitTesting() == old_style->VisibleToHitTesting()) {
290     return;
291   }
292 
293   auto* frame_owner = GetFrameOwnerElement();
294   if (!frame_owner)
295     return;
296 
297   auto* frame = frame_owner->ContentFrame();
298   if (!frame)
299     return;
300 
301   frame->UpdateVisibleToHitTesting();
302 }
303 
UpdateLayout()304 void LayoutEmbeddedContent::UpdateLayout() {
305   NOT_DESTROYED();
306   DCHECK(NeedsLayout());
307   LayoutAnalyzer::Scope analyzer(*this);
308   UpdateAfterLayout();
309   ClearNeedsLayout();
310 }
311 
PaintReplaced(const PaintInfo & paint_info,const PhysicalOffset & paint_offset) const312 void LayoutEmbeddedContent::PaintReplaced(
313     const PaintInfo& paint_info,
314     const PhysicalOffset& paint_offset) const {
315   NOT_DESTROYED();
316   if (ChildPaintBlockedByDisplayLock())
317     return;
318   EmbeddedContentPainter(*this).PaintReplaced(paint_info, paint_offset);
319 }
320 
InvalidatePaint(const PaintInvalidatorContext & context) const321 void LayoutEmbeddedContent::InvalidatePaint(
322     const PaintInvalidatorContext& context) const {
323   NOT_DESTROYED();
324   LayoutReplaced::InvalidatePaint(context);
325   if (auto* plugin = Plugin())
326     plugin->InvalidatePaint();
327 }
328 
GetCursor(const PhysicalOffset & point,ui::Cursor & cursor) const329 CursorDirective LayoutEmbeddedContent::GetCursor(const PhysicalOffset& point,
330                                                  ui::Cursor& cursor) const {
331   NOT_DESTROYED();
332   if (Plugin()) {
333     // A plugin is responsible for setting the cursor when the pointer is over
334     // it.
335     return kDoNotSetCursor;
336   }
337   return LayoutReplaced::GetCursor(point, cursor);
338 }
339 
ReplacedContentRect() const340 PhysicalRect LayoutEmbeddedContent::ReplacedContentRect() const {
341   NOT_DESTROYED();
342   PhysicalRect content_rect = PhysicalContentBoxRect();
343   // IFrames set as the root scroller should get their size from their parent.
344   if (ChildFrameView() && View() && IsEffectiveRootScroller()) {
345     content_rect.offset = PhysicalOffset();
346     content_rect.size = View()->ViewRect().size;
347   }
348 
349   // We don't propagate sub-pixel into sub-frame layout, in other words, the
350   // rect is snapped at the document boundary, and sub-pixel movement could
351   // cause the sub-frame to layout due to the 1px snap difference. In order to
352   // avoid that, the size of sub-frame is rounded in advance.
353   return PreSnappedRectForPersistentSizing(content_rect);
354 }
355 
UpdateOnEmbeddedContentViewChange()356 void LayoutEmbeddedContent::UpdateOnEmbeddedContentViewChange() {
357   NOT_DESTROYED();
358   if (!Style())
359     return;
360 
361   if (EmbeddedContentView* embedded_content_view = GetEmbeddedContentView()) {
362     if (!NeedsLayout())
363       UpdateGeometry(*embedded_content_view);
364 
365     if (StyleRef().Visibility() != EVisibility::kVisible)
366       embedded_content_view->Hide();
367     else
368       embedded_content_view->Show();
369   }
370 
371   // One of the reasons of the following is that the layout tree in the new
372   // embedded content view may have already had some paint property and paint
373   // invalidation flags set, and we need to propagate the flags into the host
374   // view. Adding, changing and removing are also significant changes to the
375   // tree so setting the flags ensures the required updates.
376   SetNeedsPaintPropertyUpdate();
377   SetShouldDoFullPaintInvalidation();
378   // Showing/hiding the embedded content view and changing the view between null
379   // and non-null affect compositing (see: PaintLayerCompositor::CanBeComposited
380   // and RootShouldAlwaysComposite).
381   if (HasLayer())
382     Layer()->SetNeedsCompositingInputsUpdate();
383 }
384 
UpdateGeometry(EmbeddedContentView & embedded_content_view)385 void LayoutEmbeddedContent::UpdateGeometry(
386     EmbeddedContentView& embedded_content_view) {
387   NOT_DESTROYED();
388   // TODO(wangxianzhu): We reset subpixel accumulation at some boundaries, so
389   // the following code is incorrect when some ancestors are such boundaries.
390   // What about multicol? Need a LayoutBox function to query sub-pixel
391   // accumulation.
392   PhysicalRect replaced_rect = ReplacedContentRect();
393   TransformState transform_state(TransformState::kApplyTransformDirection,
394                                  FloatPoint(),
395                                  FloatQuad(FloatRect(replaced_rect)));
396   MapLocalToAncestor(nullptr, transform_state, 0);
397   transform_state.Flatten();
398   PhysicalOffset absolute_location =
399       PhysicalOffset::FromFloatPointRound(transform_state.LastPlanarPoint());
400   PhysicalRect absolute_replaced_rect = replaced_rect;
401   absolute_replaced_rect.Move(absolute_location);
402   FloatRect absolute_bounding_box =
403       transform_state.LastPlanarQuad().BoundingBox();
404   IntRect frame_rect(IntPoint(),
405                      PixelSnappedIntRect(absolute_replaced_rect).Size());
406   // Normally the location of the frame rect is ignored by the painter, but
407   // currently it is still used by a family of coordinate conversion function in
408   // LocalFrameView. This is incorrect because coordinate conversion
409   // needs to take transform and into account. A few callers still use the
410   // family of conversion function, including but not exhaustive:
411   // LocalFrameView::updateViewportIntersectionIfNeeded()
412   // RemoteFrameView::frameRectsChanged().
413   // WebPluginContainerImpl::reportGeometry()
414   // TODO(trchen): Remove this hack once we fixed all callers.
415   frame_rect.SetLocation(RoundedIntPoint(absolute_bounding_box.Location()));
416 
417   // As an optimization, we don't include the root layer's scroll offset in the
418   // frame rect.  As a result, we don't need to recalculate the frame rect every
419   // time the root layer scrolls; however, each implementation of
420   // EmbeddedContentView::FrameRect() must add the root layer's scroll offset
421   // into its position.
422   // TODO(szager): Refactor this functionality into EmbeddedContentView, rather
423   // than reimplementing in each concrete subclass.
424   LayoutView* layout_view = View();
425   if (layout_view && layout_view->IsScrollContainer()) {
426     // Floored because the PixelSnappedScrollOffset returns a ScrollOffset
427     // which is a float-type but frame_rect in a content view is an IntRect. We
428     // may want to reevaluate the use of pixel snapping that since scroll
429     // offsets/layout can be fractional.
430     frame_rect.Move(
431         FlooredIntSize(layout_view->PixelSnappedScrolledContentOffset()));
432   }
433 
434   embedded_content_view.SetFrameRect(frame_rect);
435 }
436 
IsThrottledFrameView() const437 bool LayoutEmbeddedContent::IsThrottledFrameView() const {
438   NOT_DESTROYED();
439   if (auto* local_frame_view = DynamicTo<LocalFrameView>(ChildFrameView()))
440     return local_frame_view->ShouldThrottleRendering();
441   return false;
442 }
443 
444 }  // namespace blink
445