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