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