1 // Copyright 2013 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.
5 #include "chrome/browser/ui/views/apps/native_app_window_views.h"
7 #include "apps/app_window.h"
8 #include "apps/ui/views/app_window_frame_view.h"
9 #include "base/command_line.h"
10 #include "base/threading/sequenced_worker_pool.h"
11 #include "chrome/app/chrome_command_ids.h"
12 #include "chrome/browser/app_mode/app_mode_utils.h"
13 #include "chrome/browser/chrome_page_zoom.h"
14 #include "chrome/browser/extensions/extension_host.h"
15 #include "chrome/browser/favicon/favicon_tab_helper.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/ash/multi_user/multi_user_context_menu.h"
18 #include "chrome/browser/ui/host_desktop.h"
19 #include "chrome/browser/ui/views/apps/shaped_app_window_targeter.h"
20 #include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h"
21 #include "chrome/browser/ui/views/frame/taskbar_decorator.h"
22 #include "chrome/browser/web_applications/web_app.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/render_widget_host_view.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/web_contents_view.h"
29 #include "extensions/common/draggable_region.h"
30 #include "extensions/common/extension.h"
31 #include "ui/base/hit_test.h"
32 #include "ui/base/models/simple_menu_model.h"
33 #include "ui/views/controls/menu/menu_runner.h"
34 #include "ui/views/controls/webview/webview.h"
35 #include "ui/views/widget/widget.h"
36 #include "ui/views/window/non_client_view.h"
37 #include "ui/wm/public/easy_resize_window_targeter.h"
40 #include "chrome/browser/shell_integration_linux.h"
44 #include "ash/ash_constants.h"
45 #include "ash/ash_switches.h"
46 #include "ash/screen_util.h"
47 #include "ash/shell.h"
48 #include "ash/wm/custom_frame_view_ash.h"
49 #include "ash/wm/immersive_fullscreen_controller.h"
50 #include "ash/wm/panels/panel_frame_view.h"
51 #include "ash/wm/window_state.h"
52 #include "ash/wm/window_state_delegate.h"
53 #include "ash/wm/window_state_observer.h"
54 #include "chrome/browser/ui/ash/ash_util.h"
55 #include "ui/aura/client/aura_constants.h"
56 #include "ui/aura/client/window_tree_client.h"
57 #include "ui/aura/window.h"
58 #include "ui/aura/window_observer.h"
62 #include "ui/aura/window.h"
65 using apps::AppWindow;
69 const int kMinPanelWidth = 100;
70 const int kMinPanelHeight = 100;
71 const int kDefaultPanelWidth = 200;
72 const int kDefaultPanelHeight = 300;
73 const int kResizeInsideBoundsSize = 5;
74 const int kResizeAreaCornerSize = 16;
76 struct AcceleratorMapping {
77 ui::KeyboardCode keycode;
82 const AcceleratorMapping kAppWindowAcceleratorMap[] = {
83 { ui::VKEY_W, ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
84 { ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
85 { ui::VKEY_F4, ui::EF_ALT_DOWN, IDC_CLOSE_WINDOW },
88 // These accelerators will only be available in kiosk mode. These allow the
89 // user to manually zoom app windows. This is only necessary in kiosk mode
90 // (in normal mode, the user can zoom via the screen magnifier).
91 // TODO(xiyuan): Write a test for kiosk accelerators.
92 const AcceleratorMapping kAppWindowKioskAppModeAcceleratorMap[] = {
93 { ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
94 { ui::VKEY_OEM_MINUS, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
96 { ui::VKEY_SUBTRACT, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
97 { ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
98 { ui::VKEY_OEM_PLUS, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
99 { ui::VKEY_ADD, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
100 { ui::VKEY_0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL },
101 { ui::VKEY_NUMPAD0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL },
104 void AddAcceleratorsFromMapping(const AcceleratorMapping mapping[],
105 size_t mapping_length,
106 std::map<ui::Accelerator, int>* accelerators) {
107 for (size_t i = 0; i < mapping_length; ++i) {
108 ui::Accelerator accelerator(mapping[i].keycode, mapping[i].modifiers);
109 (*accelerators)[accelerator] = mapping[i].command_id;
113 const std::map<ui::Accelerator, int>& GetAcceleratorTable() {
114 typedef std::map<ui::Accelerator, int> AcceleratorMap;
115 CR_DEFINE_STATIC_LOCAL(AcceleratorMap, accelerators, ());
116 if (accelerators.empty()) {
117 AddAcceleratorsFromMapping(
118 kAppWindowAcceleratorMap,
119 arraysize(kAppWindowAcceleratorMap),
122 // Add accelerators for kiosk mode.
123 if (chrome::IsRunningInForcedAppMode()) {
124 AddAcceleratorsFromMapping(
125 kAppWindowKioskAppModeAcceleratorMap,
126 arraysize(kAppWindowKioskAppModeAcceleratorMap),
134 // This class handles a user's fullscreen request (Shift+F4/F4).
135 class NativeAppWindowStateDelegate : public ash::wm::WindowStateDelegate,
136 public ash::wm::WindowStateObserver,
137 public aura::WindowObserver {
139 NativeAppWindowStateDelegate(AppWindow* app_window,
140 apps::NativeAppWindow* native_app_window)
141 : app_window_(app_window),
143 ash::wm::GetWindowState(native_app_window->GetNativeWindow())) {
144 // Add a window state observer to exit fullscreen properly in case
145 // fullscreen is exited without going through AppWindow::Restore(). This
146 // is the case when exiting immersive fullscreen via the "Restore" window
148 // TODO(pkotwicz): This is a hack. Remove ASAP. http://crbug.com/319048
149 window_state_->AddObserver(this);
150 window_state_->window()->AddObserver(this);
152 virtual ~NativeAppWindowStateDelegate(){
154 window_state_->RemoveObserver(this);
155 window_state_->window()->RemoveObserver(this);
160 // Overridden from ash::wm::WindowStateDelegate.
161 virtual bool ToggleFullscreen(ash::wm::WindowState* window_state) OVERRIDE {
162 // Windows which cannot be maximized should not be fullscreened.
163 DCHECK(window_state->IsFullscreen() || window_state->CanMaximize());
164 if (window_state->IsFullscreen())
165 app_window_->Restore();
166 else if (window_state->CanMaximize())
167 app_window_->OSFullscreen();
171 // Overridden from ash::wm::WindowStateObserver:
172 virtual void OnPostWindowShowTypeChange(
173 ash::wm::WindowState* window_state,
174 ash::wm::WindowShowType old_type) OVERRIDE {
175 if (!window_state->IsFullscreen() && !window_state->IsMinimized() &&
176 app_window_->GetBaseWindow()->IsFullscreenOrPending()) {
177 app_window_->Restore();
178 // Usually OnNativeWindowChanged() is called when the window bounds are
179 // changed as a result of a show type change. Because the change in show
180 // type has already occurred, we need to call OnNativeWindowChanged()
182 app_window_->OnNativeWindowChanged();
186 // Overridden from aura::WindowObserver:
187 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
188 window_state_->RemoveObserver(this);
189 window_state_->window()->RemoveObserver(this);
190 window_state_ = NULL;
194 AppWindow* app_window_;
195 ash::wm::WindowState* window_state_;
197 DISALLOW_COPY_AND_ASSIGN(NativeAppWindowStateDelegate);
203 NativeAppWindowViews::NativeAppWindowViews()
206 is_fullscreen_(false),
207 weak_ptr_factory_(this) {
210 void NativeAppWindowViews::Init(apps::AppWindow* app_window,
211 const AppWindow::CreateParams& create_params) {
212 app_window_ = app_window;
213 frameless_ = create_params.frame == AppWindow::FRAME_NONE;
214 transparent_background_ = create_params.transparent_background;
215 resizable_ = create_params.resizable;
216 Observe(web_contents());
218 window_ = new views::Widget;
219 if (create_params.window_type == AppWindow::WINDOW_TYPE_PANEL ||
220 create_params.window_type == AppWindow::WINDOW_TYPE_V1_PANEL) {
221 InitializePanelWindow(create_params);
223 InitializeDefaultWindow(create_params);
225 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews(
226 Profile::FromBrowserContext(browser_context()),
227 window_->GetFocusManager(),
228 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
232 window_->AddObserver(this);
235 if (ShouldUseChromeStyleFrame() &&
236 chrome::GetHostDesktopTypeForNativeWindow(window_->GetNativeWindow()) !=
237 chrome::HOST_DESKTOP_TYPE_ASH) {
238 InstallEasyResizeTargeterOnContainer();
243 NativeAppWindowViews::~NativeAppWindowViews() {
244 web_view_->SetWebContents(NULL);
247 void NativeAppWindowViews::OnBeforeWidgetInit(
248 views::Widget::InitParams* init_params,
249 views::Widget* widget) {}
251 void NativeAppWindowViews::InitializeDefaultWindow(
252 const AppWindow::CreateParams& create_params) {
253 std::string app_name =
254 web_app::GenerateApplicationNameFromExtensionId(extension()->id());
256 views::Widget::InitParams init_params(views::Widget::InitParams::TYPE_WINDOW);
257 init_params.delegate = this;
258 init_params.remove_standard_frame = ShouldUseChromeStyleFrame();
259 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
260 // On Linux, remove the standard frame. Instead, we will use CustomFrameView
261 // to draw a native-like frame.
262 // TODO(mgiuca): Remove this during fix for http://crbug.com/322256.
263 init_params.remove_standard_frame = true;
265 init_params.use_system_default_icon = true;
266 // TODO(erg): Conceptually, these are toplevel windows, but we theoretically
267 // could plumb context through to here in some cases.
268 init_params.top_level = true;
269 if (create_params.transparent_background)
270 init_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
271 init_params.keep_on_top = create_params.always_on_top;
272 gfx::Rect window_bounds = create_params.bounds;
273 bool position_specified =
274 window_bounds.x() != INT_MIN && window_bounds.y() != INT_MIN;
275 if (position_specified && !window_bounds.IsEmpty())
276 init_params.bounds = window_bounds;
278 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
279 // Set up a custom WM_CLASS for app windows. This allows task switchers in
280 // X11 environments to distinguish them from main browser windows.
281 init_params.wm_class_name = web_app::GetWMClassFromAppName(app_name);
282 init_params.wm_class_class = ShellIntegrationLinux::GetProgramClassName();
283 const char kX11WindowRoleApp[] = "app";
284 init_params.wm_role_name = std::string(kX11WindowRoleApp);
287 OnBeforeWidgetInit(&init_params, window_);
288 window_->Init(init_params);
290 gfx::Rect adjusted_bounds = window_bounds;
291 adjusted_bounds.Inset(-GetFrameInsets());
292 // Center window if no position was specified.
293 if (!position_specified)
294 window_->CenterWindow(adjusted_bounds.size());
295 else if (!adjusted_bounds.IsEmpty() && adjusted_bounds != window_bounds)
296 window_->SetBounds(adjusted_bounds);
298 // Register accelarators supported by app windows.
299 // TODO(jeremya/stevenjb): should these be registered for panels too?
300 views::FocusManager* focus_manager = GetFocusManager();
301 const std::map<ui::Accelerator, int>& accelerator_table =
302 GetAcceleratorTable();
303 const bool is_kiosk_app_mode = chrome::IsRunningInForcedAppMode();
305 // Ensures that kiosk mode accelerators are enabled when in kiosk mode (to be
306 // future proof). This is needed because GetAcceleratorTable() uses a static
307 // to store data and only checks kiosk mode once. If a platform app is
308 // launched before kiosk mode starts, the kiosk accelerators will not be
309 // registered. This DCHECK catches the case.
310 DCHECK(!is_kiosk_app_mode ||
311 accelerator_table.size() ==
312 arraysize(kAppWindowAcceleratorMap) +
313 arraysize(kAppWindowKioskAppModeAcceleratorMap));
315 for (std::map<ui::Accelerator, int>::const_iterator iter =
316 accelerator_table.begin();
317 iter != accelerator_table.end(); ++iter) {
318 if (is_kiosk_app_mode && !chrome::IsCommandAllowedInAppMode(iter->second))
321 focus_manager->RegisterAccelerator(
322 iter->first, ui::AcceleratorManager::kNormalPriority, this);
326 void NativeAppWindowViews::InitializePanelWindow(
327 const AppWindow::CreateParams& create_params) {
328 views::Widget::InitParams params(views::Widget::InitParams::TYPE_PANEL);
329 params.delegate = this;
331 preferred_size_ = gfx::Size(create_params.bounds.width(),
332 create_params.bounds.height());
333 if (preferred_size_.width() == 0)
334 preferred_size_.set_width(kDefaultPanelWidth);
335 else if (preferred_size_.width() < kMinPanelWidth)
336 preferred_size_.set_width(kMinPanelWidth);
338 if (preferred_size_.height() == 0)
339 preferred_size_.set_height(kDefaultPanelHeight);
340 else if (preferred_size_.height() < kMinPanelHeight)
341 preferred_size_.set_height(kMinPanelHeight);
343 if (ash::Shell::HasInstance()) {
344 // Open a new panel on the target root.
345 aura::Window* target = ash::Shell::GetTargetRootWindow();
346 params.bounds = ash::ScreenUtil::ConvertRectToScreen(
347 target, gfx::Rect(preferred_size_));
349 params.bounds = gfx::Rect(preferred_size_);
352 params.bounds = gfx::Rect(preferred_size_);
354 // TODO(erg): Conceptually, these are toplevel windows, but we theoretically
355 // could plumb context through to here in some cases.
356 params.top_level = true;
357 window_->Init(params);
358 window_->set_focus_on_creation(create_params.focused);
361 if (create_params.state == ui::SHOW_STATE_DETACHED) {
362 gfx::Rect window_bounds(create_params.bounds.x(),
363 create_params.bounds.y(),
364 preferred_size_.width(),
365 preferred_size_.height());
366 aura::Window* native_window = GetNativeWindow();
367 ash::wm::GetWindowState(native_window)->set_panel_attached(false);
368 aura::client::ParentWindowWithContext(native_window,
369 native_window->GetRootWindow(),
370 native_window->GetBoundsInScreen());
371 window_->SetBounds(window_bounds);
374 // TODO(stevenjb): NativeAppWindow panels need to be implemented for other
379 bool NativeAppWindowViews::ShouldUseChromeStyleFrame() const {
383 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
384 // Linux always uses native style frames.
388 return !CommandLine::ForCurrentProcess()->HasSwitch(
389 switches::kAppsUseNativeFrame);
392 void NativeAppWindowViews::InstallEasyResizeTargeterOnContainer() const {
393 aura::Window* root_window = window_->GetNativeWindow()->GetRootWindow();
394 gfx::Insets inset(kResizeInsideBoundsSize, kResizeInsideBoundsSize,
395 kResizeInsideBoundsSize, kResizeInsideBoundsSize);
396 root_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
397 new wm::EasyResizeWindowTargeter(root_window, inset, inset)));
400 apps::AppWindowFrameView* NativeAppWindowViews::CreateAppWindowFrameView() {
401 // By default the user can resize the window from slightly inside the bounds.
402 int resize_inside_bounds_size = kResizeInsideBoundsSize;
403 int resize_outside_bounds_size = 0;
404 int resize_outside_scale_for_touch = 1;
405 int resize_area_corner_size = kResizeAreaCornerSize;
407 // For Aura windows on the Ash desktop the sizes are different and the user
408 // can resize the window from slightly outside the bounds as well.
409 if (chrome::IsNativeWindowInAsh(window_->GetNativeWindow())) {
410 resize_inside_bounds_size = ash::kResizeInsideBoundsSize;
411 resize_outside_bounds_size = ash::kResizeOutsideBoundsSize;
412 resize_outside_scale_for_touch = ash::kResizeOutsideBoundsScaleForTouch;
413 resize_area_corner_size = ash::kResizeAreaCornerSize;
416 apps::AppWindowFrameView* frame_view = new apps::AppWindowFrameView(this);
417 frame_view->Init(window_,
418 resize_inside_bounds_size,
419 resize_outside_bounds_size,
420 resize_outside_scale_for_touch,
421 resize_area_corner_size);
425 // ui::BaseWindow implementation.
427 bool NativeAppWindowViews::IsActive() const {
428 return window_->IsActive();
431 bool NativeAppWindowViews::IsMaximized() const {
432 return window_->IsMaximized();
435 bool NativeAppWindowViews::IsMinimized() const {
436 return window_->IsMinimized();
439 bool NativeAppWindowViews::IsFullscreen() const {
440 return window_->IsFullscreen();
443 gfx::NativeWindow NativeAppWindowViews::GetNativeWindow() {
444 return window_->GetNativeWindow();
447 gfx::Rect NativeAppWindowViews::GetRestoredBounds() const {
448 return window_->GetRestoredBounds();
451 ui::WindowShowState NativeAppWindowViews::GetRestoredState() const {
453 return ui::SHOW_STATE_MAXIMIZED;
454 if (IsFullscreen()) {
456 if (immersive_fullscreen_controller_.get() &&
457 immersive_fullscreen_controller_->IsEnabled()) {
458 // Restore windows which were previously in immersive fullscreen to
459 // maximized. Restoring the window to a different fullscreen type
460 // makes for a bad experience.
461 return ui::SHOW_STATE_MAXIMIZED;
464 return ui::SHOW_STATE_FULLSCREEN;
467 // Use kRestoreShowStateKey in case a window is minimized/hidden.
468 ui::WindowShowState restore_state =
469 window_->GetNativeWindow()->GetProperty(
470 aura::client::kRestoreShowStateKey);
471 // Whitelist states to return so that invalid and transient states
472 // are not saved and used to restore windows when they are recreated.
473 switch (restore_state) {
474 case ui::SHOW_STATE_NORMAL:
475 case ui::SHOW_STATE_MAXIMIZED:
476 case ui::SHOW_STATE_FULLSCREEN:
477 case ui::SHOW_STATE_DETACHED:
478 return restore_state;
480 case ui::SHOW_STATE_DEFAULT:
481 case ui::SHOW_STATE_MINIMIZED:
482 case ui::SHOW_STATE_INACTIVE:
483 case ui::SHOW_STATE_END:
484 return ui::SHOW_STATE_NORMAL;
487 return ui::SHOW_STATE_NORMAL;
490 gfx::Rect NativeAppWindowViews::GetBounds() const {
491 return window_->GetWindowBoundsInScreen();
494 void NativeAppWindowViews::Show() {
495 if (window_->IsVisible()) {
503 void NativeAppWindowViews::ShowInactive() {
504 if (window_->IsVisible())
506 window_->ShowInactive();
509 void NativeAppWindowViews::Hide() {
513 void NativeAppWindowViews::Close() {
517 void NativeAppWindowViews::Activate() {
521 void NativeAppWindowViews::Deactivate() {
522 window_->Deactivate();
525 void NativeAppWindowViews::Maximize() {
529 void NativeAppWindowViews::Minimize() {
533 void NativeAppWindowViews::Restore() {
537 void NativeAppWindowViews::SetBounds(const gfx::Rect& bounds) {
538 window_->SetBounds(bounds);
541 void NativeAppWindowViews::FlashFrame(bool flash) {
542 window_->FlashFrame(flash);
545 bool NativeAppWindowViews::IsAlwaysOnTop() const {
546 if (app_window_->window_type_is_panel()) {
548 return ash::wm::GetWindowState(window_->GetNativeWindow())->
554 return window_->IsAlwaysOnTop();
558 void NativeAppWindowViews::SetAlwaysOnTop(bool always_on_top) {
559 window_->SetAlwaysOnTop(always_on_top);
562 void NativeAppWindowViews::ShowContextMenuForView(
565 ui::MenuSourceType source_type) {
566 #if defined(USE_ASH) & defined(OS_CHROMEOS)
567 scoped_ptr<ui::MenuModel> model =
568 CreateMultiUserContextMenu(app_window_->GetNativeWindow());
572 // Only show context menu if point is in caption.
573 gfx::Point point_in_view_coords(p);
574 views::View::ConvertPointFromScreen(window_->non_client_view(),
575 &point_in_view_coords);
576 int hit_test = window_->non_client_view()->NonClientHitTest(
577 point_in_view_coords);
578 if (hit_test == HTCAPTION) {
579 menu_runner_.reset(new views::MenuRunner(model.get()));
580 if (menu_runner_->RunMenuAt(source->GetWidget(), NULL,
581 gfx::Rect(p, gfx::Size(0,0)), views::MenuItemView::TOPLEFT,
583 views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) ==
584 views::MenuRunner::MENU_DELETED)
590 gfx::NativeView NativeAppWindowViews::GetHostView() const {
591 return window_->GetNativeView();
594 gfx::Point NativeAppWindowViews::GetDialogPosition(const gfx::Size& size) {
595 gfx::Size app_window_size = window_->GetWindowBoundsInScreen().size();
596 return gfx::Point(app_window_size.width() / 2 - size.width() / 2,
597 app_window_size.height() / 2 - size.height() / 2);
600 gfx::Size NativeAppWindowViews::GetMaximumDialogSize() {
601 return window_->GetWindowBoundsInScreen().size();
604 void NativeAppWindowViews::AddObserver(
605 web_modal::ModalDialogHostObserver* observer) {
606 observer_list_.AddObserver(observer);
608 void NativeAppWindowViews::RemoveObserver(
609 web_modal::ModalDialogHostObserver* observer) {
610 observer_list_.RemoveObserver(observer);
613 // Private method. TODO(stevenjb): Move this below InitializePanelWindow()
614 // to match declaration order.
615 void NativeAppWindowViews::OnViewWasResized() {
616 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
618 OnPositionRequiresUpdate());
621 // WidgetDelegate implementation.
623 void NativeAppWindowViews::OnWidgetMove() {
624 app_window_->OnNativeWindowChanged();
627 views::View* NativeAppWindowViews::GetInitiallyFocusedView() {
631 bool NativeAppWindowViews::CanResize() const {
632 return resizable_ && !app_window_->size_constraints().HasFixedSize();
635 bool NativeAppWindowViews::CanMaximize() const {
636 return resizable_ && !app_window_->size_constraints().HasMaximumSize() &&
637 !app_window_->window_type_is_panel();
640 base::string16 NativeAppWindowViews::GetWindowTitle() const {
641 return app_window_->GetTitle();
644 bool NativeAppWindowViews::ShouldShowWindowTitle() const {
645 return app_window_->window_type() == AppWindow::WINDOW_TYPE_V1_PANEL;
648 gfx::ImageSkia NativeAppWindowViews::GetWindowAppIcon() {
649 gfx::Image app_icon = app_window_->app_icon();
650 if (app_icon.IsEmpty())
651 return GetWindowIcon();
653 return *app_icon.ToImageSkia();
656 gfx::ImageSkia NativeAppWindowViews::GetWindowIcon() {
657 content::WebContents* web_contents = app_window_->web_contents();
659 FaviconTabHelper* favicon_tab_helper =
660 FaviconTabHelper::FromWebContents(web_contents);
661 gfx::Image app_icon = favicon_tab_helper->GetFavicon();
662 if (!app_icon.IsEmpty())
663 return *app_icon.ToImageSkia();
665 return gfx::ImageSkia();
668 bool NativeAppWindowViews::ShouldShowWindowIcon() const {
669 return app_window_->window_type() == AppWindow::WINDOW_TYPE_V1_PANEL;
672 void NativeAppWindowViews::SaveWindowPlacement(const gfx::Rect& bounds,
673 ui::WindowShowState show_state) {
674 views::WidgetDelegate::SaveWindowPlacement(bounds, show_state);
675 app_window_->OnNativeWindowChanged();
678 void NativeAppWindowViews::DeleteDelegate() {
679 window_->RemoveObserver(this);
680 app_window_->OnNativeClose();
683 views::Widget* NativeAppWindowViews::GetWidget() {
687 const views::Widget* NativeAppWindowViews::GetWidget() const {
691 views::View* NativeAppWindowViews::GetContentsView() {
695 views::NonClientFrameView* NativeAppWindowViews::CreateNonClientFrameView(
696 views::Widget* widget) {
698 if (chrome::IsNativeViewInAsh(widget->GetNativeView())) {
699 // Set the delegate now because CustomFrameViewAsh sets the
700 // WindowStateDelegate if one is not already set.
701 ash::wm::GetWindowState(GetNativeWindow())->SetDelegate(
702 scoped_ptr<ash::wm::WindowStateDelegate>(
703 new NativeAppWindowStateDelegate(app_window_, this)).Pass());
705 if (app_window_->window_type_is_panel()) {
706 ash::PanelFrameView::FrameType frame_type = frameless_ ?
707 ash::PanelFrameView::FRAME_NONE : ash::PanelFrameView::FRAME_ASH;
708 views::NonClientFrameView* frame_view =
709 new ash::PanelFrameView(widget, frame_type);
710 frame_view->set_context_menu_controller(this);
715 ash::CustomFrameViewAsh* custom_frame_view =
716 new ash::CustomFrameViewAsh(widget);
717 #if defined(OS_CHROMEOS)
718 // Non-frameless app windows can be put into immersive fullscreen.
719 // TODO(pkotwicz): Investigate if immersive fullscreen can be enabled for
721 if (ash::switches::UseImmersiveFullscreenForAllWindows()) {
722 immersive_fullscreen_controller_.reset(
723 new ash::ImmersiveFullscreenController());
724 custom_frame_view->InitImmersiveFullscreenControllerForView(
725 immersive_fullscreen_controller_.get());
728 custom_frame_view->GetHeaderView()->set_context_menu_controller(this);
729 return custom_frame_view;
733 if (ShouldUseChromeStyleFrame())
734 return CreateAppWindowFrameView();
735 return views::WidgetDelegateView::CreateNonClientFrameView(widget);
738 bool NativeAppWindowViews::WidgetHasHitTestMask() const {
739 return shape_ != NULL;
742 void NativeAppWindowViews::GetWidgetHitTestMask(gfx::Path* mask) const {
743 shape_->getBoundaryPath(mask);
746 bool NativeAppWindowViews::ShouldDescendIntoChildForEventHandling(
747 gfx::NativeView child,
748 const gfx::Point& location) {
749 #if defined(USE_AURA)
750 if (child->Contains(web_view_->web_contents()->GetView()->GetNativeView())) {
751 // App window should claim mouse events that fall within the draggable
753 return !draggable_region_.get() ||
754 !draggable_region_->contains(location.x(), location.y());
761 // WidgetObserver implementation.
763 void NativeAppWindowViews::OnWidgetVisibilityChanged(views::Widget* widget,
765 app_window_->OnNativeWindowChanged();
768 void NativeAppWindowViews::OnWidgetActivationChanged(views::Widget* widget,
770 app_window_->OnNativeWindowChanged();
772 app_window_->OnNativeWindowActivated();
775 // WebContentsObserver implementation.
777 void NativeAppWindowViews::RenderViewCreated(
778 content::RenderViewHost* render_view_host) {
779 if (transparent_background_) {
780 // Use a background with transparency to trigger transparency in Webkit.
782 background.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
783 background.allocPixels();
784 background.eraseARGB(0x00, 0x00, 0x00, 0x00);
786 content::RenderWidgetHostView* view = render_view_host->GetView();
788 view->SetBackground(background);
792 void NativeAppWindowViews::RenderViewHostChanged(
793 content::RenderViewHost* old_host,
794 content::RenderViewHost* new_host) {
798 // views::View implementation.
800 void NativeAppWindowViews::Layout() {
802 web_view_->SetBounds(0, 0, width(), height());
806 void NativeAppWindowViews::ViewHierarchyChanged(
807 const ViewHierarchyChangedDetails& details) {
808 if (details.is_add && details.child == this) {
809 web_view_ = new views::WebView(NULL);
810 AddChildView(web_view_);
811 web_view_->SetWebContents(web_contents());
815 gfx::Size NativeAppWindowViews::GetPreferredSize() {
816 if (!preferred_size_.IsEmpty())
817 return preferred_size_;
818 return views::View::GetPreferredSize();
821 gfx::Size NativeAppWindowViews::GetMinimumSize() {
822 return app_window_->size_constraints().GetMinimumSize();
825 gfx::Size NativeAppWindowViews::GetMaximumSize() {
826 return app_window_->size_constraints().GetMaximumSize();
829 void NativeAppWindowViews::OnFocus() {
830 web_view_->RequestFocus();
833 bool NativeAppWindowViews::AcceleratorPressed(
834 const ui::Accelerator& accelerator) {
835 const std::map<ui::Accelerator, int>& accelerator_table =
836 GetAcceleratorTable();
837 std::map<ui::Accelerator, int>::const_iterator iter =
838 accelerator_table.find(accelerator);
839 DCHECK(iter != accelerator_table.end());
840 int command_id = iter->second;
841 switch (command_id) {
842 case IDC_CLOSE_WINDOW:
846 chrome_page_zoom::Zoom(web_view_->GetWebContents(),
847 content::PAGE_ZOOM_OUT);
849 case IDC_ZOOM_NORMAL:
850 chrome_page_zoom::Zoom(web_view_->GetWebContents(),
851 content::PAGE_ZOOM_RESET);
854 chrome_page_zoom::Zoom(web_view_->GetWebContents(),
855 content::PAGE_ZOOM_IN);
858 NOTREACHED() << "Unknown accelerator sent to app window.";
863 // NativeAppWindow implementation.
865 void NativeAppWindowViews::SetFullscreen(int fullscreen_types) {
866 // Fullscreen not supported by panels.
867 if (app_window_->window_type_is_panel())
869 is_fullscreen_ = (fullscreen_types != AppWindow::FULLSCREEN_TYPE_NONE);
870 window_->SetFullscreen(is_fullscreen_);
873 if (immersive_fullscreen_controller_.get()) {
874 // |immersive_fullscreen_controller_| should only be set if immersive
875 // fullscreen is the fullscreen type used by the OS.
876 immersive_fullscreen_controller_->SetEnabled(
877 ash::ImmersiveFullscreenController::WINDOW_TYPE_PACKAGED_APP,
878 (fullscreen_types & AppWindow::FULLSCREEN_TYPE_OS) != 0);
879 // Autohide the shelf instead of hiding the shelf completely when only in
881 ash::wm::WindowState* window_state =
882 ash::wm::GetWindowState(window_->GetNativeWindow());
883 window_state->set_hide_shelf_when_fullscreen(fullscreen_types !=
884 AppWindow::FULLSCREEN_TYPE_OS);
885 DCHECK(ash::Shell::HasInstance());
886 ash::Shell::GetInstance()->UpdateShelfVisibility();
890 // TODO(jeremya) we need to call RenderViewHost::ExitFullscreen() if we
891 // ever drop the window out of fullscreen in response to something that
892 // wasn't the app calling webkitCancelFullScreen().
895 bool NativeAppWindowViews::IsFullscreenOrPending() const {
896 return is_fullscreen_;
899 bool NativeAppWindowViews::IsDetached() const {
900 if (!app_window_->window_type_is_panel())
903 return !ash::wm::GetWindowState(window_->GetNativeWindow())->panel_attached();
909 void NativeAppWindowViews::UpdateWindowIcon() {
910 window_->UpdateWindowIcon();
913 void NativeAppWindowViews::UpdateWindowTitle() {
914 window_->UpdateWindowTitle();
917 void NativeAppWindowViews::UpdateBadgeIcon() {
918 const gfx::Image* icon = NULL;
919 if (!app_window_->badge_icon().IsEmpty()) {
920 icon = &app_window_->badge_icon();
921 // chrome::DrawTaskbarDecoration can do interesting things with non-square
923 // TODO(benwells): Refactor chrome::DrawTaskbarDecoration to not be avatar
924 // specific, and lift this restriction.
925 if (icon->Width() != icon->Height()) {
926 LOG(ERROR) << "Attempt to set a non-square badge; request ignored.";
930 chrome::DrawTaskbarDecoration(GetNativeWindow(), icon);
933 void NativeAppWindowViews::UpdateDraggableRegions(
934 const std::vector<extensions::DraggableRegion>& regions) {
935 // Draggable region is not supported for non-frameless window.
939 draggable_region_.reset(AppWindow::RawDraggableRegionsToSkRegion(regions));
943 SkRegion* NativeAppWindowViews::GetDraggableRegion() {
944 return draggable_region_.get();
947 void NativeAppWindowViews::UpdateShape(scoped_ptr<SkRegion> region) {
948 bool had_shape = shape_;
949 shape_ = region.Pass();
951 aura::Window* native_window = window_->GetNativeWindow();
953 window_->SetShape(new SkRegion(*shape_));
955 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
956 new ShapedAppWindowTargeter(native_window, this)));
959 window_->SetShape(NULL);
961 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>());
965 void NativeAppWindowViews::HandleKeyboardEvent(
966 const content::NativeWebKeyboardEvent& event) {
967 unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
971 bool NativeAppWindowViews::IsFrameless() const {
975 gfx::Insets NativeAppWindowViews::GetFrameInsets() const {
977 return gfx::Insets();
979 // The pretend client_bounds passed in need to be large enough to ensure that
980 // GetWindowBoundsForClientBounds() doesn't decide that it needs more than
981 // the specified amount of space to fit the window controls in, and return a
982 // number larger than the real frame insets. Most window controls are smaller
983 // than 1000x1000px, so this should be big enough.
984 gfx::Rect client_bounds = gfx::Rect(1000, 1000);
985 gfx::Rect window_bounds =
986 window_->non_client_view()->GetWindowBoundsForClientBounds(
988 return window_bounds.InsetsFrom(client_bounds);
991 void NativeAppWindowViews::HideWithApp() {}
992 void NativeAppWindowViews::ShowWithApp() {}
993 void NativeAppWindowViews::UpdateWindowMinMaxSize() {}