- add sources.
[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/shell_window.h"
8 #include "apps/ui/views/shell_window_frame_view.h"
9 #include "base/command_line.h"
10 #include "base/file_util.h"
11 #include "base/path_service.h"
12 #include "base/threading/sequenced_worker_pool.h"
13 #include "chrome/app/chrome_command_ids.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/host_desktop.h"
18 #include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h"
19 #include "chrome/browser/web_applications/web_app.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/extensions/extension.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/render_view_host.h"
24 #include "content/public/browser/render_widget_host_view.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/browser/web_contents_view.h"
27 #include "extensions/common/draggable_region.h"
28 #include "ui/views/controls/webview/webview.h"
29 #include "ui/views/widget/widget.h"
30 #include "ui/views/window/non_client_view.h"
31
32 #if defined(OS_WIN)
33 #include "base/strings/utf_string_conversions.h"
34 #include "chrome/browser/ui/web_applications/web_app_ui.h"
35 #include "chrome/browser/web_applications/web_app_win.h"
36 #include "ui/base/win/shell.h"
37 #include "ui/views/win/hwnd_util.h"
38 #endif
39
40 #if defined(OS_LINUX)
41 #include "chrome/browser/shell_integration_linux.h"
42 #endif
43
44 #if defined(USE_ASH)
45 #include "ash/ash_constants.h"
46 #include "ash/screen_ash.h"
47 #include "ash/shell.h"
48 #include "ash/wm/custom_frame_view_ash.h"
49 #include "ash/wm/panels/panel_frame_view.h"
50 #include "ash/wm/window_state.h"
51 #include "ash/wm/window_state_delegate.h"
52 #include "chrome/browser/ui/ash/ash_util.h"
53 #include "ui/aura/client/aura_constants.h"
54 #include "ui/aura/client/window_tree_client.h"
55 #include "ui/aura/window.h"
56 #endif
57
58 #if defined(USE_AURA)
59 #include "ui/aura/window.h"
60 #endif
61
62 using apps::ShellWindow;
63
64 namespace {
65
66 const int kMinPanelWidth = 100;
67 const int kMinPanelHeight = 100;
68 const int kDefaultPanelWidth = 200;
69 const int kDefaultPanelHeight = 300;
70 const int kResizeInsideBoundsSize = 5;
71 const int kResizeAreaCornerSize = 16;
72
73 struct AcceleratorMapping {
74   ui::KeyboardCode keycode;
75   int modifiers;
76   int command_id;
77 };
78 const AcceleratorMapping kAppWindowAcceleratorMap[] = {
79   { ui::VKEY_W, ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
80   { ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
81   { ui::VKEY_F4, ui::EF_ALT_DOWN, IDC_CLOSE_WINDOW },
82 };
83
84 const std::map<ui::Accelerator, int>& GetAcceleratorTable() {
85   typedef std::map<ui::Accelerator, int> AcceleratorMap;
86   CR_DEFINE_STATIC_LOCAL(AcceleratorMap, accelerators, ());
87   if (accelerators.empty()) {
88     for (size_t i = 0; i < arraysize(kAppWindowAcceleratorMap); ++i) {
89       ui::Accelerator accelerator(kAppWindowAcceleratorMap[i].keycode,
90                                   kAppWindowAcceleratorMap[i].modifiers);
91       accelerators[accelerator] = kAppWindowAcceleratorMap[i].command_id;
92     }
93   }
94   return accelerators;
95 }
96
97 #if defined(OS_WIN)
98 void CreateIconAndSetRelaunchDetails(
99     const base::FilePath web_app_path,
100     const base::FilePath icon_file,
101     const ShellIntegration::ShortcutInfo& shortcut_info,
102     const HWND hwnd) {
103   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
104
105   // Set the relaunch data so "Pin this program to taskbar" has the app's
106   // information.
107   CommandLine command_line = ShellIntegration::CommandLineArgsForLauncher(
108       shortcut_info.url,
109       shortcut_info.extension_id,
110       shortcut_info.profile_path);
111
112   base::FilePath chrome_exe;
113   if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
114      NOTREACHED();
115      return;
116   }
117   command_line.SetProgram(chrome_exe);
118   ui::win::SetRelaunchDetailsForWindow(command_line.GetCommandLineString(),
119       shortcut_info.title, hwnd);
120
121   if (!base::PathExists(web_app_path) &&
122       !file_util::CreateDirectory(web_app_path)) {
123     return;
124   }
125   ui::win::SetAppIconForWindow(icon_file.value(), hwnd);
126   web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon);
127 }
128 #endif
129
130 #if defined(USE_ASH)
131 // This class handles a user's fullscreen request (Shift+F4/F4).
132 class NativeAppWindowStateDelegate : public ash::wm::WindowStateDelegate {
133  public:
134   explicit NativeAppWindowStateDelegate(ShellWindow* shell_window)
135       : shell_window_(shell_window) {
136     DCHECK(shell_window_);
137   }
138   virtual ~NativeAppWindowStateDelegate(){}
139
140   // Overridden from ash::wm::WindowStateDelegate.
141   virtual bool ToggleFullscreen(ash::wm::WindowState* window_state) OVERRIDE {
142     // Windows which cannot be maximized should not be fullscreened.
143     DCHECK(window_state->IsFullscreen() || window_state->CanMaximize());
144     if (window_state->IsFullscreen())
145       shell_window_->Restore();
146     else if (window_state->CanMaximize())
147       shell_window_->Fullscreen();
148     return true;
149   }
150
151  private:
152   ShellWindow* shell_window_;  // not owned.
153
154   DISALLOW_COPY_AND_ASSIGN(NativeAppWindowStateDelegate);
155 };
156 #endif  // USE_ASH
157
158 }  // namespace
159
160 NativeAppWindowViews::NativeAppWindowViews(
161     ShellWindow* shell_window,
162     const ShellWindow::CreateParams& create_params)
163     : shell_window_(shell_window),
164       web_view_(NULL),
165       window_(NULL),
166       is_fullscreen_(false),
167       frameless_(create_params.frame == ShellWindow::FRAME_NONE),
168       transparent_background_(create_params.transparent_background),
169       resizable_(create_params.resizable),
170       weak_ptr_factory_(this) {
171   Observe(web_contents());
172
173   window_ = new views::Widget;
174   if (create_params.window_type == ShellWindow::WINDOW_TYPE_PANEL ||
175       create_params.window_type == ShellWindow::WINDOW_TYPE_V1_PANEL) {
176     InitializePanelWindow(create_params);
177   } else {
178     InitializeDefaultWindow(create_params);
179   }
180   extension_keybinding_registry_.reset(
181       new ExtensionKeybindingRegistryViews(
182           profile(),
183           window_->GetFocusManager(),
184           extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
185           shell_window_));
186
187   OnViewWasResized();
188   window_->AddObserver(this);
189 #if defined(USE_ASH)
190   if (chrome::GetHostDesktopTypeForNativeView(GetNativeWindow()) ==
191       chrome::HOST_DESKTOP_TYPE_ASH) {
192     ash::wm::GetWindowState(GetNativeWindow())->SetDelegate(
193         scoped_ptr<ash::wm::WindowStateDelegate>(
194             new NativeAppWindowStateDelegate(shell_window)).Pass());
195   }
196 #endif
197 }
198
199 NativeAppWindowViews::~NativeAppWindowViews() {
200   web_view_->SetWebContents(NULL);
201 }
202
203 void NativeAppWindowViews::InitializeDefaultWindow(
204     const ShellWindow::CreateParams& create_params) {
205   std::string app_name =
206       web_app::GenerateApplicationNameFromExtensionId(extension()->id());
207
208   views::Widget::InitParams init_params(views::Widget::InitParams::TYPE_WINDOW);
209   init_params.delegate = this;
210   init_params.remove_standard_frame = ShouldUseChromeStyleFrame();
211   init_params.use_system_default_icon = true;
212   // TODO(erg): Conceptually, these are toplevel windows, but we theoretically
213   // could plumb context through to here in some cases.
214   init_params.top_level = true;
215   init_params.opacity = create_params.transparent_background
216                             ? views::Widget::InitParams::TRANSLUCENT_WINDOW
217                             : views::Widget::InitParams::INFER_OPACITY;
218   init_params.keep_on_top = create_params.always_on_top;
219   gfx::Rect window_bounds = create_params.bounds;
220   bool position_specified =
221       window_bounds.x() != INT_MIN && window_bounds.y() != INT_MIN;
222   if (position_specified && !window_bounds.IsEmpty())
223     init_params.bounds = window_bounds;
224
225 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
226   // Set up a custom WM_CLASS for app windows. This allows task switchers in
227   // X11 environments to distinguish them from main browser windows.
228   init_params.wm_class_name = web_app::GetWMClassFromAppName(app_name);
229   init_params.wm_class_class = ShellIntegrationLinux::GetProgramClassName();
230 #endif
231
232   window_->Init(init_params);
233
234   gfx::Rect adjusted_bounds = window_bounds;
235   adjusted_bounds.Inset(-GetFrameInsets());
236   // Center window if no position was specified.
237   if (!position_specified)
238     window_->CenterWindow(adjusted_bounds.size());
239   else if (!adjusted_bounds.IsEmpty() && adjusted_bounds != window_bounds)
240     window_->SetBounds(adjusted_bounds);
241
242   // Register accelarators supported by app windows.
243   // TODO(jeremya/stevenjb): should these be registered for panels too?
244   views::FocusManager* focus_manager = GetFocusManager();
245   const std::map<ui::Accelerator, int>& accelerator_table =
246       GetAcceleratorTable();
247   for (std::map<ui::Accelerator, int>::const_iterator iter =
248            accelerator_table.begin();
249        iter != accelerator_table.end(); ++iter) {
250     focus_manager->RegisterAccelerator(
251         iter->first, ui::AcceleratorManager::kNormalPriority, this);
252   }
253
254 #if defined(OS_WIN)
255   string16 app_name_wide = UTF8ToWide(app_name);
256   HWND hwnd = GetNativeAppWindowHWND();
257   ui::win::SetAppIdForWindow(ShellIntegration::GetAppModelIdForProfile(
258       app_name_wide, profile()->GetPath()), hwnd);
259
260   web_app::UpdateShortcutInfoAndIconForApp(
261       *extension(), profile(),
262       base::Bind(&NativeAppWindowViews::OnShortcutInfoLoaded,
263                  weak_ptr_factory_.GetWeakPtr()));
264 #endif
265 }
266
267 #if defined(OS_WIN)
268 void NativeAppWindowViews::OnShortcutInfoLoaded(
269     const ShellIntegration::ShortcutInfo& shortcut_info) {
270   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
271
272   HWND hwnd = GetNativeAppWindowHWND();
273
274   // Set window's icon to the one we're about to create/update in the web app
275   // path. The icon cache will refresh on icon creation.
276   base::FilePath web_app_path = web_app::GetWebAppDataDirectory(
277       shortcut_info.profile_path, shortcut_info.extension_id,
278       shortcut_info.url);
279   base::FilePath icon_file = web_app_path
280       .Append(web_app::internals::GetSanitizedFileName(shortcut_info.title))
281       .ReplaceExtension(FILE_PATH_LITERAL(".ico"));
282
283   content::BrowserThread::PostBlockingPoolTask(
284       FROM_HERE,
285       base::Bind(&CreateIconAndSetRelaunchDetails,
286                  web_app_path, icon_file, shortcut_info, hwnd));
287 }
288
289 HWND NativeAppWindowViews::GetNativeAppWindowHWND() const {
290   return views::HWNDForWidget(GetWidget()->GetTopLevelWidget());
291 }
292 #endif
293
294 void NativeAppWindowViews::InitializePanelWindow(
295     const ShellWindow::CreateParams& create_params) {
296   views::Widget::InitParams params(views::Widget::InitParams::TYPE_PANEL);
297   params.delegate = this;
298
299   preferred_size_ = gfx::Size(create_params.bounds.width(),
300                               create_params.bounds.height());
301   if (preferred_size_.width() == 0)
302     preferred_size_.set_width(kDefaultPanelWidth);
303   else if (preferred_size_.width() < kMinPanelWidth)
304     preferred_size_.set_width(kMinPanelWidth);
305
306   if (preferred_size_.height() == 0)
307     preferred_size_.set_height(kDefaultPanelHeight);
308   else if (preferred_size_.height() < kMinPanelHeight)
309     preferred_size_.set_height(kMinPanelHeight);
310 #if defined(USE_ASH)
311   if (ash::Shell::HasInstance()) {
312     // Open a new panel on the target root.
313     aura::Window* target = ash::Shell::GetTargetRootWindow();
314     params.bounds = ash::ScreenAsh::ConvertRectToScreen(
315         target, gfx::Rect(preferred_size_));
316   } else {
317     params.bounds = gfx::Rect(preferred_size_);
318   }
319 #else
320   params.bounds = gfx::Rect(preferred_size_);
321 #endif
322   // TODO(erg): Conceptually, these are toplevel windows, but we theoretically
323   // could plumb context through to here in some cases.
324   params.top_level = true;
325   window_->Init(params);
326   window_->set_focus_on_creation(create_params.focused);
327
328 #if defined(USE_ASH)
329   if (create_params.state == ui::SHOW_STATE_DETACHED) {
330     gfx::Rect window_bounds(create_params.bounds.x(),
331                             create_params.bounds.y(),
332                             preferred_size_.width(),
333                             preferred_size_.height());
334     aura::Window* native_window = GetNativeWindow();
335     ash::wm::GetWindowState(native_window)->set_panel_attached(false);
336     aura::client::ParentWindowWithContext(native_window,
337                                           native_window->GetRootWindow(),
338                                           native_window->GetBoundsInScreen());
339     window_->SetBounds(window_bounds);
340   }
341 #else
342   // TODO(stevenjb): NativeAppWindow panels need to be implemented for other
343   // platforms.
344 #endif
345 }
346
347 bool NativeAppWindowViews::ShouldUseChromeStyleFrame() const {
348   return !CommandLine::ForCurrentProcess()->HasSwitch(
349       switches::kAppsUseNativeFrame) || frameless_;
350 }
351
352 apps::ShellWindowFrameView* NativeAppWindowViews::CreateShellWindowFrameView() {
353   // By default the user can resize the window from slightly inside the bounds.
354   int resize_inside_bounds_size = kResizeInsideBoundsSize;
355   int resize_outside_bounds_size = 0;
356   int resize_outside_scale_for_touch = 1;
357   int resize_area_corner_size = kResizeAreaCornerSize;
358 #if defined(USE_ASH)
359   // For Aura windows on the Ash desktop the sizes are different and the user
360   // can resize the window from slightly outside the bounds as well.
361   if (chrome::IsNativeWindowInAsh(window_->GetNativeWindow())) {
362     resize_inside_bounds_size = ash::kResizeInsideBoundsSize;
363     resize_outside_bounds_size = ash::kResizeOutsideBoundsSize;
364     resize_outside_scale_for_touch = ash::kResizeOutsideBoundsScaleForTouch;
365     resize_area_corner_size = ash::kResizeAreaCornerSize;
366   }
367 #endif
368   apps::ShellWindowFrameView* frame_view = new apps::ShellWindowFrameView(this);
369   frame_view->Init(window_,
370                    resize_inside_bounds_size,
371                    resize_outside_bounds_size,
372                    resize_outside_scale_for_touch,
373                    resize_area_corner_size);
374   return frame_view;
375 }
376
377 // ui::BaseWindow implementation.
378
379 bool NativeAppWindowViews::IsActive() const {
380   return window_->IsActive();
381 }
382
383 bool NativeAppWindowViews::IsMaximized() const {
384   return window_->IsMaximized();
385 }
386
387 bool NativeAppWindowViews::IsMinimized() const {
388   return window_->IsMinimized();
389 }
390
391 bool NativeAppWindowViews::IsFullscreen() const {
392   return window_->IsFullscreen();
393 }
394
395 gfx::NativeWindow NativeAppWindowViews::GetNativeWindow() {
396   return window_->GetNativeWindow();
397 }
398
399 gfx::Rect NativeAppWindowViews::GetRestoredBounds() const {
400   return window_->GetRestoredBounds();
401 }
402
403 ui::WindowShowState NativeAppWindowViews::GetRestoredState() const {
404   if (IsMaximized())
405     return ui::SHOW_STATE_MAXIMIZED;
406   if (IsFullscreen())
407     return ui::SHOW_STATE_FULLSCREEN;
408 #if defined(USE_ASH)
409   // Use kRestoreShowStateKey in case a window is minimized/hidden.
410   ui::WindowShowState restore_state =
411       window_->GetNativeWindow()->GetProperty(
412           aura::client::kRestoreShowStateKey);
413   // Whitelist states to return so that invalid and transient states
414   // are not saved and used to restore windows when they are recreated.
415   switch (restore_state) {
416     case ui::SHOW_STATE_NORMAL:
417     case ui::SHOW_STATE_MAXIMIZED:
418     case ui::SHOW_STATE_FULLSCREEN:
419     case ui::SHOW_STATE_DETACHED:
420       return restore_state;
421
422     case ui::SHOW_STATE_DEFAULT:
423     case ui::SHOW_STATE_MINIMIZED:
424     case ui::SHOW_STATE_INACTIVE:
425     case ui::SHOW_STATE_END:
426       return ui::SHOW_STATE_NORMAL;
427   }
428 #endif
429   return ui::SHOW_STATE_NORMAL;
430 }
431
432 gfx::Rect NativeAppWindowViews::GetBounds() const {
433   return window_->GetWindowBoundsInScreen();
434 }
435
436 void NativeAppWindowViews::Show() {
437   if (window_->IsVisible()) {
438     window_->Activate();
439     return;
440   }
441
442   window_->Show();
443 }
444
445 void NativeAppWindowViews::ShowInactive() {
446   if (window_->IsVisible())
447     return;
448   window_->ShowInactive();
449 }
450
451 void NativeAppWindowViews::Hide() {
452   window_->Hide();
453 }
454
455 void NativeAppWindowViews::Close() {
456   window_->Close();
457 }
458
459 void NativeAppWindowViews::Activate() {
460   window_->Activate();
461 }
462
463 void NativeAppWindowViews::Deactivate() {
464   window_->Deactivate();
465 }
466
467 void NativeAppWindowViews::Maximize() {
468   window_->Maximize();
469 }
470
471 void NativeAppWindowViews::Minimize() {
472   window_->Minimize();
473 }
474
475 void NativeAppWindowViews::Restore() {
476   window_->Restore();
477 }
478
479 void NativeAppWindowViews::SetBounds(const gfx::Rect& bounds) {
480   GetWidget()->SetBounds(bounds);
481 }
482
483 void NativeAppWindowViews::FlashFrame(bool flash) {
484   window_->FlashFrame(flash);
485 }
486
487 bool NativeAppWindowViews::IsAlwaysOnTop() const {
488   if (shell_window_->window_type_is_panel()) {
489 #if defined(USE_ASH)
490   return ash::wm::GetWindowState(window_->GetNativeWindow())->
491       panel_attached();
492 #else
493   return true;
494 #endif
495   } else {
496     return window_->IsAlwaysOnTop();
497   }
498 }
499
500 void NativeAppWindowViews::SetAlwaysOnTop(bool always_on_top) {
501   window_->SetAlwaysOnTop(always_on_top);
502   shell_window_->OnNativeWindowChanged();
503 }
504
505 gfx::NativeView NativeAppWindowViews::GetHostView() const {
506   return window_->GetNativeView();
507 }
508
509 gfx::Point NativeAppWindowViews::GetDialogPosition(const gfx::Size& size) {
510   gfx::Size shell_window_size = window_->GetWindowBoundsInScreen().size();
511   return gfx::Point(shell_window_size.width() / 2 - size.width() / 2,
512                     shell_window_size.height() / 2 - size.height() / 2);
513 }
514
515 gfx::Size NativeAppWindowViews::GetMaximumDialogSize() {
516   return window_->GetWindowBoundsInScreen().size();
517 }
518
519 void NativeAppWindowViews::AddObserver(
520     web_modal::ModalDialogHostObserver* observer) {
521   observer_list_.AddObserver(observer);
522 }
523 void NativeAppWindowViews::RemoveObserver(
524     web_modal::ModalDialogHostObserver* observer) {
525   observer_list_.RemoveObserver(observer);
526 }
527
528 // Private method. TODO(stevenjb): Move this below InitializePanelWindow()
529 // to match declaration order.
530 void NativeAppWindowViews::OnViewWasResized() {
531   // TODO(jeremya): this doesn't seem like a terribly elegant way to keep the
532   // window shape in sync.
533 #if defined(OS_WIN) && !defined(USE_AURA)
534   DCHECK(window_);
535   DCHECK(web_view_);
536   gfx::Size sz = web_view_->size();
537   int height = sz.height(), width = sz.width();
538   if (ShouldUseChromeStyleFrame()) {
539     // Set the window shape of the RWHV.
540     const int kCornerRadius = 1;
541     gfx::Path path;
542     if (window_->IsMaximized() || window_->IsFullscreen()) {
543       // Don't round the corners when the window is maximized or fullscreen.
544       path.addRect(0, 0, width, height);
545     } else {
546       if (frameless_) {
547         path.moveTo(0, kCornerRadius);
548         path.lineTo(kCornerRadius, 0);
549         path.lineTo(width - kCornerRadius, 0);
550         path.lineTo(width, kCornerRadius);
551       } else {
552         // Don't round the top corners in chrome-style frame mode.
553         path.moveTo(0, 0);
554         path.lineTo(width, 0);
555       }
556       path.lineTo(width, height - kCornerRadius - 1);
557       path.lineTo(width - kCornerRadius - 1, height);
558       path.lineTo(kCornerRadius + 1, height);
559       path.lineTo(0, height - kCornerRadius - 1);
560       path.close();
561     }
562     SetWindowRgn(web_contents()->GetView()->GetNativeView(),
563                  path.CreateNativeRegion(), 1);
564   }
565
566   SkRegion* rgn = new SkRegion;
567   if (!window_->IsFullscreen()) {
568     if (draggable_region_)
569       rgn->op(*draggable_region_, SkRegion::kUnion_Op);
570     if (!window_->IsMaximized()) {
571       if (frameless_)
572         rgn->op(0, 0, width, kResizeInsideBoundsSize, SkRegion::kUnion_Op);
573       rgn->op(0, 0, kResizeInsideBoundsSize, height, SkRegion::kUnion_Op);
574       rgn->op(width - kResizeInsideBoundsSize, 0, width, height,
575           SkRegion::kUnion_Op);
576       rgn->op(0, height - kResizeInsideBoundsSize, width, height,
577           SkRegion::kUnion_Op);
578     }
579   }
580   if (web_contents()->GetRenderViewHost()->GetView())
581     web_contents()->GetRenderViewHost()->GetView()->SetClickthroughRegion(rgn);
582 #endif
583
584   FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
585                     observer_list_,
586                     OnPositionRequiresUpdate());
587 }
588
589 // WidgetDelegate implementation.
590
591 void NativeAppWindowViews::OnWidgetMove() {
592   shell_window_->OnNativeWindowChanged();
593 }
594
595 views::View* NativeAppWindowViews::GetInitiallyFocusedView() {
596   return web_view_;
597 }
598
599 bool NativeAppWindowViews::CanResize() const {
600   return resizable_ && !shell_window_->size_constraints().HasFixedSize();
601 }
602
603 bool NativeAppWindowViews::CanMaximize() const {
604   return resizable_ && !shell_window_->size_constraints().HasMaximumSize() &&
605       !shell_window_->window_type_is_panel();
606 }
607
608 string16 NativeAppWindowViews::GetWindowTitle() const {
609   return shell_window_->GetTitle();
610 }
611
612 bool NativeAppWindowViews::ShouldShowWindowTitle() const {
613   return shell_window_->window_type() == ShellWindow::WINDOW_TYPE_V1_PANEL;
614 }
615
616 gfx::ImageSkia NativeAppWindowViews::GetWindowAppIcon() {
617   gfx::Image app_icon = shell_window_->app_icon();
618   if (app_icon.IsEmpty())
619     return GetWindowIcon();
620   else
621     return *app_icon.ToImageSkia();
622 }
623
624 gfx::ImageSkia NativeAppWindowViews::GetWindowIcon() {
625   content::WebContents* web_contents = shell_window_->web_contents();
626   if (web_contents) {
627     FaviconTabHelper* favicon_tab_helper =
628         FaviconTabHelper::FromWebContents(web_contents);
629     gfx::Image app_icon = favicon_tab_helper->GetFavicon();
630     if (!app_icon.IsEmpty())
631       return *app_icon.ToImageSkia();
632   }
633   return gfx::ImageSkia();
634 }
635
636 bool NativeAppWindowViews::ShouldShowWindowIcon() const {
637   return shell_window_->window_type() == ShellWindow::WINDOW_TYPE_V1_PANEL;
638 }
639
640 void NativeAppWindowViews::SaveWindowPlacement(const gfx::Rect& bounds,
641                                                ui::WindowShowState show_state) {
642   views::WidgetDelegate::SaveWindowPlacement(bounds, show_state);
643   shell_window_->OnNativeWindowChanged();
644 }
645
646 void NativeAppWindowViews::DeleteDelegate() {
647   window_->RemoveObserver(this);
648   shell_window_->OnNativeClose();
649 }
650
651 views::Widget* NativeAppWindowViews::GetWidget() {
652   return window_;
653 }
654
655 const views::Widget* NativeAppWindowViews::GetWidget() const {
656   return window_;
657 }
658
659 views::View* NativeAppWindowViews::GetContentsView() {
660   return this;
661 }
662
663 views::NonClientFrameView* NativeAppWindowViews::CreateNonClientFrameView(
664     views::Widget* widget) {
665 #if defined(USE_ASH)
666   if (chrome::IsNativeViewInAsh(widget->GetNativeView())) {
667     if (shell_window_->window_type_is_panel()) {
668       ash::PanelFrameView::FrameType frame_type = frameless_ ?
669           ash::PanelFrameView::FRAME_NONE : ash::PanelFrameView::FRAME_ASH;
670       return new ash::PanelFrameView(widget, frame_type);
671     }
672     if (!frameless_) {
673       return new ash::CustomFrameViewAsh(widget);
674     }
675   }
676 #endif
677   if (ShouldUseChromeStyleFrame())
678     return CreateShellWindowFrameView();
679   return views::WidgetDelegateView::CreateNonClientFrameView(widget);
680 }
681
682 bool NativeAppWindowViews::WidgetHasHitTestMask() const {
683   return input_region_ != NULL;
684 }
685
686 void NativeAppWindowViews::GetWidgetHitTestMask(gfx::Path* mask) const {
687   input_region_->getBoundaryPath(mask);
688 }
689
690 bool NativeAppWindowViews::ShouldDescendIntoChildForEventHandling(
691     gfx::NativeView child,
692     const gfx::Point& location) {
693 #if defined(USE_AURA)
694   if (child->Contains(web_view_->web_contents()->GetView()->GetNativeView())) {
695     // Shell window should claim mouse events that fall within the draggable
696     // region.
697     return !draggable_region_.get() ||
698            !draggable_region_->contains(location.x(), location.y());
699   }
700 #endif
701
702   return true;
703 }
704
705 // WidgetObserver implementation.
706
707 void NativeAppWindowViews::OnWidgetVisibilityChanged(views::Widget* widget,
708                                                      bool visible) {
709   shell_window_->OnNativeWindowChanged();
710 }
711
712 void NativeAppWindowViews::OnWidgetActivationChanged(views::Widget* widget,
713                                                      bool active) {
714   shell_window_->OnNativeWindowChanged();
715   if (active)
716     shell_window_->OnNativeWindowActivated();
717 }
718
719 // WebContentsObserver implementation.
720
721 void NativeAppWindowViews::RenderViewCreated(
722     content::RenderViewHost* render_view_host) {
723   if (transparent_background_) {
724     // Use a background with transparency to trigger transparency in Webkit.
725     SkBitmap background;
726     background.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
727     background.allocPixels();
728     background.eraseARGB(0x00, 0x00, 0x00, 0x00);
729
730     content::RenderWidgetHostView* view = render_view_host->GetView();
731     DCHECK(view);
732     view->SetBackground(background);
733   }
734 }
735
736 void NativeAppWindowViews::RenderViewHostChanged(
737     content::RenderViewHost* old_host,
738     content::RenderViewHost* new_host) {
739   OnViewWasResized();
740 }
741
742 // views::View implementation.
743
744 void NativeAppWindowViews::Layout() {
745   DCHECK(web_view_);
746   web_view_->SetBounds(0, 0, width(), height());
747   OnViewWasResized();
748 }
749
750 void NativeAppWindowViews::ViewHierarchyChanged(
751     const ViewHierarchyChangedDetails& details) {
752   if (details.is_add && details.child == this) {
753     web_view_ = new views::WebView(NULL);
754     AddChildView(web_view_);
755     web_view_->SetWebContents(web_contents());
756   }
757 }
758
759 gfx::Size NativeAppWindowViews::GetPreferredSize() {
760   if (!preferred_size_.IsEmpty())
761     return preferred_size_;
762   return views::View::GetPreferredSize();
763 }
764
765 gfx::Size NativeAppWindowViews::GetMinimumSize() {
766   return shell_window_->size_constraints().GetMinimumSize();
767 }
768
769 gfx::Size NativeAppWindowViews::GetMaximumSize() {
770   return shell_window_->size_constraints().GetMaximumSize();
771 }
772
773 void NativeAppWindowViews::OnFocus() {
774   web_view_->RequestFocus();
775 }
776
777 bool NativeAppWindowViews::AcceleratorPressed(
778     const ui::Accelerator& accelerator) {
779   const std::map<ui::Accelerator, int>& accelerator_table =
780       GetAcceleratorTable();
781   std::map<ui::Accelerator, int>::const_iterator iter =
782       accelerator_table.find(accelerator);
783   DCHECK(iter != accelerator_table.end());
784   int command_id = iter->second;
785   switch (command_id) {
786     case IDC_CLOSE_WINDOW:
787       Close();
788       return true;
789     default:
790       NOTREACHED() << "Unknown accelerator sent to app window.";
791   }
792   return false;
793 }
794
795 // NativeAppWindow implementation.
796
797 void NativeAppWindowViews::SetFullscreen(bool fullscreen) {
798   // Fullscreen not supported by panels.
799   if (shell_window_->window_type_is_panel())
800     return;
801   is_fullscreen_ = fullscreen;
802   window_->SetFullscreen(fullscreen);
803   // TODO(jeremya) we need to call RenderViewHost::ExitFullscreen() if we
804   // ever drop the window out of fullscreen in response to something that
805   // wasn't the app calling webkitCancelFullScreen().
806 }
807
808 bool NativeAppWindowViews::IsFullscreenOrPending() const {
809   return is_fullscreen_;
810 }
811
812 bool NativeAppWindowViews::IsDetached() const {
813   if (!shell_window_->window_type_is_panel())
814     return false;
815 #if defined(USE_ASH)
816   return !ash::wm::GetWindowState(window_->GetNativeWindow())->panel_attached();
817 #else
818   return false;
819 #endif
820 }
821
822 void NativeAppWindowViews::UpdateWindowIcon() {
823   window_->UpdateWindowIcon();
824 }
825
826 void NativeAppWindowViews::UpdateWindowTitle() {
827   window_->UpdateWindowTitle();
828 }
829
830 void NativeAppWindowViews::UpdateDraggableRegions(
831     const std::vector<extensions::DraggableRegion>& regions) {
832   // Draggable region is not supported for non-frameless window.
833   if (!frameless_)
834     return;
835
836   draggable_region_.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions));
837   OnViewWasResized();
838 }
839
840 SkRegion* NativeAppWindowViews::GetDraggableRegion() {
841   return draggable_region_.get();
842 }
843
844 void NativeAppWindowViews::UpdateInputRegion(scoped_ptr<SkRegion> region) {
845   input_region_ = region.Pass();
846
847 #if defined(USE_AURA)
848   if (input_region_)
849     window_->SetShape(new SkRegion(*input_region_));
850   else
851     window_->SetShape(NULL);
852 #endif // defined(USE_AURA)
853 }
854
855 void NativeAppWindowViews::HandleKeyboardEvent(
856     const content::NativeWebKeyboardEvent& event) {
857   unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
858                                                         GetFocusManager());
859 }
860
861 bool NativeAppWindowViews::IsFrameless() const {
862   return frameless_;
863 }
864
865 gfx::Insets NativeAppWindowViews::GetFrameInsets() const {
866   if (frameless_)
867     return gfx::Insets();
868
869   // The pretend client_bounds passed in need to be large enough to ensure that
870   // GetWindowBoundsForClientBounds() doesn't decide that it needs more than
871   // the specified amount of space to fit the window controls in, and return a
872   // number larger than the real frame insets. Most window controls are smaller
873   // than 1000x1000px, so this should be big enough.
874   gfx::Rect client_bounds = gfx::Rect(1000, 1000);
875   gfx::Rect window_bounds =
876       window_->non_client_view()->GetWindowBoundsForClientBounds(
877           client_bounds);
878   return window_bounds.InsetsFrom(client_bounds);
879 }
880
881 void NativeAppWindowViews::HideWithApp() {}
882 void NativeAppWindowViews::ShowWithApp() {}
883 void NativeAppWindowViews::UpdateWindowMinMaxSize() {}