Skip to content

Commit

Permalink
Show an InfoBar when trying to start Packaged Apps from Metro mode.
Browse files Browse the repository at this point in the history
Platform Apps do not function properly while in Metro mode.

This change intercepts platform app launch requests done
while the browser is in metro mode, redirecting them to an
infobar. If "yes" is chosen, a local pref is stored with the
extension id and Profile that tried to launch it, and a relaunch
of Chrome in Desktop mode is triggered. Upon restart,
apps::AppLaunchOnRestartService is responsible for launching the
selected app, and clearing the pref.

BUG=153426
TEST=With Chrome configured for Windows 8 mode, try to start a
packaged app. This can be done from NTP (if launcher not enabled),
or from a taskbar shortcut, a desktop shortcut, or a pinned start
page tile. InfoBar should display offering to switch to Desktop mode.
If switching, selected packaged app should launch after a brief
delay.

Review URL: https://chromiumcodereview.appspot.com/12450014

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190306 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
tapted@chromium.org committed Mar 25, 2013
1 parent 0700471 commit c1dbcb1
Show file tree
Hide file tree
Showing 14 changed files with 237 additions and 32 deletions.
1 change: 1 addition & 0 deletions apps/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ include_rules = [
"+base",
"+content",
"+ui",
"+win8",
# Temporary allowed includes.
# TODO(benwells): remove these (http://crbug.com/159366)
"+chrome/browser/browser_process.h",
Expand Down
95 changes: 95 additions & 0 deletions apps/app_launch_for_metro_restart_win.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "apps/app_launch_for_metro_restart_win.h"

#include "apps/pref_names.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/message_loop.h"
#include "base/prefs/pref_service.h"
#include "base/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/app_runtime/app_runtime_api.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/platform_app_launcher.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "win8/util/win8_util.h"

using extensions::Extension;
using extensions::ExtensionSystem;

namespace apps {

namespace {

void LaunchAppWithId(Profile* profile,
const std::string& extension_id) {
ExtensionService* extension_service =
ExtensionSystem::Get(profile)->extension_service();
if (!extension_service)
return;

const Extension* extension =
extension_service->GetExtensionById(extension_id, false);
if (!extension)
return;

extensions::AppEventRouter::DispatchOnLaunchedEvent(profile, extension);
}

} // namespace

void HandleAppLaunchForMetroRestart(Profile* profile) {
PrefService* prefs = g_browser_process->local_state();
if (!prefs->HasPrefPath(prefs::kAppLaunchForMetroRestartProfile))
return;

// This will be called for each profile that had a browser window open before
// relaunch. After checking that the preference is set, check that the
// profile that is starting up matches the profile that initially wanted to
// launch the app.
base::FilePath profile_dir = base::FilePath::FromUTF8Unsafe(
prefs->GetString(prefs::kAppLaunchForMetroRestartProfile));
if (profile_dir.empty() || profile->GetPath().BaseName() != profile_dir)
return;

prefs->ClearPref(prefs::kAppLaunchForMetroRestartProfile);

if (!prefs->HasPrefPath(prefs::kAppLaunchForMetroRestart))
return;

std::string extension_id = prefs->GetString(prefs::kAppLaunchForMetroRestart);
if (extension_id.empty())
return;

prefs->ClearPref(prefs::kAppLaunchForMetroRestart);

if (win8::IsSingleWindowMetroMode()) {
// In this case we have relaunched with the correct profile, but we are not
// in Desktop mode, so can not launch apps. Leave the preferences cleared so
// there are no surprises later.
return;
}

const int kRestartAppLaunchDelayMs = 1000;
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&LaunchAppWithId,
profile,
extension_id),
base::TimeDelta::FromMilliseconds(kRestartAppLaunchDelayMs));
}

