1 // Copyright 2014 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/chrome_native_app_window_views_win.h" 6 7 #include "apps/ui/views/app_window_frame_view.h" 8 #include "ash/shell.h" 9 #include "base/command_line.h" 10 #include "base/files/file_util.h" 11 #include "base/path_service.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "base/threading/sequenced_worker_pool.h" 14 #include "chrome/browser/apps/per_app_settings_service.h" 15 #include "chrome/browser/apps/per_app_settings_service_factory.h" 16 #include "chrome/browser/jumplist_updater_win.h" 17 #include "chrome/browser/metro_utils/metro_chrome_win.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/shell_integration.h" 20 #include "chrome/browser/ui/views/apps/app_window_desktop_native_widget_aura_win.h" 21 #include "chrome/browser/ui/views/apps/glass_app_window_frame_view_win.h" 22 #include "chrome/browser/web_applications/web_app.h" 23 #include "chrome/browser/web_applications/web_app_win.h" 24 #include "chrome/common/chrome_icon_resources_win.h" 25 #include "chrome/common/chrome_switches.h" 26 #include "chrome/grit/generated_resources.h" 27 #include "content/public/browser/browser_thread.h" 28 #include "extensions/browser/app_window/app_window.h" 29 #include "extensions/browser/app_window/app_window_registry.h" 30 #include "extensions/browser/extension_util.h" 31 #include "extensions/common/extension.h" 32 #include "ui/aura/remote_window_tree_host_win.h" 33 #include "ui/base/l10n/l10n_util.h" 34 #include "ui/base/win/shell.h" 35 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" 36 #include "ui/views/win/hwnd_util.h" 37 38 ChromeNativeAppWindowViewsWin::ChromeNativeAppWindowViewsWin() 39 : glass_frame_view_(NULL), weak_ptr_factory_(this) { 40 } 41 42 void ChromeNativeAppWindowViewsWin::ActivateParentDesktopIfNecessary() { 43 // Only switching into Ash from Native is supported. Tearing the user out of 44 // Metro mode can only be done by launching a process from Metro mode itself. 45 // This is done for launching apps, but not regular activations. 46 if (IsRunningInAsh() && 47 chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_NATIVE) { 48 chrome::ActivateMetroChrome(); 49 } 50 } 51 52 HWND ChromeNativeAppWindowViewsWin::GetNativeAppWindowHWND() const { 53 return views::HWNDForWidget(widget()->GetTopLevelWidget()); 54 } 55 56 bool ChromeNativeAppWindowViewsWin::IsRunningInAsh() { 57 if (!ash::Shell::HasInstance()) 58 return false; 59 60 views::Widget* widget = 61 implicit_cast<views::WidgetDelegate*>(this)->GetWidget(); 62 chrome::HostDesktopType host_desktop_type = 63 chrome::GetHostDesktopTypeForNativeWindow(widget->GetNativeWindow()); 64 return host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH; 65 } 66 67 void ChromeNativeAppWindowViewsWin::EnsureCaptionStyleSet() { 68 // Windows seems to have issues maximizing windows without WS_CAPTION. 69 // The default views / Aura implementation will remove this if we are using 70 // frameless or colored windows, so we put it back here. 71 HWND hwnd = GetNativeAppWindowHWND(); 72 int current_style = ::GetWindowLong(hwnd, GWL_STYLE); 73 ::SetWindowLong(hwnd, GWL_STYLE, current_style | WS_CAPTION); 74 } 75 76 void ChromeNativeAppWindowViewsWin::OnBeforeWidgetInit( 77 views::Widget::InitParams* init_params, 78 views::Widget* widget) { 79 content::BrowserContext* browser_context = app_window()->browser_context(); 80 std::string extension_id = app_window()->extension_id(); 81 // If an app has any existing windows, ensure new ones are created on the 82 // same desktop. 83 extensions::AppWindow* any_existing_window = 84 extensions::AppWindowRegistry::Get(browser_context) 85 ->GetCurrentAppWindowForApp(extension_id); 86 chrome::HostDesktopType desktop_type; 87 if (any_existing_window) { 88 desktop_type = chrome::GetHostDesktopTypeForNativeWindow( 89 any_existing_window->GetNativeWindow()); 90 } else { 91 PerAppSettingsService* settings = 92 PerAppSettingsServiceFactory::GetForBrowserContext(browser_context); 93 if (settings->HasDesktopLastLaunchedFrom(extension_id)) { 94 desktop_type = settings->GetDesktopLastLaunchedFrom(extension_id); 95 } else { 96 // We don't know what desktop this app was last launched from, so take our 97 // best guess as to what desktop the user is on. 98 desktop_type = chrome::GetActiveDesktop(); 99 } 100 } 101 if (desktop_type == chrome::HOST_DESKTOP_TYPE_ASH) 102 init_params->context = ash::Shell::GetPrimaryRootWindow(); 103 else 104 init_params->native_widget = new AppWindowDesktopNativeWidgetAuraWin(this); 105 } 106 107 void ChromeNativeAppWindowViewsWin::InitializeDefaultWindow( 108 const extensions::AppWindow::CreateParams& create_params) { 109 ChromeNativeAppWindowViews::InitializeDefaultWindow(create_params); 110 111 // Remaining initialization is for Windows shell integration, which doesn't 112 // apply to app windows in Ash. 113 if (IsRunningInAsh()) 114 return; 115 116 const extensions::Extension* extension = app_window()->GetExtension(); 117 if (!extension) 118 return; 119 120 std::string app_name = 121 web_app::GenerateApplicationNameFromExtensionId(extension->id()); 122 base::string16 app_name_wide = base::UTF8ToWide(app_name); 123 HWND hwnd = GetNativeAppWindowHWND(); 124 Profile* profile = 125 Profile::FromBrowserContext(app_window()->browser_context()); 126 app_model_id_ = 127 ShellIntegration::GetAppModelIdForProfile(app_name_wide, 128 profile->GetPath()); 129 ui::win::SetAppIdForWindow(app_model_id_, hwnd); 130 web_app::UpdateRelaunchDetailsForApp(profile, extension, hwnd); 131 132 if (!create_params.alpha_enabled) 133 EnsureCaptionStyleSet(); 134 UpdateShelfMenu(); 135 } 136 137 views::NonClientFrameView* 138 ChromeNativeAppWindowViewsWin::CreateStandardDesktopAppFrame() { 139 glass_frame_view_ = NULL; 140 if (ui::win::IsAeroGlassEnabled()) { 141 glass_frame_view_ = new GlassAppWindowFrameViewWin(this, widget()); 142 return glass_frame_view_; 143 } 144 return ChromeNativeAppWindowViews::CreateStandardDesktopAppFrame(); 145 } 146 147 void ChromeNativeAppWindowViewsWin::Show() { 148 ActivateParentDesktopIfNecessary(); 149 ChromeNativeAppWindowViews::Show(); 150 } 151 152 void ChromeNativeAppWindowViewsWin::Activate() { 153 ActivateParentDesktopIfNecessary(); 154 ChromeNativeAppWindowViews::Activate(); 155 } 156 157 void ChromeNativeAppWindowViewsWin::UpdateShelfMenu() { 158 if (!JumpListUpdater::IsEnabled() || IsRunningInAsh()) 159 return; 160 161 // Currently the only option is related to ephemeral apps, so avoid updating 162 // the app's jump list when the feature is not enabled. 163 if (!CommandLine::ForCurrentProcess()->HasSwitch( 164 switches::kEnableEphemeralApps)) { 165 return; 166 } 167 168 const extensions::Extension* extension = app_window()->GetExtension(); 169 if (!extension) 170 return; 171 172 // For the icon resources. 173 base::FilePath chrome_path; 174 if (!PathService::Get(base::FILE_EXE, &chrome_path)) 175 return; 176 177 DCHECK(!app_model_id_.empty()); 178 179 JumpListUpdater jumplist_updater(app_model_id_); 180 if (!jumplist_updater.BeginUpdate()) 181 return; 182 183 // Add item to install ephemeral apps. 184 if (extensions::util::IsEphemeralApp(extension->id(), 185 app_window()->browser_context())) { 186 scoped_refptr<ShellLinkItem> link(new ShellLinkItem()); 187 link->set_title(l10n_util::GetStringUTF16(IDS_APP_INSTALL_TITLE)); 188 link->set_icon(chrome_path.value(), 189 icon_resources::kInstallPackagedAppIndex); 190 ShellIntegration::AppendProfileArgs( 191 app_window()->browser_context()->GetPath(), link->GetCommandLine()); 192 link->GetCommandLine()->AppendSwitchASCII( 193 switches::kInstallEphemeralAppFromWebstore, extension->id()); 194 195 ShellLinkItemList items; 196 items.push_back(link); 197 jumplist_updater.AddTasks(items); 198 } 199 200 // Note that an empty jumplist must still be committed to clear all items. 201 jumplist_updater.CommitUpdate(); 202 } 203