chrome_native_app_window_views_win.cc revision 010d83a9304c5a91596085d917d248abff47903a
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/common/extension.h"
30#include "grit/generated_resources.h"
31#include "ui/aura/remote_window_tree_host_win.h"
32#include "ui/base/l10n/l10n_util.h"
33#include "ui/base/win/shell.h"
34#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
35#include "ui/views/win/hwnd_util.h"
36
37namespace {
38
39void CreateIconAndSetRelaunchDetails(
40    const base::FilePath& web_app_path,
41    const base::FilePath& icon_file,
42    const web_app::ShortcutInfo& shortcut_info,
43    const HWND hwnd) {
44  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
45
46  // Set the relaunch data so "Pin this program to taskbar" has the app's
47  // information.
48  CommandLine command_line = ShellIntegration::CommandLineArgsForLauncher(
49      shortcut_info.url,
50      shortcut_info.extension_id,
51      shortcut_info.profile_path);
52
53  base::FilePath chrome_exe;
54  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
55    NOTREACHED();
56    return;
57  }
58  command_line.SetProgram(chrome_exe);
59  ui::win::SetRelaunchDetailsForWindow(command_line.GetCommandLineString(),
60      shortcut_info.title, hwnd);
61
62  if (!base::PathExists(web_app_path) &&
63      !base::CreateDirectory(web_app_path)) {
64    return;
65  }
66
67  ui::win::SetAppIconForWindow(icon_file.value(), hwnd);
68  web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon);
69}
70
71}  // namespace
72
73ChromeNativeAppWindowViewsWin::ChromeNativeAppWindowViewsWin()
74    : weak_ptr_factory_(this), glass_frame_view_(NULL) {
75}
76
77void ChromeNativeAppWindowViewsWin::ActivateParentDesktopIfNecessary() {
78  if (!ash::Shell::HasInstance())
79    return;
80
81  views::Widget* widget =
82      implicit_cast<views::WidgetDelegate*>(this)->GetWidget();
83  chrome::HostDesktopType host_desktop_type =
84      chrome::GetHostDesktopTypeForNativeWindow(widget->GetNativeWindow());
85  // Only switching into Ash from Native is supported. Tearing the user out of
86  // Metro mode can only be done by launching a process from Metro mode itself.
87  // This is done for launching apps, but not regular activations.
88  if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH &&
89      chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_NATIVE) {
90    chrome::ActivateMetroChrome();
91  }
92}
93
94void ChromeNativeAppWindowViewsWin::OnShortcutInfoLoaded(
95    const web_app::ShortcutInfo& shortcut_info) {
96  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
97
98  HWND hwnd = GetNativeAppWindowHWND();
99
100  // Set window's icon to the one we're about to create/update in the web app
101  // path. The icon cache will refresh on icon creation.
102  base::FilePath web_app_path = web_app::GetWebAppDataDirectory(
103      shortcut_info.profile_path, shortcut_info.extension_id,
104      shortcut_info.url);
105  base::FilePath icon_file = web_app_path
106      .Append(web_app::internals::GetSanitizedFileName(shortcut_info.title))
107      .ReplaceExtension(FILE_PATH_LITERAL(".ico"));
108
109  content::BrowserThread::PostBlockingPoolTask(
110      FROM_HERE,
111      base::Bind(&CreateIconAndSetRelaunchDetails,
112                 web_app_path, icon_file, shortcut_info, hwnd));
113}
114
115HWND ChromeNativeAppWindowViewsWin::GetNativeAppWindowHWND() const {
116  return views::HWNDForWidget(widget()->GetTopLevelWidget());
117}
118
119void ChromeNativeAppWindowViewsWin::EnsureCaptionStyleSet() {
120  // Windows seems to have issues maximizing windows without WS_CAPTION.
121  // The default views / Aura implementation will remove this if we are using
122  // frameless or colored windows, so we put it back here.
123  HWND hwnd = GetNativeAppWindowHWND();
124  int current_style = ::GetWindowLong(hwnd, GWL_STYLE);
125  ::SetWindowLong(hwnd, GWL_STYLE, current_style | WS_CAPTION);
126}
127
128void ChromeNativeAppWindowViewsWin::OnBeforeWidgetInit(
129    views::Widget::InitParams* init_params,
130    views::Widget* widget) {
131  content::BrowserContext* browser_context = app_window()->browser_context();
132  std::string extension_id = app_window()->extension_id();
133  // If an app has any existing windows, ensure new ones are created on the
134  // same desktop.
135  apps::AppWindow* any_existing_window =
136      apps::AppWindowRegistry::Get(browser_context)
137          ->GetCurrentAppWindowForApp(extension_id);
138  chrome::HostDesktopType desktop_type;
139  if (any_existing_window) {
140    desktop_type = chrome::GetHostDesktopTypeForNativeWindow(
141        any_existing_window->GetNativeWindow());
142  } else {
143    PerAppSettingsService* settings =
144        PerAppSettingsServiceFactory::GetForBrowserContext(browser_context);
145    if (settings->HasDesktopLastLaunchedFrom(extension_id)) {
146      desktop_type = settings->GetDesktopLastLaunchedFrom(extension_id);
147    } else {
148      // We don't know what desktop this app was last launched from, so take our
149      // best guess as to what desktop the user is on.
150      desktop_type = chrome::GetActiveDesktop();
151    }
152  }
153  if (desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
154    init_params->context = ash::Shell::GetPrimaryRootWindow();
155  else
156    init_params->native_widget = new AppWindowDesktopNativeWidgetAuraWin(this);
157}
158
159void ChromeNativeAppWindowViewsWin::InitializeDefaultWindow(
160    const apps::AppWindow::CreateParams& create_params) {
161  ChromeNativeAppWindowViews::InitializeDefaultWindow(create_params);
162
163  const extensions::Extension* extension = app_window()->GetExtension();
164  if (!extension)
165    return;
166
167  std::string app_name =
168      web_app::GenerateApplicationNameFromExtensionId(extension->id());
169  base::string16 app_name_wide = base::UTF8ToWide(app_name);
170  HWND hwnd = GetNativeAppWindowHWND();
171  Profile* profile =
172      Profile::FromBrowserContext(app_window()->browser_context());
173  app_model_id_ =
174      ShellIntegration::GetAppModelIdForProfile(app_name_wide,
175                                                profile->GetPath());
176  ui::win::SetAppIdForWindow(app_model_id_, hwnd);
177
178  web_app::UpdateShortcutInfoAndIconForApp(
179      extension,
180      profile,
181      base::Bind(&ChromeNativeAppWindowViewsWin::OnShortcutInfoLoaded,
182                 weak_ptr_factory_.GetWeakPtr()));
183
184  EnsureCaptionStyleSet();
185  UpdateShelfMenu();
186}
187
188views::NonClientFrameView*
189ChromeNativeAppWindowViewsWin::CreateStandardDesktopAppFrame() {
190  glass_frame_view_ = NULL;
191  if (ui::win::IsAeroGlassEnabled()) {
192    glass_frame_view_ = new GlassAppWindowFrameViewWin(this, widget());
193    return glass_frame_view_;
194  }
195  return ChromeNativeAppWindowViews::CreateStandardDesktopAppFrame();
196}
197
198void ChromeNativeAppWindowViewsWin::Show() {
199  ActivateParentDesktopIfNecessary();
200  ChromeNativeAppWindowViews::Show();
201}
202
203void ChromeNativeAppWindowViewsWin::Activate() {
204  ActivateParentDesktopIfNecessary();
205  ChromeNativeAppWindowViews::Activate();
206}
207
208void ChromeNativeAppWindowViewsWin::UpdateShelfMenu() {
209  if (!JumpListUpdater::IsEnabled())
210    return;
211
212  // Currently the only option is related to ephemeral apps, so avoid updating
213  // the app's jump list when the feature is not enabled.
214  if (!CommandLine::ForCurrentProcess()->HasSwitch(
215          switches::kEnableEphemeralApps)) {
216    return;
217  }
218
219  const extensions::Extension* extension = app_window()->GetExtension();
220  if (!extension)
221    return;
222
223  // For the icon resources.
224  base::FilePath chrome_path;
225  if (!PathService::Get(base::FILE_EXE, &chrome_path))
226    return;
227
228  JumpListUpdater jumplist_updater(app_model_id_);
229  if (!jumplist_updater.BeginUpdate())
230    return;
231
232  // Add item to install ephemeral apps.
233  if (extension->is_ephemeral()) {
234    scoped_refptr<ShellLinkItem> link(new ShellLinkItem());
235    link->set_title(l10n_util::GetStringUTF16(IDS_APP_INSTALL_TITLE));
236    link->set_icon(chrome_path.value(),
237                   icon_resources::kInstallPackagedAppIndex);
238    ShellIntegration::AppendProfileArgs(
239        app_window()->browser_context()->GetPath(), link->GetCommandLine());
240    link->GetCommandLine()->AppendSwitchASCII(switches::kInstallFromWebstore,
241                                              extension->id());
242
243    ShellLinkItemList items;
244    items.push_back(link);
245    jumplist_updater.AddTasks(items);
246  }
247
248  jumplist_updater.CommitUpdate();
249}
250