// Copyright (c) 2009 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. #import "chrome/browser/cocoa/html_dialog_window_controller.h" #include "base/gfx/size.h" #include "base/logging.h" #include "base/scoped_nsobject.h" #include "base/sys_string_conversions.h" #include "chrome/browser/browser.h" #import "chrome/browser/cocoa/browser_command_executor.h" #import "chrome/browser/cocoa/chrome_event_processing_window.h" #include "chrome/browser/dom_ui/html_dialog_ui.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "googleurl/src/gurl.h" HtmlDialogWindowDelegateBridge::HtmlDialogWindowDelegateBridge( HtmlDialogUIDelegate* delegate, NSWindowController* controller, NSWindow* window, Browser* browser) : delegate_(delegate), controller_(controller), window_(window), browser_(browser) { DCHECK(delegate_); DCHECK(controller_); DCHECK(window_); DCHECK(browser_); } HtmlDialogWindowDelegateBridge::~HtmlDialogWindowDelegateBridge() {} void HtmlDialogWindowDelegateBridge::WindowControllerClosed() { DelegateOnDialogClosed(""); } bool HtmlDialogWindowDelegateBridge::DelegateOnDialogClosed( const std::string& json_retval) { if (delegate_) { HtmlDialogUIDelegate* real_delegate = delegate_; delegate_ = NULL; real_delegate->OnDialogClosed(json_retval); return true; } return false; } // HtmlDialogUIDelegate definitions. // All of these functions check for NULL first since delegate_ is set // to NULL when the window is closed. bool HtmlDialogWindowDelegateBridge::IsDialogModal() const { // TODO(akalin): Support modal dialog boxes. if (delegate_ && delegate_->IsDialogModal()) { LOG(WARNING) << "Modal HTML dialogs are not supported yet"; } return false; } std::wstring HtmlDialogWindowDelegateBridge::GetDialogTitle() const { return delegate_ ? delegate_->GetDialogTitle() : L""; } GURL HtmlDialogWindowDelegateBridge::GetDialogContentURL() const { return delegate_ ? delegate_->GetDialogContentURL() : GURL(); } void HtmlDialogWindowDelegateBridge::GetDOMMessageHandlers( std::vector* handlers) const { if (delegate_) { delegate_->GetDOMMessageHandlers(handlers); } else { // TODO(akalin): Add this clause in the windows version. Also // make sure that everything expects handlers to be non-NULL and // document it. handlers->clear(); } } void HtmlDialogWindowDelegateBridge::GetDialogSize(gfx::Size* size) const { if (delegate_) { delegate_->GetDialogSize(size); } else { *size = gfx::Size(); } } std::string HtmlDialogWindowDelegateBridge::GetDialogArgs() const { return delegate_ ? delegate_->GetDialogArgs() : ""; } void HtmlDialogWindowDelegateBridge::OnDialogClosed( const std::string& json_retval) { // [controller_ close] should be called at most once, too. if (DelegateOnDialogClosed(json_retval)) { [controller_ close]; } } // TabContentsDelegate definitions. Most of this logic is copied from // chrome/browser/views/html_dialog_view.cc . All functions with empty // bodies are notifications we don't care about. void HtmlDialogWindowDelegateBridge::OpenURLFromTab( TabContents* source, const GURL& url, const GURL& referrer, WindowOpenDisposition disposition, PageTransition::Type transition) { // Force all links to open in a new window. static_cast(browser_)-> OpenURLFromTab(source, url, referrer, NEW_WINDOW, transition); } void HtmlDialogWindowDelegateBridge::NavigationStateChanged( const TabContents* source, unsigned changed_flags) { } void HtmlDialogWindowDelegateBridge::AddNewContents( TabContents* source, TabContents* new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture) { // Force this to open in a new window, too. static_cast(browser_)-> AddNewContents(source, new_contents, NEW_WINDOW, initial_pos, user_gesture); } void HtmlDialogWindowDelegateBridge::ActivateContents(TabContents* contents) {} void HtmlDialogWindowDelegateBridge::LoadingStateChanged(TabContents* source) {} void HtmlDialogWindowDelegateBridge::CloseContents(TabContents* source) {} void HtmlDialogWindowDelegateBridge::MoveContents(TabContents* source, const gfx::Rect& pos) { // TODO(akalin): Actually set the window bounds. } bool HtmlDialogWindowDelegateBridge::IsPopup(TabContents* source) { // This needs to return true so that we are allowed to be resized by // our contents. return true; } void HtmlDialogWindowDelegateBridge::ToolbarSizeChanged( TabContents* source, bool is_animating) { // TODO(akalin): Figure out what to do here. } void HtmlDialogWindowDelegateBridge::URLStarredChanged( TabContents* source, bool starred) { // We don't have a visible star to click in the window. NOTREACHED(); } void HtmlDialogWindowDelegateBridge::UpdateTargetURL( TabContents* source, const GURL& url) {} // ChromeEventProcessingWindow expect its controller to implement this // protocol. @interface HtmlDialogWindowController (InternalAPI) - (void)executeCommand:(int)command; @end @implementation HtmlDialogWindowController (InternalAPI) - (void)executeCommand:(int)command { if (browser_->command_updater()->IsCommandEnabled(command)) { browser_->ExecuteCommand(command); } } @end @implementation HtmlDialogWindowController + (void)showHtmlDialog:(HtmlDialogUIDelegate*)delegate parentWindow:(gfx::NativeWindow)parent_window browser:(Browser*)browser { HtmlDialogWindowController* html_dialog_window_controller = [[HtmlDialogWindowController alloc] initWithDelegate:delegate parentWindow:parent_window browser:browser]; [html_dialog_window_controller loadDialogContents]; [html_dialog_window_controller showWindow:nil]; } - (id)initWithDelegate:(HtmlDialogUIDelegate*)delegate parentWindow:(gfx::NativeWindow)parent_window browser:(Browser*)browser { DCHECK(delegate); DCHECK(parent_window); DCHECK(browser); // Put the dialog box in the center of the window. // // TODO(akalin): Surely there must be a cleaner way to do this. // // TODO(akalin): Perhaps use [window center] instead, which centers // the dialog to the screen, although it doesn't match the Windows // behavior. NSRect parent_window_frame = [parent_window frame]; NSPoint parent_window_origin = parent_window_frame.origin; NSSize parent_window_size = parent_window_frame.size; gfx::Size dialog_size; delegate->GetDialogSize(&dialog_size); NSRect dialog_rect = NSMakeRect(parent_window_origin.x + (parent_window_size.width - dialog_size.width()) / 2, parent_window_origin.y + (parent_window_size.height - dialog_size.height()) / 2, dialog_size.width(), dialog_size.height()); // TODO(akalin): Make the window resizable (but with the minimum size being // dialog_size and always on top (but not modal) to match the Windows // behavior. NSUInteger style = NSTitledWindowMask | NSClosableWindowMask; scoped_nsobject window( [[ChromeEventProcessingWindow alloc] initWithContentRect:dialog_rect styleMask:style backing:NSBackingStoreBuffered defer:YES]); if (!window.get()) { return nil; } self = [super initWithWindow:window]; if (!self) { return nil; } [window setWindowController:self]; [window setDelegate:self]; [window setTitle:base::SysWideToNSString(delegate->GetDialogTitle())]; browser_ = browser; delegate_.reset( new HtmlDialogWindowDelegateBridge(delegate, self, window, browser)); return self; } - (void)loadDialogContents { // TODO(akalin): Figure out if this can be an incognito profile. Profile* profile = browser_->profile(); tab_contents_.reset(new TabContents(profile, NULL, MSG_ROUTING_NONE, NULL)); [[self window] setContentView:tab_contents_->GetNativeView()]; tab_contents_->set_delegate(delegate_.get()); // This must be done before loading the page; see the comments in // HtmlDialogUI. HtmlDialogUI::GetPropertyAccessor().SetProperty(tab_contents_->property_bag(), delegate_.get()); tab_contents_->controller().LoadURL(delegate_->GetDialogContentURL(), GURL(), PageTransition::START_PAGE); // TODO(akalin): add accelerator for ESC to close the dialog box. // // TODO(akalin): Figure out why implementing (void)cancel:(id)sender // to do the above doesn't work. } - (void)windowWillClose:(NSNotification*)notification { delegate_->WindowControllerClosed(); [self autorelease]; } @end