blob: 44596695d0957291a6be811cbdce8db4c870712f [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 "services/content/public/cpp/navigable_contents_view.h"
#include <map>
#include "base/callback.h"
#include "base/no_destructor.h"
#include "base/synchronization/atomic_flag.h"
#include "base/unguessable_token.h"
#include "services/content/public/cpp/buildflags.h"
#if defined(TOOLKIT_VIEWS)
#include "ui/views/layout/fill_layout.h" // nogncheck
#include "ui/views/view.h" // nogncheck
#if BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
#include "services/ws/public/mojom/window_tree_constants.mojom.h" // nogncheck
#include "ui/base/ui_base_features.h" // nogncheck
#include "ui/views/controls/native/native_view_host.h" // nogncheck
#include "ui/views/mus/remote_view/remote_view_host.h" // nogncheck
#endif // BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
#endif // defined(TOOLKIT_VIEWS)
#if defined(USE_AURA)
#include "ui/aura/layout_manager.h" // nogncheck
#include "ui/aura/window.h" // nogncheck
#endif
namespace content {
namespace {
using InProcessEmbeddingMap =
std::map<base::UnguessableToken,
base::OnceCallback<void(NavigableContentsView*)>>;
InProcessEmbeddingMap& GetInProcessEmbeddingMap() {
static base::NoDestructor<InProcessEmbeddingMap> embedding_map;
return *embedding_map;
}
base::AtomicFlag& GetInServiceProcessFlag() {
static base::NoDestructor<base::AtomicFlag> in_service_process;
return *in_service_process;
}
#if BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
std::unique_ptr<NavigableContentsView::RemoteViewManager>&
GetRemoteViewManager() {
static base::NoDestructor<
std::unique_ptr<NavigableContentsView::RemoteViewManager>>
manager;
return *manager;
}
#endif // BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
#if defined(TOOLKIT_VIEWS) && defined(USE_AURA)
// Keeps child windows sized to the same bounds as the owning window.
class LocalWindowLayoutManager : public aura::LayoutManager {
public:
explicit LocalWindowLayoutManager(aura::Window* owner) : owner_(owner) {}
~LocalWindowLayoutManager() override = default;
// aura::LayoutManger:
void OnWindowResized() override { ResizeChildren(); }
void OnWindowAddedToLayout(aura::Window* child) override { ResizeChildren(); }
void OnWillRemoveWindowFromLayout(aura::Window* child) override {}
void OnWindowRemovedFromLayout(aura::Window* child) override {}
void OnChildWindowVisibilityChanged(aura::Window* child,
bool visible) override {}
void SetChildBounds(aura::Window* child,
const gfx::Rect& requested_bounds) override {}
private:
void ResizeChildren() {
for (auto* child : owner_->children())
SetChildBoundsDirect(child, owner_->bounds());
}
aura::Window* const owner_;
DISALLOW_COPY_AND_ASSIGN(LocalWindowLayoutManager);
};
// Owns an Aura window which parents another Aura window in the same process,
// corresponding to a web contents view hosted in the process.
class LocalViewHost : public views::NativeViewHost {
public:
explicit LocalViewHost(aura::Window* window) : window_(window) {
window_->SetLayoutManager(new LocalWindowLayoutManager(window_));
}
~LocalViewHost() override = default;
// views::View:
void AddedToWidget() override {
if (!native_view())
Attach(window_);
}
private:
aura::Window* const window_;
DISALLOW_COPY_AND_ASSIGN(LocalViewHost);
};
#endif // defined(TOOLKIT_VIEWS) && defined(USE_AURA)
} // namespace
NavigableContentsView::~NavigableContentsView() = default;
// static
void NavigableContentsView::SetClientRunningInServiceProcess() {
GetInServiceProcessFlag().Set();
}
// static
bool NavigableContentsView::IsClientRunningInServiceProcess() {
return GetInServiceProcessFlag().IsSet();
}
NavigableContentsView::NavigableContentsView() {
#if defined(TOOLKIT_VIEWS) && defined(USE_AURA)
#if BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
if (!IsClientRunningInServiceProcess()) {
RemoteViewManager* manager = GetRemoteViewManager().get();
if (manager)
view_ = manager->CreateRemoteViewHost();
else
view_ = std::make_unique<views::RemoteViewHost>();
view_->set_owned_by_client();
return;
}
#endif // BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
window_ = std::make_unique<aura::Window>(nullptr);
window_->set_owned_by_parent(false);
window_->SetName("NavigableContentsViewWindow");
window_->SetType(aura::client::WINDOW_TYPE_CONTROL);
window_->Init(ui::LAYER_NOT_DRAWN);
window_->Show();
view_ = std::make_unique<LocalViewHost>(window_.get());
view_->set_owned_by_client();
#endif // defined(TOOLKIT_VIEWS) && defined(USE_AURA)
}
#if BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
// static
void NavigableContentsView::SetRemoteViewManager(
std::unique_ptr<RemoteViewManager> manager) {
GetRemoteViewManager() = std::move(manager);
}
#endif // BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
void NavigableContentsView::EmbedUsingToken(
const base::UnguessableToken& token) {
#if defined(TOOLKIT_VIEWS)
#if BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
if (!IsClientRunningInServiceProcess()) {
RemoteViewManager* manager = GetRemoteViewManager().get();
if (manager) {
manager->EmbedUsingToken(view_.get(), token);
} else {
constexpr uint32_t kEmbedFlags =
ws::mojom::kEmbedFlagEmbedderInterceptsEvents |
ws::mojom::kEmbedFlagEmbedderControlsVisibility;
static_cast<views::RemoteViewHost*>(view_.get())
->EmbedUsingToken(token, kEmbedFlags, base::DoNothing());
}
return;
}
#endif // BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
DCHECK(IsClientRunningInServiceProcess());
// |token| should already have an embed callback entry in the in-process
// callback map, injected by the in-process Content Service implementation.
auto& embeddings = GetInProcessEmbeddingMap();
auto it = embeddings.find(token);
if (it == embeddings.end()) {
DLOG(ERROR) << "Unable to embed with unknown token " << token.ToString();
return;
}
// Invoke a callback provided by the Content Service's host environment. This
// should parent a web content view to our own |view()|, as well as set
// |native_view_| to the corresponding web contents' own NativeView.
auto callback = std::move(it->second);
embeddings.erase(it);
std::move(callback).Run(this);
#endif // defined(TOOLKIT_VIEWS)
}
// static
void NavigableContentsView::RegisterInProcessEmbedCallback(
const base::UnguessableToken& token,
base::OnceCallback<void(NavigableContentsView*)> callback) {
GetInProcessEmbeddingMap()[token] = std::move(callback);
}
} // namespace content