| // Copyright (c) 2012 The Chromium Embedded Framework Authors. |
| // Portions copyright (c) 2012 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. |
| |
| #include "libcef/browser/native/javascript_dialog_runner_mac.h" |
| |
| #import <Cocoa/Cocoa.h> |
| |
| #include "base/bind.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| |
| // Helper object that receives the notification that the dialog/sheet is |
| // going away. Is responsible for cleaning itself up. |
| @interface CefJavaScriptDialogHelper : NSObject <NSAlertDelegate> { |
| @private |
| base::scoped_nsobject<NSAlert> alert_; |
| NSTextField* textField_; // WEAK; owned by alert_ |
| |
| // Copies of the fields in CefJavaScriptDialog because they're private. |
| CefJavaScriptDialogRunner::DialogClosedCallback callback_; |
| } |
| |
| - (id)initHelperWithCallback: |
| (CefJavaScriptDialogRunner::DialogClosedCallback)callback; |
| - (NSAlert*)alert; |
| - (NSTextField*)textField; |
| - (void)alertDidEnd:(NSAlert*)alert |
| returnCode:(int)returnCode |
| contextInfo:(void*)contextInfo; |
| - (void)cancel; |
| |
| @end |
| |
| @implementation CefJavaScriptDialogHelper |
| |
| - (id)initHelperWithCallback: |
| (CefJavaScriptDialogRunner::DialogClosedCallback)callback { |
| if (self = [super init]) |
| callback_ = std::move(callback); |
| |
| return self; |
| } |
| |
| - (NSAlert*)alert { |
| alert_.reset([[NSAlert alloc] init]); |
| return alert_; |
| } |
| |
| - (NSTextField*)textField { |
| textField_ = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 300, 22)]; |
| [[textField_ cell] setLineBreakMode:NSLineBreakByTruncatingTail]; |
| [alert_ setAccessoryView:textField_]; |
| [[alert_ window] setInitialFirstResponder:textField_]; |
| [textField_ release]; |
| |
| return textField_; |
| } |
| |
| - (void)alertDidEnd:(NSAlert*)alert |
| returnCode:(int)returnCode |
| contextInfo:(void*)contextInfo { |
| if (returnCode == NSModalResponseStop) |
| return; |
| |
| bool success = returnCode == NSAlertFirstButtonReturn; |
| base::string16 input; |
| if (textField_) |
| input = base::SysNSStringToUTF16([textField_ stringValue]); |
| |
| std::move(callback_).Run(success, input); |
| } |
| |
| - (void)cancel { |
| [NSApp endSheet:[alert_ window]]; |
| alert_.reset(); |
| } |
| |
| @end |
| |
| CefJavaScriptDialogRunnerMac::CefJavaScriptDialogRunnerMac() |
| : weak_ptr_factory_(this) {} |
| |
| CefJavaScriptDialogRunnerMac::~CefJavaScriptDialogRunnerMac() { |
| Cancel(); |
| } |
| |
| void CefJavaScriptDialogRunnerMac::Run( |
| CefBrowserHostImpl* browser, |
| content::JavaScriptDialogType message_type, |
| const base::string16& display_url, |
| const base::string16& message_text, |
| const base::string16& default_prompt_text, |
| DialogClosedCallback callback) { |
| DCHECK(!helper_.get()); |
| callback_ = std::move(callback); |
| |
| bool text_field = message_type == content::JAVASCRIPT_DIALOG_TYPE_PROMPT; |
| bool one_button = message_type == content::JAVASCRIPT_DIALOG_TYPE_ALERT; |
| |
| helper_.reset([[CefJavaScriptDialogHelper alloc] |
| initHelperWithCallback:base::BindOnce( |
| &CefJavaScriptDialogRunnerMac::DialogClosed, |
| weak_ptr_factory_.GetWeakPtr())]); |
| |
| // Show the modal dialog. |
| NSAlert* alert = [helper_ alert]; |
| NSTextField* field = nil; |
| if (text_field) { |
| field = [helper_ textField]; |
| [field setStringValue:base::SysUTF16ToNSString(default_prompt_text)]; |
| } |
| [alert setDelegate:helper_]; |
| [alert setInformativeText:base::SysUTF16ToNSString(message_text)]; |
| |
| base::string16 label; |
| switch (message_type) { |
| case content::JAVASCRIPT_DIALOG_TYPE_ALERT: |
| label = base::ASCIIToUTF16("JavaScript Alert"); |
| break; |
| case content::JAVASCRIPT_DIALOG_TYPE_PROMPT: |
| label = base::ASCIIToUTF16("JavaScript Prompt"); |
| break; |
| case content::JAVASCRIPT_DIALOG_TYPE_CONFIRM: |
| label = base::ASCIIToUTF16("JavaScript Confirm"); |
| break; |
| } |
| if (!display_url.empty()) |
| label += base::ASCIIToUTF16(" - ") + display_url; |
| |
| [alert setMessageText:base::SysUTF16ToNSString(label)]; |
| |
| [alert addButtonWithTitle:@"OK"]; |
| if (!one_button) { |
| NSButton* other = [alert addButtonWithTitle:@"Cancel"]; |
| [other setKeyEquivalent:@"\e"]; |
| } |
| |
| // Calling beginSheetModalForWindow:nil is wrong API usage. For now work |
| // around the "callee requires a non-null argument" error that occurs when |
| // building with the 10.11 SDK. See http://crbug.com/383820 for related |
| // discussion. |
| // We can't use the newer beginSheetModalForWindow:completionHandler: variant |
| // because it fails silently when passed a nil argument (see issue #2726). |
| id nilArg = nil; |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| [alert beginSheetModalForWindow:nilArg // nil here makes it app-modal |
| modalDelegate:helper_ |
| didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) |
| contextInfo:this]; |
| #pragma clang diagnostic pop |
| |
| if ([alert accessoryView]) |
| [[alert window] makeFirstResponder:[alert accessoryView]]; |
| } |
| |
| void CefJavaScriptDialogRunnerMac::Cancel() { |
| if (helper_.get()) { |
| [helper_ cancel]; |
| helper_.reset(nil); |
| } |
| } |
| |
| void CefJavaScriptDialogRunnerMac::DialogClosed( |
| bool success, |
| const base::string16& user_input) { |
| helper_.reset(nil); |
| std::move(callback_).Run(success, user_input); |
| } |