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