blob: 3ea384243321a82d2000f8c0084d581f4a3795ad [file] [log] [blame]
// Copyright 2020 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.
#ifndef CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_MANAGER_H_
#define CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_MANAGER_H_
#include <memory>
#include <utility>
#include "base/scoped_observation.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/bubble/bubble_contents_wrapper_service.h"
#include "chrome/browser/ui/views/bubble/bubble_contents_wrapper_service_factory.h"
#include "chrome/browser/ui/views/bubble/webui_bubble_dialog_view.h"
#include "chrome/browser/ui/views/close_bubble_on_tab_activation_helper.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
class GURL;
class Profile;
class WebUIBubbleDialogView;
// WebUIBubbleManager handles the creation / destruction of the WebUI bubble.
// This is needed to deal with the asynchronous presentation of WebUI.
class WebUIBubbleManager : public views::WidgetObserver {
public:
WebUIBubbleManager();
WebUIBubbleManager(const WebUIBubbleManager&) = delete;
const WebUIBubbleManager& operator=(const WebUIBubbleManager&) = delete;
~WebUIBubbleManager() override;
bool ShowBubble();
void CloseBubble();
views::Widget* GetBubbleWidget() const;
bool bubble_using_cached_web_contents() const {
return bubble_using_cached_web_contents_;
}
virtual base::WeakPtr<WebUIBubbleDialogView> CreateWebUIBubbleDialog() = 0;
// views::WidgetObserver:
void OnWidgetDestroying(views::Widget* widget) override;
base::WeakPtr<WebUIBubbleDialogView> bubble_view_for_testing() {
return bubble_view_;
}
void ResetContentsWrapperForTesting();
void DisableCloseBubbleHelperForTesting();
protected:
BubbleContentsWrapper* cached_contents_wrapper() {
return cached_contents_wrapper_.get();
}
void set_cached_contents_wrapper(
std::unique_ptr<BubbleContentsWrapper> cached_contents_wrapper) {
cached_contents_wrapper_ = std::move(cached_contents_wrapper);
}
void set_bubble_using_cached_web_contents(bool is_cached) {
bubble_using_cached_web_contents_ = is_cached;
}
private:
void ResetContentsWrapper();
base::WeakPtr<WebUIBubbleDialogView> bubble_view_;
// Stores a cached BubbleContentsWrapper for reuse in the WebUIBubbleDialog.
std::unique_ptr<BubbleContentsWrapper> cached_contents_wrapper_;
// Tracks whether the current bubble was created by reusing a preloaded web
// contents.
bool bubble_using_cached_web_contents_ = false;
// A timer controlling how long the |cached_web_view_| is cached for.
std::unique_ptr<base::RetainingOneShotTimer> cache_timer_;
base::ScopedObservation<views::Widget, views::WidgetObserver>
bubble_widget_observation_{this};
// This is necessary to prevent a bug closing the active tab in the bubble.
// See https://crbug.com/1139028.
std::unique_ptr<CloseBubbleOnTabActivationHelper> close_bubble_helper_;
// Controls whether `close_bubble_helper_` is set when ShowBubble() is called.
bool disable_close_bubble_helper_ = false;
};
template <typename T>
class WebUIBubbleManagerT : public WebUIBubbleManager {
public:
WebUIBubbleManagerT(views::View* anchor_view,
Profile* profile,
const GURL& webui_url,
int task_manager_string_id,
bool enable_extension_apis = false)
: anchor_view_(anchor_view),
profile_(profile),
webui_url_(webui_url),
task_manager_string_id_(task_manager_string_id),
enable_extension_apis_(enable_extension_apis) {
if (base::FeatureList::IsEnabled(
features::kWebUIBubblePerProfilePersistence)) {
auto* service =
BubbleContentsWrapperServiceFactory::GetForProfile(profile_, true);
if (service && !service->GetBubbleContentsWrapperFromURL(webui_url_)) {
service->template InitBubbleContentsWrapper<T>(
webui_url_, task_manager_string_id_, enable_extension_apis_);
}
}
}
~WebUIBubbleManagerT() override = default;
base::WeakPtr<WebUIBubbleDialogView> CreateWebUIBubbleDialog() override {
BubbleContentsWrapper* contents_wrapper = nullptr;
// Only use per profile peristence if the flag is set and if a
// BubbleContentsWrapperService exists for the current profile. The service
// may not exist for off the record profiles.
auto* service =
BubbleContentsWrapperServiceFactory::GetForProfile(profile_, true);
if (service && base::FeatureList::IsEnabled(
features::kWebUIBubblePerProfilePersistence)) {
set_bubble_using_cached_web_contents(true);
// If using per-profile WebContents persistence get the associated
// BubbleContentsWrapper from the BubbleContentsWrapperService.
contents_wrapper = service->GetBubbleContentsWrapperFromURL(webui_url_);
DCHECK(contents_wrapper);
// If there is a host currently associated to this contents wrapper ensure
// the host has closed and the association has been removed.
if (contents_wrapper->GetHost())
contents_wrapper->CloseUI();
DCHECK(!contents_wrapper->GetHost());
// If the wrapped WebContents has crashed ensure we reload it here before
// passing it over to the dialog host.
if (contents_wrapper->web_contents()->IsCrashed())
contents_wrapper->ReloadWebContents();
} else {
set_bubble_using_cached_web_contents(!!cached_contents_wrapper());
if (!cached_contents_wrapper()) {
set_cached_contents_wrapper(std::make_unique<BubbleContentsWrapperT<T>>(
webui_url_, profile_, task_manager_string_id_,
enable_extension_apis_));
cached_contents_wrapper()->ReloadWebContents();
}
contents_wrapper = cached_contents_wrapper();
}
auto bubble_view =
std::make_unique<WebUIBubbleDialogView>(anchor_view_, contents_wrapper);
auto weak_ptr = bubble_view->GetWeakPtr();
views::BubbleDialogDelegateView::CreateBubble(std::move(bubble_view));
return weak_ptr;
}
private:
views::View* const anchor_view_;
Profile* const profile_;
const GURL webui_url_;
const int task_manager_string_id_;
const bool enable_extension_apis_;
};
#endif // CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_MANAGER_H_