blob: 31827a00bcdef4b958e222d1726735d00a12870d [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/devtools/devtools_ui_controller.h"
#include "chrome/browser/devtools/devtools_contents_resizing_strategy.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/ui/views/frame/contents_container_view.h"
#include "chrome/browser/ui/views/frame/contents_web_view.h"
#include "ui/views/focus/external_focus_tracker.h"
DevtoolsUIController::DevtoolsUIController(
std::vector<ContentsContainerView*> contents_container_views) {
for (ContentsContainerView* contents_container_view :
contents_container_views) {
devtools_web_view_controllers_[contents_container_view] =
std::make_unique<DevtoolsWebViewController>(contents_container_view);
}
}
DevtoolsUIController::~DevtoolsUIController() = default;
void DevtoolsUIController::TearDown() {
devtools_web_view_controllers_.clear();
}
bool DevtoolsUIController::UpdateDevtools(
ContentsContainerView* contents_container,
content::WebContents* web_contents,
bool update_devtools_web_contents) {
DevtoolsWebViewController* controller =
devtools_web_view_controllers_[contents_container].get();
CHECK(controller);
if (update_devtools_web_contents &&
!DevToolsWindow::GetInTabWebContents(web_contents, nullptr)) {
controller->RestoreFocus();
}
return controller->UpdateDevtools(web_contents, update_devtools_web_contents);
}
DevtoolsUIController::DevtoolsWebViewController::DevtoolsWebViewController(
ContentsContainerView* contents_container_view)
: contents_container_view_(contents_container_view) {
ContentsWebView* contents_web_view =
contents_container_view_->GetContentsView();
web_contents_attached_subscription_ =
contents_web_view->AddWebContentsAttachedCallback(
base::BindRepeating(&DevtoolsWebViewController::OnWebContentsAttached,
base::Unretained(this)));
web_contents_detached_subscription_ =
contents_web_view->AddWebContentsDetachedCallback(
base::BindRepeating(&DevtoolsWebViewController::OnWebContentsDetached,
base::Unretained(this)));
OnWebContentsAttached(contents_web_view);
}
DevtoolsUIController::DevtoolsWebViewController::~DevtoolsWebViewController() =
default;
void DevtoolsUIController::DevtoolsWebViewController::OnWebContentsAttached(
views::WebView* web_view) {
UpdateDevtools(contents_container_view_->GetContentsView()->web_contents(),
true);
}
void DevtoolsUIController::DevtoolsWebViewController::OnWebContentsDetached(
views::WebView* web_view) {
UpdateDevtools(nullptr, true);
}
bool DevtoolsUIController::DevtoolsWebViewController::UpdateDevtools(
content::WebContents* web_contents,
bool update_devtools_web_contents) {
ContentsWebView* contents_view = contents_container_view_->GetContentsView();
views::WebView* devtools_web_view =
contents_container_view_->GetDevtoolsWebView();
DevToolsContentsResizingStrategy strategy;
content::WebContents* devtools =
DevToolsWindow::GetInTabWebContents(web_contents, &strategy);
// Replace devtools WebContents.
if (devtools_web_view->web_contents() != devtools &&
update_devtools_web_contents) {
if (!devtools_web_view->web_contents() && devtools &&
!devtools_focus_tracker_.get()) {
// Install devtools focus tracker when dev tools window is shown for the
// first time.
devtools_focus_tracker_ = std::make_unique<views::ExternalFocusTracker>(
devtools_web_view, contents_container_view_->GetFocusManager());
}
// Reset the focus tracker when hiding devtools window.
if (devtools_web_view->web_contents() && !devtools &&
devtools_focus_tracker_.get()) {
devtools_focus_tracker_.reset();
}
devtools_web_view->SetWebContents(devtools);
}
bool devtools_layout_updated;
if (devtools) {
devtools_layout_updated =
!devtools_web_view->GetVisible() ||
!contents_container_view_->contents_resizing_strategy().Equals(
strategy);
devtools_web_view->SetVisible(true);
contents_container_view_->SetContentsResizingStrategy(strategy);
} else {
devtools_layout_updated =
devtools_web_view->GetVisible() ||
!contents_container_view_->contents_resizing_strategy().Equals(
DevToolsContentsResizingStrategy());
devtools_web_view->SetVisible(false);
contents_container_view_->SetContentsResizingStrategy(
DevToolsContentsResizingStrategy());
}
if (devtools) {
// When strategy.hide_inspected_contents() returns true, we are hiding the
// WebContents behind the devtools_web_view_. Otherwise, the WebContents
// should be right above the devtools_web_view_.
size_t devtools_index =
contents_container_view_->GetIndexOf(devtools_web_view).value();
size_t contents_index =
contents_container_view_->GetIndexOf(contents_view).value();
bool devtools_is_on_top = devtools_index > contents_index;
if (strategy.hide_inspected_contents() != devtools_is_on_top) {
contents_container_view_->ReorderChildView(contents_view, devtools_index);
}
}
return devtools_layout_updated;
}
void DevtoolsUIController::DevtoolsWebViewController::RestoreFocus() {
if (devtools_focus_tracker_.get()) {
devtools_focus_tracker_->FocusLastFocusedExternalView();
}
}