void SetAppLaunchForMetroRestart(Profile* profile,
const std::string& extension_id) {
PrefService* prefs = g_browser_process->local_state();
prefs->SetString(prefs::kAppLaunchForMetroRestartProfile,
profile->GetPath().BaseName().MaybeAsASCII());
prefs->SetString(prefs::kAppLaunchForMetroRestart, extension_id);
}

} // namespace apps
26 changes: 26 additions & 0 deletions apps/app_launch_for_metro_restart_win.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef APPS_APP_LAUNCH_FOR_METRO_RESTART_WIN_H
#define APPS_APP_LAUNCH_FOR_METRO_RESTART_WIN_H

#include <string>

#include "base/basictypes.h"

class Profile;

namespace apps {

// Handles launching apps on browser startup due to an attempt to launch an app
// in Windows 8 Metro mode.
void HandleAppLaunchForMetroRestart(Profile* profile);

// Set a local pref to launch an app before relaunching chrome in desktop mode.
void SetAppLaunchForMetroRestart(Profile* profile,
const std::string& extension_id);

} // namespace apps

#endif // APPS_APP_LAUNCH_FOR_METRO_RESTART_WIN_H
12 changes: 7 additions & 5 deletions apps/apps.gypi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

Expand All @@ -22,16 +22,18 @@
'<(INTERMEDIATE_DIR)',
],
'sources': [
'app_shim/app_shim_host_mac.cc',
'app_shim/app_shim_host_mac.h',
'app_shim/app_shim_host_manager_mac.h',
'app_shim/app_shim_host_manager_mac.mm',
'app_launch_for_metro_restart_win.cc',
'app_launch_for_metro_restart_win.h',
'app_launcher.cc',
'app_launcher.h',
'app_restore_service.cc',
'app_restore_service.h',
'app_restore_service_factory.cc',
'app_restore_service_factory.h',
'app_shim/app_shim_host_mac.cc',
'app_shim/app_shim_host_mac.h',
'app_shim/app_shim_host_manager_mac.h',
'app_shim/app_shim_host_manager_mac.mm',
'pref_names.cc',
'pref_names.h',
'prefs.cc',
Expand Down
9 changes: 9 additions & 0 deletions apps/pref_names.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ namespace prefs {
const char kAppLauncherIsEnabled[] =
"apps.app_launcher.should_show_apps_page";

// If set, the user requested to launch the app with this extension id while
// in Metro mode, and then relaunched to Desktop mode to start it.
const char kAppLaunchForMetroRestart[] = "apps.app_launch_for_metro_restart";

// Set with |kAppLaunchForMetroRestart|, the profile whose loading triggers
// launch of the specified app when restarting Chrome in desktop mode.
const char kAppLaunchForMetroRestartProfile[] =
"apps.app_launch_for_metro_restart_profile";

} // namespace prefs

} // namespace apps
2 changes: 2 additions & 0 deletions apps/pref_names.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ namespace prefs {
// Alphabetical list of preference names specific to Apps component.
// Keep alphabetized and document each one in the source file.
extern const char kAppLauncherIsEnabled[];
extern const char kAppLaunchForMetroRestart[];
extern const char kAppLaunchForMetroRestartProfile[];

} // namespace prefs
} // namespace apps
Expand Down
5 changes: 5 additions & 0 deletions apps/prefs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ void RegisterPrefs(PrefRegistrySimple* registry) {
// GetIsAppLauncherEnabled().
registry->RegisterBooleanPref(prefs::kAppLauncherIsEnabled,
MaybeIsAppLauncherEnabled());

#if defined(OS_WIN)
registry->RegisterStringPref(prefs::kAppLaunchForMetroRestart, "");
registry->RegisterStringPref(prefs::kAppLaunchForMetroRestartProfile, "");
#endif
}

} // namespace apps
8 changes: 6 additions & 2 deletions chrome/app/chromium_strings.grd
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,14 @@ be available for now. -->
<message name="IDS_PRODUCT_APP_LAUNCHER_NAME" desc="The Chrome App Launcher application name">
Chromium App Launcher
</message>
<message name="IDS_WIN8_INFOBAR_DESKTOP_RESTART_TO_LAUNCH_APPS"
desc="Infobar message to restart chrome in desktop mode to launch Chrome Apps. Aplies to Windows 8 only">
<message name="IDS_WIN8_INFOBAR_DESKTOP_RESTART_FOR_APP_LIST"
desc="Infobar message to restart chrome in desktop mode to display the App Launcher. Aplies to Windows 8 only">
You need to switch Chromium to desktop mode to use the App Launcher.
</message>
<message name="IDS_WIN8_INFOBAR_DESKTOP_RESTART_FOR_PACKAGED_APP"
desc="Infobar message to restart chrome in desktop mode to launch a Chrome Packaged App. Aplies to Windows 8 only">
You need to switch Chromium to desktop mode to use Apps.
</message>
<message name="IDS_PRODUCT_BINARIES_NAME" desc="The Chrome Binaries application name">
Chromium Binaries
</message>
Expand Down
8 changes: 6 additions & 2 deletions chrome/app/google_chrome_strings.grd
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,14 @@ Chrome supports. -->
<message name="IDS_PRODUCT_APP_LAUNCHER_NAME" desc="The Chrome App Launcher application name">
Google Chrome App Launcher
</message>
<message name="IDS_WIN8_INFOBAR_DESKTOP_RESTART_TO_LAUNCH_APPS"
desc="Infobar message to restart chrome in desktop mode to launch Chrome Apps. Aplies to Windows 8 only">
<message name="IDS_WIN8_INFOBAR_DESKTOP_RESTART_FOR_APP_LIST"
desc="Infobar message to restart chrome in desktop mode to display the App Launcher. Aplies to Windows 8 only">
You need to switch Chrome to desktop mode to use the App Launcher.
</message>
<message name="IDS_WIN8_INFOBAR_DESKTOP_RESTART_FOR_PACKAGED_APP"
desc="Infobar message to restart chrome in desktop mode to launch a Chrome Packaged App. Aplies to Windows 8 only">
You need to switch Chrome to desktop mode to use Apps.
</message>
<message name="IDS_PRODUCT_BINARIES_NAME" desc="The Chrome Binaries application name">
Google Chrome Binaries
</message>
Expand Down
17 changes: 17 additions & 0 deletions chrome/browser/extensions/platform_app_launcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/lazy_background_task_queue.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/extensions/app_metro_infobar_delegate_win.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_messages.h"
#include "content/public/browser/browser_thread.h"
Expand All @@ -39,6 +40,10 @@
#include "chrome/browser/chromeos/drive/drive_system_service.h"
#endif

