|  | // 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 "ui/web_dialogs/web_dialog_ui.h" | 
|  |  | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/functional/callback_helpers.h" | 
|  | #include "base/lazy_instance.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/memory/raw_ptr.h" | 
|  | #include "base/values.h" | 
|  | #include "content/public/browser/render_frame_host.h" | 
|  | #include "content/public/browser/web_contents.h" | 
|  | #include "content/public/browser/web_ui.h" | 
|  | #include "content/public/browser/web_ui_message_handler.h" | 
|  | #include "content/public/common/bindings_policy.h" | 
|  | #include "ui/web_dialogs/web_dialog_delegate.h" | 
|  |  | 
|  | using content::RenderFrameHost; | 
|  | using content::WebUIMessageHandler; | 
|  |  | 
|  | namespace ui { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kWebDialogDelegateUserDataKey[] = "WebDialogDelegateUserData"; | 
|  |  | 
|  | class WebDialogDelegateUserData : public base::SupportsUserData::Data { | 
|  | public: | 
|  | explicit WebDialogDelegateUserData(WebDialogDelegate* delegate) | 
|  | : delegate_(delegate) {} | 
|  | ~WebDialogDelegateUserData() override {} | 
|  | WebDialogDelegate* delegate() { return delegate_; } | 
|  |  | 
|  | private: | 
|  | raw_ptr<WebDialogDelegate> delegate_;  // unowned | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | void WebDialogUIBase::SetDelegate(content::WebContents* web_contents, | 
|  | WebDialogDelegate* delegate) { | 
|  | web_contents->SetUserData( | 
|  | &kWebDialogDelegateUserDataKey, | 
|  | std::make_unique<WebDialogDelegateUserData>(delegate)); | 
|  | } | 
|  |  | 
|  | WebDialogUIBase::WebDialogUIBase(content::WebUI* web_ui) : web_ui_(web_ui) {} | 
|  |  | 
|  | // Don't unregister our user data. During the teardown of the WebContents, this | 
|  | // will be deleted, but the WebContents will already be destroyed. | 
|  | // | 
|  | // This object is owned indirectly by the WebContents. WebUIs can change, so | 
|  | // it's scary if this WebUI is changed out and replaced with something else, | 
|  | // since the user data will still point to the old delegate. But the delegate is | 
|  | // itself the owner of the WebContents for a dialog so will be in scope, and the | 
|  | // HTML dialogs won't swap WebUIs anyway since they don't navigate. | 
|  | WebDialogUIBase::~WebDialogUIBase() = default; | 
|  |  | 
|  | void WebDialogUIBase::CloseDialog(const base::Value::List& args) { | 
|  | OnDialogClosed(args); | 
|  | } | 
|  |  | 
|  | WebDialogDelegate* WebDialogUIBase::GetDelegate( | 
|  | content::WebContents* web_contents) { | 
|  | WebDialogDelegateUserData* user_data = | 
|  | static_cast<WebDialogDelegateUserData*>( | 
|  | web_contents->GetUserData(&kWebDialogDelegateUserDataKey)); | 
|  |  | 
|  | return user_data ? user_data->delegate() : NULL; | 
|  | } | 
|  |  | 
|  | void WebDialogUIBase::HandleRenderFrameCreated( | 
|  | RenderFrameHost* render_frame_host) { | 
|  | // Hook up the javascript function calls, also known as chrome.send("foo") | 
|  | // calls in the HTML, to the actual C++ functions. | 
|  | web_ui_->RegisterMessageCallback( | 
|  | "dialogClose", base::BindRepeating(&WebDialogUIBase::OnDialogClosed, | 
|  | base::Unretained(this))); | 
|  |  | 
|  | // Pass the arguments to the renderer supplied by the delegate. | 
|  | std::string dialog_args; | 
|  | std::vector<WebUIMessageHandler*> handlers; | 
|  | WebDialogDelegate* delegate = GetDelegate(web_ui_->GetWebContents()); | 
|  | if (delegate) { | 
|  | dialog_args = delegate->GetDialogArgs(); | 
|  | delegate->GetWebUIMessageHandlers(&handlers); | 
|  | } | 
|  |  | 
|  | if (content::BINDINGS_POLICY_NONE != | 
|  | (web_ui_->GetBindings() & content::BINDINGS_POLICY_WEB_UI)) { | 
|  | render_frame_host->SetWebUIProperty("dialogArguments", dialog_args); | 
|  | } | 
|  | for (WebUIMessageHandler* handler : handlers) | 
|  | web_ui_->AddMessageHandler(base::WrapUnique(handler)); | 
|  |  | 
|  | if (delegate) | 
|  | delegate->OnDialogShown(web_ui_); | 
|  | } | 
|  |  | 
|  | void WebDialogUIBase::OnDialogClosed(const base::Value::List& args) { | 
|  | WebDialogDelegate* delegate = GetDelegate(web_ui_->GetWebContents()); | 
|  | if (delegate) { | 
|  | std::string json_retval; | 
|  | if (!args.empty()) { | 
|  | if (args[0].is_string()) | 
|  | json_retval = args[0].GetString(); | 
|  | else | 
|  | NOTREACHED() << "Could not read JSON argument"; | 
|  | } | 
|  |  | 
|  | delegate->OnDialogCloseFromWebUI(json_retval); | 
|  | } | 
|  | } | 
|  |  | 
|  | WebDialogUI::WebDialogUI(content::WebUI* web_ui) | 
|  | : WebDialogUIBase(web_ui), content::WebUIController(web_ui) {} | 
|  |  | 
|  | WebDialogUI::~WebDialogUI() = default; | 
|  |  | 
|  | void WebDialogUI::WebUIRenderFrameCreated(RenderFrameHost* render_frame_host) { | 
|  | HandleRenderFrameCreated(render_frame_host); | 
|  | } | 
|  |  | 
|  | // Note: chrome.send() must always be enabled for dialogs, since dialogs rely on | 
|  | // chrome.send() to notify their handlers that the dialog should be closed. See | 
|  | // the "dialogClose" message handler above in | 
|  | // WebDialogUIBase::HandleRenderFrameCreated(). | 
|  | MojoWebDialogUI::MojoWebDialogUI(content::WebUI* web_ui) | 
|  | : WebDialogUIBase(web_ui), | 
|  | MojoWebUIController(web_ui, /*enable_chrome_send=*/true) {} | 
|  |  | 
|  | MojoWebDialogUI::~MojoWebDialogUI() = default; | 
|  |  | 
|  | void MojoWebDialogUI::WebUIRenderFrameCreated( | 
|  | content::RenderFrameHost* render_frame_host) { | 
|  | content::WebUIController::WebUIRenderFrameCreated(render_frame_host); | 
|  | HandleRenderFrameCreated(render_frame_host); | 
|  | } | 
|  |  | 
|  | }  // namespace ui |