diff --git a/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.cc b/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.cc index 529489571dc5..6b23b6244aab 100644 --- a/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.cc +++ b/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.cc @@ -6,6 +6,8 @@ #include "brave/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.h" #include +#include +#include #include #include @@ -15,7 +17,9 @@ #include "base/strings/string_util.h" #include "base/task/sequenced_task_runner.h" #include "base/task/thread_pool.h" +#include "base/values.h" #include "brave/browser/brave_browser_process.h" +#include "brave/browser/ui/brave_browser_window.h" #include "brave/browser/ui/webui/brave_webui_source.h" #include "brave/browser/ui/webui/webcompat_reporter/webcompat_reporter_dialog.h" #include "brave/browser/webcompat_reporter/webcompat_reporter_service_factory.h" @@ -31,16 +35,20 @@ #include "brave/components/webcompat_reporter/resources/grit/webcompat_reporter_generated_map.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/browser_list.h" #include "components/grit/brave_components_resources.h" #include "components/language/core/browser/pref_names.h" #include "components/prefs/pref_service.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui_data_source.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +#include "ui/compositor/compositor.h" #include "ui/gfx/codec/png_codec.h" -#include "url/gurl.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" #if BUILDFLAG(ENABLE_BRAVE_VPN) #include "brave/browser/brave_vpn/brave_vpn_service_factory.h" @@ -53,6 +61,58 @@ namespace { constexpr char kUISourceHistogramName[] = "Brave.Webcompat.UISource"; constexpr int kMaxScreenshotPixelCount = 1280 * 720; +constexpr char kGetViewPortSizeParamName[] = "height"; +constexpr char kOnViewPortSizeChangedEventName[] = "onViewPortSizeChanged"; + +content::WebContents* GetActiveWebContents() { + const auto* browser = BrowserList::GetInstance()->GetLastActive(); + if (!browser) { + return nullptr; + } + return browser->tab_strip_model()->GetActiveWebContents(); +} + +const std::optional GetContainerBounds(content::WebUI* web_ui) { + if (!web_ui) { + auto* web_contents = GetActiveWebContents(); + return web_contents ? std::make_optional(web_contents->GetContainerBounds()) + : std::nullopt; + } + + return web_ui->GetWebContents()->GetContainerBounds(); +} + +views::Widget* GetBrowserWidget() { + const auto* browser = BrowserList::GetInstance()->GetLastActive(); + if (!browser) { + return nullptr; + } + + auto* widget = views::Widget::GetWidgetForNativeWindow( + browser->window()->GetNativeWindow()); + if (!widget) { + return nullptr; + } + + return widget->GetPrimaryWindowWidget(); +} + +const std::optional GetDlgMaxHeight(content::WebUI* web_ui, + const views::Widget* browser_widget) { + DCHECK(browser_widget); + if (!browser_widget) { + return std::nullopt; + } + const auto modal_dlg_bounds = GetContainerBounds(web_ui); + if (!modal_dlg_bounds) { + return std::nullopt; + } + + const auto browser_wnd_bounds = + browser_widget->client_view()->GetBoundsInScreen(); + + return browser_wnd_bounds.bottom() - modal_dlg_bounds->y(); +} } // namespace @@ -65,16 +125,6 @@ WebcompatReporterDOMHandler::WebcompatReporterDOMHandler(Profile* profile) DCHECK_CURRENTLY_ON(content::BrowserThread::UI); InitAdditionalParameters(profile); - - auto* browser = chrome::FindLastActiveWithProfile(profile); - if (!browser) { - return; - } - auto* web_contents = browser->tab_strip_model()->GetActiveWebContents(); - if (!web_contents) { - return; - } - render_widget_host_view_ = web_contents->GetTopLevelRenderWidgetHostView(); } void WebcompatReporterDOMHandler::InitAdditionalParameters(Profile* profile) { @@ -97,6 +147,18 @@ void WebcompatReporterDOMHandler::InitAdditionalParameters(Profile* profile) { WebcompatReporterDOMHandler::~WebcompatReporterDOMHandler() = default; +base::WeakPtr +WebcompatReporterDOMHandler::AsWeekPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +void WebcompatReporterDOMHandler::OnWindowResize(const int& height) { + AllowJavascript(); + base::Value::Dict event_data; + event_data.Set(kGetViewPortSizeParamName, height); + FireWebUIListener(kOnViewPortSizeChangedEventName, event_data); +} + void WebcompatReporterDOMHandler::RegisterMessages() { web_ui()->RegisterMessageCallback( "webcompat_reporter.submitReport", @@ -115,16 +177,22 @@ void WebcompatReporterDOMHandler::RegisterMessages() { "webcompat_reporter.clearScreenshot", base::BindRepeating(&WebcompatReporterDOMHandler::HandleClearScreenshot, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "webcompat_reporter.init", + base::BindRepeating(&WebcompatReporterDOMHandler::HandleInit, + base::Unretained(this))); } void WebcompatReporterDOMHandler::HandleCaptureScreenshot( const base::Value::List& args) { - CHECK(render_widget_host_view_); + auto* render_widget_host_view = + web_ui()->GetWebContents()->GetTopLevelRenderWidgetHostView(); + CHECK(render_widget_host_view); CHECK_EQ(args.size(), 1u); AllowJavascript(); - auto output_size = render_widget_host_view_->GetVisibleViewportSize(); + auto output_size = render_widget_host_view->GetVisibleViewportSize(); auto original_area = output_size.GetArea(); if (original_area > kMaxScreenshotPixelCount) { @@ -134,7 +202,7 @@ void WebcompatReporterDOMHandler::HandleCaptureScreenshot( output_size = gfx::ScaleToRoundedSize(output_size, output_scale); } - render_widget_host_view_->CopyFromSurface( + render_widget_host_view->CopyFromSurface( {}, output_size, base::BindOnce( [](base::WeakPtr handler, @@ -192,6 +260,22 @@ void WebcompatReporterDOMHandler::HandleGetCapturedScreenshot( ResolveJavascriptCallback(args[0], screenshot_b64); } +void WebcompatReporterDOMHandler::HandleInit(const base::Value::List& args) { + CHECK_EQ(args.size(), 1u); + + AllowJavascript(); + + const auto max_height = GetDlgMaxHeight(nullptr, GetBrowserWidget()); + if (!max_height) { + RejectJavascriptCallback(args[0], {}); + return; + } + + base::Value::Dict event_data; + event_data.Set(kGetViewPortSizeParamName, *max_height); + ResolveJavascriptCallback(args[0], event_data); +} + void WebcompatReporterDOMHandler::HandleClearScreenshot( const base::Value::List& args) { pending_report_->screenshot_png = std::nullopt; @@ -248,12 +332,33 @@ WebcompatReporterUI::WebcompatReporterUI(content::WebUI* web_ui) CreateAndAddWebUIDataSource( web_ui, kWebcompatReporterHost, kWebcompatReporterGenerated, kWebcompatReporterGeneratedSize, IDR_WEBCOMPAT_REPORTER_HTML); - Profile* profile = Profile::FromWebUI(web_ui); + auto* profile = Profile::FromWebUI(web_ui); + + auto webcompat_reporter_handler = + std::make_unique(profile); - web_ui->AddMessageHandler( - std::make_unique(profile)); + webcompat_reporter_handler_ = webcompat_reporter_handler->AsWeekPtr(); + + web_ui->AddMessageHandler(std::move(webcompat_reporter_handler)); + + if (auto* widget = GetBrowserWidget()) { + observed_windows_.AddObservation(widget); + } } WebcompatReporterUI::~WebcompatReporterUI() = default; +void WebcompatReporterUI::OnWidgetBoundsChanged(views::Widget* widget, + const gfx::Rect& new_bounds) { + DCHECK(widget); + DCHECK(webcompat_reporter_handler_); + if (!webcompat_reporter_handler_ || !widget) { + return; + } + + if (const auto max_height = GetDlgMaxHeight(web_ui(), widget)) { + webcompat_reporter_handler_->OnWindowResize(*max_height); + } +} + } // namespace webcompat_reporter diff --git a/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.h b/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.h index 7f7756973dbd..6f84f6c97db9 100644 --- a/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.h +++ b/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.h @@ -6,11 +6,12 @@ #ifndef BRAVE_BROWSER_UI_WEBUI_WEBCOMPAT_REPORTER_WEBCOMPAT_REPORTER_UI_H_ #define BRAVE_BROWSER_UI_WEBUI_WEBCOMPAT_REPORTER_WEBCOMPAT_REPORTER_UI_H_ -#include -#include +#include #include +#include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" +#include "base/scoped_multi_source_observation.h" #include "base/task/sequenced_task_runner.h" #include "brave/components/constants/webui_url_constants.h" #include "brave/components/webcompat_reporter/common/webcompat_reporter.mojom-forward.h" @@ -19,11 +20,14 @@ #include "content/public/browser/web_ui_message_handler.h" #include "content/public/browser/webui_config.h" #include "content/public/common/url_constants.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_observer.h" namespace content { class RenderWidgetHostView; class WebUI; -} +} // namespace content namespace webcompat_reporter { @@ -45,6 +49,9 @@ class WebcompatReporterDOMHandler : public content::WebUIMessageHandler { delete; ~WebcompatReporterDOMHandler() override; + void OnWindowResize(const int& height); + base::WeakPtr AsWeekPtr(); + // WebUIMessageHandler implementation. void RegisterMessages() override; @@ -61,10 +68,10 @@ class WebcompatReporterDOMHandler : public content::WebUIMessageHandler { void HandleClearScreenshot(const base::Value::List& args); void HandleSubmitReport(const base::Value::List& args); + void HandleInit(const base::Value::List& args); raw_ptr reporter_service_ = nullptr; raw_ptr pref_service_ = nullptr; - raw_ptr render_widget_host_view_; scoped_refptr ui_task_runner_; mojom::ReportInfoPtr pending_report_; @@ -72,12 +79,22 @@ class WebcompatReporterDOMHandler : public content::WebUIMessageHandler { base::WeakPtrFactory weak_ptr_factory_{this}; }; -class WebcompatReporterUI : public ConstrainedWebDialogUI { +class WebcompatReporterUI : public ConstrainedWebDialogUI, + public views::WidgetObserver { public: explicit WebcompatReporterUI(content::WebUI* web_ui); WebcompatReporterUI(const WebcompatReporterUI&) = delete; WebcompatReporterUI& operator=(const WebcompatReporterUI&) = delete; ~WebcompatReporterUI() override; + + // views::WidgetObserver + void OnWidgetBoundsChanged(views::Widget* widget, + const gfx::Rect& new_bounds) override; + + private: + base::WeakPtr webcompat_reporter_handler_; + base::ScopedMultiSourceObservation + observed_windows_{this}; }; } // namespace webcompat_reporter diff --git a/components/webcompat_reporter/ui/browser_proxy.ts b/components/webcompat_reporter/ui/browser_proxy.ts index 72ca69e3283c..1af0181e1aec 100644 --- a/components/webcompat_reporter/ui/browser_proxy.ts +++ b/components/webcompat_reporter/ui/browser_proxy.ts @@ -2,7 +2,10 @@ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. -import { sendWithPromise } from 'chrome://resources/js/cr.js'; +import { + addWebUiListener, + sendWithPromise +} from 'chrome://resources/js/cr.js'; export function submitReport(reportDetails: { [key: string]: any }) { chrome.send('webcompat_reporter.submitReport', [ @@ -29,3 +32,13 @@ export function clearScreenshot() { export function getCapturedScreenshot(): Promise { return sendWithPromise('webcompat_reporter.getCapturedScreenshot') } + +export interface ViewPortSizeChangedObject { + height: number +} + +export function setViewPortChangeListener( + calback: (data: ViewPortSizeChangedObject) => void) { + sendWithPromise('webcompat_reporter.init').then(calback) + addWebUiListener('onViewPortSizeChanged', calback) +} \ No newline at end of file diff --git a/components/webcompat_reporter/ui/components/ReportView.tsx b/components/webcompat_reporter/ui/components/ReportView.tsx index ae96f6bf45af..1743e92846f0 100644 --- a/components/webcompat_reporter/ui/components/ReportView.tsx +++ b/components/webcompat_reporter/ui/components/ReportView.tsx @@ -11,7 +11,6 @@ import { ModalTitle, TextSection, InfoText, - NonInteractiveURL, DisclaimerText, SideBySideButtons, PaddedButton, @@ -24,6 +23,8 @@ import { ScreenshotLink } from './basic' +import ShortenedUrl from './ShortenedUrl' + // Localization data import { getLocale } from '../../../common/locale' import { @@ -129,7 +130,7 @@ export default class ReportView extends React.PureComponent { {!isIneligiblePage && <> - {siteUrl} +