blob: 1d2a4bcfae71c74eec1a6ff3457ac4e72381173f [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/frame/RemoteFrameView.h"
#include "core/dom/ElementVisibilityObserver.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameView.h"
#include "core/frame/RemoteFrame.h"
#include "core/frame/RemoteFrameClient.h"
#include "core/html/HTMLFrameOwnerElement.h"
#include "core/intersection_observer/IntersectionObserverEntry.h"
#include "core/layout/LayoutEmbeddedContent.h"
#include "core/layout/LayoutView.h"
namespace blink {
RemoteFrameView::RemoteFrameView(RemoteFrame* remote_frame)
: remote_frame_(remote_frame), is_attached_(false) {
DCHECK(remote_frame);
}
RemoteFrameView::~RemoteFrameView() = default;
LocalFrameView* RemoteFrameView::ParentFrameView() const {
if (!is_attached_)
return nullptr;
Frame* parent_frame = remote_frame_->Tree().Parent();
if (parent_frame && parent_frame->IsLocalFrame())
return ToLocalFrame(parent_frame)->View();
return nullptr;
}
void RemoteFrameView::AttachToLayout() {
DCHECK(!is_attached_);
is_attached_ = true;
if (ParentFrameView()->IsVisible())
SetParentVisible(true);
SetupRenderThrottling();
subtree_throttled_ = ParentFrameView()->CanThrottleRendering();
FrameRectsChanged();
}
void RemoteFrameView::DetachFromLayout() {
DCHECK(is_attached_);
SetParentVisible(false);
is_attached_ = false;
}
RemoteFrameView* RemoteFrameView::Create(RemoteFrame* remote_frame) {
RemoteFrameView* view = new RemoteFrameView(remote_frame);
view->Show();
return view;
}
void RemoteFrameView::UpdateViewportIntersectionsForSubtree(
DocumentLifecycle::LifecycleState target_state) {
if (!remote_frame_->OwnerLayoutObject())
return;
LocalFrameView* local_root_view =
ToLocalFrame(remote_frame_->Tree().Parent())->LocalFrameRoot().View();
if (!local_root_view)
return;
// Start with rect in remote frame's coordinate space. Then
// mapToVisualRectInAncestorSpace will move it to the local root's coordinate
// space and account for any clip from containing elements such as a
// scrollable div. Passing nullptr as an argument to
// mapToVisualRectInAncestorSpace causes it to be clipped to the viewport,
// even if there are RemoteFrame ancestors in the frame tree.
LayoutRect rect(0, 0, frame_rect_.Width(), frame_rect_.Height());
rect.Move(remote_frame_->OwnerLayoutObject()->ContentBoxOffset());
IntRect viewport_intersection;
if (remote_frame_->OwnerLayoutObject()->MapToVisualRectInAncestorSpace(
nullptr, rect)) {
IntRect root_visible_rect = local_root_view->VisibleContentRect();
IntRect intersected_rect(rect);
intersected_rect.Intersect(root_visible_rect);
intersected_rect.Move(-local_root_view->ScrollOffsetInt());
// Translate the intersection rect from the root frame's coordinate space
// to the remote frame's coordinate space.
viewport_intersection = ConvertFromRootFrame(intersected_rect);
}
if (viewport_intersection != last_viewport_intersection_) {
remote_frame_->Client()->UpdateRemoteViewportIntersection(
viewport_intersection);
}
last_viewport_intersection_ = viewport_intersection;
}
void RemoteFrameView::Dispose() {
HTMLFrameOwnerElement* owner_element = remote_frame_->DeprecatedLocalOwner();
// ownerElement can be null during frame swaps, because the
// RemoteFrameView is disconnected before detachment.
if (owner_element && owner_element->OwnedEmbeddedContentView() == this)
owner_element->SetEmbeddedContentView(nullptr);
}
void RemoteFrameView::InvalidateRect(const IntRect& rect) {
auto* object = remote_frame_->OwnerLayoutObject();
if (!object)
return;
LayoutRect repaint_rect(rect);
repaint_rect.Move(object->BorderLeft() + object->PaddingLeft(),
object->BorderTop() + object->PaddingTop());
object->InvalidatePaintRectangle(repaint_rect);
}
void RemoteFrameView::SetFrameRect(const IntRect& frame_rect) {
if (frame_rect == frame_rect_)
return;
frame_rect_ = frame_rect;
FrameRectsChanged();
}
void RemoteFrameView::FrameRectsChanged() {
// Update the rect to reflect the position of the frame relative to the
// containing local frame root. The position of the local root within
// any remote frames, if any, is accounted for by the embedder.
IntRect screen_space_rect = frame_rect_;
if (LocalFrameView* parent = ParentFrameView()) {
screen_space_rect =
parent->ConvertToRootFrame(parent->ContentsToFrame(screen_space_rect));
}
remote_frame_->Client()->FrameRectsChanged(frame_rect_, screen_space_rect);
}
void RemoteFrameView::UpdateGeometry() {
if (LayoutEmbeddedContent* layout = remote_frame_->OwnerLayoutObject())
layout->UpdateGeometry(*this);
}
void RemoteFrameView::Hide() {
self_visible_ = false;
remote_frame_->Client()->VisibilityChanged(false);
}
void RemoteFrameView::Show() {
self_visible_ = true;
remote_frame_->Client()->VisibilityChanged(true);
}
void RemoteFrameView::SetParentVisible(bool visible) {
if (parent_visible_ == visible)
return;
parent_visible_ = visible;
if (!self_visible_)
return;
remote_frame_->Client()->VisibilityChanged(self_visible_ && parent_visible_);
}
IntRect RemoteFrameView::ConvertFromRootFrame(
const IntRect& rect_in_root_frame) const {
if (LocalFrameView* parent = ParentFrameView()) {
IntRect parent_rect = parent->ConvertFromRootFrame(rect_in_root_frame);
parent_rect.SetLocation(
parent->ConvertSelfToChild(*this, parent_rect.Location()));
return parent_rect;
}
return rect_in_root_frame;
}
void RemoteFrameView::SetupRenderThrottling() {
if (visibility_observer_)
return;
Element* target_element = GetFrame().DeprecatedLocalOwner();
if (!target_element)
return;
visibility_observer_ = new ElementVisibilityObserver(
target_element, WTF::BindRepeating(
[](RemoteFrameView* remote_view, bool is_visible) {
remote_view->UpdateRenderThrottlingStatus(
!is_visible, remote_view->subtree_throttled_);
},
WrapWeakPersistent(this)));
visibility_observer_->Start();
}
void RemoteFrameView::UpdateRenderThrottlingStatus(bool hidden,
bool subtree_throttled) {
TRACE_EVENT0("blink", "RemoteFrameView::UpdateRenderThrottlingStatus");
if (!remote_frame_->Client())
return;
bool was_throttled = CanThrottleRendering();
// Note that we disallow throttling of 0x0 and display:none frames because
// some sites use them to drive UI logic.
HTMLFrameOwnerElement* owner_element = remote_frame_->DeprecatedLocalOwner();
hidden_for_throttling_ = hidden && !FrameRect().IsEmpty() &&
(owner_element && owner_element->GetLayoutObject());
subtree_throttled_ = subtree_throttled;
bool is_throttled = CanThrottleRendering();
if (was_throttled != is_throttled) {
remote_frame_->Client()->UpdateRenderThrottlingStatus(is_throttled,
subtree_throttled_);
}
}
bool RemoteFrameView::CanThrottleRendering() const {
if (!RuntimeEnabledFeatures::RenderingPipelineThrottlingEnabled())
return false;
if (subtree_throttled_)
return true;
return hidden_for_throttling_;
}
void RemoteFrameView::SetIntrinsicSizeInfo(
const IntrinsicSizingInfo& size_info) {
intrinsic_sizing_info_ = size_info;
has_intrinsic_sizing_info_ = true;
}
bool RemoteFrameView::GetIntrinsicSizingInfo(
IntrinsicSizingInfo& sizing_info) const {
if (!has_intrinsic_sizing_info_)
return false;
sizing_info = intrinsic_sizing_info_;
return true;
}
bool RemoteFrameView::HasIntrinsicSizingInfo() const {
return has_intrinsic_sizing_info_;
}
void RemoteFrameView::Trace(blink::Visitor* visitor) {
visitor->Trace(remote_frame_);
visitor->Trace(visibility_observer_);
}
} // namespace blink