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"
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"
49 #include "chrome/browser/shell_integration_linux.h"
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"
71 #include "ui/aura/window.h"
74 using apps::AppWindow
;
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
;
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
,
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
),
131 // Add accelerators for kiosk mode.
132 if (chrome::IsRunningInForcedAppMode()) {
133 AddAcceleratorsFromMapping(
134 kAppWindowKioskAppModeAcceleratorMap
,
135 arraysize(kAppWindowKioskAppModeAcceleratorMap
),
143 void CreateIconAndSetRelaunchDetails(
144 const base::FilePath web_app_path
,
145 const base::FilePath icon_file
,
146 const ShellIntegration::ShortcutInfo
& shortcut_info
,
148 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
150 // Set the relaunch data so "Pin this program to taskbar" has the app's
152 CommandLine command_line
= ShellIntegration::CommandLineArgsForLauncher(
154 shortcut_info
.extension_id
,
155 shortcut_info
.profile_path
);
157 base::FilePath chrome_exe
;
158 if (!PathService::Get(base::FILE_EXE
, &chrome_exe
)) {
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
))
170 ui::win::SetAppIconForWindow(icon_file
.value(), hwnd
);
171 web_app::internals::CheckAndSaveIcon(icon_file
, shortcut_info
.favicon
);
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
{
181 NativeAppWindowStateDelegate(AppWindow
* app_window
,
182 apps::NativeAppWindow
* native_app_window
)
183 : app_window_(app_window
),
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
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(){
196 window_state_
->RemoveObserver(this);
197 window_state_
->window()->RemoveObserver(this);
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();
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()
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
;
236 AppWindow
* app_window_
;
237 ash::wm::WindowState
* window_state_
;
239 DISALLOW_COPY_AND_ASSIGN(NativeAppWindowStateDelegate
);
245 NativeAppWindowViews::NativeAppWindowViews()
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
);
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
,
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;
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
);
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
))
355 focus_manager
->RegisterAccelerator(
356 iter
->first
, ui::AcceleratorManager::kNormalPriority
, this);
360 base::string16 app_name_wide
= base::UTF8ToWide(app_name
);
361 HWND hwnd
= GetNativeAppWindowHWND();
362 ui::win::SetAppIdForWindow(
363 ShellIntegration::GetAppModelIdForProfile(
365 Profile::FromBrowserContext(browser_context())->GetPath()),
368 web_app::UpdateShortcutInfoAndIconForApp(
370 Profile::FromBrowserContext(browser_context()),
371 base::Bind(&NativeAppWindowViews::OnShortcutInfoLoaded
,
372 weak_ptr_factory_
.GetWeakPtr()));
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
,
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(
394 base::Bind(&CreateIconAndSetRelaunchDetails
,
395 web_app_path
, icon_file
, shortcut_info
, hwnd
));
398 HWND
NativeAppWindowViews::GetNativeAppWindowHWND() const {
399 return views::HWNDForWidget(window_
->GetTopLevelWidget());
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
);
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_
));
426 params
.bounds
= gfx::Rect(preferred_size_
);
429 params
.bounds
= gfx::Rect(preferred_size_
);
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
);
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
);
451 // TODO(stevenjb): NativeAppWindow panels need to be implemented for other
456 bool NativeAppWindowViews::ShouldUseChromeStyleFrame() const {
460 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
461 // Linux always uses native style frames.
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
;
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
;
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
);
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 {
522 return ui::SHOW_STATE_MAXIMIZED
;
523 if (IsFullscreen()) {
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
;
533 return ui::SHOW_STATE_FULLSCREEN
;
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
;
556 return ui::SHOW_STATE_NORMAL
;
559 gfx::Rect
NativeAppWindowViews::GetBounds() const {
560 return window_
->GetWindowBoundsInScreen();
563 void NativeAppWindowViews::Show() {
564 if (window_
->IsVisible()) {
572 void NativeAppWindowViews::ShowInactive() {
573 if (window_
->IsVisible())
575 window_
->ShowInactive();
578 void NativeAppWindowViews::Hide() {
582 void NativeAppWindowViews::Close() {
586 void NativeAppWindowViews::Activate() {
590 void NativeAppWindowViews::Deactivate() {
591 window_
->Deactivate();
594 void NativeAppWindowViews::Maximize() {
598 void NativeAppWindowViews::Minimize() {
602 void NativeAppWindowViews::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()) {
617 return ash::wm::GetWindowState(window_
->GetNativeWindow())->
623 return window_
->IsAlwaysOnTop();
627 void NativeAppWindowViews::SetAlwaysOnTop(bool always_on_top
) {
628 window_
->SetAlwaysOnTop(always_on_top
);
631 void NativeAppWindowViews::ShowContextMenuForView(
634 ui::MenuSourceType source_type
) {
635 #if defined(USE_ASH) & defined(OS_CHROMEOS)
636 scoped_ptr
<ui::MenuModel
> model
=
637 CreateMultiUserContextMenu(app_window_
->GetNativeWindow());
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
,
652 views::MenuRunner::HAS_MNEMONICS
| views::MenuRunner::CONTEXT_MENU
) ==
653 views::MenuRunner::MENU_DELETED
)
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
,
687 OnPositionRequiresUpdate());
690 // WidgetDelegate implementation.
692 void NativeAppWindowViews::OnWidgetMove() {
693 app_window_
->OnNativeWindowChanged();
696 views::View
* NativeAppWindowViews::GetInitiallyFocusedView() {
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();
722 return *app_icon
.ToImageSkia();
725 gfx::ImageSkia
NativeAppWindowViews::GetWindowIcon() {
726 content::WebContents
* web_contents
= app_window_
->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() {
756 const views::Widget
* NativeAppWindowViews::GetWidget() const {
760 views::View
* NativeAppWindowViews::GetContentsView() {
764 views::NonClientFrameView
* NativeAppWindowViews::CreateNonClientFrameView(
765 views::Widget
* widget
) {
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);
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
790 if (ash::switches::UseImmersiveFullscreenForAllWindows()) {
791 immersive_fullscreen_controller_
.reset(
792 new ash::ImmersiveFullscreenController());
793 custom_frame_view
->InitImmersiveFullscreenControllerForView(
794 immersive_fullscreen_controller_
.get());
797 custom_frame_view
->GetHeaderView()->set_context_menu_controller(this);
798 return custom_frame_view
;
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
822 return !draggable_region_
.get() ||
823 !draggable_region_
->contains(location
.x(), location
.y());
830 // WidgetObserver implementation.
832 void NativeAppWindowViews::OnWidgetVisibilityChanged(views::Widget
* widget
,
834 app_window_
->OnNativeWindowChanged();
837 void NativeAppWindowViews::OnWidgetActivationChanged(views::Widget
* widget
,
839 app_window_
->OnNativeWindowChanged();
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.
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();
857 view
->SetBackground(background
);
861 void NativeAppWindowViews::RenderViewHostChanged(
862 content::RenderViewHost
* old_host
,
863 content::RenderViewHost
* new_host
) {
867 // views::View implementation.
869 void NativeAppWindowViews::Layout() {
871 web_view_
->SetBounds(0, 0, width(), height());
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
:
915 chrome_page_zoom::Zoom(web_view_
->GetWebContents(),
916 content::PAGE_ZOOM_OUT
);
918 case IDC_ZOOM_NORMAL
:
919 chrome_page_zoom::Zoom(web_view_
->GetWebContents(),
920 content::PAGE_ZOOM_RESET
);
923 chrome_page_zoom::Zoom(web_view_
->GetWebContents(),
924 content::PAGE_ZOOM_IN
);
927 NOTREACHED() << "Unknown accelerator sent to app window.";
932 // NativeAppWindow implementation.
934 void NativeAppWindowViews::SetFullscreen(int fullscreen_types
) {
935 // Fullscreen not supported by panels.
936 if (app_window_
->window_type_is_panel())
938 is_fullscreen_
= (fullscreen_types
!= AppWindow::FULLSCREEN_TYPE_NONE
);
939 window_
->SetFullscreen(is_fullscreen_
);
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
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();
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())
972 return !ash::wm::GetWindowState(window_
->GetNativeWindow())->panel_attached();
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
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.";
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.
1008 draggable_region_
.reset(AppWindow::RawDraggableRegionsToSkRegion(regions
));
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();
1022 window_
->SetShape(new SkRegion(*shape_
));
1024 native_window
->SetEventTargeter(scoped_ptr
<ui::EventTargeter
>(
1025 new ShapedAppWindowTargeter(native_window
, this)));
1028 window_
->SetShape(NULL
);
1030 native_window
->SetEventTargeter(scoped_ptr
<ui::EventTargeter
>());
1034 void NativeAppWindowViews::HandleKeyboardEvent(
1035 const content::NativeWebKeyboardEvent
& event
) {
1036 unhandled_keyboard_event_handler_
.HandleKeyboardEvent(event
,
1040 bool NativeAppWindowViews::IsFrameless() const {
1044 gfx::Insets
NativeAppWindowViews::GetFrameInsets() const {
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(
1057 return window_bounds
.InsetsFrom(client_bounds
);
1060 void NativeAppWindowViews::HideWithApp() {}
1061 void NativeAppWindowViews::ShowWithApp() {}
1062 void NativeAppWindowViews::UpdateWindowMinMaxSize() {}