NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / ui / views / apps / native_app_window_views.cc
blob47a8ec649863fdd31be1a049203976e4497cb269
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/apps/native_app_window_views.h"
7 #include "apps/app_window.h"
8 #include "apps/ui/views/app_window_frame_view.h"
9 #include "base/command_line.h"
10 #include "base/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/app_mode/app_mode_utils.h"
15 #include "chrome/browser/chrome_page_zoom.h"
16 #include "chrome/browser/extensions/extension_host.h"
17 #include "chrome/browser/favicon/favicon_tab_helper.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/ash/multi_user/multi_user_context_menu.h"
20 #include "chrome/browser/ui/host_desktop.h"
21 #include "chrome/browser/ui/views/apps/shaped_app_window_targeter.h"
22 #include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h"
23 #include "chrome/browser/ui/views/frame/taskbar_decorator.h"
24 #include "chrome/browser/web_applications/web_app.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/render_view_host.h"
28 #include "content/public/browser/render_widget_host_view.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/browser/web_contents_view.h"
31 #include "extensions/common/draggable_region.h"
32 #include "extensions/common/extension.h"
33 #include "ui/base/hit_test.h"
34 #include "ui/base/models/simple_menu_model.h"
35 #include "ui/views/controls/menu/menu_runner.h"
36 #include "ui/views/controls/webview/webview.h"
37 #include "ui/views/widget/widget.h"
38 #include "ui/views/window/non_client_view.h"
40 #if defined(OS_WIN)
41 #include "base/strings/utf_string_conversions.h"
42 #include "chrome/browser/ui/web_applications/web_app_ui.h"
43 #include "chrome/browser/web_applications/web_app_win.h"
44 #include "ui/base/win/shell.h"
45 #include "ui/views/win/hwnd_util.h"
46 #endif
48 #if defined(OS_LINUX)
49 #include "chrome/browser/shell_integration_linux.h"
50 #endif
52 #if defined(USE_ASH)
53 #include "ash/ash_constants.h"
54 #include "ash/ash_switches.h"
55 #include "ash/screen_util.h"
56 #include "ash/shell.h"
57 #include "ash/wm/custom_frame_view_ash.h"
58 #include "ash/wm/immersive_fullscreen_controller.h"
59 #include "ash/wm/panels/panel_frame_view.h"
60 #include "ash/wm/window_state.h"
61 #include "ash/wm/window_state_delegate.h"
62 #include "ash/wm/window_state_observer.h"
63 #include "chrome/browser/ui/ash/ash_util.h"
64 #include "ui/aura/client/aura_constants.h"
65 #include "ui/aura/client/window_tree_client.h"
66 #include "ui/aura/window.h"
67 #include "ui/aura/window_observer.h"
68 #endif
70 #if defined(USE_AURA)
71 #include "ui/aura/window.h"
72 #endif
74 using apps::AppWindow;
76 namespace {
78 const int kMinPanelWidth = 100;
79 const int kMinPanelHeight = 100;
80 const int kDefaultPanelWidth = 200;
81 const int kDefaultPanelHeight = 300;
82 const int kResizeInsideBoundsSize = 5;
83 const int kResizeAreaCornerSize = 16;
85 struct AcceleratorMapping {
86 ui::KeyboardCode keycode;
87 int modifiers;
88 int command_id;
91 const AcceleratorMapping kAppWindowAcceleratorMap[] = {
92 { ui::VKEY_W, ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
93 { ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
94 { ui::VKEY_F4, ui::EF_ALT_DOWN, IDC_CLOSE_WINDOW },
97 // These accelerators will only be available in kiosk mode. These allow the
98 // user to manually zoom app windows. This is only necessary in kiosk mode
99 // (in normal mode, the user can zoom via the screen magnifier).
100 // TODO(xiyuan): Write a test for kiosk accelerators.
101 const AcceleratorMapping kAppWindowKioskAppModeAcceleratorMap[] = {
102 { ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
103 { ui::VKEY_OEM_MINUS, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
104 IDC_ZOOM_MINUS },
105 { ui::VKEY_SUBTRACT, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
106 { ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
107 { ui::VKEY_OEM_PLUS, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
108 { ui::VKEY_ADD, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
109 { ui::VKEY_0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL },
110 { ui::VKEY_NUMPAD0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL },
113 void AddAcceleratorsFromMapping(const AcceleratorMapping mapping[],
114 size_t mapping_length,
115 std::map<ui::Accelerator, int>* accelerators) {
116 for (size_t i = 0; i < mapping_length; ++i) {
117 ui::Accelerator accelerator(mapping[i].keycode, mapping[i].modifiers);
118 (*accelerators)[accelerator] = mapping[i].command_id;
122 const std::map<ui::Accelerator, int>& GetAcceleratorTable() {
123 typedef std::map<ui::Accelerator, int> AcceleratorMap;
124 CR_DEFINE_STATIC_LOCAL(AcceleratorMap, accelerators, ());
125 if (accelerators.empty()) {
126 AddAcceleratorsFromMapping(
127 kAppWindowAcceleratorMap,
128 arraysize(kAppWindowAcceleratorMap),
129 &accelerators);
131 // Add accelerators for kiosk mode.
132 if (chrome::IsRunningInForcedAppMode()) {
133 AddAcceleratorsFromMapping(
134 kAppWindowKioskAppModeAcceleratorMap,
135 arraysize(kAppWindowKioskAppModeAcceleratorMap),
136 &accelerators);
139 return accelerators;
142 #if defined(OS_WIN)
143 void CreateIconAndSetRelaunchDetails(
144 const base::FilePath web_app_path,
145 const base::FilePath icon_file,
146 const ShellIntegration::ShortcutInfo& shortcut_info,
147 const HWND hwnd) {
148 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
150 // Set the relaunch data so "Pin this program to taskbar" has the app's
151 // information.
152 CommandLine command_line = ShellIntegration::CommandLineArgsForLauncher(
153 shortcut_info.url,
154 shortcut_info.extension_id,
155 shortcut_info.profile_path);
157 base::FilePath chrome_exe;
158 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
159 NOTREACHED();
160 return;
162 command_line.SetProgram(chrome_exe);
163 ui::win::SetRelaunchDetailsForWindow(command_line.GetCommandLineString(),
164 shortcut_info.title, hwnd);
166 if (!base::PathExists(web_app_path) &&
167 !base::CreateDirectory(web_app_path))
168 return;
170 ui::win::SetAppIconForWindow(icon_file.value(), hwnd);
171 web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon);
173 #endif
175 #if defined(USE_ASH)
176 // This class handles a user's fullscreen request (Shift+F4/F4).
177 class NativeAppWindowStateDelegate : public ash::wm::WindowStateDelegate,
178 public ash::wm::WindowStateObserver,
179 public aura::WindowObserver {
180 public:
181 NativeAppWindowStateDelegate(AppWindow* app_window,
182 apps::NativeAppWindow* native_app_window)
183 : app_window_(app_window),
184 window_state_(
185 ash::wm::GetWindowState(native_app_window->GetNativeWindow())) {
186 // Add a window state observer to exit fullscreen properly in case
187 // fullscreen is exited without going through AppWindow::Restore(). This
188 // is the case when exiting immersive fullscreen via the "Restore" window
189 // control.
190 // TODO(pkotwicz): This is a hack. Remove ASAP. http://crbug.com/319048
191 window_state_->AddObserver(this);
192 window_state_->window()->AddObserver(this);
194 virtual ~NativeAppWindowStateDelegate(){
195 if (window_state_) {
196 window_state_->RemoveObserver(this);
197 window_state_->window()->RemoveObserver(this);
201 private:
202 // Overridden from ash::wm::WindowStateDelegate.
203 virtual bool ToggleFullscreen(ash::wm::WindowState* window_state) OVERRIDE {
204 // Windows which cannot be maximized should not be fullscreened.
205 DCHECK(window_state->IsFullscreen() || window_state->CanMaximize());
206 if (window_state->IsFullscreen())
207 app_window_->Restore();
208 else if (window_state->CanMaximize())
209 app_window_->OSFullscreen();
210 return true;
213 // Overridden from ash::wm::WindowStateObserver:
214 virtual void OnPostWindowShowTypeChange(
215 ash::wm::WindowState* window_state,
216 ash::wm::WindowShowType old_type) OVERRIDE {
217 if (!window_state->IsFullscreen() && !window_state->IsMinimized() &&
218 app_window_->GetBaseWindow()->IsFullscreenOrPending()) {
219 app_window_->Restore();
220 // Usually OnNativeWindowChanged() is called when the window bounds are
221 // changed as a result of a show type change. Because the change in show
222 // type has already occurred, we need to call OnNativeWindowChanged()
223 // explicitly.
224 app_window_->OnNativeWindowChanged();
228 // Overridden from aura::WindowObserver:
229 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
230 window_state_->RemoveObserver(this);
231 window_state_->window()->RemoveObserver(this);
232 window_state_ = NULL;
235 // Not owned.
236 AppWindow* app_window_;
237 ash::wm::WindowState* window_state_;
239 DISALLOW_COPY_AND_ASSIGN(NativeAppWindowStateDelegate);
241 #endif // USE_ASH
243 } // namespace
245 NativeAppWindowViews::NativeAppWindowViews()
246 : web_view_(NULL),
247 window_(NULL),
248 is_fullscreen_(false),
249 weak_ptr_factory_(this) {
252 void NativeAppWindowViews::Init(apps::AppWindow* app_window,
253 const AppWindow::CreateParams& create_params) {
254 app_window_ = app_window;
255 frameless_ = create_params.frame == AppWindow::FRAME_NONE;
256 transparent_background_ = create_params.transparent_background;
257 resizable_ = create_params.resizable;
258 Observe(web_contents());
260 window_ = new views::Widget;
261 if (create_params.window_type == AppWindow::WINDOW_TYPE_PANEL ||
262 create_params.window_type == AppWindow::WINDOW_TYPE_V1_PANEL) {
263 InitializePanelWindow(create_params);
264 } else {
265 InitializeDefaultWindow(create_params);
267 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews(
268 Profile::FromBrowserContext(browser_context()),
269 window_->GetFocusManager(),
270 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
271 app_window_));
273 OnViewWasResized();
274 window_->AddObserver(this);
277 NativeAppWindowViews::~NativeAppWindowViews() {
278 web_view_->SetWebContents(NULL);
281 void NativeAppWindowViews::OnBeforeWidgetInit(
282 views::Widget::InitParams* init_params,
283 views::Widget* widget) {}
285 void NativeAppWindowViews::InitializeDefaultWindow(
286 const AppWindow::CreateParams& create_params) {
287 std::string app_name =
288 web_app::GenerateApplicationNameFromExtensionId(extension()->id());
290 views::Widget::InitParams init_params(views::Widget::InitParams::TYPE_WINDOW);
291 init_params.delegate = this;
292 init_params.remove_standard_frame = ShouldUseChromeStyleFrame();
293 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
294 // On Linux, remove the standard frame. Instead, we will use CustomFrameView
295 // to draw a native-like frame.
296 // TODO(mgiuca): Remove this during fix for http://crbug.com/322256.
297 init_params.remove_standard_frame = true;
298 #endif
299 init_params.use_system_default_icon = true;
300 // TODO(erg): Conceptually, these are toplevel windows, but we theoretically
301 // could plumb context through to here in some cases.
302 init_params.top_level = true;
303 if (create_params.transparent_background)
304 init_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
305 init_params.keep_on_top = create_params.always_on_top;
306 gfx::Rect window_bounds = create_params.bounds;
307 bool position_specified =
308 window_bounds.x() != INT_MIN && window_bounds.y() != INT_MIN;
309 if (position_specified && !window_bounds.IsEmpty())
310 init_params.bounds = window_bounds;
312 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
313 // Set up a custom WM_CLASS for app windows. This allows task switchers in
314 // X11 environments to distinguish them from main browser windows.
315 init_params.wm_class_name = web_app::GetWMClassFromAppName(app_name);
316 init_params.wm_class_class = ShellIntegrationLinux::GetProgramClassName();
317 const char kX11WindowRoleApp[] = "app";
318 init_params.wm_role_name = std::string(kX11WindowRoleApp);
319 #endif
321 OnBeforeWidgetInit(&init_params, window_);
322 window_->Init(init_params);
324 gfx::Rect adjusted_bounds = window_bounds;
325 adjusted_bounds.Inset(-GetFrameInsets());
326 // Center window if no position was specified.
327 if (!position_specified)
328 window_->CenterWindow(adjusted_bounds.size());
329 else if (!adjusted_bounds.IsEmpty() && adjusted_bounds != window_bounds)
330 window_->SetBounds(adjusted_bounds);
332 // Register accelarators supported by app windows.
333 // TODO(jeremya/stevenjb): should these be registered for panels too?
334 views::FocusManager* focus_manager = GetFocusManager();
335 const std::map<ui::Accelerator, int>& accelerator_table =
336 GetAcceleratorTable();
337 const bool is_kiosk_app_mode = chrome::IsRunningInForcedAppMode();
339 // Ensures that kiosk mode accelerators are enabled when in kiosk mode (to be
340 // future proof). This is needed because GetAcceleratorTable() uses a static
341 // to store data and only checks kiosk mode once. If a platform app is
342 // launched before kiosk mode starts, the kiosk accelerators will not be
343 // registered. This DCHECK catches the case.
344 DCHECK(!is_kiosk_app_mode ||
345 accelerator_table.size() ==
346 arraysize(kAppWindowAcceleratorMap) +
347 arraysize(kAppWindowKioskAppModeAcceleratorMap));
349 for (std::map<ui::Accelerator, int>::const_iterator iter =
350 accelerator_table.begin();
351 iter != accelerator_table.end(); ++iter) {
352 if (is_kiosk_app_mode && !chrome::IsCommandAllowedInAppMode(iter->second))
353 continue;
355 focus_manager->RegisterAccelerator(
356 iter->first, ui::AcceleratorManager::kNormalPriority, this);
359 #if defined(OS_WIN)
360 base::string16 app_name_wide = base::UTF8ToWide(app_name);
361 HWND hwnd = GetNativeAppWindowHWND();
362 ui::win::SetAppIdForWindow(
363 ShellIntegration::GetAppModelIdForProfile(
364 app_name_wide,
365 Profile::FromBrowserContext(browser_context())->GetPath()),
366 hwnd);
368 web_app::UpdateShortcutInfoAndIconForApp(
369 *extension(),
370 Profile::FromBrowserContext(browser_context()),
371 base::Bind(&NativeAppWindowViews::OnShortcutInfoLoaded,
372 weak_ptr_factory_.GetWeakPtr()));
373 #endif
376 #if defined(OS_WIN)
377 void NativeAppWindowViews::OnShortcutInfoLoaded(
378 const ShellIntegration::ShortcutInfo& shortcut_info) {
379 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
381 HWND hwnd = GetNativeAppWindowHWND();
383 // Set window's icon to the one we're about to create/update in the web app
384 // path. The icon cache will refresh on icon creation.
385 base::FilePath web_app_path = web_app::GetWebAppDataDirectory(
386 shortcut_info.profile_path, shortcut_info.extension_id,
387 shortcut_info.url);
388 base::FilePath icon_file = web_app_path
389 .Append(web_app::internals::GetSanitizedFileName(shortcut_info.title))
390 .ReplaceExtension(FILE_PATH_LITERAL(".ico"));
392 content::BrowserThread::PostBlockingPoolTask(
393 FROM_HERE,
394 base::Bind(&CreateIconAndSetRelaunchDetails,
395 web_app_path, icon_file, shortcut_info, hwnd));
398 HWND NativeAppWindowViews::GetNativeAppWindowHWND() const {
399 return views::HWNDForWidget(window_->GetTopLevelWidget());
401 #endif
403 void NativeAppWindowViews::InitializePanelWindow(
404 const AppWindow::CreateParams& create_params) {
405 views::Widget::InitParams params(views::Widget::InitParams::TYPE_PANEL);
406 params.delegate = this;
408 preferred_size_ = gfx::Size(create_params.bounds.width(),
409 create_params.bounds.height());
410 if (preferred_size_.width() == 0)
411 preferred_size_.set_width(kDefaultPanelWidth);
412 else if (preferred_size_.width() < kMinPanelWidth)
413 preferred_size_.set_width(kMinPanelWidth);
415 if (preferred_size_.height() == 0)
416 preferred_size_.set_height(kDefaultPanelHeight);
417 else if (preferred_size_.height() < kMinPanelHeight)
418 preferred_size_.set_height(kMinPanelHeight);
419 #if defined(USE_ASH)
420 if (ash::Shell::HasInstance()) {
421 // Open a new panel on the target root.
422 aura::Window* target = ash::Shell::GetTargetRootWindow();
423 params.bounds = ash::ScreenUtil::ConvertRectToScreen(
424 target, gfx::Rect(preferred_size_));
425 } else {
426 params.bounds = gfx::Rect(preferred_size_);
428 #else
429 params.bounds = gfx::Rect(preferred_size_);
430 #endif
431 // TODO(erg): Conceptually, these are toplevel windows, but we theoretically
432 // could plumb context through to here in some cases.
433 params.top_level = true;
434 window_->Init(params);
435 window_->set_focus_on_creation(create_params.focused);
437 #if defined(USE_ASH)
438 if (create_params.state == ui::SHOW_STATE_DETACHED) {
439 gfx::Rect window_bounds(create_params.bounds.x(),
440 create_params.bounds.y(),
441 preferred_size_.width(),
442 preferred_size_.height());
443 aura::Window* native_window = GetNativeWindow();
444 ash::wm::GetWindowState(native_window)->set_panel_attached(false);
445 aura::client::ParentWindowWithContext(native_window,
446 native_window->GetRootWindow(),
447 native_window->GetBoundsInScreen());
448 window_->SetBounds(window_bounds);
450 #else
451 // TODO(stevenjb): NativeAppWindow panels need to be implemented for other
452 // platforms.
453 #endif
456 bool NativeAppWindowViews::ShouldUseChromeStyleFrame() const {
457 if (frameless_)
458 return true;
460 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
461 // Linux always uses native style frames.
462 return false;
463 #endif
465 return !CommandLine::ForCurrentProcess()->HasSwitch(
466 switches::kAppsUseNativeFrame);
469 apps::AppWindowFrameView* NativeAppWindowViews::CreateAppWindowFrameView() {
470 // By default the user can resize the window from slightly inside the bounds.
471 int resize_inside_bounds_size = kResizeInsideBoundsSize;
472 int resize_outside_bounds_size = 0;
473 int resize_outside_scale_for_touch = 1;
474 int resize_area_corner_size = kResizeAreaCornerSize;
475 #if defined(USE_ASH)
476 // For Aura windows on the Ash desktop the sizes are different and the user
477 // can resize the window from slightly outside the bounds as well.
478 if (chrome::IsNativeWindowInAsh(window_->GetNativeWindow())) {
479 resize_inside_bounds_size = ash::kResizeInsideBoundsSize;
480 resize_outside_bounds_size = ash::kResizeOutsideBoundsSize;
481 resize_outside_scale_for_touch = ash::kResizeOutsideBoundsScaleForTouch;
482 resize_area_corner_size = ash::kResizeAreaCornerSize;
484 #endif
485 apps::AppWindowFrameView* frame_view = new apps::AppWindowFrameView(this);
486 frame_view->Init(window_,
487 resize_inside_bounds_size,
488 resize_outside_bounds_size,
489 resize_outside_scale_for_touch,
490 resize_area_corner_size);
491 return frame_view;
494 // ui::BaseWindow implementation.
496 bool NativeAppWindowViews::IsActive() const {
497 return window_->IsActive();
500 bool NativeAppWindowViews::IsMaximized() const {
501 return window_->IsMaximized();
504 bool NativeAppWindowViews::IsMinimized() const {
505 return window_->IsMinimized();
508 bool NativeAppWindowViews::IsFullscreen() const {
509 return window_->IsFullscreen();
512 gfx::NativeWindow NativeAppWindowViews::GetNativeWindow() {
513 return window_->GetNativeWindow();
516 gfx::Rect NativeAppWindowViews::GetRestoredBounds() const {
517 return window_->GetRestoredBounds();
520 ui::WindowShowState NativeAppWindowViews::GetRestoredState() const {
521 if (IsMaximized())
522 return ui::SHOW_STATE_MAXIMIZED;
523 if (IsFullscreen()) {
524 #if defined(USE_ASH)
525 if (immersive_fullscreen_controller_.get() &&
526 immersive_fullscreen_controller_->IsEnabled()) {
527 // Restore windows which were previously in immersive fullscreen to
528 // maximized. Restoring the window to a different fullscreen type
529 // makes for a bad experience.
530 return ui::SHOW_STATE_MAXIMIZED;
532 #endif
533 return ui::SHOW_STATE_FULLSCREEN;
535 #if defined(USE_ASH)
536 // Use kRestoreShowStateKey in case a window is minimized/hidden.
537 ui::WindowShowState restore_state =
538 window_->GetNativeWindow()->GetProperty(
539 aura::client::kRestoreShowStateKey);
540 // Whitelist states to return so that invalid and transient states
541 // are not saved and used to restore windows when they are recreated.
542 switch (restore_state) {
543 case ui::SHOW_STATE_NORMAL:
544 case ui::SHOW_STATE_MAXIMIZED:
545 case ui::SHOW_STATE_FULLSCREEN:
546 case ui::SHOW_STATE_DETACHED:
547 return restore_state;
549 case ui::SHOW_STATE_DEFAULT:
550 case ui::SHOW_STATE_MINIMIZED:
551 case ui::SHOW_STATE_INACTIVE:
552 case ui::SHOW_STATE_END:
553 return ui::SHOW_STATE_NORMAL;
555 #endif
556 return ui::SHOW_STATE_NORMAL;
559 gfx::Rect NativeAppWindowViews::GetBounds() const {
560 return window_->GetWindowBoundsInScreen();
563 void NativeAppWindowViews::Show() {
564 if (window_->IsVisible()) {
565 window_->Activate();
566 return;
569 window_->Show();
572 void NativeAppWindowViews::ShowInactive() {
573 if (window_->IsVisible())
574 return;
575 window_->ShowInactive();
578 void NativeAppWindowViews::Hide() {
579 window_->Hide();
582 void NativeAppWindowViews::Close() {
583 window_->Close();
586 void NativeAppWindowViews::Activate() {
587 window_->Activate();
590 void NativeAppWindowViews::Deactivate() {
591 window_->Deactivate();
594 void NativeAppWindowViews::Maximize() {
595 window_->Maximize();
598 void NativeAppWindowViews::Minimize() {
599 window_->Minimize();
602 void NativeAppWindowViews::Restore() {
603 window_->Restore();
606 void NativeAppWindowViews::SetBounds(const gfx::Rect& bounds) {
607 window_->SetBounds(bounds);
610 void NativeAppWindowViews::FlashFrame(bool flash) {
611 window_->FlashFrame(flash);
614 bool NativeAppWindowViews::IsAlwaysOnTop() const {
615 if (app_window_->window_type_is_panel()) {
616 #if defined(USE_ASH)
617 return ash::wm::GetWindowState(window_->GetNativeWindow())->
618 panel_attached();
619 #else
620 return true;
621 #endif
622 } else {
623 return window_->IsAlwaysOnTop();
627 void NativeAppWindowViews::SetAlwaysOnTop(bool always_on_top) {
628 window_->SetAlwaysOnTop(always_on_top);
631 void NativeAppWindowViews::ShowContextMenuForView(
632 views::View* source,
633 const gfx::Point& p,
634 ui::MenuSourceType source_type) {
635 #if defined(USE_ASH) & defined(OS_CHROMEOS)
636 scoped_ptr<ui::MenuModel> model =
637 CreateMultiUserContextMenu(app_window_->GetNativeWindow());
638 if (!model.get())
639 return;
641 // Only show context menu if point is in caption.
642 gfx::Point point_in_view_coords(p);
643 views::View::ConvertPointFromScreen(window_->non_client_view(),
644 &point_in_view_coords);
645 int hit_test = window_->non_client_view()->NonClientHitTest(
646 point_in_view_coords);
647 if (hit_test == HTCAPTION) {
648 menu_runner_.reset(new views::MenuRunner(model.get()));
649 if (menu_runner_->RunMenuAt(source->GetWidget(), NULL,
650 gfx::Rect(p, gfx::Size(0,0)), views::MenuItemView::TOPLEFT,
651 source_type,
652 views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) ==
653 views::MenuRunner::MENU_DELETED)
654 return;
656 #endif
659 gfx::NativeView NativeAppWindowViews::GetHostView() const {
660 return window_->GetNativeView();
663 gfx::Point NativeAppWindowViews::GetDialogPosition(const gfx::Size& size) {
664 gfx::Size app_window_size = window_->GetWindowBoundsInScreen().size();
665 return gfx::Point(app_window_size.width() / 2 - size.width() / 2,
666 app_window_size.height() / 2 - size.height() / 2);
669 gfx::Size NativeAppWindowViews::GetMaximumDialogSize() {
670 return window_->GetWindowBoundsInScreen().size();
673 void NativeAppWindowViews::AddObserver(
674 web_modal::ModalDialogHostObserver* observer) {
675 observer_list_.AddObserver(observer);
677 void NativeAppWindowViews::RemoveObserver(
678 web_modal::ModalDialogHostObserver* observer) {
679 observer_list_.RemoveObserver(observer);
682 // Private method. TODO(stevenjb): Move this below InitializePanelWindow()
683 // to match declaration order.
684 void NativeAppWindowViews::OnViewWasResized() {
685 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
686 observer_list_,
687 OnPositionRequiresUpdate());
690 // WidgetDelegate implementation.
692 void NativeAppWindowViews::OnWidgetMove() {
693 app_window_->OnNativeWindowChanged();
696 views::View* NativeAppWindowViews::GetInitiallyFocusedView() {
697 return web_view_;
700 bool NativeAppWindowViews::CanResize() const {
701 return resizable_ && !app_window_->size_constraints().HasFixedSize();
704 bool NativeAppWindowViews::CanMaximize() const {
705 return resizable_ && !app_window_->size_constraints().HasMaximumSize() &&
706 !app_window_->window_type_is_panel();
709 base::string16 NativeAppWindowViews::GetWindowTitle() const {
710 return app_window_->GetTitle();
713 bool NativeAppWindowViews::ShouldShowWindowTitle() const {
714 return app_window_->window_type() == AppWindow::WINDOW_TYPE_V1_PANEL;
717 gfx::ImageSkia NativeAppWindowViews::GetWindowAppIcon() {
718 gfx::Image app_icon = app_window_->app_icon();
719 if (app_icon.IsEmpty())
720 return GetWindowIcon();
721 else
722 return *app_icon.ToImageSkia();
725 gfx::ImageSkia NativeAppWindowViews::GetWindowIcon() {
726 content::WebContents* web_contents = app_window_->web_contents();
727 if (web_contents) {
728 FaviconTabHelper* favicon_tab_helper =
729 FaviconTabHelper::FromWebContents(web_contents);
730 gfx::Image app_icon = favicon_tab_helper->GetFavicon();
731 if (!app_icon.IsEmpty())
732 return *app_icon.ToImageSkia();
734 return gfx::ImageSkia();
737 bool NativeAppWindowViews::ShouldShowWindowIcon() const {
738 return app_window_->window_type() == AppWindow::WINDOW_TYPE_V1_PANEL;
741 void NativeAppWindowViews::SaveWindowPlacement(const gfx::Rect& bounds,
742 ui::WindowShowState show_state) {
743 views::WidgetDelegate::SaveWindowPlacement(bounds, show_state);
744 app_window_->OnNativeWindowChanged();
747 void NativeAppWindowViews::DeleteDelegate() {
748 window_->RemoveObserver(this);
749 app_window_->OnNativeClose();
752 views::Widget* NativeAppWindowViews::GetWidget() {
753 return window_;
756 const views::Widget* NativeAppWindowViews::GetWidget() const {
757 return window_;
760 views::View* NativeAppWindowViews::GetContentsView() {
761 return this;
764 views::NonClientFrameView* NativeAppWindowViews::CreateNonClientFrameView(
765 views::Widget* widget) {
766 #if defined(USE_ASH)
767 if (chrome::IsNativeViewInAsh(widget->GetNativeView())) {
768 // Set the delegate now because CustomFrameViewAsh sets the
769 // WindowStateDelegate if one is not already set.
770 ash::wm::GetWindowState(GetNativeWindow())->SetDelegate(
771 scoped_ptr<ash::wm::WindowStateDelegate>(
772 new NativeAppWindowStateDelegate(app_window_, this)).Pass());
774 if (app_window_->window_type_is_panel()) {
775 ash::PanelFrameView::FrameType frame_type = frameless_ ?
776 ash::PanelFrameView::FRAME_NONE : ash::PanelFrameView::FRAME_ASH;
777 views::NonClientFrameView* frame_view =
778 new ash::PanelFrameView(widget, frame_type);
779 frame_view->set_context_menu_controller(this);
780 return frame_view;
783 if (!frameless_) {
784 ash::CustomFrameViewAsh* custom_frame_view =
785 new ash::CustomFrameViewAsh(widget);
786 #if defined(OS_CHROMEOS)
787 // Non-frameless app windows can be put into immersive fullscreen.
788 // TODO(pkotwicz): Investigate if immersive fullscreen can be enabled for
789 // Windows Ash.
790 if (ash::switches::UseImmersiveFullscreenForAllWindows()) {
791 immersive_fullscreen_controller_.reset(
792 new ash::ImmersiveFullscreenController());
793 custom_frame_view->InitImmersiveFullscreenControllerForView(
794 immersive_fullscreen_controller_.get());
796 #endif
797 custom_frame_view->GetHeaderView()->set_context_menu_controller(this);
798 return custom_frame_view;
801 #endif
802 if (ShouldUseChromeStyleFrame())
803 return CreateAppWindowFrameView();
804 return views::WidgetDelegateView::CreateNonClientFrameView(widget);
807 bool NativeAppWindowViews::WidgetHasHitTestMask() const {
808 return shape_ != NULL;
811 void NativeAppWindowViews::GetWidgetHitTestMask(gfx::Path* mask) const {
812 shape_->getBoundaryPath(mask);
815 bool NativeAppWindowViews::ShouldDescendIntoChildForEventHandling(
816 gfx::NativeView child,
817 const gfx::Point& location) {
818 #if defined(USE_AURA)
819 if (child->Contains(web_view_->web_contents()->GetView()->GetNativeView())) {
820 // App window should claim mouse events that fall within the draggable
821 // region.
822 return !draggable_region_.get() ||
823 !draggable_region_->contains(location.x(), location.y());
825 #endif
827 return true;
830 // WidgetObserver implementation.
832 void NativeAppWindowViews::OnWidgetVisibilityChanged(views::Widget* widget,
833 bool visible) {
834 app_window_->OnNativeWindowChanged();
837 void NativeAppWindowViews::OnWidgetActivationChanged(views::Widget* widget,
838 bool active) {
839 app_window_->OnNativeWindowChanged();
840 if (active)
841 app_window_->OnNativeWindowActivated();
844 // WebContentsObserver implementation.
846 void NativeAppWindowViews::RenderViewCreated(
847 content::RenderViewHost* render_view_host) {
848 if (transparent_background_) {
849 // Use a background with transparency to trigger transparency in Webkit.
850 SkBitmap background;
851 background.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
852 background.allocPixels();
853 background.eraseARGB(0x00, 0x00, 0x00, 0x00);
855 content::RenderWidgetHostView* view = render_view_host->GetView();
856 DCHECK(view);
857 view->SetBackground(background);
861 void NativeAppWindowViews::RenderViewHostChanged(
862 content::RenderViewHost* old_host,
863 content::RenderViewHost* new_host) {
864 OnViewWasResized();
867 // views::View implementation.
869 void NativeAppWindowViews::Layout() {
870 DCHECK(web_view_);
871 web_view_->SetBounds(0, 0, width(), height());
872 OnViewWasResized();
875 void NativeAppWindowViews::ViewHierarchyChanged(
876 const ViewHierarchyChangedDetails& details) {
877 if (details.is_add && details.child == this) {
878 web_view_ = new views::WebView(NULL);
879 AddChildView(web_view_);
880 web_view_->SetWebContents(web_contents());
884 gfx::Size NativeAppWindowViews::GetPreferredSize() {
885 if (!preferred_size_.IsEmpty())
886 return preferred_size_;
887 return views::View::GetPreferredSize();
890 gfx::Size NativeAppWindowViews::GetMinimumSize() {
891 return app_window_->size_constraints().GetMinimumSize();
894 gfx::Size NativeAppWindowViews::GetMaximumSize() {
895 return app_window_->size_constraints().GetMaximumSize();
898 void NativeAppWindowViews::OnFocus() {
899 web_view_->RequestFocus();
902 bool NativeAppWindowViews::AcceleratorPressed(
903 const ui::Accelerator& accelerator) {
904 const std::map<ui::Accelerator, int>& accelerator_table =
905 GetAcceleratorTable();
906 std::map<ui::Accelerator, int>::const_iterator iter =
907 accelerator_table.find(accelerator);
908 DCHECK(iter != accelerator_table.end());
909 int command_id = iter->second;
910 switch (command_id) {
911 case IDC_CLOSE_WINDOW:
912 Close();
913 return true;
914 case IDC_ZOOM_MINUS:
915 chrome_page_zoom::Zoom(web_view_->GetWebContents(),
916 content::PAGE_ZOOM_OUT);
917 return true;
918 case IDC_ZOOM_NORMAL:
919 chrome_page_zoom::Zoom(web_view_->GetWebContents(),
920 content::PAGE_ZOOM_RESET);
921 return true;
922 case IDC_ZOOM_PLUS:
923 chrome_page_zoom::Zoom(web_view_->GetWebContents(),
924 content::PAGE_ZOOM_IN);
925 return true;
926 default:
927 NOTREACHED() << "Unknown accelerator sent to app window.";
929 return false;
932 // NativeAppWindow implementation.
934 void NativeAppWindowViews::SetFullscreen(int fullscreen_types) {
935 // Fullscreen not supported by panels.
936 if (app_window_->window_type_is_panel())
937 return;
938 is_fullscreen_ = (fullscreen_types != AppWindow::FULLSCREEN_TYPE_NONE);
939 window_->SetFullscreen(is_fullscreen_);
941 #if defined(USE_ASH)
942 if (immersive_fullscreen_controller_.get()) {
943 // |immersive_fullscreen_controller_| should only be set if immersive
944 // fullscreen is the fullscreen type used by the OS.
945 immersive_fullscreen_controller_->SetEnabled(
946 ash::ImmersiveFullscreenController::WINDOW_TYPE_PACKAGED_APP,
947 (fullscreen_types & AppWindow::FULLSCREEN_TYPE_OS) != 0);
948 // Autohide the shelf instead of hiding the shelf completely when only in
949 // OS fullscreen.
950 ash::wm::WindowState* window_state =
951 ash::wm::GetWindowState(window_->GetNativeWindow());
952 window_state->set_hide_shelf_when_fullscreen(fullscreen_types !=
953 AppWindow::FULLSCREEN_TYPE_OS);
954 DCHECK(ash::Shell::HasInstance());
955 ash::Shell::GetInstance()->UpdateShelfVisibility();
957 #endif
959 // TODO(jeremya) we need to call RenderViewHost::ExitFullscreen() if we
960 // ever drop the window out of fullscreen in response to something that
961 // wasn't the app calling webkitCancelFullScreen().
964 bool NativeAppWindowViews::IsFullscreenOrPending() const {
965 return is_fullscreen_;
968 bool NativeAppWindowViews::IsDetached() const {
969 if (!app_window_->window_type_is_panel())
970 return false;
971 #if defined(USE_ASH)
972 return !ash::wm::GetWindowState(window_->GetNativeWindow())->panel_attached();
973 #else
974 return false;
975 #endif
978 void NativeAppWindowViews::UpdateWindowIcon() {
979 window_->UpdateWindowIcon();
982 void NativeAppWindowViews::UpdateWindowTitle() {
983 window_->UpdateWindowTitle();
986 void NativeAppWindowViews::UpdateBadgeIcon() {
987 const gfx::Image* icon = NULL;
988 if (!app_window_->badge_icon().IsEmpty()) {
989 icon = &app_window_->badge_icon();
990 // chrome::DrawTaskbarDecoration can do interesting things with non-square
991 // bitmaps.
992 // TODO(benwells): Refactor chrome::DrawTaskbarDecoration to not be avatar
993 // specific, and lift this restriction.
994 if (icon->Width() != icon->Height()) {
995 LOG(ERROR) << "Attempt to set a non-square badge; request ignored.";
996 return;
999 chrome::DrawTaskbarDecoration(GetNativeWindow(), icon);
1002 void NativeAppWindowViews::UpdateDraggableRegions(
1003 const std::vector<extensions::DraggableRegion>& regions) {
1004 // Draggable region is not supported for non-frameless window.
1005 if (!frameless_)
1006 return;
1008 draggable_region_.reset(AppWindow::RawDraggableRegionsToSkRegion(regions));
1009 OnViewWasResized();
1012 SkRegion* NativeAppWindowViews::GetDraggableRegion() {
1013 return draggable_region_.get();
1016 void NativeAppWindowViews::UpdateShape(scoped_ptr<SkRegion> region) {
1017 bool had_shape = shape_;
1018 shape_ = region.Pass();
1020 aura::Window* native_window = window_->GetNativeWindow();
1021 if (shape_) {
1022 window_->SetShape(new SkRegion(*shape_));
1023 if (!had_shape) {
1024 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
1025 new ShapedAppWindowTargeter(native_window, this)));
1027 } else {
1028 window_->SetShape(NULL);
1029 if (had_shape)
1030 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>());
1034 void NativeAppWindowViews::HandleKeyboardEvent(
1035 const content::NativeWebKeyboardEvent& event) {
1036 unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
1037 GetFocusManager());
1040 bool NativeAppWindowViews::IsFrameless() const {
1041 return frameless_;
1044 gfx::Insets NativeAppWindowViews::GetFrameInsets() const {
1045 if (frameless_)
1046 return gfx::Insets();
1048 // The pretend client_bounds passed in need to be large enough to ensure that
1049 // GetWindowBoundsForClientBounds() doesn't decide that it needs more than
1050 // the specified amount of space to fit the window controls in, and return a
1051 // number larger than the real frame insets. Most window controls are smaller
1052 // than 1000x1000px, so this should be big enough.
1053 gfx::Rect client_bounds = gfx::Rect(1000, 1000);
1054 gfx::Rect window_bounds =
1055 window_->non_client_view()->GetWindowBoundsForClientBounds(
1056 client_bounds);
1057 return window_bounds.InsetsFrom(client_bounds);
1060 void NativeAppWindowViews::HideWithApp() {}
1061 void NativeAppWindowViews::ShowWithApp() {}
1062 void NativeAppWindowViews::UpdateWindowMinMaxSize() {}