blob: 007144d6dddd7505a89853c05a990d65236db2e2 [file] [log] [blame]
* Copyright (C) 1999 Lars Knoll (
* (C) 2000 Simon Hausmann <>
* (C) 2000 Stefan Schimanski (
* 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
* 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 "third_party/blink/renderer/core/layout/layout_embedded_content.h"
#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
#include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
#include "third_party/blink/renderer/core/frame/embedded_content_view.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/remote_frame_view.h"
#include "third_party/blink/renderer/core/html/html_frame_element_base.h"
#include "third_party/blink/renderer/core/html/html_plugin_element.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/core/layout/layout_analyzer.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/paint/embedded_content_painter.h"
namespace blink {
LayoutEmbeddedContent::LayoutEmbeddedContent(Element* element)
: LayoutReplaced(element),
// Reference counting is used to prevent the part from being destroyed
// while inside the EmbeddedContentView code, which might not be able to
// handle that.
ref_count_(1) {
void LayoutEmbeddedContent::Release() {
if (--ref_count_ <= 0)
delete this;
void LayoutEmbeddedContent::WillBeDestroyed() {
if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) {
Node* node = GetNode();
if (node && node->IsFrameOwnerElement())
void LayoutEmbeddedContent::Destroy() {
// We call clearNode here because LayoutEmbeddedContent is ref counted. This
// call to destroy may not actually destroy the layout object. We can keep it
// around because of references from the LocalFrameView class. (The actual
// destruction of the class happens in PostDestroy() which is called from
// Release()).
// 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.
LayoutEmbeddedContent::~LayoutEmbeddedContent() {
DCHECK_LE(ref_count_, 0);
FrameView* LayoutEmbeddedContent::ChildFrameView() const {
EmbeddedContentView* embedded_content_view = GetEmbeddedContentView();
if (embedded_content_view && embedded_content_view->IsFrameView())
return ToFrameView(embedded_content_view);
return nullptr;
WebPluginContainerImpl* LayoutEmbeddedContent::Plugin() const {
EmbeddedContentView* embedded_content_view = GetEmbeddedContentView();
if (embedded_content_view && embedded_content_view->IsPluginView())
return ToWebPluginContainerImpl(embedded_content_view);
return nullptr;
EmbeddedContentView* LayoutEmbeddedContent::GetEmbeddedContentView() const {
Node* node = GetNode();
if (node && node->IsFrameOwnerElement())
return ToHTMLFrameOwnerElement(node)->OwnedEmbeddedContentView();
return nullptr;
PaintLayerType LayoutEmbeddedContent::LayerTypeRequired() const {
if (RequiresAcceleratedCompositing())
return kNormalPaintLayer;
PaintLayerType type = LayoutReplaced::LayerTypeRequired();
if (type != kNoPaintLayer)
return type;
return kForcedPaintLayer;
bool LayoutEmbeddedContent::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.
WebPluginContainerImpl* plugin_view = Plugin();
if (plugin_view && plugin_view->CcLayer())
return true;
if (!GetNode() || !GetNode()->IsFrameOwnerElement())
return false;
HTMLFrameOwnerElement* element = ToHTMLFrameOwnerElement(GetNode());
if (element->ContentFrame() && element->ContentFrame()->IsRemoteFrame())
return true;
if (Document* content_document = element->contentDocument()) {
auto* layout_view = content_document->GetLayoutView();
if (layout_view)
return layout_view->UsesCompositing();
return false;
bool LayoutEmbeddedContent::NodeAtPointOverEmbeddedContentView(
HitTestResult& result,
const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset,
HitTestAction action) {
bool had_result = result.InnerNode();
bool inside = LayoutReplaced::NodeAtPoint(result, location_in_container,
accumulated_offset, action);
// Check to see if we are really over the EmbeddedContentView itself (and not
// just in the border/padding area).
if ((inside || location_in_container.IsRectBasedTest()) && !had_result &&
result.InnerNode() == GetNode()) {
return inside;
bool LayoutEmbeddedContent::NodeAtPoint(
HitTestResult& result,
const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset,
HitTestAction action) {
FrameView* frame_view = ChildFrameView();
bool skip_contents = (result.GetHitTestRequest().GetStopNode() == this ||
if (!frame_view || !frame_view->IsLocalFrameView() || skip_contents) {
return NodeAtPointOverEmbeddedContentView(result, location_in_container,
accumulated_offset, action);
LocalFrameView* local_frame_view = ToLocalFrameView(frame_view);
// 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 (local_frame_view->ShouldThrottleRendering()) {
return NodeAtPointOverEmbeddedContentView(result, location_in_container,
accumulated_offset, action);
if (action == kHitTestForeground) {
auto* child_layout_view = local_frame_view->GetLayoutView();
if (VisibleToHitTestRequest(result.GetHitTestRequest()) &&
child_layout_view) {
LayoutPoint adjusted_location = accumulated_offset + Location();
LayoutPoint content_offset =
LayoutPoint(BorderLeft() + PaddingLeft(), BorderTop() + PaddingTop());
HitTestLocation new_hit_test_location(
location_in_container, -adjusted_location - content_offset);
HitTestRequest new_hit_test_request(
result.GetHitTestRequest().GetType() |
HitTestResult child_frame_result(new_hit_test_request,
// The frame's layout and style must be up to date if we reach here.
bool is_inside_child_frame = child_layout_view->HitTestNoLifecycleUpdate(
new_hit_test_location, child_frame_result);
if (result.GetHitTestRequest().ListBased()) {
} else if (is_inside_child_frame) {
// 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.
result = child_frame_result;
// Don't trust |isInsideChildFrame|. For rect-based hit-test, returns
// true only when the hit test rect is totally within the iframe,
// i.e. nodeAtPointOverEmbeddedContentView() 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 (is_inside_child_frame) {
if (!location_in_container.IsRectBasedTest())
return true;
HitTestResult point_over_embedded_content_view_result = result;
bool point_over_embedded_content_view =
point_over_embedded_content_view_result, location_in_container,
accumulated_offset, action);
if (point_over_embedded_content_view)
return true;
result = point_over_embedded_content_view_result;
return false;
return NodeAtPointOverEmbeddedContentView(result, location_in_container,
accumulated_offset, action);
CompositingReasons LayoutEmbeddedContent::AdditionalCompositingReasons() const {
if (RequiresAcceleratedCompositing())
return CompositingReason::kIFrame;
return CompositingReason::kNone;
void LayoutEmbeddedContent::StyleDidChange(StyleDifference diff,
const ComputedStyle* old_style) {
LayoutReplaced::StyleDidChange(diff, old_style);
if (!old_style || Style()->PointerEvents() != old_style->PointerEvents()) {
Node* node = GetNode();
if (node->IsFrameOwnerElement())
EmbeddedContentView* embedded_content_view = GetEmbeddedContentView();
if (!embedded_content_view)
if (StyleRef().Visibility() != EVisibility::kVisible) {
} else {
void LayoutEmbeddedContent::UpdateLayout() {
LayoutAnalyzer::Scope analyzer(*this);
void LayoutEmbeddedContent::PaintReplaced(
const PaintInfo& paint_info,
const LayoutPoint& paint_offset) const {
EmbeddedContentPainter(*this).PaintReplaced(paint_info, paint_offset);
void LayoutEmbeddedContent::InvalidatePaint(
const PaintInvalidatorContext& context) const {
if (auto* plugin = Plugin())
CursorDirective LayoutEmbeddedContent::GetCursor(const LayoutPoint& point,
Cursor& cursor) const {
if (Plugin()) {
// A plugin is responsible for setting the cursor when the pointer is over
// it.
return kDoNotSetCursor;
return LayoutReplaced::GetCursor(point, cursor);
LayoutRect LayoutEmbeddedContent::ReplacedContentRect() const {
LayoutRect content_rect = PhysicalContentBoxRect();
// IFrames set as the root scroller should get their size from their parent.
if (ChildFrameView() && View() && IsEffectiveRootScroller())
content_rect = LayoutRect(LayoutPoint(), View()->ViewRect().Size());
// 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.
return PreSnappedRectForPersistentSizing(content_rect);
void LayoutEmbeddedContent::UpdateOnEmbeddedContentViewChange() {
EmbeddedContentView* embedded_content_view = GetEmbeddedContentView();
if (!embedded_content_view)
if (!Style())
if (!NeedsLayout())
if (StyleRef().Visibility() != EVisibility::kVisible) {
} else {
// FIXME: Why do we issue a full paint invalidation in this case, but not
// the other?
void LayoutEmbeddedContent::UpdateGeometry(
EmbeddedContentView& embedded_content_view) {
// 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.
LayoutRect replaced_rect = ReplacedContentRect();
TransformState transform_state(TransformState::kApplyTransformDirection,
MapLocalToAncestor(nullptr, transform_state,
kApplyContainerFlip | kUseTransforms);
LayoutPoint absolute_location(transform_state.LastPlanarPoint());
LayoutRect absolute_replaced_rect(replaced_rect);
FloatRect absolute_bounding_box =
IntRect frame_rect(IntPoint(),
// 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
// LocalFrameView. 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:
// LocalFrameView::updateViewportIntersectionIfNeeded()
// RemoteFrameView::frameRectsChanged().
// WebPluginContainerImpl::reportGeometry()
// TODO(trchen): Remove this hack once we fixed all callers.
// As an optimization, we don't include the root layer's scroll offset in the
// frame rect. As a result, we don't need to recalculate the frame rect every
// time the root layer scrolls; however, each implementation of
// EmbeddedContentView::FrameRect() must add the root layer's scroll offset
// into its position.
// TODO(szager): Refactor this functionality into EmbeddedContentView, rather
// than reimplementing in each concrete subclass.
LayoutView* layout_view = View();
if (layout_view && layout_view->HasOverflowClip())
bool LayoutEmbeddedContent::IsThrottledFrameView() const {
FrameView* frame_view = ChildFrameView();
if (frame_view && frame_view->IsLocalFrameView())
return ToLocalFrameView(frame_view)->ShouldThrottleRendering();
return false;
} // namespace blink