Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / apps / native_app_window_views.cc
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.
4
5 #include "chrome/browser/ui/views/apps/native_app_window_views.h"
6
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"
38
39 #if defined(OS_LINUX)
40 #include "chrome/browser/shell_integration_linux.h"
41 #endif
42
43 #if defined(USE_ASH)
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"
59 #endif
60
61 #if defined(USE_AURA)
62 #include "ui/aura/window.h"
63 #endif
64
65 using apps::AppWindow;
66
67 namespace {
68
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;
75
76 struct AcceleratorMapping {
77   ui::KeyboardCode keycode;
78   int modifiers;
79   int command_id;
80 };
81
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 },
86 };
87
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,
95     IDC_ZOOM_MINUS },
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 },
102 };
103
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;
110   }
111 }
112
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),
120         &accelerators);
121
122     // Add accelerators for kiosk mode.
123     if (chrome::IsRunningInForcedAppMode()) {
124       AddAcceleratorsFromMapping(
125           kAppWindowKioskAppModeAcceleratorMap,
126           arraysize(kAppWindowKioskAppModeAcceleratorMap),
127           &accelerators);
128     }
129   }
130   return accelerators;
131 }
132
133 #if defined(USE_ASH)
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 {
138  public:
139   NativeAppWindowStateDelegate(AppWindow* app_window,
140                                apps::NativeAppWindow* native_app_window)
141       : app_window_(app_window),
142         window_state_(
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
147     // control.
148     // TODO(pkotwicz): This is a hack. Remove ASAP. http://crbug.com/319048
149     window_state_->AddObserver(this);
150     window_state_->window()->AddObserver(this);
151   }
152   virtual ~NativeAppWindowStateDelegate(){
153     if (window_state_) {
154       window_state_->RemoveObserver(this);
155       window_state_->window()->RemoveObserver(this);
156     }
157   }
158
159  private:
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();
168     return true;
169   }
170
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()
181       // explicitly.
182       app_window_->OnNativeWindowChanged();
183     }
184   }
185
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;
191   }
192
193   // Not owned.
194   AppWindow* app_window_;
195   ash::wm::WindowState* window_state_;
196
197   DISALLOW_COPY_AND_ASSIGN(NativeAppWindowStateDelegate);
198 };
199 #endif  // USE_ASH
200
201 }  // namespace
202
203 NativeAppWindowViews::NativeAppWindowViews()
204     : web_view_(NULL),
205       window_(NULL),
206       is_fullscreen_(false),
207       weak_ptr_factory_(this) {
208 }
209
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());
217
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);
222   } else {
223     InitializeDefaultWindow(create_params);
224   }
225   extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews(
226       Profile::FromBrowserContext(browser_context()),
227       window_->GetFocusManager(),
228       extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
229       app_window_));
230
231   OnViewWasResized();
232   window_->AddObserver(this);
233
234 #if defined(OS_WIN)
235   if (ShouldUseChromeStyleFrame() &&
236       chrome::GetHostDesktopTypeForNativeWindow(window_->GetNativeWindow()) !=
237       chrome::HOST_DESKTOP_TYPE_ASH) {
238     InstallEasyResizeTargeterOnContainer();
239   }
240 #endif
241 }
242
243 NativeAppWindowViews::~NativeAppWindowViews() {
244   web_view_->SetWebContents(NULL);
245 }
246
247 void NativeAppWindowViews::OnBeforeWidgetInit(
248     views::Widget::InitParams* init_params,
249     views::Widget* widget) {}
250
251 void NativeAppWindowViews::InitializeDefaultWindow(
252     const AppWindow::CreateParams& create_params) {
253   std::string app_name =
254       web_app::GenerateApplicationNameFromExtensionId(extension()->id());
255
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;
264 #endif
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;
277
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);
285 #endif
286
287   OnBeforeWidgetInit(&init_params, window_);
288   window_->Init(init_params);
289
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);
297
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();
304
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));
314
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))
319       continue;
320
321     focus_manager->RegisterAccelerator(
322         iter->first, ui::AcceleratorManager::kNormalPriority, this);
323   }
324 }
325
326 void NativeAppWindowViews::InitializePanelWindow(
327     const AppWindow::CreateParams& create_params) {
328   views::Widget::InitParams params(views::Widget::InitParams::TYPE_PANEL);
329   params.delegate = this;
330
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);
337
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);
342 #if defined(USE_ASH)
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_));
348   } else {
349     params.bounds = gfx::Rect(preferred_size_);
350   }
351 #else
352   params.bounds = gfx::Rect(preferred_size_);
353 #endif
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);
359
360 #if defined(USE_ASH)
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);
372   }
373 #else
374   // TODO(stevenjb): NativeAppWindow panels need to be implemented for other
375   // platforms.
376 #endif
377 }
378
379 bool NativeAppWindowViews::ShouldUseChromeStyleFrame() const {
380   if (frameless_)
381     return true;
382
383 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
384   // Linux always uses native style frames.
385   return false;
386 #endif
387
388   return !CommandLine::ForCurrentProcess()->HasSwitch(
389       switches::kAppsUseNativeFrame);
390 }
391
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)));
398 }
399
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;
406 #if defined(USE_ASH)
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;
414   }
415 #endif
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);
422   return frame_view;
423 }
424
425 // ui::BaseWindow implementation.
426
427 bool NativeAppWindowViews::IsActive() const {
428   return window_->IsActive();
429 }
430
431 bool NativeAppWindowViews::IsMaximized() const {
432   return window_->IsMaximized();
433 }
434
435 bool NativeAppWindowViews::IsMinimized() const {
436   return window_->IsMinimized();
437 }
438
439 bool NativeAppWindowViews::IsFullscreen() const {
440   return window_->IsFullscreen();
441 }
442
443 gfx::NativeWindow NativeAppWindowViews::GetNativeWindow() {
444   return window_->GetNativeWindow();
445 }
446
447 gfx::Rect NativeAppWindowViews::GetRestoredBounds() const {
448   return window_->GetRestoredBounds();
449 }
450
451 ui::WindowShowState NativeAppWindowViews::GetRestoredState() const {
452   if (IsMaximized())
453     return ui::SHOW_STATE_MAXIMIZED;
454   if (IsFullscreen()) {
455 #if defined(USE_ASH)
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;
462     }
463 #endif
464     return ui::SHOW_STATE_FULLSCREEN;
465   }
466 #if defined(USE_ASH)
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;
479
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;
485   }
486 #endif
487   return ui::SHOW_STATE_NORMAL;
488 }
489
490 gfx::Rect NativeAppWindowViews::GetBounds() const {
491   return window_->GetWindowBoundsInScreen();
492 }
493
494 void NativeAppWindowViews::Show() {
495   if (window_->IsVisible()) {
496     window_->Activate();
497     return;
498   }
499
500   window_->Show();
501 }
502
503 void NativeAppWindowViews::ShowInactive() {
504   if (window_->IsVisible())
505     return;
506   window_->ShowInactive();
507 }
508
509 void NativeAppWindowViews::Hide() {
510   window_->Hide();
511 }
512
513 void NativeAppWindowViews::Close() {
514   window_->Close();
515 }
516
517 void NativeAppWindowViews::Activate() {
518   window_->Activate();
519 }
520
521 void NativeAppWindowViews::Deactivate() {
522   window_->Deactivate();
523 }
524
525 void NativeAppWindowViews::Maximize() {
526   window_->Maximize();
527 }
528
529 void NativeAppWindowViews::Minimize() {
530   window_->Minimize();
531 }
532
533 void NativeAppWindowViews::Restore() {
534   window_->Restore();
535 }
536
537 void NativeAppWindowViews::SetBounds(const gfx::Rect& bounds) {
538   window_->SetBounds(bounds);
539 }
540
541 void NativeAppWindowViews::FlashFrame(bool flash) {
542   window_->FlashFrame(flash);
543 }
544
545 bool NativeAppWindowViews::IsAlwaysOnTop() const {
546   if (app_window_->window_type_is_panel()) {
547 #if defined(USE_ASH)
548     return ash::wm::GetWindowState(window_->GetNativeWindow())->
549         panel_attached();
550 #else
551     return true;
552 #endif
553   } else {
554     return window_->IsAlwaysOnTop();
555   }
556 }
557
558 void NativeAppWindowViews::SetAlwaysOnTop(bool always_on_top) {
559   window_->SetAlwaysOnTop(always_on_top);
560 }
561
562 void NativeAppWindowViews::ShowContextMenuForView(
563     views::View* source,
564     const gfx::Point& p,
565     ui::MenuSourceType source_type) {
566 #if defined(USE_ASH) & defined(OS_CHROMEOS)
567   scoped_ptr<ui::MenuModel> model =
568       CreateMultiUserContextMenu(app_window_->GetNativeWindow());
569   if (!model.get())
570     return;
571
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,
582           source_type,
583           views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) ==
584         views::MenuRunner::MENU_DELETED)
585       return;
586   }
587 #endif
588 }
589
590 gfx::NativeView NativeAppWindowViews::GetHostView() const {
591   return window_->GetNativeView();
592 }
593
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);
598 }
599
600 gfx::Size NativeAppWindowViews::GetMaximumDialogSize() {
601   return window_->GetWindowBoundsInScreen().size();
602 }
603
604 void NativeAppWindowViews::AddObserver(
605     web_modal::ModalDialogHostObserver* observer) {
606   observer_list_.AddObserver(observer);
607 }
608 void NativeAppWindowViews::RemoveObserver(
609     web_modal::ModalDialogHostObserver* observer) {
610   observer_list_.RemoveObserver(observer);
611 }
612
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,
617                     observer_list_,
618                     OnPositionRequiresUpdate());
619 }
620
621 // WidgetDelegate implementation.
622
623 void NativeAppWindowViews::OnWidgetMove() {
624   app_window_->OnNativeWindowChanged();
625 }
626
627 views::View* NativeAppWindowViews::GetInitiallyFocusedView() {
628   return web_view_;
629 }
630
631 bool NativeAppWindowViews::CanResize() const {
632   return resizable_ && !app_window_->size_constraints().HasFixedSize();
633 }
634
635 bool NativeAppWindowViews::CanMaximize() const {
636   return resizable_ && !app_window_->size_constraints().HasMaximumSize() &&
637          !app_window_->window_type_is_panel();
638 }
639
640 base::string16 NativeAppWindowViews::GetWindowTitle() const {
641   return app_window_->GetTitle();
642 }
643
644 bool NativeAppWindowViews::ShouldShowWindowTitle() const {
645   return app_window_->window_type() == AppWindow::WINDOW_TYPE_V1_PANEL;
646 }
647
648 gfx::ImageSkia NativeAppWindowViews::GetWindowAppIcon() {
649   gfx::Image app_icon = app_window_->app_icon();
650   if (app_icon.IsEmpty())
651     return GetWindowIcon();
652   else
653     return *app_icon.ToImageSkia();
654 }
655
656 gfx::ImageSkia NativeAppWindowViews::GetWindowIcon() {
657   content::WebContents* web_contents = app_window_->web_contents();
658   if (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();
664   }
665   return gfx::ImageSkia();
666 }
667
668 bool NativeAppWindowViews::ShouldShowWindowIcon() const {
669   return app_window_->window_type() == AppWindow::WINDOW_TYPE_V1_PANEL;
670 }
671
672 void NativeAppWindowViews::SaveWindowPlacement(const gfx::Rect& bounds,
673                                                ui::WindowShowState show_state) {
674   views::WidgetDelegate::SaveWindowPlacement(bounds, show_state);
675   app_window_->OnNativeWindowChanged();
676 }
677
678 void NativeAppWindowViews::DeleteDelegate() {
679   window_->RemoveObserver(this);
680   app_window_->OnNativeClose();
681 }
682
683 views::Widget* NativeAppWindowViews::GetWidget() {
684   return window_;
685 }
686
687 const views::Widget* NativeAppWindowViews::GetWidget() const {
688   return window_;
689 }
690
691 views::View* NativeAppWindowViews::GetContentsView() {
692   return this;
693 }
694
695 views::NonClientFrameView* NativeAppWindowViews::CreateNonClientFrameView(
696     views::Widget* widget) {
697 #if defined(USE_ASH)
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());
704
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);
711       return frame_view;
712     }
713
714     if (!frameless_) {
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
720       // Windows Ash.
721       if (ash::switches::UseImmersiveFullscreenForAllWindows()) {
722         immersive_fullscreen_controller_.reset(
723             new ash::ImmersiveFullscreenController());
724         custom_frame_view->InitImmersiveFullscreenControllerForView(
725             immersive_fullscreen_controller_.get());
726       }
727 #endif
728       custom_frame_view->GetHeaderView()->set_context_menu_controller(this);
729       return custom_frame_view;
730     }
731   }
732 #endif
733   if (ShouldUseChromeStyleFrame())
734     return CreateAppWindowFrameView();
735   return views::WidgetDelegateView::CreateNonClientFrameView(widget);
736 }
737
738 bool NativeAppWindowViews::WidgetHasHitTestMask() const {
739   return shape_ != NULL;
740 }
741
742 void NativeAppWindowViews::GetWidgetHitTestMask(gfx::Path* mask) const {
743   shape_->getBoundaryPath(mask);
744 }
745
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
752     // region.
753     return !draggable_region_.get() ||
754            !draggable_region_->contains(location.x(), location.y());
755   }
756 #endif
757
758   return true;
759 }
760
761 // WidgetObserver implementation.
762
763 void NativeAppWindowViews::OnWidgetVisibilityChanged(views::Widget* widget,
764                                                      bool visible) {
765   app_window_->OnNativeWindowChanged();
766 }
767
768 void NativeAppWindowViews::OnWidgetActivationChanged(views::Widget* widget,
769                                                      bool active) {
770   app_window_->OnNativeWindowChanged();
771   if (active)
772     app_window_->OnNativeWindowActivated();
773 }
774
775 // WebContentsObserver implementation.
776
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.
781     SkBitmap background;
782     background.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
783     background.allocPixels();
784     background.eraseARGB(0x00, 0x00, 0x00, 0x00);
785
786     content::RenderWidgetHostView* view = render_view_host->GetView();
787     DCHECK(view);
788     view->SetBackground(background);
789   }
790 }
791
792 void NativeAppWindowViews::RenderViewHostChanged(
793     content::RenderViewHost* old_host,
794     content::RenderViewHost* new_host) {
795   OnViewWasResized();
796 }
797
798 // views::View implementation.
799
800 void NativeAppWindowViews::Layout() {
801   DCHECK(web_view_);
802   web_view_->SetBounds(0, 0, width(), height());
803   OnViewWasResized();
804 }
805
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());
812   }
813 }
814
815 gfx::Size NativeAppWindowViews::GetPreferredSize() {
816   if (!preferred_size_.IsEmpty())
817     return preferred_size_;
818   return views::View::GetPreferredSize();
819 }
820
821 gfx::Size NativeAppWindowViews::GetMinimumSize() {
822   return app_window_->size_constraints().GetMinimumSize();
823 }
824
825 gfx::Size NativeAppWindowViews::GetMaximumSize() {
826   return app_window_->size_constraints().GetMaximumSize();
827 }
828
829 void NativeAppWindowViews::OnFocus() {
830   web_view_->RequestFocus();
831 }
832
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:
843       Close();
844       return true;
845     case IDC_ZOOM_MINUS:
846       chrome_page_zoom::Zoom(web_view_->GetWebContents(),
847                              content::PAGE_ZOOM_OUT);
848       return true;
849     case IDC_ZOOM_NORMAL:
850       chrome_page_zoom::Zoom(web_view_->GetWebContents(),
851                              content::PAGE_ZOOM_RESET);
852       return true;
853     case IDC_ZOOM_PLUS:
854       chrome_page_zoom::Zoom(web_view_->GetWebContents(),
855                              content::PAGE_ZOOM_IN);
856       return true;
857     default:
858       NOTREACHED() << "Unknown accelerator sent to app window.";
859   }
860   return false;
861 }
862
863 // NativeAppWindow implementation.
864
865 void NativeAppWindowViews::SetFullscreen(int fullscreen_types) {
866   // Fullscreen not supported by panels.
867   if (app_window_->window_type_is_panel())
868     return;
869   is_fullscreen_ = (fullscreen_types != AppWindow::FULLSCREEN_TYPE_NONE);
870   window_->SetFullscreen(is_fullscreen_);
871
872 #if defined(USE_ASH)
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
880     // OS fullscreen.
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();
887   }
888 #endif
889
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().
893 }
894
895 bool NativeAppWindowViews::IsFullscreenOrPending() const {
896   return is_fullscreen_;
897 }
898
899 bool NativeAppWindowViews::IsDetached() const {
900   if (!app_window_->window_type_is_panel())
901     return false;
902 #if defined(USE_ASH)
903   return !ash::wm::GetWindowState(window_->GetNativeWindow())->panel_attached();
904 #else
905   return false;
906 #endif
907 }
908
909 void NativeAppWindowViews::UpdateWindowIcon() {
910   window_->UpdateWindowIcon();
911 }
912
913 void NativeAppWindowViews::UpdateWindowTitle() {
914   window_->UpdateWindowTitle();
915 }
916
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
922     // bitmaps.
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.";
927       return;
928     }
929   }
930   chrome::DrawTaskbarDecoration(GetNativeWindow(), icon);
931 }
932
933 void NativeAppWindowViews::UpdateDraggableRegions(
934     const std::vector<extensions::DraggableRegion>& regions) {
935   // Draggable region is not supported for non-frameless window.
936   if (!frameless_)
937     return;
938
939   draggable_region_.reset(AppWindow::RawDraggableRegionsToSkRegion(regions));
940   OnViewWasResized();
941 }
942
943 SkRegion* NativeAppWindowViews::GetDraggableRegion() {
944   return draggable_region_.get();
945 }
946
947 void NativeAppWindowViews::UpdateShape(scoped_ptr<SkRegion> region) {
948   bool had_shape = shape_;
949   shape_ = region.Pass();
950
951   aura::Window* native_window = window_->GetNativeWindow();
952   if (shape_) {
953     window_->SetShape(new SkRegion(*shape_));
954     if (!had_shape) {
955       native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
956           new ShapedAppWindowTargeter(native_window, this)));
957     }
958   } else {
959     window_->SetShape(NULL);
960     if (had_shape)
961       native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>());
962   }
963 }
964
965 void NativeAppWindowViews::HandleKeyboardEvent(
966     const content::NativeWebKeyboardEvent& event) {
967   unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
968                                                         GetFocusManager());
969 }
970
971 bool NativeAppWindowViews::IsFrameless() const {
972   return frameless_;
973 }
974
975 gfx::Insets NativeAppWindowViews::GetFrameInsets() const {
976   if (frameless_)
977     return gfx::Insets();
978
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(
987           client_bounds);
988   return window_bounds.InsetsFrom(client_bounds);
989 }
990
991 void NativeAppWindowViews::HideWithApp() {}
992 void NativeAppWindowViews::ShowWithApp() {}
993 void NativeAppWindowViews::UpdateWindowMinMaxSize() {}