blob: 95fd56803e93cae95d8de7a277043da86874bf7a [file] [log] [blame]
// Copyright 2018 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 "content/browser/portal/portal.h"
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/frame_host/render_frame_host_manager.h"
#include "content/browser/frame_host/render_frame_proxy_host.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/content_switches.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#include "third_party/blink/public/common/features.h"
namespace content {
Portal::Portal(RenderFrameHostImpl* owner_render_frame_host)
: WebContentsObserver(
WebContents::FromRenderFrameHost(owner_render_frame_host)),
owner_render_frame_host_(owner_render_frame_host),
portal_token_(base::UnguessableToken::Create()) {}
Portal::~Portal() {}
// static
bool Portal::IsEnabled() {
return base::FeatureList::IsEnabled(blink::features::kPortals) ||
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
}
// static
Portal* Portal::Create(RenderFrameHostImpl* owner_render_frame_host,
blink::mojom::PortalRequest request) {
auto portal_ptr = base::WrapUnique(new Portal(owner_render_frame_host));
Portal* portal = portal_ptr.get();
portal->binding_ =
mojo::MakeStrongBinding(std::move(portal_ptr), std::move(request));
return portal;
}
// static
std::unique_ptr<Portal> Portal::CreateForTesting(
RenderFrameHostImpl* owner_render_frame_host) {
return base::WrapUnique(new Portal(owner_render_frame_host));
}
RenderFrameProxyHost* Portal::CreateProxyAndAttachPortal() {
WebContentsImpl* outer_contents_impl = static_cast<WebContentsImpl*>(
WebContents::FromRenderFrameHost(owner_render_frame_host_));
service_manager::mojom::InterfaceProviderPtr interface_provider;
auto interface_provider_request(mojo::MakeRequest(&interface_provider));
// Create a FrameTreeNode in the outer WebContents to host the portal, in
// response to the creation of a portal in the renderer process.
FrameTreeNode* outer_node = outer_contents_impl->GetFrameTree()->AddFrame(
owner_render_frame_host_->frame_tree_node(),
owner_render_frame_host_->GetProcess()->GetID(),
owner_render_frame_host_->GetProcess()->GetNextRoutingID(),
std::move(interface_provider_request), blink::WebTreeScopeType::kDocument,
"", "", true, base::UnguessableToken::Create(), blink::FramePolicy(),
FrameOwnerProperties(), false, blink::FrameOwnerElementType::kPortal);
// Create the Portal WebContents.
WebContents::CreateParams params(outer_contents_impl->GetBrowserContext());
std::unique_ptr<WebContents> portal_contents = WebContents::Create(params);
portal_contents_impl_ = static_cast<WebContentsImpl*>(portal_contents.get());
portal_contents_impl_->set_portal(this);
portal_contents_impl_->AttachToOuterWebContentsFrame(
std::move(portal_contents), outer_node->current_frame_host());
FrameTreeNode* frame_tree_node =
portal_contents_impl_->GetMainFrame()->frame_tree_node();
RenderFrameProxyHost* proxy_host =
frame_tree_node->render_manager()->GetProxyToOuterDelegate();
proxy_host->set_render_frame_proxy_created(true);
outer_contents_impl->GetDelegate()->PortalWebContentsCreated(
portal_contents_impl_);
return proxy_host;
}
void Portal::Navigate(const GURL& url) {
NavigationController::LoadURLParams load_url_params(url);
portal_contents_impl_->GetController().LoadURLWithParams(load_url_params);
}
void Portal::Activate(
base::OnceCallback<void(blink::mojom::PortalActivationStatus)> callback) {
WebContents* outer_contents =
WebContents::FromRenderFrameHost(owner_render_frame_host_);
WebContentsDelegate* delegate = outer_contents->GetDelegate();
if (delegate) {
FrameTreeNode* outer_node = FrameTreeNode::GloballyFindByID(
portal_contents_impl_->GetOuterDelegateFrameTreeNodeId());
bool is_loading = portal_contents_impl_->IsLoading();
std::unique_ptr<WebContents> portal_contents =
portal_contents_impl_->DetachFromOuterWebContents();
// TODO(lfg): If there are nested portals, this would replace the entire tab
// upon a nested portal's activation. We should handle that case so that it
// would only replace the nested portal's contents.
std::unique_ptr<WebContents> contents = delegate->SwapWebContents(
outer_contents, std::move(portal_contents), true, is_loading);
if (contents.get() == outer_contents) {
// TODO(lfg): The old WebContents is currently discarded, but should be
// kept and passed to the new page.
portal_contents_impl_->set_portal(nullptr);
std::move(callback).Run(blink::mojom::PortalActivationStatus::kSuccess);
} else {
DCHECK_EQ(portal_contents_impl_, contents.get());
portal_contents_impl_->AttachToOuterWebContentsFrame(
std::move(contents), outer_node->current_frame_host());
std::move(callback).Run(
blink::mojom::PortalActivationStatus::kNotSupported);
}
return;
}
std::move(callback).Run(blink::mojom::PortalActivationStatus::kNotSupported);
}
void Portal::RenderFrameDeleted(RenderFrameHost* render_frame_host) {
if (render_frame_host == owner_render_frame_host_)
binding_->Close(); // Also deletes |this|.
}
void Portal::WebContentsDestroyed() {
binding_->Close(); // Also deletes |this|.
}
WebContentsImpl* Portal::GetPortalContents() {
return portal_contents_impl_;
}
void Portal::SetBindingForTesting(
mojo::StrongBindingPtr<blink::mojom::Portal> binding) {
binding_ = binding;
}
} // namespace content