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/app_window.h" 8 #include "apps/app_window_registry.h" 9 #include "apps/ui/views/app_window_frame_view.h" 10 #include "ash/shell.h" 11 #include "base/command_line.h" 12 #include "base/file_util.h" 13 #include "base/path_service.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/threading/sequenced_worker_pool.h" 16 #include "chrome/browser/apps/per_app_settings_service.h" 17 #include "chrome/browser/apps/per_app_settings_service_factory.h" 18 #include "chrome/browser/jumplist_updater_win.h" 19 #include "chrome/browser/metro_utils/metro_chrome_win.h" 20 #include "chrome/browser/profiles/profile.h" 21 #include "chrome/browser/shell_integration.h" 22 #include "chrome/browser/ui/views/apps/app_window_desktop_native_widget_aura_win.h" 23 #include "chrome/browser/ui/views/apps/glass_app_window_frame_view_win.h" 24 #include "chrome/browser/web_applications/web_app.h" 25 #include "chrome/browser/web_applications/web_app_win.h" 26 #include "chrome/common/chrome_icon_resources_win.h" 27 #include "chrome/common/chrome_switches.h" 28 #include "content/public/browser/browser_thread.h" 29 #include "extensions/browser/extension_util.h" 30 #include "extensions/common/extension.h" 31 #include "grit/generated_resources.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 : weak_ptr_factory_(this), glass_frame_view_(NULL) { 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 apps::AppWindow* any_existing_window = 84 apps::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 apps::AppWindow::CreateParams& create_params) { 109 ChromeNativeAppWindowViews::InitializeDefaultWindow(create_params); 110 111 const extensions::Extension* extension = app_window()->GetExtension(); 112 if (!extension) 113 return; 114 115 std::string app_name = 116 web_app::GenerateApplicationNameFromExtensionId(extension->id()); 117 base::string16 app_name_wide = base::UTF8ToWide(app_name); 118 HWND hwnd = GetNativeAppWindowHWND(); 119 Profile* profile = 120 Profile::FromBrowserContext(app_window()->browser_context()); 121 app_model_id_ = 122 ShellIntegration::GetAppModelIdForProfile(app_name_wide, 123 profile->GetPath()); 124 ui::win::SetAppIdForWindow(app_model_id_, hwnd); 125 126 web_app::UpdateRelaunchDetailsForApp(profile, extension, hwnd); 127 128 if (!create_params.transparent_background && !IsRunningInAsh()) 129 EnsureCaptionStyleSet(); 130 UpdateShelfMenu(); 131 } 132 133 views::NonClientFrameView* 134 ChromeNativeAppWindowViewsWin::CreateStandardDesktopAppFrame() { 135 glass_frame_view_ = NULL; 136 if (ui::win::IsAeroGlassEnabled()) { 137 glass_frame_view_ = new GlassAppWindowFrameViewWin(this, widget()); 138 return glass_frame_view_; 139 } 140 return ChromeNativeAppWindowViews::CreateStandardDesktopAppFrame(); 141 } 142 143 void ChromeNativeAppWindowViewsWin::Show() { 144 ActivateParentDesktopIfNecessary(); 145 ChromeNativeAppWindowViews::Show(); 146 } 147 148 void ChromeNativeAppWindowViewsWin::Activate() { 149 ActivateParentDesktopIfNecessary(); 150 ChromeNativeAppWindowViews::Activate(); 151 } 152 153 void ChromeNativeAppWindowViewsWin::UpdateShelfMenu() { 154 if (!JumpListUpdater::IsEnabled()) 155 return; 156 157 // Currently the only option is related to ephemeral apps, so avoid updating 158 // the app's jump list when the feature is not enabled. 159 if (!CommandLine::ForCurrentProcess()->HasSwitch( 160 switches::kEnableEphemeralApps)) { 161 return; 162 } 163 164 const extensions::Extension* extension = app_window()->GetExtension(); 165 if (!extension) 166 return; 167 168 // For the icon resources. 169 base::FilePath chrome_path; 170 if (!PathService::Get(base::FILE_EXE, &chrome_path)) 171 return; 172 173 JumpListUpdater jumplist_updater(app_model_id_); 174 if (!jumplist_updater.BeginUpdate()) 175 return; 176 177 // Add item to install ephemeral apps. 178 if (extensions::util::IsEphemeralApp(extension->id(), 179 app_window()->browser_context())) { 180 scoped_refptr<ShellLinkItem> link(new ShellLinkItem()); 181 link->set_title(l10n_util::GetStringUTF16(IDS_APP_INSTALL_TITLE)); 182 link->set_icon(chrome_path.value(), 183 icon_resources::kInstallPackagedAppIndex); 184 ShellIntegration::AppendProfileArgs( 185 app_window()->browser_context()->GetPath(), link->GetCommandLine()); 186 link->GetCommandLine()->AppendSwitchASCII(switches::kInstallFromWebstore, 187 extension->id()); 188 189 ShellLinkItemList items; 190 items.push_back(link); 191 jumplist_updater.AddTasks(items); 192 } 193 194 // Note that an empty jumplist must still be committed to clear all items. 195 jumplist_updater.CommitUpdate(); 196 } 197