| // Copyright 2012 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "components/javascript_dialogs/app_modal_dialog_controller.h" | |
| #include <utility> | |
| #include "base/check_op.h" | |
| #include "build/build_config.h" | |
| #include "components/javascript_dialogs/app_modal_dialog_manager.h" | |
| #include "components/javascript_dialogs/app_modal_dialog_queue.h" | |
| #include "components/javascript_dialogs/app_modal_dialog_view.h" | |
| #include "ui/gfx/text_elider.h" | |
| namespace javascript_dialogs { | |
| namespace { | |
| AppModalDialogObserver* app_modal_dialog_observer = nullptr; | |
| // Control maximum sizes of various texts passed to us from javascript. | |
| #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) | |
| // Two-dimensional eliding. Reformat the text of the message dialog | |
| // inserting line breaks because otherwise a single long line can overflow | |
| // the message dialog (and crash/hang the GTK, depending on the version). | |
| const int kMessageTextMaxRows = 32; | |
| const int kMessageTextMaxCols = 132; | |
| const int kDefaultPromptMaxRows = 24; | |
| const int kDefaultPromptMaxCols = 132; | |
| std::u16string EnforceMaxTextSize(const std::u16string& in_string) { | |
| std::u16string out_string; | |
| gfx::ElideRectangleString(in_string, kMessageTextMaxRows, kMessageTextMaxCols, | |
| false, &out_string); | |
| return out_string; | |
| } | |
| std::u16string EnforceMaxPromptSize(const std::u16string& in_string) { | |
| std::u16string out_string; | |
| gfx::ElideRectangleString(in_string, kDefaultPromptMaxRows, | |
| kDefaultPromptMaxCols, false, &out_string); | |
| return out_string; | |
| } | |
| #else | |
| // One-dimensional eliding. Trust the window system to break the string | |
| // appropriately, but limit its overall length to something reasonable. | |
| const size_t kMessageTextMaxSize = 2000; | |
| const size_t kDefaultPromptMaxSize = 2000; | |
| std::u16string EnforceMaxTextSize(const std::u16string& in_string) { | |
| std::u16string out_string; | |
| gfx::ElideString(in_string, kMessageTextMaxSize, &out_string); | |
| return out_string; | |
| } | |
| std::u16string EnforceMaxPromptSize(const std::u16string& in_string) { | |
| std::u16string out_string; | |
| gfx::ElideString(in_string, kDefaultPromptMaxSize, &out_string); | |
| return out_string; | |
| } | |
| #endif | |
| } // namespace | |
| ChromeJavaScriptDialogExtraData::ChromeJavaScriptDialogExtraData() = default; | |
| AppModalDialogController::AppModalDialogController( | |
| content::WebContents* web_contents, | |
| ExtraDataMap* extra_data_map, | |
| const std::u16string& title, | |
| content::JavaScriptDialogType javascript_dialog_type, | |
| const std::u16string& message_text, | |
| const std::u16string& default_prompt_text, | |
| bool display_suppress_checkbox, | |
| bool is_before_unload_dialog, | |
| bool is_reload, | |
| content::JavaScriptDialogManager::DialogClosedCallback callback) | |
| : content::WebContentsObserver(web_contents), | |
| title_(title), | |
| extra_data_map_(extra_data_map), | |
| javascript_dialog_type_(javascript_dialog_type), | |
| message_text_(EnforceMaxTextSize(message_text)), | |
| default_prompt_text_(EnforceMaxPromptSize(default_prompt_text)), | |
| display_suppress_checkbox_(display_suppress_checkbox), | |
| is_before_unload_dialog_(is_before_unload_dialog), | |
| is_reload_(is_reload), | |
| callback_(std::move(callback)) {} | |
| AppModalDialogController::~AppModalDialogController() { | |
| CompleteDialog(); | |
| } | |
| void AppModalDialogController::ShowModalDialog( | |
| std::unique_ptr<AppModalDialogController> controller) { | |
| CHECK_EQ(this, controller.get()); | |
| view_ = AppModalDialogManager::GetInstance()->view_factory()->Run( | |
| std::move(controller)); | |
| view_->ShowAppModalDialog(); | |
| if (app_modal_dialog_observer) { | |
| app_modal_dialog_observer->Notify(this); | |
| } | |
| } | |
| void AppModalDialogController::ActivateModalDialog() { | |
| DCHECK(view_); | |
| view_->ActivateAppModalDialog(); | |
| } | |
| void AppModalDialogController::CloseModalDialog() { | |
| DCHECK(view_); | |
| view_->CloseAppModalDialog(); | |
| } | |
| void AppModalDialogController::CompleteDialog() { | |
| // If |view_| is non-null, then |this| is the active dialog and the next one | |
| // should be shown. Otherwise, |this| was never shown. | |
| if (view_) { | |
| view_ = nullptr; | |
| AppModalDialogQueue::GetInstance()->ShowNextDialog(); | |
| } else { | |
| DCHECK(!valid_); | |
| } | |
| } | |
| bool AppModalDialogController::IsValid() { | |
| return valid_; | |
| } | |
| void AppModalDialogController::Invalidate() { | |
| if (!valid_) { | |
| return; | |
| } | |
| valid_ = false; | |
| CallDialogClosedCallback(false, std::u16string()); | |
| if (view_) { | |
| CloseModalDialog(); | |
| } | |
| } | |
| void AppModalDialogController::OnCancel(bool suppress_js_messages) { | |
| // We need to do this before WM_DESTROY (WindowClosing()) as any parent frame | |
| // will receive its activation messages before this dialog receives | |
| // WM_DESTROY. The parent frame would then try to activate any modal dialogs | |
| // that were still open in the ModalDialogQueue, which would send activation | |
| // back to this one. The framework should be improved to handle this, so this | |
| // is a temporary workaround. | |
| CompleteDialog(); | |
| NotifyDelegate(/*success=*/false, std::u16string(), suppress_js_messages); | |
| } | |
| void AppModalDialogController::OnAccept(const std::u16string& prompt_text, | |
| bool suppress_js_messages) { | |
| CompleteDialog(); | |
| NotifyDelegate(/*success=*/true, override_prompt_text_.value_or(prompt_text), | |
| suppress_js_messages); | |
| } | |
| void AppModalDialogController::OnClose() { | |
| NotifyDelegate(false, std::u16string(), false); | |
| } | |
| void AppModalDialogController::SetOverridePromptText( | |
| const std::u16string& override_prompt_text) { | |
| override_prompt_text_ = override_prompt_text; | |
| } | |
| void AppModalDialogController::WebContentsDestroyed() { | |
| Invalidate(); | |
| } | |
| void AppModalDialogController::NotifyDelegate(bool success, | |
| const std::u16string& user_input, | |
| bool suppress_js_messages) { | |
| if (!valid_) { | |
| return; | |
| } | |
| CallDialogClosedCallback(success, user_input); | |
| // The close callback above may delete web_contents_, thus removing the extra | |
| // data from the map owned by ::AppModalDialogManager. Make sure | |
| // to only use the data if still present. https://crbug.com/41009870 | |
| if (auto* contents = web_contents()) { | |
| auto extra_data = extra_data_map_->find(contents); | |
| if (extra_data != extra_data_map_->end()) { | |
| extra_data->second.has_already_shown_a_dialog_ = true; | |
| extra_data->second.suppress_javascript_messages_ = suppress_js_messages; | |
| } | |
| } | |
| // On Views, we can end up coming through this code path twice :(. | |
| // See https://crbug.com/40085084. | |
| valid_ = false; | |
| } | |
| void AppModalDialogController::CallDialogClosedCallback( | |
| bool success, | |
| const std::u16string& user_input) { | |
| if (!callback_.is_null()) { | |
| std::move(callback_).Run(success, user_input); | |
| } | |
| } | |
| AppModalDialogObserver::AppModalDialogObserver() { | |
| DCHECK(!app_modal_dialog_observer); | |
| app_modal_dialog_observer = this; | |
| } | |
| AppModalDialogObserver::~AppModalDialogObserver() { | |
| DCHECK(app_modal_dialog_observer); | |
| app_modal_dialog_observer = nullptr; | |
| } | |
| } // namespace javascript_dialogs |