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