Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / apps / chrome_native_app_window_views.cc
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
55 using apps::AppWindow;
56
57 namespace {
58
59 const int kMinPanelWidth = 100;
60 const int kMinPanelHeight = 100;
61 const int kDefaultPanelWidth = 200;
62 const int kDefaultPanelHeight = 300;
63
64 struct AcceleratorMapping {
65   ui::KeyboardCode keycode;
66   int modifiers;
67   int command_id;
68 };
69
70 const 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.
80 const 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
92 void 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
101 const 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).
123 class NativeAppWindowStateDelegate : public ash::wm::WindowStateDelegate,
124                                      public ash::wm::WindowStateObserver,
125                                      public aura::WindowObserver {
126  public:
127   NativeAppWindowStateDelegate(AppWindow* app_window,
128                                apps::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
194 ChromeNativeAppWindowViews::ChromeNativeAppWindowViews()
195     : is_fullscreen_(false),
196       has_frame_color_(false),
197       active_frame_color_(SK_ColorBLACK),
198       inactive_frame_color_(SK_ColorBLACK) {
199 }
200
201 ChromeNativeAppWindowViews::~ChromeNativeAppWindowViews() {}
202
203 void ChromeNativeAppWindowViews::OnBeforeWidgetInit(
204     views::Widget::InitParams* init_params,
205     views::Widget* widget) {}
206
207 void 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
220 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
221   // Set up a custom WM_CLASS for app windows. This allows task switchers in
222   // X11 environments to distinguish them from main browser windows.
223   init_params.wm_class_name = web_app::GetWMClassFromAppName(app_name);
224   init_params.wm_class_class = shell_integration_linux::GetProgramClassName();
225   const char kX11WindowRoleApp[] = "app";
226   init_params.wm_role_name = std::string(kX11WindowRoleApp);
227 #endif
228
229   OnBeforeWidgetInit(&init_params, widget());
230   widget()->Init(init_params);
231
232   // The frame insets are required to resolve the bounds specifications
233   // correctly. So we set the window bounds and constraints now.
234   gfx::Insets frame_insets = GetFrameInsets();
235   gfx::Rect window_bounds = create_params.GetInitialWindowBounds(frame_insets);
236   SetContentSizeConstraints(create_params.GetContentMinimumSize(frame_insets),
237                             create_params.GetContentMaximumSize(frame_insets));
238   if (!window_bounds.IsEmpty()) {
239     typedef apps::AppWindow::BoundsSpecification BoundsSpecification;
240     bool position_specified =
241         window_bounds.x() != BoundsSpecification::kUnspecifiedPosition &&
242         window_bounds.y() != BoundsSpecification::kUnspecifiedPosition;
243     if (!position_specified)
244       widget()->CenterWindow(window_bounds.size());
245     else
246       widget()->SetBounds(window_bounds);
247   }
248
249   if (IsFrameless() &&
250       init_params.opacity == views::Widget::InitParams::TRANSLUCENT_WINDOW) {
251     // The given window is most likely not rectangular since it uses
252     // transparency and has no standard frame, don't show a shadow for it.
253     // TODO(skuhne): If we run into an application which should have a shadow
254     // but does not have, a new attribute has to be added.
255     wm::SetShadowType(widget()->GetNativeWindow(), wm::SHADOW_TYPE_NONE);
256   }
257
258   // Register accelarators supported by app windows.
259   // TODO(jeremya/stevenjb): should these be registered for panels too?
260   views::FocusManager* focus_manager = GetFocusManager();
261   const std::map<ui::Accelerator, int>& accelerator_table =
262       GetAcceleratorTable();
263   const bool is_kiosk_app_mode = chrome::IsRunningInForcedAppMode();
264
265   // Ensures that kiosk mode accelerators are enabled when in kiosk mode (to be
266   // future proof). This is needed because GetAcceleratorTable() uses a static
267   // to store data and only checks kiosk mode once. If a platform app is
268   // launched before kiosk mode starts, the kiosk accelerators will not be
269   // registered. This DCHECK catches the case.
270   DCHECK(!is_kiosk_app_mode ||
271          accelerator_table.size() ==
272              arraysize(kAppWindowAcceleratorMap) +
273                  arraysize(kAppWindowKioskAppModeAcceleratorMap));
274
275   // Ensure there is a ZoomController in kiosk mode, otherwise the processing
276   // of the accelerators will cause a crash.
277   DCHECK(!is_kiosk_app_mode ||
278          ZoomController::FromWebContents(web_view()->GetWebContents()));
279
280   for (std::map<ui::Accelerator, int>::const_iterator iter =
281            accelerator_table.begin();
282        iter != accelerator_table.end(); ++iter) {
283     if (is_kiosk_app_mode && !chrome::IsCommandAllowedInAppMode(iter->second))
284       continue;
285
286     focus_manager->RegisterAccelerator(
287         iter->first, ui::AcceleratorManager::kNormalPriority, this);
288   }
289 }
290
291 void ChromeNativeAppWindowViews::InitializePanelWindow(
292     const AppWindow::CreateParams& create_params) {
293   views::Widget::InitParams params(views::Widget::InitParams::TYPE_PANEL);
294   params.delegate = this;
295
296   gfx::Rect initial_window_bounds =
297       create_params.GetInitialWindowBounds(gfx::Insets());
298   preferred_size_ = gfx::Size(initial_window_bounds.width(),
299                               initial_window_bounds.height());
300   if (preferred_size_.width() == 0)
301     preferred_size_.set_width(kDefaultPanelWidth);
302   else if (preferred_size_.width() < kMinPanelWidth)
303     preferred_size_.set_width(kMinPanelWidth);
304
305   if (preferred_size_.height() == 0)
306     preferred_size_.set_height(kDefaultPanelHeight);
307   else if (preferred_size_.height() < kMinPanelHeight)
308     preferred_size_.set_height(kMinPanelHeight);
309 #if defined(USE_ASH)
310   if (ash::Shell::HasInstance()) {
311     // Open a new panel on the target root.
312     aura::Window* target = ash::Shell::GetTargetRootWindow();
313     params.bounds = ash::ScreenUtil::ConvertRectToScreen(
314         target, gfx::Rect(preferred_size_));
315   } else {
316     params.bounds = gfx::Rect(preferred_size_);
317   }
318 #else
319   params.bounds = gfx::Rect(preferred_size_);
320 #endif
321   widget()->Init(params);
322   widget()->set_focus_on_creation(create_params.focused);
323
324 #if defined(USE_ASH)
325   if (create_params.state == ui::SHOW_STATE_DETACHED) {
326     gfx::Rect window_bounds(initial_window_bounds.x(),
327                             initial_window_bounds.y(),
328                             preferred_size_.width(),
329                             preferred_size_.height());
330     aura::Window* native_window = GetNativeWindow();
331     ash::wm::GetWindowState(native_window)->set_panel_attached(false);
332     aura::client::ParentWindowWithContext(native_window,
333                                           native_window->GetRootWindow(),
334                                           native_window->GetBoundsInScreen());
335     widget()->SetBounds(window_bounds);
336   }
337 #else
338   // TODO(stevenjb): NativeAppWindow panels need to be implemented for other
339   // platforms.
340 #endif
341 }
342
343 views::NonClientFrameView*
344 ChromeNativeAppWindowViews::CreateStandardDesktopAppFrame() {
345   return views::WidgetDelegateView::CreateNonClientFrameView(widget());
346 }
347
348 apps::AppWindowFrameView*
349 ChromeNativeAppWindowViews::CreateNonStandardAppFrame() {
350   apps::AppWindowFrameView* frame =
351       new apps::AppWindowFrameView(widget(),
352                                    this,
353                                    has_frame_color_,
354                                    active_frame_color_,
355                                    inactive_frame_color_);
356   frame->Init();
357 #if defined(USE_ASH)
358   // For Aura windows on the Ash desktop the sizes are different and the user
359   // can resize the window from slightly outside the bounds as well.
360   if (chrome::IsNativeWindowInAsh(widget()->GetNativeWindow())) {
361     frame->SetResizeSizes(ash::kResizeInsideBoundsSize,
362                           ash::kResizeOutsideBoundsSize,
363                           ash::kResizeAreaCornerSize);
364   }
365 #endif
366
367 #if !defined(OS_CHROMEOS)
368   // For non-Ash windows, install an easy resize window targeter, which ensures
369   // that the root window (not the app) receives mouse events on the edges.
370   if (chrome::GetHostDesktopTypeForNativeWindow(widget()->GetNativeWindow()) !=
371       chrome::HOST_DESKTOP_TYPE_ASH) {
372     aura::Window* window = widget()->GetNativeWindow();
373     int resize_inside = frame->resize_inside_bounds_size();
374     gfx::Insets inset(
375         resize_inside, resize_inside, resize_inside, resize_inside);
376     // Add the EasyResizeWindowTargeter on the window, not its root window. The
377     // root window does not have a delegate, which is needed to handle the event
378     // in Linux.
379     window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
380         new wm::EasyResizeWindowTargeter(window, inset, inset)));
381   }
382 #endif
383
384   return frame;
385 }
386
387 // ui::BaseWindow implementation.
388
389 gfx::Rect ChromeNativeAppWindowViews::GetRestoredBounds() const {
390 #if defined(USE_ASH)
391   gfx::Rect* bounds = widget()->GetNativeWindow()->GetProperty(
392       ash::kRestoreBoundsOverrideKey);
393   if (bounds && !bounds->IsEmpty())
394     return *bounds;
395 #endif
396   return widget()->GetRestoredBounds();
397 }
398
399 ui::WindowShowState ChromeNativeAppWindowViews::GetRestoredState() const {
400 #if !defined(USE_ASH)
401   if (IsMaximized())
402     return ui::SHOW_STATE_MAXIMIZED;
403   if (IsFullscreen())
404     return ui::SHOW_STATE_FULLSCREEN;
405 #else
406   // Use kRestoreShowStateKey in case a window is minimized/hidden.
407   ui::WindowShowState restore_state = widget()->GetNativeWindow()->GetProperty(
408       aura::client::kRestoreShowStateKey);
409   if (widget()->GetNativeWindow()->GetProperty(
410           ash::kRestoreBoundsOverrideKey)) {
411     // If an override is given, we use that restore state (after filtering).
412     restore_state = widget()->GetNativeWindow()->GetProperty(
413                         ash::kRestoreShowStateOverrideKey);
414   } else {
415     // Otherwise first normal states are checked.
416     if (IsMaximized())
417       return ui::SHOW_STATE_MAXIMIZED;
418     if (IsFullscreen()) {
419       if (immersive_fullscreen_controller_.get() &&
420           immersive_fullscreen_controller_->IsEnabled()) {
421         // Restore windows which were previously in immersive fullscreen to
422         // maximized. Restoring the window to a different fullscreen type
423         // makes for a bad experience.
424         return ui::SHOW_STATE_MAXIMIZED;
425       }
426       return ui::SHOW_STATE_FULLSCREEN;
427     }
428   }
429   // Whitelist states to return so that invalid and transient states
430   // are not saved and used to restore windows when they are recreated.
431   switch (restore_state) {
432     case ui::SHOW_STATE_NORMAL:
433     case ui::SHOW_STATE_MAXIMIZED:
434     case ui::SHOW_STATE_FULLSCREEN:
435     case ui::SHOW_STATE_DETACHED:
436       return restore_state;
437
438     case ui::SHOW_STATE_DEFAULT:
439     case ui::SHOW_STATE_MINIMIZED:
440     case ui::SHOW_STATE_INACTIVE:
441     case ui::SHOW_STATE_END:
442       return ui::SHOW_STATE_NORMAL;
443   }
444 #endif  // !defined(USE_ASH)
445   return ui::SHOW_STATE_NORMAL;
446 }
447
448 bool ChromeNativeAppWindowViews::IsAlwaysOnTop() const {
449   if (app_window()->window_type_is_panel()) {
450 #if defined(USE_ASH)
451     return ash::wm::GetWindowState(widget()->GetNativeWindow())
452         ->panel_attached();
453 #else
454     return true;
455 #endif
456   } else {
457     return widget()->IsAlwaysOnTop();
458   }
459 }
460
461 // views::ContextMenuController implementation.
462
463 void ChromeNativeAppWindowViews::ShowContextMenuForView(
464     views::View* source,
465     const gfx::Point& p,
466     ui::MenuSourceType source_type) {
467 #if defined(USE_ASH) && defined(OS_CHROMEOS)
468   scoped_ptr<ui::MenuModel> model =
469       CreateMultiUserContextMenu(app_window()->GetNativeWindow());
470   if (!model.get())
471     return;
472
473   // Only show context menu if point is in caption.
474   gfx::Point point_in_view_coords(p);
475   views::View::ConvertPointFromScreen(widget()->non_client_view(),
476                                       &point_in_view_coords);
477   int hit_test =
478       widget()->non_client_view()->NonClientHitTest(point_in_view_coords);
479   if (hit_test == HTCAPTION) {
480     menu_runner_.reset(new views::MenuRunner(
481         model.get(),
482         views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
483     if (menu_runner_->RunMenuAt(source->GetWidget(),
484                                 NULL,
485                                 gfx::Rect(p, gfx::Size(0, 0)),
486                                 views::MENU_ANCHOR_TOPLEFT,
487                                 source_type) ==
488         views::MenuRunner::MENU_DELETED) {
489       return;
490     }
491   }
492 #endif
493 }
494
495 // views::WidgetDelegate implementation.
496
497 gfx::ImageSkia ChromeNativeAppWindowViews::GetWindowAppIcon() {
498   gfx::Image app_icon = app_window()->app_icon();
499   if (app_icon.IsEmpty())
500     return GetWindowIcon();
501   else
502     return *app_icon.ToImageSkia();
503 }
504
505 gfx::ImageSkia ChromeNativeAppWindowViews::GetWindowIcon() {
506   content::WebContents* web_contents = app_window()->web_contents();
507   if (web_contents) {
508     FaviconTabHelper* favicon_tab_helper =
509         FaviconTabHelper::FromWebContents(web_contents);
510     gfx::Image app_icon = favicon_tab_helper->GetFavicon();
511     if (!app_icon.IsEmpty())
512       return *app_icon.ToImageSkia();
513   }
514   return gfx::ImageSkia();
515 }
516
517 views::NonClientFrameView* ChromeNativeAppWindowViews::CreateNonClientFrameView(
518     views::Widget* widget) {
519 #if defined(USE_ASH)
520   if (chrome::IsNativeViewInAsh(widget->GetNativeView())) {
521     // Set the delegate now because CustomFrameViewAsh sets the
522     // WindowStateDelegate if one is not already set.
523     ash::wm::GetWindowState(GetNativeWindow())->SetDelegate(
524         scoped_ptr<ash::wm::WindowStateDelegate>(
525             new NativeAppWindowStateDelegate(app_window(), this)).Pass());
526
527     if (app_window()->window_type_is_panel()) {
528       ash::PanelFrameView::FrameType frame_type = IsFrameless() ?
529           ash::PanelFrameView::FRAME_NONE : ash::PanelFrameView::FRAME_ASH;
530       views::NonClientFrameView* frame_view =
531           new ash::PanelFrameView(widget, frame_type);
532       frame_view->set_context_menu_controller(this);
533       return frame_view;
534     }
535
536     if (IsFrameless())
537       return CreateNonStandardAppFrame();
538
539     ash::CustomFrameViewAsh* custom_frame_view =
540         new ash::CustomFrameViewAsh(widget);
541     // Non-frameless app windows can be put into immersive fullscreen.
542     immersive_fullscreen_controller_.reset(
543         new ash::ImmersiveFullscreenController());
544     custom_frame_view->InitImmersiveFullscreenControllerForView(
545         immersive_fullscreen_controller_.get());
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
554 bool ChromeNativeAppWindowViews::WidgetHasHitTestMask() const {
555   return shape_ != NULL;
556 }
557
558 void ChromeNativeAppWindowViews::GetWidgetHitTestMask(gfx::Path* mask) const {
559   shape_->getBoundaryPath(mask);
560 }
561
562 // views::View implementation.
563
564 gfx::Size ChromeNativeAppWindowViews::GetPreferredSize() const {
565   if (!preferred_size_.IsEmpty())
566     return preferred_size_;
567   return NativeAppWindowViews::GetPreferredSize();
568 }
569
570 bool 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
602 void 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
632 bool ChromeNativeAppWindowViews::IsFullscreenOrPending() const {
633   return is_fullscreen_;
634 }
635
636 bool 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
647 void 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
663 void 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
681 bool ChromeNativeAppWindowViews::HasFrameColor() const {
682   return has_frame_color_;
683 }
684
685 SkColor ChromeNativeAppWindowViews::ActiveFrameColor() const {
686   return active_frame_color_;
687 }
688
689 SkColor ChromeNativeAppWindowViews::InactiveFrameColor() const {
690   return inactive_frame_color_;
691 }
692
693 // NativeAppWindowViews implementation.
694
695 void 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 }