chrome_native_app_window_views.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
1// Copyright 2014 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 "chrome/browser/ui/views/apps/chrome_native_app_window_views.h" 6 7#include "apps/ui/views/app_window_frame_view.h" 8#include "base/command_line.h" 9#include "chrome/app/chrome_command_ids.h" 10#include "chrome/browser/app_mode/app_mode_utils.h" 11#include "chrome/browser/chrome_page_zoom.h" 12#include "chrome/browser/favicon/favicon_tab_helper.h" 13#include "chrome/browser/profiles/profile.h" 14#include "chrome/browser/ui/host_desktop.h" 15#include "chrome/browser/ui/views/apps/shaped_app_window_targeter.h" 16#include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h" 17#include "chrome/browser/ui/views/frame/taskbar_decorator.h" 18#include "chrome/browser/web_applications/web_app.h" 19#include "chrome/common/chrome_switches.h" 20#include "extensions/common/extension.h" 21#include "ui/base/hit_test.h" 22#include "ui/base/models/simple_menu_model.h" 23#include "ui/gfx/image/image_skia.h" 24#include "ui/views/controls/menu/menu_runner.h" 25#include "ui/views/controls/webview/webview.h" 26#include "ui/views/widget/widget.h" 27#include "ui/wm/core/easy_resize_window_targeter.h" 28#include "ui/wm/core/shadow_types.h" 29 30#if defined(OS_LINUX) 31#include "chrome/browser/shell_integration_linux.h" 32#endif 33 34#if defined(USE_ASH) 35#include "ash/ash_constants.h" 36#include "ash/ash_switches.h" 37#include "ash/frame/custom_frame_view_ash.h" 38#include "ash/screen_util.h" 39#include "ash/shell.h" 40#include "ash/wm/immersive_fullscreen_controller.h" 41#include "ash/wm/panels/panel_frame_view.h" 42#include "ash/wm/window_state.h" 43#include "ash/wm/window_state_delegate.h" 44#include "ash/wm/window_state_observer.h" 45#include "chrome/browser/ui/ash/ash_util.h" 46#include "chrome/browser/ui/ash/multi_user/multi_user_context_menu.h" 47#include "ui/aura/client/aura_constants.h" 48#include "ui/aura/client/window_tree_client.h" 49#include "ui/aura/window.h" 50#include "ui/aura/window_observer.h" 51#endif 52 53#if defined(USE_AURA) 54#include "ui/aura/window.h" 55#endif 56 57using apps::AppWindow; 58 59namespace { 60 61const int kMinPanelWidth = 100; 62const int kMinPanelHeight = 100; 63const int kDefaultPanelWidth = 200; 64const int kDefaultPanelHeight = 300; 65const int kResizeInsideBoundsSize = 5; 66const int kResizeAreaCornerSize = 16; 67 68struct AcceleratorMapping { 69 ui::KeyboardCode keycode; 70 int modifiers; 71 int command_id; 72}; 73 74const AcceleratorMapping kAppWindowAcceleratorMap[] = { 75 { ui::VKEY_W, ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW }, 76 { ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW }, 77 { ui::VKEY_F4, ui::EF_ALT_DOWN, IDC_CLOSE_WINDOW }, 78}; 79 80// These accelerators will only be available in kiosk mode. These allow the 81// user to manually zoom app windows. This is only necessary in kiosk mode 82// (in normal mode, the user can zoom via the screen magnifier). 83// TODO(xiyuan): Write a test for kiosk accelerators. 84const AcceleratorMapping kAppWindowKioskAppModeAcceleratorMap[] = { 85 { ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS }, 86 { ui::VKEY_OEM_MINUS, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, 87 IDC_ZOOM_MINUS }, 88 { ui::VKEY_SUBTRACT, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS }, 89 { ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS }, 90 { ui::VKEY_OEM_PLUS, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS }, 91 { ui::VKEY_ADD, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS }, 92 { ui::VKEY_0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL }, 93 { ui::VKEY_NUMPAD0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL }, 94}; 95 96void AddAcceleratorsFromMapping(const AcceleratorMapping mapping[], 97 size_t mapping_length, 98 std::map<ui::Accelerator, int>* accelerators) { 99 for (size_t i = 0; i < mapping_length; ++i) { 100 ui::Accelerator accelerator(mapping[i].keycode, mapping[i].modifiers); 101 (*accelerators)[accelerator] = mapping[i].command_id; 102 } 103} 104 105const std::map<ui::Accelerator, int>& GetAcceleratorTable() { 106 typedef std::map<ui::Accelerator, int> AcceleratorMap; 107 CR_DEFINE_STATIC_LOCAL(AcceleratorMap, accelerators, ()); 108 if (accelerators.empty()) { 109 AddAcceleratorsFromMapping( 110 kAppWindowAcceleratorMap, 111 arraysize(kAppWindowAcceleratorMap), 112 &accelerators); 113 114 // Add accelerators for kiosk mode. 115 if (chrome::IsRunningInForcedAppMode()) { 116 AddAcceleratorsFromMapping( 117 kAppWindowKioskAppModeAcceleratorMap, 118 arraysize(kAppWindowKioskAppModeAcceleratorMap), 119 &accelerators); 120 } 121 } 122 return accelerators; 123} 124 125#if defined(USE_ASH) 126// This class handles a user's fullscreen request (Shift+F4/F4). 127class NativeAppWindowStateDelegate : public ash::wm::WindowStateDelegate, 128 public ash::wm::WindowStateObserver, 129 public aura::WindowObserver { 130 public: 131 NativeAppWindowStateDelegate(AppWindow* app_window, 132 apps::NativeAppWindow* native_app_window) 133 : app_window_(app_window), 134 window_state_( 135 ash::wm::GetWindowState(native_app_window->GetNativeWindow())) { 136 // Add a window state observer to exit fullscreen properly in case 137 // fullscreen is exited without going through AppWindow::Restore(). This 138 // is the case when exiting immersive fullscreen via the "Restore" window 139 // control. 140 // TODO(pkotwicz): This is a hack. Remove ASAP. http://crbug.com/319048 141 window_state_->AddObserver(this); 142 window_state_->window()->AddObserver(this); 143 } 144 virtual ~NativeAppWindowStateDelegate(){ 145 if (window_state_) { 146 window_state_->RemoveObserver(this); 147 window_state_->window()->RemoveObserver(this); 148 } 149 } 150 151 private: 152 // Overridden from ash::wm::WindowStateDelegate. 153 virtual bool ToggleFullscreen(ash::wm::WindowState* window_state) OVERRIDE { 154 // Windows which cannot be maximized should not be fullscreened. 155 DCHECK(window_state->IsFullscreen() || window_state->CanMaximize()); 156 if (window_state->IsFullscreen()) 157 app_window_->Restore(); 158 else if (window_state->CanMaximize()) 159 app_window_->OSFullscreen(); 160 return true; 161 } 162 163 // Overridden from ash::wm::WindowStateObserver: 164 virtual void OnPostWindowStateTypeChange( 165 ash::wm::WindowState* window_state, 166 ash::wm::WindowStateType old_type) OVERRIDE { 167 // Since the window state might get set by a window manager, it is possible 168 // to come here before the application set its |BaseWindow|. 169 if (!window_state->IsFullscreen() && !window_state->IsMinimized() && 170 app_window_->GetBaseWindow() && 171 app_window_->GetBaseWindow()->IsFullscreenOrPending()) { 172 app_window_->Restore(); 173 // Usually OnNativeWindowChanged() is called when the window bounds are 174 // changed as a result of a state type change. Because the change in state 175 // type has already occurred, we need to call OnNativeWindowChanged() 176 // explicitly. 177 app_window_->OnNativeWindowChanged(); 178 } 179 } 180 181 // Overridden from aura::WindowObserver: 182 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { 183 window_state_->RemoveObserver(this); 184 window_state_->window()->RemoveObserver(this); 185 window_state_ = NULL; 186 } 187 188 // Not owned. 189 AppWindow* app_window_; 190 ash::wm::WindowState* window_state_; 191 192 DISALLOW_COPY_AND_ASSIGN(NativeAppWindowStateDelegate); 193}; 194#endif // USE_ASH 195 196} // namespace 197 198ChromeNativeAppWindowViews::ChromeNativeAppWindowViews() 199 : is_fullscreen_(false), 200 has_frame_color_(false), 201 frame_color_(SK_ColorBLACK) {} 202 203ChromeNativeAppWindowViews::~ChromeNativeAppWindowViews() {} 204 205void ChromeNativeAppWindowViews::OnBeforeWidgetInit( 206 views::Widget::InitParams* init_params, 207 views::Widget* widget) {} 208 209void ChromeNativeAppWindowViews::InitializeDefaultWindow( 210 const AppWindow::CreateParams& create_params) { 211 std::string app_name = 212 web_app::GenerateApplicationNameFromExtensionId( 213 app_window()->extension()->id()); 214 215 views::Widget::InitParams init_params(views::Widget::InitParams::TYPE_WINDOW); 216 init_params.delegate = this; 217 init_params.remove_standard_frame = IsFrameless() || has_frame_color_; 218 init_params.use_system_default_icon = true; 219 // TODO(erg): Conceptually, these are toplevel windows, but we theoretically 220 // could plumb context through to here in some cases. 221 init_params.top_level = true; 222 if (create_params.transparent_background) 223 init_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 224 init_params.keep_on_top = create_params.always_on_top; 225 226#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 227 // Set up a custom WM_CLASS for app windows. This allows task switchers in 228 // X11 environments to distinguish them from main browser windows. 229 init_params.wm_class_name = web_app::GetWMClassFromAppName(app_name); 230 init_params.wm_class_class = ShellIntegrationLinux::GetProgramClassName(); 231 const char kX11WindowRoleApp[] = "app"; 232 init_params.wm_role_name = std::string(kX11WindowRoleApp); 233#endif 234 235 OnBeforeWidgetInit(&init_params, widget()); 236 widget()->Init(init_params); 237 238 // The frame insets are required to resolve the bounds specifications 239 // correctly. So we set the window bounds and constraints now. 240 gfx::Insets frame_insets = GetFrameInsets(); 241 gfx::Rect window_bounds = create_params.GetInitialWindowBounds(frame_insets); 242 SetContentSizeConstraints(create_params.GetContentMinimumSize(frame_insets), 243 create_params.GetContentMaximumSize(frame_insets)); 244 if (!window_bounds.IsEmpty()) { 245 typedef apps::AppWindow::BoundsSpecification BoundsSpecification; 246 bool position_specified = 247 window_bounds.x() != BoundsSpecification::kUnspecifiedPosition && 248 window_bounds.y() != BoundsSpecification::kUnspecifiedPosition; 249 if (!position_specified) 250 widget()->CenterWindow(window_bounds.size()); 251 else 252 widget()->SetBounds(window_bounds); 253 } 254 255 if (IsFrameless() && 256 init_params.opacity == views::Widget::InitParams::TRANSLUCENT_WINDOW && 257 !create_params.resizable) { 258 // The given window is most likely not rectangular since it uses 259 // transparency, has no standard frame and the user cannot resize it using 260 // the OS supplied methods. Therefore we do not use a shadow for it. 261 // TODO(skuhne): If we run into an application which should have a shadow 262 // but does not have, a new attribute has to be added. 263 wm::SetShadowType(widget()->GetNativeWindow(), wm::SHADOW_TYPE_NONE); 264 } 265 266 // Register accelarators supported by app windows. 267 // TODO(jeremya/stevenjb): should these be registered for panels too? 268 views::FocusManager* focus_manager = GetFocusManager(); 269 const std::map<ui::Accelerator, int>& accelerator_table = 270 GetAcceleratorTable(); 271 const bool is_kiosk_app_mode = chrome::IsRunningInForcedAppMode(); 272 273 // Ensures that kiosk mode accelerators are enabled when in kiosk mode (to be 274 // future proof). This is needed because GetAcceleratorTable() uses a static 275 // to store data and only checks kiosk mode once. If a platform app is 276 // launched before kiosk mode starts, the kiosk accelerators will not be 277 // registered. This DCHECK catches the case. 278 DCHECK(!is_kiosk_app_mode || 279 accelerator_table.size() == 280 arraysize(kAppWindowAcceleratorMap) + 281 arraysize(kAppWindowKioskAppModeAcceleratorMap)); 282 283 for (std::map<ui::Accelerator, int>::const_iterator iter = 284 accelerator_table.begin(); 285 iter != accelerator_table.end(); ++iter) { 286 if (is_kiosk_app_mode && !chrome::IsCommandAllowedInAppMode(iter->second)) 287 continue; 288 289 focus_manager->RegisterAccelerator( 290 iter->first, ui::AcceleratorManager::kNormalPriority, this); 291 } 292} 293 294void ChromeNativeAppWindowViews::InitializePanelWindow( 295 const AppWindow::CreateParams& create_params) { 296 views::Widget::InitParams params(views::Widget::InitParams::TYPE_PANEL); 297 params.delegate = this; 298 299 gfx::Rect initial_window_bounds = 300 create_params.GetInitialWindowBounds(gfx::Insets()); 301 preferred_size_ = gfx::Size(initial_window_bounds.width(), 302 initial_window_bounds.height()); 303 if (preferred_size_.width() == 0) 304 preferred_size_.set_width(kDefaultPanelWidth); 305 else if (preferred_size_.width() < kMinPanelWidth) 306 preferred_size_.set_width(kMinPanelWidth); 307 308 if (preferred_size_.height() == 0) 309 preferred_size_.set_height(kDefaultPanelHeight); 310 else if (preferred_size_.height() < kMinPanelHeight) 311 preferred_size_.set_height(kMinPanelHeight); 312#if defined(USE_ASH) 313 if (ash::Shell::HasInstance()) { 314 // Open a new panel on the target root. 315 aura::Window* target = ash::Shell::GetTargetRootWindow(); 316 params.bounds = ash::ScreenUtil::ConvertRectToScreen( 317 target, gfx::Rect(preferred_size_)); 318 } else { 319 params.bounds = gfx::Rect(preferred_size_); 320 } 321#else 322 params.bounds = gfx::Rect(preferred_size_); 323#endif 324 // TODO(erg): Conceptually, these are toplevel windows, but we theoretically 325 // could plumb context through to here in some cases. 326 params.top_level = true; 327 widget()->Init(params); 328 widget()->set_focus_on_creation(create_params.focused); 329 330#if defined(USE_ASH) 331 if (create_params.state == ui::SHOW_STATE_DETACHED) { 332 gfx::Rect window_bounds(initial_window_bounds.x(), 333 initial_window_bounds.y(), 334 preferred_size_.width(), 335 preferred_size_.height()); 336 aura::Window* native_window = GetNativeWindow(); 337 ash::wm::GetWindowState(native_window)->set_panel_attached(false); 338 aura::client::ParentWindowWithContext(native_window, 339 native_window->GetRootWindow(), 340 native_window->GetBoundsInScreen()); 341 widget()->SetBounds(window_bounds); 342 } 343#else 344 // TODO(stevenjb): NativeAppWindow panels need to be implemented for other 345 // platforms. 346#endif 347} 348 349void ChromeNativeAppWindowViews::InstallEasyResizeTargeterOnContainer() const { 350 aura::Window* window = widget()->GetNativeWindow(); 351 gfx::Insets inset(kResizeInsideBoundsSize, kResizeInsideBoundsSize, 352 kResizeInsideBoundsSize, kResizeInsideBoundsSize); 353 // Add the EasyResizeWindowTargeter on the window, not its root window. The 354 // root window does not have a delegate, which is needed to handle the event 355 // in Linux. 356 window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( 357 new wm::EasyResizeWindowTargeter(window, inset, inset))); 358} 359 360apps::AppWindowFrameView* 361ChromeNativeAppWindowViews::CreateAppWindowFrameView() { 362 // By default the user can resize the window from slightly inside the bounds. 363 int resize_inside_bounds_size = kResizeInsideBoundsSize; 364 int resize_outside_bounds_size = 0; 365 int resize_outside_scale_for_touch = 1; 366 int resize_area_corner_size = kResizeAreaCornerSize; 367#if defined(USE_ASH) 368 // For Aura windows on the Ash desktop the sizes are different and the user 369 // can resize the window from slightly outside the bounds as well. 370 if (chrome::IsNativeWindowInAsh(widget()->GetNativeWindow())) { 371 resize_inside_bounds_size = ash::kResizeInsideBoundsSize; 372 resize_outside_bounds_size = ash::kResizeOutsideBoundsSize; 373 resize_outside_scale_for_touch = ash::kResizeOutsideBoundsScaleForTouch; 374 resize_area_corner_size = ash::kResizeAreaCornerSize; 375 } 376#endif 377 apps::AppWindowFrameView* frame_view = new apps::AppWindowFrameView(); 378 frame_view->Init(widget(), 379 this, 380 has_frame_color_, 381 frame_color_, 382 resize_inside_bounds_size, 383 resize_outside_bounds_size, 384 resize_outside_scale_for_touch, 385 resize_area_corner_size); 386 return frame_view; 387} 388 389// ui::BaseWindow implementation. 390 391ui::WindowShowState ChromeNativeAppWindowViews::GetRestoredState() const { 392 if (IsMaximized()) 393 return ui::SHOW_STATE_MAXIMIZED; 394 if (IsFullscreen()) { 395#if defined(USE_ASH) 396 if (immersive_fullscreen_controller_.get() && 397 immersive_fullscreen_controller_->IsEnabled()) { 398 // Restore windows which were previously in immersive fullscreen to 399 // maximized. Restoring the window to a different fullscreen type 400 // makes for a bad experience. 401 return ui::SHOW_STATE_MAXIMIZED; 402 } 403#endif 404 return ui::SHOW_STATE_FULLSCREEN; 405 } 406#if defined(USE_ASH) 407 // Use kRestoreShowStateKey in case a window is minimized/hidden. 408 ui::WindowShowState restore_state = widget()->GetNativeWindow()->GetProperty( 409 aura::client::kRestoreShowStateKey); 410 // Whitelist states to return so that invalid and transient states 411 // are not saved and used to restore windows when they are recreated. 412 switch (restore_state) { 413 case ui::SHOW_STATE_NORMAL: 414 case ui::SHOW_STATE_MAXIMIZED: 415 case ui::SHOW_STATE_FULLSCREEN: 416 case ui::SHOW_STATE_DETACHED: 417 return restore_state; 418 419 case ui::SHOW_STATE_DEFAULT: 420 case ui::SHOW_STATE_MINIMIZED: 421 case ui::SHOW_STATE_INACTIVE: 422 case ui::SHOW_STATE_END: 423 return ui::SHOW_STATE_NORMAL; 424 } 425#endif 426 return ui::SHOW_STATE_NORMAL; 427} 428 429bool ChromeNativeAppWindowViews::IsAlwaysOnTop() const { 430 if (app_window()->window_type_is_panel()) { 431#if defined(USE_ASH) 432 return ash::wm::GetWindowState(widget()->GetNativeWindow()) 433 ->panel_attached(); 434#else 435 return true; 436#endif 437 } else { 438 return widget()->IsAlwaysOnTop(); 439 } 440} 441 442// views::ContextMenuController implementation. 443 444void ChromeNativeAppWindowViews::ShowContextMenuForView( 445 views::View* source, 446 const gfx::Point& p, 447 ui::MenuSourceType source_type) { 448#if defined(USE_ASH) && defined(OS_CHROMEOS) 449 scoped_ptr<ui::MenuModel> model = 450 CreateMultiUserContextMenu(app_window()->GetNativeWindow()); 451 if (!model.get()) 452 return; 453 454 // Only show context menu if point is in caption. 455 gfx::Point point_in_view_coords(p); 456 views::View::ConvertPointFromScreen(widget()->non_client_view(), 457 &point_in_view_coords); 458 int hit_test = 459 widget()->non_client_view()->NonClientHitTest(point_in_view_coords); 460 if (hit_test == HTCAPTION) { 461 menu_runner_.reset(new views::MenuRunner(model.get())); 462 if (menu_runner_->RunMenuAt(source->GetWidget(), NULL, 463 gfx::Rect(p, gfx::Size(0,0)), views::MenuItemView::TOPLEFT, 464 source_type, 465 views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) == 466 views::MenuRunner::MENU_DELETED) 467 return; 468 } 469#endif 470} 471 472// views::WidgetDelegate implementation. 473 474gfx::ImageSkia ChromeNativeAppWindowViews::GetWindowAppIcon() { 475 gfx::Image app_icon = app_window()->app_icon(); 476 if (app_icon.IsEmpty()) 477 return GetWindowIcon(); 478 else 479 return *app_icon.ToImageSkia(); 480} 481 482gfx::ImageSkia ChromeNativeAppWindowViews::GetWindowIcon() { 483 content::WebContents* web_contents = app_window()->web_contents(); 484 if (web_contents) { 485 FaviconTabHelper* favicon_tab_helper = 486 FaviconTabHelper::FromWebContents(web_contents); 487 gfx::Image app_icon = favicon_tab_helper->GetFavicon(); 488 if (!app_icon.IsEmpty()) 489 return *app_icon.ToImageSkia(); 490 } 491 return gfx::ImageSkia(); 492} 493 494views::NonClientFrameView* ChromeNativeAppWindowViews::CreateNonClientFrameView( 495 views::Widget* widget) { 496#if defined(USE_ASH) 497 if (chrome::IsNativeViewInAsh(widget->GetNativeView())) { 498 // Set the delegate now because CustomFrameViewAsh sets the 499 // WindowStateDelegate if one is not already set. 500 ash::wm::GetWindowState(GetNativeWindow())->SetDelegate( 501 scoped_ptr<ash::wm::WindowStateDelegate>( 502 new NativeAppWindowStateDelegate(app_window(), this)).Pass()); 503 504 if (app_window()->window_type_is_panel()) { 505 ash::PanelFrameView::FrameType frame_type = IsFrameless() ? 506 ash::PanelFrameView::FRAME_NONE : ash::PanelFrameView::FRAME_ASH; 507 views::NonClientFrameView* frame_view = 508 new ash::PanelFrameView(widget, frame_type); 509 frame_view->set_context_menu_controller(this); 510 return frame_view; 511 } 512 513 if (!IsFrameless()) { 514 ash::CustomFrameViewAsh* custom_frame_view = 515 new ash::CustomFrameViewAsh(widget); 516 // Non-frameless app windows can be put into immersive fullscreen. 517 immersive_fullscreen_controller_.reset( 518 new ash::ImmersiveFullscreenController()); 519 custom_frame_view->InitImmersiveFullscreenControllerForView( 520 immersive_fullscreen_controller_.get()); 521 custom_frame_view->GetHeaderView()->set_context_menu_controller(this); 522 return custom_frame_view; 523 } 524 } 525#endif 526#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 527 return CreateAppWindowFrameView(); 528#else 529 if (IsFrameless() || has_frame_color_) 530 return CreateAppWindowFrameView(); 531#endif 532 return views::WidgetDelegateView::CreateNonClientFrameView(widget); 533} 534 535bool ChromeNativeAppWindowViews::WidgetHasHitTestMask() const { 536 return shape_ != NULL; 537} 538 539void ChromeNativeAppWindowViews::GetWidgetHitTestMask(gfx::Path* mask) const { 540 shape_->getBoundaryPath(mask); 541} 542 543// views::View implementation. 544 545gfx::Size ChromeNativeAppWindowViews::GetPreferredSize() { 546 if (!preferred_size_.IsEmpty()) 547 return preferred_size_; 548 return NativeAppWindowViews::GetPreferredSize(); 549} 550 551bool ChromeNativeAppWindowViews::AcceleratorPressed( 552 const ui::Accelerator& accelerator) { 553 const std::map<ui::Accelerator, int>& accelerator_table = 554 GetAcceleratorTable(); 555 std::map<ui::Accelerator, int>::const_iterator iter = 556 accelerator_table.find(accelerator); 557 DCHECK(iter != accelerator_table.end()); 558 int command_id = iter->second; 559 switch (command_id) { 560 case IDC_CLOSE_WINDOW: 561 Close(); 562 return true; 563 case IDC_ZOOM_MINUS: 564 chrome_page_zoom::Zoom(web_view()->GetWebContents(), 565 content::PAGE_ZOOM_OUT); 566 return true; 567 case IDC_ZOOM_NORMAL: 568 chrome_page_zoom::Zoom(web_view()->GetWebContents(), 569 content::PAGE_ZOOM_RESET); 570 return true; 571 case IDC_ZOOM_PLUS: 572 chrome_page_zoom::Zoom(web_view()->GetWebContents(), 573 content::PAGE_ZOOM_IN); 574 return true; 575 default: 576 NOTREACHED() << "Unknown accelerator sent to app window."; 577 } 578 return NativeAppWindowViews::AcceleratorPressed(accelerator); 579} 580 581// NativeAppWindow implementation. 582 583void ChromeNativeAppWindowViews::SetFullscreen(int fullscreen_types) { 584 // Fullscreen not supported by panels. 585 if (app_window()->window_type_is_panel()) 586 return; 587 is_fullscreen_ = (fullscreen_types != AppWindow::FULLSCREEN_TYPE_NONE); 588 widget()->SetFullscreen(is_fullscreen_); 589 590#if defined(USE_ASH) 591 if (immersive_fullscreen_controller_.get()) { 592 // |immersive_fullscreen_controller_| should only be set if immersive 593 // fullscreen is the fullscreen type used by the OS. 594 immersive_fullscreen_controller_->SetEnabled( 595 ash::ImmersiveFullscreenController::WINDOW_TYPE_PACKAGED_APP, 596 (fullscreen_types & AppWindow::FULLSCREEN_TYPE_OS) != 0); 597 // Autohide the shelf instead of hiding the shelf completely when only in 598 // OS fullscreen. 599 ash::wm::WindowState* window_state = 600 ash::wm::GetWindowState(widget()->GetNativeWindow()); 601 window_state->set_hide_shelf_when_fullscreen(fullscreen_types != 602 AppWindow::FULLSCREEN_TYPE_OS); 603 DCHECK(ash::Shell::HasInstance()); 604 ash::Shell::GetInstance()->UpdateShelfVisibility(); 605 } 606#endif 607 608 // TODO(jeremya) we need to call RenderViewHost::ExitFullscreen() if we 609 // ever drop the window out of fullscreen in response to something that 610 // wasn't the app calling webkitCancelFullScreen(). 611} 612 613bool ChromeNativeAppWindowViews::IsFullscreenOrPending() const { 614 return is_fullscreen_; 615} 616 617bool ChromeNativeAppWindowViews::IsDetached() const { 618 if (!app_window()->window_type_is_panel()) 619 return false; 620#if defined(USE_ASH) 621 return !ash::wm::GetWindowState(widget()->GetNativeWindow()) 622 ->panel_attached(); 623#else 624 return false; 625#endif 626} 627 628void ChromeNativeAppWindowViews::UpdateBadgeIcon() { 629 const gfx::Image* icon = NULL; 630 if (!app_window()->badge_icon().IsEmpty()) { 631 icon = &app_window()->badge_icon(); 632 // chrome::DrawTaskbarDecoration can do interesting things with non-square 633 // bitmaps. 634 // TODO(benwells): Refactor chrome::DrawTaskbarDecoration to not be avatar 635 // specific, and lift this restriction. 636 if (icon->Width() != icon->Height()) { 637 LOG(ERROR) << "Attempt to set a non-square badge; request ignored."; 638 return; 639 } 640 } 641 chrome::DrawTaskbarDecoration(GetNativeWindow(), icon); 642} 643 644void ChromeNativeAppWindowViews::UpdateShape(scoped_ptr<SkRegion> region) { 645 bool had_shape = shape_; 646 shape_ = region.Pass(); 647 648 aura::Window* native_window = widget()->GetNativeWindow(); 649 if (shape_) { 650 widget()->SetShape(new SkRegion(*shape_)); 651 if (!had_shape) { 652 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( 653 new ShapedAppWindowTargeter(native_window, this))); 654 } 655 } else { 656 widget()->SetShape(NULL); 657 if (had_shape) 658 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>()); 659 } 660} 661 662bool ChromeNativeAppWindowViews::HasFrameColor() const { 663 return has_frame_color_; 664} 665 666SkColor ChromeNativeAppWindowViews::FrameColor() const { return frame_color_; } 667 668// NativeAppWindowViews implementation. 669 670void ChromeNativeAppWindowViews::InitializeWindow( 671 AppWindow* app_window, 672 const AppWindow::CreateParams& create_params) { 673 DCHECK(widget()); 674 has_frame_color_ = create_params.has_frame_color; 675 frame_color_ = create_params.frame_color; 676 if (create_params.window_type == AppWindow::WINDOW_TYPE_PANEL || 677 create_params.window_type == AppWindow::WINDOW_TYPE_V1_PANEL) { 678 InitializePanelWindow(create_params); 679 } else { 680 InitializeDefaultWindow(create_params); 681 } 682 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews( 683 Profile::FromBrowserContext(app_window->browser_context()), 684 widget()->GetFocusManager(), 685 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY, 686 NULL)); 687 688#if !defined(OS_CHROMEOS) 689 if ((IsFrameless() || has_frame_color_) && 690 chrome::GetHostDesktopTypeForNativeWindow(widget()->GetNativeWindow()) != 691 chrome::HOST_DESKTOP_TYPE_ASH) { 692 InstallEasyResizeTargeterOnContainer(); 693 } 694#endif 695} 696