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