Commit 99a7bfe4 authored by tmdiep@chromium.org's avatar tmdiep@chromium.org

Add option to install an ephemeral app to ChromeOS shelf context menu

This is the ChromeOS implementation for adding an option to install an
ephemeral app in the shelf context menu. Includes some refactoring of
the WebstoreStandaloneInstallers to reuse common code.

BUG=339253
TEST=browser_tests (LauncherPlatformAppBrowserTest.InstallEphemeralApp)
Manual: Enable the ephemeral apps experimental feature and launch an
ephemeral app. Right-click on the app's icon in the shelf. There should
be an "Install app" item, which invoked, will install the app and make
it visible in the app launcher and NTP.

Review URL: https://codereview.chromium.org/298303002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@273749 0039d316-1c4b-4281-b951-d872f2087c98
parent 03dd3253
...@@ -5207,11 +5207,9 @@ Keep your key file in a safe place. You will need it to create new versions of y ...@@ -5207,11 +5207,9 @@ Keep your key file in a safe place. You will need it to create new versions of y
</if> </if>
<!-- Packaged Apps --> <!-- Packaged Apps -->
<if expr="is_win"> <message name="IDS_APP_INSTALL_TITLE" desc="Caption for installing an ephemeral app">
<message name="IDS_APP_INSTALL_TITLE" desc="Caption for installing an ephemeral app"> Install app
Install app </message>
</message>
</if>
<!-- Components --> <!-- Components -->
<message name="IDS_COMPONENTS_TITLE" desc="Title for the chrome://components page."> <message name="IDS_COMPONENTS_TITLE" desc="Title for the chrome://components page.">
......
// Copyright 2014 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 "chrome/browser/extensions/webstore_install_with_prompt.h"
#include "chrome/browser/extensions/webstore_installer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
#include "content/public/browser/web_contents.h"
using content::WebContents;
namespace extensions {
WebstoreInstallWithPrompt::WebstoreInstallWithPrompt(
const std::string& webstore_item_id,
Profile* profile,
const Callback& callback)
: WebstoreStandaloneInstaller(webstore_item_id, profile, callback),
dummy_web_contents_(
WebContents::Create(WebContents::CreateParams(profile))),
parent_window_(NULL) {
set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER);
}
WebstoreInstallWithPrompt::WebstoreInstallWithPrompt(
const std::string& webstore_item_id,
Profile* profile,
gfx::NativeWindow parent_window,
const Callback& callback)
: WebstoreStandaloneInstaller(webstore_item_id, profile, callback),
dummy_web_contents_(
WebContents::Create(WebContents::CreateParams(profile))),
parent_window_(parent_window) {
DCHECK(parent_window);
set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER);
}
WebstoreInstallWithPrompt::~WebstoreInstallWithPrompt() {
}
bool WebstoreInstallWithPrompt::CheckRequestorAlive() const {
// Assume the requestor is always alive.
return true;
}
const GURL& WebstoreInstallWithPrompt::GetRequestorURL() const {
return dummy_requestor_url_;
}
scoped_ptr<ExtensionInstallPrompt::Prompt>
WebstoreInstallWithPrompt::CreateInstallPrompt() const {
scoped_ptr<ExtensionInstallPrompt::Prompt> prompt(
new ExtensionInstallPrompt::Prompt(
ExtensionInstallPrompt::INSTALL_PROMPT));
return prompt.Pass();
}
scoped_ptr<ExtensionInstallPrompt>
WebstoreInstallWithPrompt::CreateInstallUI() {
// Create an ExtensionInstallPrompt. If the parent window is NULL, the dialog
// will be placed in the middle of the screen.
return make_scoped_ptr(
new ExtensionInstallPrompt(profile(), parent_window_, this));
}
bool WebstoreInstallWithPrompt::ShouldShowPostInstallUI() const {
return false;
}
bool WebstoreInstallWithPrompt::ShouldShowAppInstalledBubble() const {
return false;
}
WebContents* WebstoreInstallWithPrompt::GetWebContents() const {
return dummy_web_contents_.get();
}
bool WebstoreInstallWithPrompt::CheckInlineInstallPermitted(
const base::DictionaryValue& webstore_data,
std::string* error) const {
// Assume the requestor is trusted.
*error = std::string();
return true;
}
bool WebstoreInstallWithPrompt::CheckRequestorPermitted(
const base::DictionaryValue& webstore_data,
std::string* error) const {
// Assume the requestor is trusted.
*error = std::string();
return true;
}
content::WebContents* WebstoreInstallWithPrompt::OpenURL(
const content::OpenURLParams& params) {
chrome::ScopedTabbedBrowserDisplayer displayer(profile(),
chrome::GetActiveDesktop());
return displayer.browser()->OpenURL(params);
}
} // namespace extensions
// Copyright 2014 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 CHROME_BROWSER_EXTENSIONS_WEBSTORE_INSTALL_WITH_PROMPT_H_
#define CHROME_BROWSER_EXTENSIONS_WEBSTORE_INSTALL_WITH_PROMPT_H_
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/extensions/webstore_standalone_installer.h"
#include "content/public/browser/page_navigator.h"
#include "ui/gfx/native_widget_types.h"
#include "url/gurl.h"
namespace content {
class WebContents;
}
namespace extensions {
// Initiates the install of an extension from the webstore. Downloads and parses
// metadata from the webstore, shows an install UI and starts the download once
// the user confirms. No post-install UI is shown.
//
// Clients will be notified of success or failure via the |callback| argument
// passed into the constructor.
//
// Clients of this class must be trusted, as verification of the requestor is
// skipped. This class stubs out many WebstoreStandaloneInstaller abstract
// methods and can be used as a base class.
class WebstoreInstallWithPrompt : public WebstoreStandaloneInstaller,
public content::PageNavigator {
public:
// Use this constructor when there is no parent window. The install dialog
// will be centered on the screen.
WebstoreInstallWithPrompt(const std::string& webstore_item_id,
Profile* profile,
const Callback& callback);
// If this constructor is used, the parent of the install dialog will be
// |parent_window|.
WebstoreInstallWithPrompt(const std::string& webstore_item_id,
Profile* profile,
gfx::NativeWindow parent_window,
const Callback& callback);
protected:
friend class base::RefCountedThreadSafe<WebstoreInstallWithPrompt>;
virtual ~WebstoreInstallWithPrompt();
// extensions::WebstoreStandaloneInstaller overrides:
virtual bool CheckRequestorAlive() const OVERRIDE;
virtual const GURL& GetRequestorURL() const OVERRIDE;
virtual bool ShouldShowPostInstallUI() const OVERRIDE;
virtual bool ShouldShowAppInstalledBubble() const OVERRIDE;
virtual content::WebContents* GetWebContents() const OVERRIDE;
virtual scoped_ptr<ExtensionInstallPrompt::Prompt> CreateInstallPrompt()
const OVERRIDE;
virtual scoped_ptr<ExtensionInstallPrompt> CreateInstallUI() OVERRIDE;
virtual bool CheckInlineInstallPermitted(
const base::DictionaryValue& webstore_data,
std::string* error) const OVERRIDE;
virtual bool CheckRequestorPermitted(
const base::DictionaryValue& webstore_data,
std::string* error) const OVERRIDE;
// content::PageNavigator overrides:
virtual content::WebContents* OpenURL(
const content::OpenURLParams& params) OVERRIDE;
private:
GURL dummy_requestor_url_;
// A non-visible WebContents used to download data from the webstore.
scoped_ptr<content::WebContents> dummy_web_contents_;
gfx::NativeWindow parent_window_;
DISALLOW_COPY_AND_ASSIGN(WebstoreInstallWithPrompt);
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_WEBSTORE_INSTALL_WITH_PROMPT_H_
...@@ -4,11 +4,6 @@ ...@@ -4,11 +4,6 @@
#include "chrome/browser/extensions/webstore_startup_installer.h" #include "chrome/browser/extensions/webstore_startup_installer.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/web_contents.h"
using content::WebContents;
namespace extensions { namespace extensions {
WebstoreStartupInstaller::WebstoreStartupInstaller( WebstoreStartupInstaller::WebstoreStartupInstaller(
...@@ -16,71 +11,21 @@ WebstoreStartupInstaller::WebstoreStartupInstaller( ...@@ -16,71 +11,21 @@ WebstoreStartupInstaller::WebstoreStartupInstaller(
Profile* profile, Profile* profile,
bool show_prompt, bool show_prompt,
const Callback& callback) const Callback& callback)
: WebstoreStandaloneInstaller( : WebstoreInstallWithPrompt(webstore_item_id, profile, callback),
webstore_item_id, show_prompt_(show_prompt) {
profile, set_install_source(WebstoreInstaller::INSTALL_SOURCE_INLINE);
callback),
show_prompt_(show_prompt),
dummy_web_contents_(
WebContents::Create(WebContents::CreateParams(profile))) {
} }
WebstoreStartupInstaller::~WebstoreStartupInstaller() {} WebstoreStartupInstaller::~WebstoreStartupInstaller() {}
bool WebstoreStartupInstaller::CheckRequestorAlive() const {
// Requestor is the command line, so it's always alive.
return true;
}
const GURL& WebstoreStartupInstaller::GetRequestorURL() const {
return dummy_requestor_url_;
}
scoped_ptr<ExtensionInstallPrompt::Prompt> scoped_ptr<ExtensionInstallPrompt::Prompt>
WebstoreStartupInstaller::CreateInstallPrompt() const { WebstoreStartupInstaller::CreateInstallPrompt() const {
scoped_ptr<ExtensionInstallPrompt::Prompt> prompt; scoped_ptr<ExtensionInstallPrompt::Prompt> prompt;
if (show_prompt_) if (show_prompt_) {
prompt.reset(new ExtensionInstallPrompt::Prompt( prompt.reset(new ExtensionInstallPrompt::Prompt(
ExtensionInstallPrompt::INSTALL_PROMPT)); ExtensionInstallPrompt::INSTALL_PROMPT));
}
return prompt.Pass(); return prompt.Pass();
} }
scoped_ptr<ExtensionInstallPrompt> WebstoreStartupInstaller::CreateInstallUI() {
// The WebContents passed to ExtensionInstallPrompt is used to find a parent
// window for the dialog. This class uses a dummy WebContents and has no
// associated browser window. Pass NULL so the dialog is placed in the middle
// of the screen.
return make_scoped_ptr(new ExtensionInstallPrompt(NULL));
}
bool WebstoreStartupInstaller::ShouldShowPostInstallUI() const {
return false;
}
bool WebstoreStartupInstaller::ShouldShowAppInstalledBubble() const {
return false;
}
WebContents* WebstoreStartupInstaller::GetWebContents() const {
return dummy_web_contents_.get();
}
bool WebstoreStartupInstaller::CheckInlineInstallPermitted(
const base::DictionaryValue& webstore_data,
std::string* error) const {
// Requestor is the command line: ignore the property set in the store
// and always permit inline installs.
*error = "";
return true;
}
bool WebstoreStartupInstaller::CheckRequestorPermitted(
const base::DictionaryValue& webstore_data,
std::string* error) const {
// Requestor is the command line: always treat it as trusted.
*error = "";
return true;
}
} // namespace extensions } // namespace extensions
...@@ -5,12 +5,7 @@ ...@@ -5,12 +5,7 @@
#ifndef CHROME_BROWSER_EXTENSIONS_WEBSTORE_STARTUP_INSTALLER_H_ #ifndef CHROME_BROWSER_EXTENSIONS_WEBSTORE_STARTUP_INSTALLER_H_
#define CHROME_BROWSER_EXTENSIONS_WEBSTORE_STARTUP_INSTALLER_H_ #define CHROME_BROWSER_EXTENSIONS_WEBSTORE_STARTUP_INSTALLER_H_
#include "url/gurl.h" #include "chrome/browser/extensions/webstore_install_with_prompt.h"
#include "webstore_standalone_installer.h"
namespace content {
class WebContents;
}
namespace extensions { namespace extensions {
...@@ -21,15 +16,12 @@ namespace extensions { ...@@ -21,15 +16,12 @@ namespace extensions {
// //
// Clients will be notified of success or failure via the |callback| argument // Clients will be notified of success or failure via the |callback| argument
// passed into the constructor. // passed into the constructor.
class WebstoreStartupInstaller class WebstoreStartupInstaller : public WebstoreInstallWithPrompt {
: public WebstoreStandaloneInstaller {
public: public:
typedef WebstoreStandaloneInstaller::Callback Callback;
WebstoreStartupInstaller(const std::string& webstore_item_id, WebstoreStartupInstaller(const std::string& webstore_item_id,
Profile* profile, Profile* profile,
bool show_prompt, bool show_prompt,
const Callback& callback); const Callback& callback);
protected: protected:
friend class base::RefCountedThreadSafe<WebstoreStartupInstaller>; friend class base::RefCountedThreadSafe<WebstoreStartupInstaller>;
...@@ -37,28 +29,12 @@ class WebstoreStartupInstaller ...@@ -37,28 +29,12 @@ class WebstoreStartupInstaller
virtual ~WebstoreStartupInstaller(); virtual ~WebstoreStartupInstaller();
// Implementations WebstoreStandaloneInstaller Template Method's hooks. // Implementations of WebstoreStandaloneInstaller Template Method's hooks.
virtual bool CheckRequestorAlive() const OVERRIDE;
virtual const GURL& GetRequestorURL() const OVERRIDE;
virtual bool ShouldShowPostInstallUI() const OVERRIDE;
virtual bool ShouldShowAppInstalledBubble() const OVERRIDE;
virtual content::WebContents* GetWebContents() const OVERRIDE;
virtual scoped_ptr<ExtensionInstallPrompt::Prompt> virtual scoped_ptr<ExtensionInstallPrompt::Prompt>
CreateInstallPrompt() const OVERRIDE; CreateInstallPrompt() const OVERRIDE;
virtual scoped_ptr<ExtensionInstallPrompt> CreateInstallUI() OVERRIDE;
virtual bool CheckInlineInstallPermitted(
const base::DictionaryValue& webstore_data,
std::string* error) const OVERRIDE;
virtual bool CheckRequestorPermitted(
const base::DictionaryValue& webstore_data,
std::string* error) const OVERRIDE;
private: private:
bool show_prompt_; bool show_prompt_;
GURL dummy_requestor_url_;
// A non-visible WebContents used to download data from the webstore.
scoped_ptr<content::WebContents> dummy_web_contents_;
DISALLOW_IMPLICIT_CONSTRUCTORS(WebstoreStartupInstaller); DISALLOW_IMPLICIT_CONSTRUCTORS(WebstoreStartupInstaller);
}; };
......
...@@ -4,35 +4,20 @@ ...@@ -4,35 +4,20 @@
#include "chrome/browser/ui/app_list/search/webstore/webstore_installer.h" #include "chrome/browser/ui/app_list/search/webstore/webstore_installer.h"
#include "chrome/browser/extensions/extension_install_prompt.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
namespace app_list { namespace app_list {
WebstoreInstaller::WebstoreInstaller(const std::string& webstore_item_id, WebstoreInstaller::WebstoreInstaller(const std::string& webstore_item_id,
Profile* profile, Profile* profile,
gfx::NativeWindow parent_window, gfx::NativeWindow parent_window,
const Callback& callback) const Callback& callback)
: WebstoreStartupInstaller(webstore_item_id, profile, true, callback), : WebstoreInstallWithPrompt(webstore_item_id,
profile_(profile), profile,
parent_window_(parent_window) { parent_window,
callback) {
set_install_source( set_install_source(
extensions::WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER); extensions::WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER);
} }
WebstoreInstaller::~WebstoreInstaller() {} WebstoreInstaller::~WebstoreInstaller() {}
scoped_ptr<ExtensionInstallPrompt> WebstoreInstaller::CreateInstallUI() {
return make_scoped_ptr(
new ExtensionInstallPrompt(profile_, parent_window_, this));
}
content::WebContents* WebstoreInstaller::OpenURL(
const content::OpenURLParams& params) {
chrome::ScopedTabbedBrowserDisplayer displayer(
profile_, chrome::GetActiveDesktop());
return displayer.browser()->OpenURL(params);
}
} // namespace app_list } // namespace app_list
...@@ -5,19 +5,14 @@ ...@@ -5,19 +5,14 @@
#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_WEBSTORE_WEBSTORE_INSTALLER_H_ #ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_WEBSTORE_WEBSTORE_INSTALLER_H_
#define CHROME_BROWSER_UI_APP_LIST_SEARCH_WEBSTORE_WEBSTORE_INSTALLER_H_ #define CHROME_BROWSER_UI_APP_LIST_SEARCH_WEBSTORE_WEBSTORE_INSTALLER_H_
#include <string> #include "chrome/browser/extensions/webstore_install_with_prompt.h"
#include "base/basictypes.h"
#include "chrome/browser/extensions/webstore_startup_installer.h"
#include "content/public/browser/page_navigator.h"
class Profile; class Profile;
namespace app_list { namespace app_list {
// WebstoreInstaller handles install for web store search results. // WebstoreInstaller handles install for web store search results.
class WebstoreInstaller : public extensions::WebstoreStartupInstaller, class WebstoreInstaller : public extensions::WebstoreInstallWithPrompt {
public content::PageNavigator {
public: public:
typedef WebstoreStandaloneInstaller::Callback Callback; typedef WebstoreStandaloneInstaller::Callback Callback;
...@@ -28,19 +23,8 @@ class WebstoreInstaller : public extensions::WebstoreStartupInstaller, ...@@ -28,19 +23,8 @@ class WebstoreInstaller : public extensions::WebstoreStartupInstaller,
private: private:
friend class base::RefCountedThreadSafe<WebstoreInstaller>; friend class base::RefCountedThreadSafe<WebstoreInstaller>;
virtual ~WebstoreInstaller(); virtual ~WebstoreInstaller();
// extensions::WebstoreStartupInstaller overrides:
virtual scoped_ptr<ExtensionInstallPrompt> CreateInstallUI() OVERRIDE;
// content::PageNavigator overrides:
virtual content::WebContents* OpenURL(
const content::OpenURLParams& params) OVERRIDE;
Profile* profile_;
gfx::NativeWindow parent_window_;
DISALLOW_COPY_AND_ASSIGN(WebstoreInstaller); DISALLOW_COPY_AND_ASSIGN(WebstoreInstaller);
}; };
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "ash/shelf/shelf_model.h" #include "ash/shelf/shelf_model.h"
#include "ash/wm/window_state.h" #include "ash/wm/window_state.h"
#include "ash/wm/window_util.h" #include "ash/wm/window_util.h"
#include "chrome/browser/extensions/webstore_install_with_prompt.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h" #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.h" #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
...@@ -50,6 +51,14 @@ scoped_ptr<gfx::Image> GetAppListIcon(AppWindow* app_window) { ...@@ -50,6 +51,14 @@ scoped_ptr<gfx::Image> GetAppListIcon(AppWindow* app_window) {
new gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(bmp))); new gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(bmp)));
} }
// Returns true if the app window is visible on screen, i.e. not hidden or
// minimized.
bool IsAppWindowVisible(AppWindow* app_window) {
return app_window &&
!app_window->is_hidden() &&
!app_window->GetBaseWindow()->IsMinimized();
}
// Functor for std::find_if used in AppLauncherItemController. // Functor for std::find_if used in AppLauncherItemController.
class AppWindowHasWindow { class AppWindowHasWindow {
public: public:
...@@ -151,6 +160,38 @@ void AppWindowLauncherItemController::ActivateIndexedApp(size_t index) { ...@@ -151,6 +160,38 @@ void AppWindowLauncherItemController::ActivateIndexedApp(size_t index) {
ShowAndActivateOrMinimize(*it); ShowAndActivateOrMinimize(*it);
} }
void AppWindowLauncherItemController::InstallApp() {
// Find a visible window in order to position the install dialog. If there is
// no visible window, the dialog will be centered on the screen.
AppWindow* parent_window = NULL;
if (IsAppWindowVisible(last_active_app_window_)) {
parent_window = last_active_app_window_;
} else {
for (AppWindowList::iterator iter = app_windows_.begin();
iter != app_windows_.end(); ++iter) {
if (IsAppWindowVisible(*iter)) {
parent_window = *iter;
break;
}
}
}
scoped_refptr<extensions::WebstoreInstallWithPrompt> installer;
if (parent_window) {
installer = new extensions::WebstoreInstallWithPrompt(
app_id(),
launcher_controller()->profile(),
parent_window->GetNativeWindow(),
extensions::WebstoreInstallWithPrompt::Callback());
} else {
installer = new extensions::WebstoreInstallWithPrompt(
app_id(),
launcher_controller()->profile(),
extensions::WebstoreInstallWithPrompt::Callback());
}
installer->BeginInstall();
}
ChromeLauncherAppMenuItems AppWindowLauncherItemController::GetApplicationList( ChromeLauncherAppMenuItems AppWindowLauncherItemController::GetApplicationList(
int event_flags) { int event_flags) {
ChromeLauncherAppMenuItems items; ChromeLauncherAppMenuItems items;
......
...@@ -77,6 +77,10 @@ class AppWindowLauncherItemController : public LauncherItemController, ...@@ -77,6 +77,10 @@ class AppWindowLauncherItemController : public LauncherItemController,
// Activates the window at position |index|. // Activates the window at position |index|.
void ActivateIndexedApp(size_t index); void ActivateIndexedApp(size_t index);
// Install the app. Only valid for ephemeral apps, which can be promoted to
// regular installed apps.
void InstallApp();
private: private:
typedef std::list<apps::AppWindow*> AppWindowList; typedef std::list<apps::AppWindow*> AppWindowList;
......
...@@ -71,6 +71,7 @@ ...@@ -71,6 +71,7 @@
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system.h"
#include "extensions/browser/extension_util.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/extension_resource.h" #include "extensions/common/extension_resource.h"
#include "extensions/common/manifest_handlers/icons_handler.h" #include "extensions/common/manifest_handlers/icons_handler.h"
...@@ -579,6 +580,34 @@ bool ChromeLauncherController::IsPinnable(ash::ShelfID id) const { ...@@ -579,6 +580,34 @@ bool ChromeLauncherController::IsPinnable(ash::ShelfID id) const {
CanPin()); CanPin());
} }
void ChromeLauncherController::Install(ash::ShelfID id) {
if (!HasItemController(id))
return;
std::string app_id = GetAppIDForShelfID(id);
if (extensions::util::IsExtensionInstalledPermanently(app_id, profile_))
return;
LauncherItemController* controller = id_to_item_controller_map_[id];
if (controller->type() == LauncherItemController::TYPE_APP) {
AppWindowLauncherItemController* app_window_controller =
static_cast<AppWindowLauncherItemController*>(controller);
app_window_controller->InstallApp();
}
}
bool ChromeLauncherController::CanInstall(ash::ShelfID id) {
int index = model_->ItemIndexByID(id);
if (index == -1)
return false;
ash::ShelfItemType type = model_->items()[index].type;
if (type != ash::TYPE_PLATFORM_APP)
return false;
return extensions::util::IsEphemeralApp(GetAppIDForShelfID(id), profile_);
}
void ChromeLauncherController::LockV1AppWithID( void ChromeLauncherController::LockV1AppWithID(
const std::string& app_id) { const std::string& app_id) {
ash::ShelfID id = GetShelfIDForAppID(app_id); ash::ShelfID id = GetShelfIDForAppID(app_id);
......
...@@ -179,6 +179,14 @@ class ChromeLauncherController : public ash::ShelfDelegate, ...@@ -179,6 +179,14 @@ class ChromeLauncherController : public ash::ShelfDelegate,
// be pinned. // be pinned.
bool IsPinnable(ash::ShelfID id) const; bool IsPinnable(ash::ShelfID id) const;
// Installs the specified id. Only valid if the id corresponds to an ephemeral
// app.
void Install(ash::ShelfID id);
// Returns true if the specified item can be installed. Only true for
// ephemeral apps.
bool CanInstall(ash::ShelfID id);
// If there is no shelf item in the shelf for application |app_id|, one // If there is no shelf item in the shelf for application |app_id|, one
// gets created. The (existing or created) shelf items get then locked // gets created. The (existing or created) shelf items get then locked
// against a users un-pinning removal. // against a users un-pinning removal.
......
...@@ -631,6 +631,47 @@ IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, WindowActivation) { ...@@ -631,6 +631,47 @@ IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, WindowActivation) {
EXPECT_EQ(item_count, shelf_model()->item_count()); EXPECT_EQ(item_count, shelf_model()->item_count());
} }
// Verify that ChromeLauncherController::CanInstall() returns true for ephemeral
// apps and false when the app is promoted to a regular installed app.
IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, InstallEphemeralApp) {
int item_count = shelf_model()->item_count();
// Sanity check to verify that ChromeLauncherController::CanInstall() returns
// false for apps that are fully installed.
const Extension* app = LoadAndLaunchPlatformApp("launch");
ASSERT_TRUE(app);
CreateAppWindow(app);
++item_count;
ASSERT_EQ(item_count, shelf_model()->item_count());
const ash::ShelfItem& app_item = GetLastLauncherItem();
ash::ShelfID app_id = app_item.id;
EXPECT_FALSE(controller_->CanInstall(app_id));
// Add an ephemeral app.
const Extension* ephemeral_app = InstallEphemeralAppWithSourceAndFlags(
test_data_dir_.AppendASCII("platform_apps").AppendASCII("launch_2"),
1,
extensions::Manifest::INTERNAL,
Extension::NO_FLAGS);
ASSERT_TRUE(ephemeral_app);
CreateAppWindow(ephemeral_app);
++item_count;
ASSERT_EQ(item_count, shelf_model()->item_count());
const ash::ShelfItem& ephemeral_item = GetLastLauncherItem();
ash::ShelfID ephemeral_id = ephemeral_item.id;
// Verify that the shelf item for the ephemeral app can be installed.
EXPECT_TRUE(controller_->CanInstall(ephemeral_id));
// Promote the ephemeral app to a regular installed app.
ExtensionService* service =
extensions::ExtensionSystem::Get(profile())->extension_service();
service->PromoteEphemeralApp(ephemeral_app, false);
// Verify that the shelf item for the app can no longer be installed.
EXPECT_FALSE(controller_->CanInstall(ephemeral_id));
}
// Confirm that Click behavior for app windows is correnct. // Confirm that Click behavior for app windows is correnct.
IN_PROC_BROWSER_TEST_F(ShelfAppBrowserNoMinimizeOnClick, AppClickBehavior) { IN_PROC_BROWSER_TEST_F(ShelfAppBrowserNoMinimizeOnClick, AppClickBehavior) {
// Launch a platform app and create a window for it. // Launch a platform app and create a window for it.
......
...@@ -146,6 +146,7 @@ void LauncherContextMenu::Init() { ...@@ -146,6 +146,7 @@ void LauncherContextMenu::Init() {
AddItem( AddItem(
MENU_PIN, MENU_PIN,
l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_PIN)); l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_PIN));
AddItem(MENU_INSTALL, l10n_util::GetStringUTF16(IDS_APP_INSTALL_TITLE));
} }
if (controller_->IsOpen(item_.id)) { if (controller_->IsOpen(item_.id)) {
AddItem(MENU_CLOSE, AddItem(MENU_CLOSE,
...@@ -260,6 +261,20 @@ bool LauncherContextMenu::IsCommandIdEnabled(int command_id) const { ...@@ -260,6 +261,20 @@ bool LauncherContextMenu::IsCommandIdEnabled(int command_id) const {
} }
} }
bool LauncherContextMenu::IsCommandIdVisible(int command_id) const {
if (item_.type != ash::TYPE_PLATFORM_APP)
return true;
switch (command_id) {
case MENU_PIN:
return !controller_->CanInstall(item_.id);
case MENU_INSTALL:
return controller_->CanInstall(item_.id);
default:
return true;
}
}
bool LauncherContextMenu::GetAcceleratorForCommandId( bool LauncherContextMenu::GetAcceleratorForCommandId(
int command_id, int command_id,
ui::Accelerator* accelerator) { ui::Accelerator* accelerator) {
...@@ -285,6 +300,9 @@ void LauncherContextMenu::ExecuteCommand(int command_id, int event_flags) { ...@@ -285,6 +300,9 @@ void LauncherContextMenu::ExecuteCommand(int command_id, int event_flags) {
case MENU_PIN: case MENU_PIN:
controller_->TogglePinned(item_.id); controller_->TogglePinned(item_.id);
break; break;
case MENU_INSTALL:
controller_->Install(item_.id);
break;
case LAUNCH_TYPE_PINNED_TAB: case LAUNCH_TYPE_PINNED_TAB:
controller_->SetLaunchType(item_.id, extensions::LAUNCH_TYPE_PINNED); controller_->SetLaunchType(item_.id, extensions::LAUNCH_TYPE_PINNED);
break; break;
......
...@@ -57,6 +57,7 @@ class LauncherContextMenu : public ui::SimpleMenuModel, ...@@ -57,6 +57,7 @@ class LauncherContextMenu : public ui::SimpleMenuModel,
virtual base::string16 GetLabelForCommandId(int command_id) const OVERRIDE; virtual base::string16 GetLabelForCommandId(int command_id) const OVERRIDE;
virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
virtual bool IsCommandIdVisible(int command_id) const OVERRIDE;
virtual bool GetAcceleratorForCommandId( virtual bool GetAcceleratorForCommandId(
int command_id, int command_id,
ui::Accelerator* accelerator) OVERRIDE; ui::Accelerator* accelerator) OVERRIDE;
...@@ -77,6 +78,7 @@ class LauncherContextMenu : public ui::SimpleMenuModel, ...@@ -77,6 +78,7 @@ class LauncherContextMenu : public ui::SimpleMenuModel,
MENU_OPEN_NEW, MENU_OPEN_NEW,
MENU_CLOSE, MENU_CLOSE,
MENU_PIN, MENU_PIN,
MENU_INSTALL,
LAUNCH_TYPE_PINNED_TAB, LAUNCH_TYPE_PINNED_TAB,
LAUNCH_TYPE_REGULAR_TAB, LAUNCH_TYPE_REGULAR_TAB,
LAUNCH_TYPE_FULLSCREEN, LAUNCH_TYPE_FULLSCREEN,
......
...@@ -925,6 +925,8 @@ ...@@ -925,6 +925,8 @@
'browser/extensions/webstore_inline_installer_factory.h', 'browser/extensions/webstore_inline_installer_factory.h',
'browser/extensions/webstore_install_helper.cc', 'browser/extensions/webstore_install_helper.cc',
'browser/extensions/webstore_install_helper.h', 'browser/extensions/webstore_install_helper.h',
'browser/extensions/webstore_install_with_prompt.cc',
'browser/extensions/webstore_install_with_prompt.h',
'browser/extensions/webstore_installer.cc', 'browser/extensions/webstore_installer.cc',
'browser/extensions/webstore_installer.h', 'browser/extensions/webstore_installer.h',
'browser/extensions/webstore_standalone_installer.cc', 'browser/extensions/webstore_standalone_installer.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment