1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/shell/browser/shell_javascript_dialog.h"
6
7#import <Cocoa/Cocoa.h>
8
9#import "base/mac/scoped_nsobject.h"
10#include "base/strings/sys_string_conversions.h"
11#include "content/shell/browser/shell_javascript_dialog_manager.h"
12
13// Helper object that receives the notification that the dialog/sheet is
14// going away. Is responsible for cleaning itself up.
15@interface ShellJavaScriptDialogHelper : NSObject<NSAlertDelegate> {
16 @private
17  base::scoped_nsobject<NSAlert> _alert;
18  NSTextField* _textField;  // WEAK; owned by alert_
19
20  // Copies of the fields in ShellJavaScriptDialog because they're private.
21  content::ShellJavaScriptDialogManager* _manager;
22  content::JavaScriptDialogManager::DialogClosedCallback _callback;
23}
24
25- (id)initHelperWithManager:(content::ShellJavaScriptDialogManager*)manager
26   andCallback:(content::JavaScriptDialogManager::DialogClosedCallback)callback;
27- (NSAlert*)alert;
28- (NSTextField*)textField;
29- (void)alertDidEndWithResult:(NSModalResponse)returnCode
30                       dialog:(content::ShellJavaScriptDialog*)dialog;
31- (void)cancel;
32
33@end
34
35@implementation ShellJavaScriptDialogHelper
36
37- (id)initHelperWithManager:(content::ShellJavaScriptDialogManager*)manager
38  andCallback:(content::JavaScriptDialogManager::DialogClosedCallback)callback {
39  if (self = [super init]) {
40    _manager = manager;
41    _callback = std::move(callback);
42  }
43
44  return self;
45}
46
47- (NSAlert*)alert {
48  _alert.reset([[NSAlert alloc] init]);
49  return _alert;
50}
51
52- (NSTextField*)textField {
53  _textField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 300, 22)];
54  [[_textField cell] setLineBreakMode:NSLineBreakByTruncatingTail];
55  [_alert setAccessoryView:_textField];
56  [[_alert window] setInitialFirstResponder:_textField];
57  [_textField release];
58
59  return _textField;
60}
61
62- (void)alertDidEndWithResult:(NSModalResponse)returnCode
63                       dialog:(content::ShellJavaScriptDialog*)dialog {
64  if (returnCode == NSModalResponseStop)
65    return;
66
67  bool success = returnCode == NSAlertFirstButtonReturn;
68  base::string16 input;
69  if (_textField)
70    input = base::SysNSStringToUTF16([_textField stringValue]);
71
72  std::move(_callback).Run(success, input);
73  _manager->DialogClosed(dialog);
74}
75
76- (void)cancel {
77  [NSApp endSheet:[_alert window]];
78  _alert.reset();
79  if (_callback)
80    std::move(_callback).Run(false, base::string16());
81}
82
83@end
84
85namespace content {
86
87ShellJavaScriptDialog::ShellJavaScriptDialog(
88    ShellJavaScriptDialogManager* manager,
89    gfx::NativeWindow parent_window,
90    JavaScriptDialogType dialog_type,
91    const base::string16& message_text,
92    const base::string16& default_prompt_text,
93    JavaScriptDialogManager::DialogClosedCallback callback) {
94  bool text_field = dialog_type == JAVASCRIPT_DIALOG_TYPE_PROMPT;
95  bool one_button = dialog_type == JAVASCRIPT_DIALOG_TYPE_ALERT;
96
97  helper_ = [[ShellJavaScriptDialogHelper alloc]
98      initHelperWithManager:manager
99                andCallback:std::move(callback)];
100
101  // Show the modal dialog.
102  NSAlert* alert = [helper_ alert];
103  NSTextField* field = nil;
104  if (text_field) {
105    field = [helper_ textField];
106    [field setStringValue:base::SysUTF16ToNSString(default_prompt_text)];
107  }
108  [alert setDelegate:helper_];
109  [alert setInformativeText:base::SysUTF16ToNSString(message_text)];
110  [alert setMessageText:@"Javascript alert"];
111  [alert addButtonWithTitle:@"OK"];
112  if (!one_button) {
113    NSButton* other = [alert addButtonWithTitle:@"Cancel"];
114    [other setKeyEquivalent:@"\e"];
115  }
116
117  [alert beginSheetModalForWindow:nil  // nil here makes it app-modal
118                completionHandler:^void(NSModalResponse returnCode) {
119                  [helper_ alertDidEndWithResult:returnCode dialog:this];
120                }];
121}
122
123ShellJavaScriptDialog::~ShellJavaScriptDialog() {
124  [helper_ release];
125}
126
127void ShellJavaScriptDialog::Cancel() {
128  [helper_ cancel];
129}
130
131}  // namespace content
132