#if defined(OS_WIN)
#include "win8/util/win8_util.h"
#endif

using content::BrowserThread;
using extensions::app_file_handler_util::FileHandlerForId;
using extensions::app_file_handler_util::FileHandlerCanHandleFileWithMimeType;
Expand Down Expand Up @@ -363,6 +368,18 @@ void LaunchPlatformApp(Profile* profile,
const Extension* extension,
const CommandLine* command_line,
const base::FilePath& current_directory) {
#if defined(OS_WIN)
// On Windows 8's single window Metro mode we can not launch platform apps.
// Offer to switch Chrome to desktop mode.
if (win8::IsSingleWindowMetroMode()) {
chrome::AppMetroInfoBarDelegateWin::Create(
profile,
chrome::AppMetroInfoBarDelegateWin::LAUNCH_PACKAGED_APP,
extension->id());
return;
}
#endif

base::FilePath path;
if (!GetAbsolutePathFromCommandLine(command_line, current_directory, &path)) {
LaunchPlatformAppWithNoData(profile, extension);
Expand Down
46 changes: 31 additions & 15 deletions chrome/browser/ui/extensions/app_metro_infobar_delegate_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

#include "chrome/browser/ui/extensions/app_metro_infobar_delegate_win.h"

#include "apps/app_launch_for_metro_restart_win.h"
#include "base/bind_helpers.h"
#include "base/message_loop.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
Expand All @@ -25,10 +27,14 @@

namespace chrome {

void AppMetroInfoBarDelegateWin::CreateAndActivateMetro(Profile* profile) {
// static
void AppMetroInfoBarDelegateWin::Create(
Profile* profile, Mode mode, const std::string& extension_id) {
DCHECK(win8::IsSingleWindowMetroMode());
DCHECK_EQ(mode == SHOW_APP_LIST, extension_id.empty());

// Chrome should never get here via the Ash desktop, so only look for browsers
// on the native desktop.
CHECK(win8::IsSingleWindowMetroMode());
Browser* browser = FindOrCreateTabbedBrowser(
profile, chrome::HOST_DESKTOP_TYPE_NATIVE);

Expand All @@ -42,7 +48,7 @@ void AppMetroInfoBarDelegateWin::CreateAndActivateMetro(Profile* profile) {
InfoBarService* info_bar_service =
InfoBarService::FromWebContents(web_contents);
info_bar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>(
new AppMetroInfoBarDelegateWin(info_bar_service)));
new AppMetroInfoBarDelegateWin(info_bar_service, mode, extension_id)));

// Use PostTask because we can get here in a COM SendMessage, and
// ActivateApplication can not be sent nested (returns error
Expand All @@ -53,8 +59,13 @@ void AppMetroInfoBarDelegateWin::CreateAndActivateMetro(Profile* profile) {
}

AppMetroInfoBarDelegateWin::AppMetroInfoBarDelegateWin(
InfoBarService* info_bar_service)
: ConfirmInfoBarDelegate(info_bar_service) {
InfoBarService* info_bar_service,
Mode mode,
const std::string& extension_id)
: ConfirmInfoBarDelegate(info_bar_service),
mode_(mode),
extension_id_(extension_id) {
DCHECK_EQ(mode_ == SHOW_APP_LIST, extension_id_.empty());
}

AppMetroInfoBarDelegateWin::~AppMetroInfoBarDelegateWin() {}
Expand All @@ -64,8 +75,9 @@ gfx::Image* AppMetroInfoBarDelegateWin::GetIcon() const {
}

string16 AppMetroInfoBarDelegateWin::GetMessageText() const {
return l10n_util::GetStringUTF16(
IDS_WIN8_INFOBAR_DESKTOP_RESTART_TO_LAUNCH_APPS);
return l10n_util::GetStringUTF16(mode_ == SHOW_APP_LIST ?
IDS_WIN8_INFOBAR_DESKTOP_RESTART_FOR_APP_LIST :
IDS_WIN8_INFOBAR_DESKTOP_RESTART_FOR_PACKAGED_APP);
}

int AppMetroInfoBarDelegateWin::GetButtons() const {
Expand All @@ -74,19 +86,23 @@ int AppMetroInfoBarDelegateWin::GetButtons() const {

string16 AppMetroInfoBarDelegateWin::GetButtonLabel(
InfoBarButton button) const {
if (button == BUTTON_CANCEL) {
return l10n_util::GetStringUTF16(
IDS_WIN8_INFOBAR_DESKTOP_RESTART_TO_LAUNCH_APPS_NO_BUTTON);
}

return l10n_util::GetStringUTF16(
return l10n_util::GetStringUTF16(button == BUTTON_CANCEL ?
IDS_WIN8_INFOBAR_DESKTOP_RESTART_TO_LAUNCH_APPS_NO_BUTTON :
IDS_WIN8_INFOBAR_DESKTOP_RESTART_TO_LAUNCH_APPS_YES_BUTTON);
}

bool AppMetroInfoBarDelegateWin::Accept() {
owner()->GetWebContents()->Close();
PrefService* prefs = g_browser_process->local_state();
prefs->SetBoolean(prefs::kRestartWithAppList, true);
content::WebContents* web_contents = owner()->GetWebContents();
if (mode_ == SHOW_APP_LIST) {
prefs->SetBoolean(prefs::kRestartWithAppList, true);
} else {
apps::SetAppLaunchForMetroRestart(
Profile::FromBrowserContext(web_contents->GetBrowserContext()),
extension_id_);
}

web_contents->Close(); // Note: deletes |this|.
chrome::AttemptRestartWithModeSwitch();
return false;
}
Expand Down
Loading

0 comments on commit c1dbcb1

Please sign in to comment.