- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / web_dialog_window_controller.mm
1 // Copyright (c) 2012 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 #import "chrome/browser/ui/cocoa/web_dialog_window_controller.h"
6
7 #include "base/logging.h"
8 #include "base/mac/scoped_nsobject.h"
9 #include "base/strings/sys_string_conversions.h"
10 #import "chrome/browser/ui/browser_dialogs.h"
11 #import "chrome/browser/ui/cocoa/browser_command_executor.h"
12 #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
13 #include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
14 #include "content/public/browser/native_web_keyboard_event.h"
15 #include "content/public/browser/web_contents.h"
16 #include "content/public/browser/web_contents_view.h"
17 #include "content/public/browser/web_ui_message_handler.h"
18 #include "ui/events/keycodes/keyboard_codes.h"
19 #include "ui/gfx/size.h"
20 #include "ui/web_dialogs/web_dialog_delegate.h"
21 #include "ui/web_dialogs/web_dialog_web_contents_delegate.h"
22
23 using content::NativeWebKeyboardEvent;
24 using content::WebContents;
25 using content::WebUIMessageHandler;
26 using ui::WebDialogDelegate;
27 using ui::WebDialogUI;
28 using ui::WebDialogWebContentsDelegate;
29
30 // Thin bridge that routes notifications to
31 // WebDialogWindowController's member variables.
32 class WebDialogWindowDelegateBridge
33     : public WebDialogDelegate,
34       public WebDialogWebContentsDelegate {
35 public:
36   // All parameters must be non-NULL/non-nil.
37   WebDialogWindowDelegateBridge(WebDialogWindowController* controller,
38                                 content::BrowserContext* context,
39                                 WebDialogDelegate* delegate);
40
41   virtual ~WebDialogWindowDelegateBridge();
42
43   // Called when the window is directly closed, e.g. from the close
44   // button or from an accelerator.
45   void WindowControllerClosed();
46
47   // WebDialogDelegate declarations.
48   virtual ui::ModalType GetDialogModalType() const OVERRIDE;
49   virtual string16 GetDialogTitle() const OVERRIDE;
50   virtual GURL GetDialogContentURL() const OVERRIDE;
51   virtual void GetWebUIMessageHandlers(
52       std::vector<WebUIMessageHandler*>* handlers) const OVERRIDE;
53   virtual void GetDialogSize(gfx::Size* size) const OVERRIDE;
54   virtual void GetMinimumDialogSize(gfx::Size* size) const OVERRIDE;
55   virtual std::string GetDialogArgs() const OVERRIDE;
56   virtual void OnDialogClosed(const std::string& json_retval) OVERRIDE;
57   virtual void OnCloseContents(WebContents* source,
58                                bool* out_close_dialog) OVERRIDE;
59   virtual bool ShouldShowDialogTitle() const OVERRIDE { return true; }
60
61   // WebDialogWebContentsDelegate declarations.
62   virtual void MoveContents(WebContents* source, const gfx::Rect& pos) OVERRIDE;
63   virtual void HandleKeyboardEvent(
64       content::WebContents* source,
65       const NativeWebKeyboardEvent& event) OVERRIDE;
66   virtual void CloseContents(WebContents* source) OVERRIDE;
67   virtual content::WebContents* OpenURLFromTab(
68       content::WebContents* source,
69       const content::OpenURLParams& params) OVERRIDE;
70   virtual void AddNewContents(content::WebContents* source,
71                               content::WebContents* new_contents,
72                               WindowOpenDisposition disposition,
73                               const gfx::Rect& initial_pos,
74                               bool user_gesture,
75                               bool* was_blocked) OVERRIDE;
76   virtual void LoadingStateChanged(content::WebContents* source) OVERRIDE;
77
78 private:
79   WebDialogWindowController* controller_;  // weak
80   WebDialogDelegate* delegate_;  // weak, owned by controller_
81
82   // Calls delegate_'s OnDialogClosed() exactly once, nulling it out afterwards
83   // so that no other WebDialogDelegate calls are sent to it. Returns whether or
84   // not the OnDialogClosed() was actually called on the delegate.
85   bool DelegateOnDialogClosed(const std::string& json_retval);
86
87   DISALLOW_COPY_AND_ASSIGN(WebDialogWindowDelegateBridge);
88 };
89
90 // ChromeEventProcessingWindow expects its controller to implement the
91 // BrowserCommandExecutor protocol.
92 @interface WebDialogWindowController (InternalAPI) <BrowserCommandExecutor>
93
94 // BrowserCommandExecutor methods.
95 - (void)executeCommand:(int)command;
96
97 @end
98
99 namespace chrome {
100
101 gfx::NativeWindow ShowWebDialog(gfx::NativeWindow parent,
102                                 content::BrowserContext* context,
103                                 WebDialogDelegate* delegate) {
104   return [WebDialogWindowController showWebDialog:delegate
105                                           context:context];
106 }
107
108 }  // namespace chrome
109
110 WebDialogWindowDelegateBridge::WebDialogWindowDelegateBridge(
111     WebDialogWindowController* controller,
112     content::BrowserContext* context,
113     WebDialogDelegate* delegate)
114     : WebDialogWebContentsDelegate(context, new ChromeWebContentsHandler),
115       controller_(controller),
116       delegate_(delegate) {
117   DCHECK(controller_);
118   DCHECK(delegate_);
119 }
120
121 WebDialogWindowDelegateBridge::~WebDialogWindowDelegateBridge() {}
122
123 void WebDialogWindowDelegateBridge::WindowControllerClosed() {
124   Detach();
125   controller_ = nil;
126   DelegateOnDialogClosed("");
127 }
128
129 bool WebDialogWindowDelegateBridge::DelegateOnDialogClosed(
130     const std::string& json_retval) {
131   if (delegate_) {
132     WebDialogDelegate* real_delegate = delegate_;
133     delegate_ = NULL;
134     real_delegate->OnDialogClosed(json_retval);
135     return true;
136   }
137   return false;
138 }
139
140 // WebDialogDelegate definitions.
141
142 // All of these functions check for NULL first since delegate_ is set
143 // to NULL when the window is closed.
144
145 ui::ModalType WebDialogWindowDelegateBridge::GetDialogModalType() const {
146   // TODO(akalin): Support modal dialog boxes.
147   if (delegate_ && delegate_->GetDialogModalType() != ui::MODAL_TYPE_NONE) {
148     LOG(WARNING) << "Modal Web dialogs are not supported yet";
149   }
150   return ui::MODAL_TYPE_NONE;
151 }
152
153 string16 WebDialogWindowDelegateBridge::GetDialogTitle() const {
154   return delegate_ ? delegate_->GetDialogTitle() : string16();
155 }
156
157 GURL WebDialogWindowDelegateBridge::GetDialogContentURL() const {
158   return delegate_ ? delegate_->GetDialogContentURL() : GURL();
159 }
160
161 void WebDialogWindowDelegateBridge::GetWebUIMessageHandlers(
162     std::vector<WebUIMessageHandler*>* handlers) const {
163   if (delegate_) {
164     delegate_->GetWebUIMessageHandlers(handlers);
165   } else {
166     // TODO(akalin): Add this clause in the windows version.  Also
167     // make sure that everything expects handlers to be non-NULL and
168     // document it.
169     handlers->clear();
170   }
171 }
172
173 void WebDialogWindowDelegateBridge::GetDialogSize(gfx::Size* size) const {
174   if (delegate_)
175     delegate_->GetDialogSize(size);
176   else
177     *size = gfx::Size();
178 }
179
180 void WebDialogWindowDelegateBridge::GetMinimumDialogSize(
181     gfx::Size* size) const {
182   if (delegate_)
183     delegate_->GetMinimumDialogSize(size);
184   else
185     *size = gfx::Size();
186 }
187
188 std::string WebDialogWindowDelegateBridge::GetDialogArgs() const {
189   return delegate_ ? delegate_->GetDialogArgs() : "";
190 }
191
192 void WebDialogWindowDelegateBridge::OnDialogClosed(
193     const std::string& json_retval) {
194   Detach();
195   // [controller_ close] should be called at most once, too.
196   if (DelegateOnDialogClosed(json_retval)) {
197     [controller_ close];
198   }
199   controller_ = nil;
200 }
201
202 void WebDialogWindowDelegateBridge::OnCloseContents(WebContents* source,
203                                                     bool* out_close_dialog) {
204   if (out_close_dialog)
205     *out_close_dialog = true;
206 }
207
208 void WebDialogWindowDelegateBridge::CloseContents(WebContents* source) {
209   bool close_dialog = false;
210   OnCloseContents(source, &close_dialog);
211   if (close_dialog)
212     OnDialogClosed(std::string());
213 }
214
215 content::WebContents* WebDialogWindowDelegateBridge::OpenURLFromTab(
216     content::WebContents* source,
217     const content::OpenURLParams& params) {
218   content::WebContents* new_contents = NULL;
219   if (delegate_ &&
220       delegate_->HandleOpenURLFromTab(source, params, &new_contents)) {
221     return new_contents;
222   }
223   return WebDialogWebContentsDelegate::OpenURLFromTab(source, params);
224 }
225
226 void WebDialogWindowDelegateBridge::AddNewContents(
227     content::WebContents* source,
228     content::WebContents* new_contents,
229     WindowOpenDisposition disposition,
230     const gfx::Rect& initial_pos,
231     bool user_gesture,
232     bool* was_blocked) {
233   if (delegate_ && delegate_->HandleAddNewContents(
234           source, new_contents, disposition, initial_pos, user_gesture)) {
235     return;
236   }
237   WebDialogWebContentsDelegate::AddNewContents(
238       source, new_contents, disposition, initial_pos, user_gesture,
239       was_blocked);
240 }
241
242 void WebDialogWindowDelegateBridge::LoadingStateChanged(
243     content::WebContents* source) {
244   if (delegate_)
245     delegate_->OnLoadingStateChanged(source);
246 }
247
248 void WebDialogWindowDelegateBridge::MoveContents(WebContents* source,
249                                                  const gfx::Rect& pos) {
250   // TODO(akalin): Actually set the window bounds.
251 }
252
253 // A simplified version of BrowserWindowCocoa::HandleKeyboardEvent().
254 // We don't handle global keyboard shortcuts here, but that's fine since
255 // they're all browser-specific. (This may change in the future.)
256 void WebDialogWindowDelegateBridge::HandleKeyboardEvent(
257     content::WebContents* source,
258     const NativeWebKeyboardEvent& event) {
259   if (event.skip_in_browser || event.type == NativeWebKeyboardEvent::Char)
260     return;
261
262   // Close ourselves if the user hits Esc or Command-. .  The normal
263   // way to do this is to implement (void)cancel:(int)sender, but
264   // since we handle keyboard events ourselves we can't do that.
265   //
266   // According to experiments, hitting Esc works regardless of the
267   // presence of other modifiers (as long as it's not an app-level
268   // shortcut, e.g. Commmand-Esc for Front Row) but no other modifiers
269   // can be present for Command-. to work.
270   //
271   // TODO(thakis): It would be nice to get cancel: to work somehow.
272   // Bug: http://code.google.com/p/chromium/issues/detail?id=32828 .
273   if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
274       ((event.windowsKeyCode == ui::VKEY_ESCAPE) ||
275        (event.windowsKeyCode == ui::VKEY_OEM_PERIOD &&
276         event.modifiers == NativeWebKeyboardEvent::MetaKey))) {
277     [controller_ close];
278     return;
279   }
280
281   ChromeEventProcessingWindow* event_window =
282       static_cast<ChromeEventProcessingWindow*>([controller_ window]);
283   DCHECK([event_window isKindOfClass:[ChromeEventProcessingWindow class]]);
284   [event_window redispatchKeyEvent:event.os_event];
285 }
286
287 @implementation WebDialogWindowController (InternalAPI)
288
289 // This gets called whenever a chrome-specific keyboard shortcut is performed
290 // in the Web dialog window.  We simply swallow all those events.
291 - (void)executeCommand:(int)command {}
292
293 @end
294
295 @implementation WebDialogWindowController
296
297 // NOTE(akalin): We'll probably have to add the parentWindow parameter back
298 // in once we implement modal dialogs.
299
300 + (NSWindow*)showWebDialog:(WebDialogDelegate*)delegate
301                    context:(content::BrowserContext*)context {
302   WebDialogWindowController* webDialogWindowController =
303     [[WebDialogWindowController alloc] initWithDelegate:delegate
304                                                 context:context];
305   [webDialogWindowController loadDialogContents];
306   [webDialogWindowController showWindow:nil];
307   return [webDialogWindowController window];
308 }
309
310 - (id)initWithDelegate:(WebDialogDelegate*)delegate
311                context:(content::BrowserContext*)context {
312   DCHECK(delegate);
313   DCHECK(context);
314
315   gfx::Size dialogSize;
316   delegate->GetDialogSize(&dialogSize);
317   NSRect dialogRect = NSMakeRect(0, 0, dialogSize.width(), dialogSize.height());
318   NSUInteger style = NSTitledWindowMask | NSClosableWindowMask |
319       NSResizableWindowMask;
320   base::scoped_nsobject<ChromeEventProcessingWindow> window(
321       [[ChromeEventProcessingWindow alloc]
322           initWithContentRect:dialogRect
323                     styleMask:style
324                       backing:NSBackingStoreBuffered
325                         defer:YES]);
326   if (!window.get()) {
327     return nil;
328   }
329   self = [super initWithWindow:window];
330   if (!self) {
331     return nil;
332   }
333   [window setWindowController:self];
334   [window setDelegate:self];
335   [window setTitle:base::SysUTF16ToNSString(delegate->GetDialogTitle())];
336   [window setMinSize:dialogRect.size];
337   [window center];
338   delegate_.reset(
339       new WebDialogWindowDelegateBridge(self, context, delegate));
340   return self;
341 }
342
343 - (void)loadDialogContents {
344   webContents_.reset(WebContents::Create(
345       WebContents::CreateParams(delegate_->browser_context())));
346   [[self window] setContentView:webContents_->GetView()->GetNativeView()];
347   webContents_->SetDelegate(delegate_.get());
348
349   // This must be done before loading the page; see the comments in
350   // WebDialogUI.
351   WebDialogUI::SetDelegate(webContents_.get(), delegate_.get());
352
353   webContents_->GetController().LoadURL(
354       delegate_->GetDialogContentURL(),
355       content::Referrer(),
356       content::PAGE_TRANSITION_AUTO_TOPLEVEL,
357       std::string());
358
359   // TODO(akalin): add accelerator for ESC to close the dialog box.
360   //
361   // TODO(akalin): Figure out why implementing (void)cancel:(id)sender
362   // to do the above doesn't work.
363 }
364
365 - (void)windowWillClose:(NSNotification*)notification {
366   delegate_->WindowControllerClosed();
367   [self autorelease];
368 }
369
370 @end