1// Copyright (c) 2012 The Chromium Embedded Framework Authors. 2// Portions copyright (c) 2012 The Chromium Authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5 6#include "libcef/browser/native/javascript_dialog_runner_mac.h" 7 8#import <Cocoa/Cocoa.h> 9 10#include "base/bind.h" 11#include "base/strings/sys_string_conversions.h" 12#include "base/strings/utf_string_conversions.h" 13 14// Helper object that receives the notification that the dialog/sheet is 15// going away. Is responsible for cleaning itself up. 16@interface CefJavaScriptDialogHelper : NSObject <NSAlertDelegate> { 17 @private 18 base::scoped_nsobject<NSAlert> alert_; 19 NSTextField* textField_; // WEAK; owned by alert_ 20 21 // Copies of the fields in CefJavaScriptDialog because they're private. 22 CefJavaScriptDialogRunner::DialogClosedCallback callback_; 23} 24 25- (id)initHelperWithCallback: 26 (CefJavaScriptDialogRunner::DialogClosedCallback)callback; 27- (NSAlert*)alert; 28- (NSTextField*)textField; 29- (void)alertDidEnd:(NSAlert*)alert 30 returnCode:(int)returnCode 31 contextInfo:(void*)contextInfo; 32- (void)cancel; 33 34@end 35 36@implementation CefJavaScriptDialogHelper 37 38- (id)initHelperWithCallback: 39 (CefJavaScriptDialogRunner::DialogClosedCallback)callback { 40 if (self = [super init]) 41 callback_ = std::move(callback); 42 43 return self; 44} 45 46- (NSAlert*)alert { 47 alert_.reset([[NSAlert alloc] init]); 48 return alert_; 49} 50 51- (NSTextField*)textField { 52 textField_ = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 300, 22)]; 53 [[textField_ cell] setLineBreakMode:NSLineBreakByTruncatingTail]; 54 [alert_ setAccessoryView:textField_]; 55 [[alert_ window] setInitialFirstResponder:textField_]; 56 [textField_ release]; 57 58 return textField_; 59} 60 61- (void)alertDidEnd:(NSAlert*)alert 62 returnCode:(int)returnCode 63 contextInfo:(void*)contextInfo { 64 if (returnCode == NSModalResponseStop) 65 return; 66 67 bool success = returnCode == NSAlertFirstButtonReturn; 68 std::u16string input; 69 if (textField_) 70 input = base::SysNSStringToUTF16([textField_ stringValue]); 71 72 std::move(callback_).Run(success, input); 73} 74 75- (void)cancel { 76 [NSApp endSheet:[alert_ window]]; 77 alert_.reset(); 78} 79 80@end 81 82CefJavaScriptDialogRunnerMac::CefJavaScriptDialogRunnerMac() 83 : weak_ptr_factory_(this) {} 84 85CefJavaScriptDialogRunnerMac::~CefJavaScriptDialogRunnerMac() { 86 Cancel(); 87} 88 89void CefJavaScriptDialogRunnerMac::Run( 90 AlloyBrowserHostImpl* browser, 91 content::JavaScriptDialogType message_type, 92 const std::u16string& display_url, 93 const std::u16string& message_text, 94 const std::u16string& default_prompt_text, 95 DialogClosedCallback callback) { 96 DCHECK(!helper_.get()); 97 callback_ = std::move(callback); 98 99 bool text_field = message_type == content::JAVASCRIPT_DIALOG_TYPE_PROMPT; 100 bool one_button = message_type == content::JAVASCRIPT_DIALOG_TYPE_ALERT; 101 102 helper_.reset([[CefJavaScriptDialogHelper alloc] 103 initHelperWithCallback:base::BindOnce( 104 &CefJavaScriptDialogRunnerMac::DialogClosed, 105 weak_ptr_factory_.GetWeakPtr())]); 106 107 // Show the modal dialog. 108 NSAlert* alert = [helper_ alert]; 109 NSTextField* field = nil; 110 if (text_field) { 111 field = [helper_ textField]; 112 [field setStringValue:base::SysUTF16ToNSString(default_prompt_text)]; 113 } 114 [alert setDelegate:helper_]; 115 [alert setInformativeText:base::SysUTF16ToNSString(message_text)]; 116 117 std::u16string label; 118 switch (message_type) { 119 case content::JAVASCRIPT_DIALOG_TYPE_ALERT: 120 label = u"JavaScript Alert"; 121 break; 122 case content::JAVASCRIPT_DIALOG_TYPE_PROMPT: 123 label = u"JavaScript Prompt"; 124 break; 125 case content::JAVASCRIPT_DIALOG_TYPE_CONFIRM: 126 label = u"JavaScript Confirm"; 127 break; 128 } 129 if (!display_url.empty()) 130 label += u" - " + display_url; 131 132 [alert setMessageText:base::SysUTF16ToNSString(label)]; 133 134 [alert addButtonWithTitle:@"OK"]; 135 if (!one_button) { 136 NSButton* other = [alert addButtonWithTitle:@"Cancel"]; 137 [other setKeyEquivalent:@"\e"]; 138 } 139 140 // Calling beginSheetModalForWindow:nil is wrong API usage. For now work 141 // around the "callee requires a non-null argument" error that occurs when 142 // building with the 10.11 SDK. See http://crbug.com/383820 for related 143 // discussion. 144 // We can't use the newer beginSheetModalForWindow:completionHandler: variant 145 // because it fails silently when passed a nil argument (see issue #2726). 146 id nilArg = nil; 147#pragma clang diagnostic push 148#pragma clang diagnostic ignored "-Wdeprecated-declarations" 149 [alert beginSheetModalForWindow:nilArg // nil here makes it app-modal 150 modalDelegate:helper_ 151 didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) 152 contextInfo:this]; 153#pragma clang diagnostic pop 154 155 if ([alert accessoryView]) 156 [[alert window] makeFirstResponder:[alert accessoryView]]; 157} 158 159void CefJavaScriptDialogRunnerMac::Cancel() { 160 if (helper_.get()) { 161 [helper_ cancel]; 162 helper_.reset(nil); 163 } 164} 165 166void CefJavaScriptDialogRunnerMac::DialogClosed( 167 bool success, 168 const std::u16string& user_input) { 169 helper_.reset(nil); 170 std::move(callback_).Run(success, user_input); 171} 172