summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-21 19:57:06 +0000
committerjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-21 19:57:06 +0000
commit32c440643c4676ed450353c1811588bbd59415bb (patch)
tree9078c683d56b9e3ffd427af2abbe00e94d5bfea3
parent500d24e7c84a4bb55315dcb4dd612e3d3800b379 (diff)
downloadchromium_src-32c440643c4676ed450353c1811588bbd59415bb.zip
chromium_src-32c440643c4676ed450353c1811588bbd59415bb.tar.gz
chromium_src-32c440643c4676ed450353c1811588bbd59415bb.tar.bz2
Revert 218674 "Remove PerBrowser launcher"
> Remove PerBrowser launcher > > Remove per-browser.* and related tests. > Remove IsPerAppLauncher() interface in LauncherDelegate. > Remove ash-disable-per-app-launcher switch. > Change class name from ChromeLauncherControllerPerApp to ChromeLauncherController. > > In the next CL, BrowserLauncherItemController will be replaced with new browser status monitor only for monitoring browser and tab status. > > R=skuhne@chromium.org,jamescook@chromium.org > BUG=169303 > TEST=unit_tests, browser_tests > > Review URL: https://chromiumcodereview.appspot.com/22887015 TBR=simon.hong81@gmail.com Review URL: https://codereview.chromium.org/22903020 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@218808 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ash/ash_switches.cc3
-rw-r--r--ash/ash_switches.h1
-rw-r--r--ash/launcher/launcher.cc5
-rw-r--r--ash/launcher/launcher.h2
-rw-r--r--ash/launcher/launcher_delegate.h5
-rw-r--r--ash/shell/launcher_delegate_impl.cc4
-rw-r--r--ash/shell/launcher_delegate_impl.h1
-rw-r--r--ash/test/test_launcher_delegate.cc4
-rw-r--r--ash/test/test_launcher_delegate.h1
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/about_flags.cc7
-rw-r--r--chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc30
-rw-r--r--chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h9
-rw-r--r--chrome/browser/ui/ash/launcher/browser_launcher_item_controller.cc74
-rw-r--r--chrome/browser/ui/ash/launcher/browser_launcher_item_controller_unittest.cc543
-rw-r--r--chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc16
-rw-r--r--chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h9
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.cc2
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.cc2
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.cc4
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.h6
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc1647
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_controller.h440
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc1060
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.cc1678
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h508
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app_browsertest.cc1616
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app_unittest.cc (renamed from chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc)233
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser.cc1476
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser.h430
-rw-r--r--chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser_unittest.cc462
-rw-r--r--chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.cc2
-rw-r--r--chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h7
-rw-r--r--chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc14
-rw-r--r--chrome/browser/ui/ash/launcher/shell_window_launcher_item_controller.cc20
-rw-r--r--chrome/chrome_browser_ui.gypi4
-rw-r--r--chrome/chrome_tests.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi4
38 files changed, 7185 insertions, 3152 deletions
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index c046bd1..acb106f 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -66,6 +66,9 @@ const char kAshDisableDisplayChangeLimiter[] =
// If present new lock animations are enabled.
const char kAshDisableNewLockAnimations[] = "ash-disable-new-lock-animations";
+// Disable the per application grouping version of the launcher.
+const char kAshDisablePerAppLauncher[] = "ash-disable-per-app-launcher";
+
// Disable immersive fullscreen mode, regardless of default setting.
const char kAshDisableImmersiveFullscreen[] =
"ash-disable-immersive-fullscreen";
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index b86d3ff..5fe137c 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -34,6 +34,7 @@ ASH_EXPORT extern const char kAshDisableAutoWindowPlacement[];
ASH_EXPORT extern const char kAshDisableDisplayChangeLimiter[];
ASH_EXPORT extern const char kAshDisableImmersiveFullscreen[];
ASH_EXPORT extern const char kAshDisableNewLockAnimations[];
+ASH_EXPORT extern const char kAshDisablePerAppLauncher[];
ASH_EXPORT extern const char kAshDisableDragAndDropAppListToLauncher[];
#if defined(OS_CHROMEOS)
ASH_EXPORT extern const char kAshDisableSoftwareMirroring[];
diff --git a/ash/launcher/launcher.cc b/ash/launcher/launcher.cc
index 008646de..5bd1262 100644
--- a/ash/launcher/launcher.cc
+++ b/ash/launcher/launcher.cc
@@ -169,7 +169,10 @@ void Launcher::LaunchAppIndexAt(int item_index) {
// There are two ways how found_index can be valid: a.) the nth item was
// found (which is true when indexes_left is -1) or b.) the last item was
// requested (which is true when index was passed in as a negative number).
- if (found_index >= 0 && (indexes_left == -1 || item_index < 0)) {
+ if (found_index >= 0 && (indexes_left == -1 || item_index < 0) &&
+ (delegate_->IsPerAppLauncher() ||
+ (items[found_index].status == ash::STATUS_RUNNING ||
+ items[found_index].status == ash::STATUS_CLOSED))) {
// Then set this one as active (or advance to the next item of its kind).
ActivateLauncherItem(found_index);
}
diff --git a/ash/launcher/launcher.h b/ash/launcher/launcher.h
index c3acf44..6781058 100644
--- a/ash/launcher/launcher.h
+++ b/ash/launcher/launcher.h
@@ -101,6 +101,8 @@ class ASH_EXPORT Launcher {
// TODO(sky): remove this!
internal::LauncherView* GetLauncherViewForTest();
+ LauncherDelegate* delegate() { return delegate_; }
+
ShelfWidget* shelf_widget() { return shelf_widget_; }
// Set the bounds of the launcher view.
diff --git a/ash/launcher/launcher_delegate.h b/ash/launcher/launcher_delegate.h
index 322df38..f835aac 100644
--- a/ash/launcher/launcher_delegate.h
+++ b/ash/launcher/launcher_delegate.h
@@ -46,7 +46,7 @@ class ASH_EXPORT LauncherDelegate {
// and has an instance of |views::View| as the event target
// but not |aura::Window|. If the |event| is of type KeyEvent, it is assumed
// that this was triggered by keyboard action (Alt+<number>) and special
- // handling might happen.
+ // handling might happen (PerApp launcher).
virtual void ItemSelected(const LauncherItem& item,
const ui::Event& event) = 0;
@@ -92,6 +92,9 @@ class ASH_EXPORT LauncherDelegate {
// exists.
virtual void OnLauncherDestroyed(Launcher* launcher) = 0;
+ // True if the running launcher is the per application launcher.
+ virtual bool IsPerAppLauncher() = 0;
+
// Get the launcher ID from an application ID.
virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) = 0;
diff --git a/ash/shell/launcher_delegate_impl.cc b/ash/shell/launcher_delegate_impl.cc
index ac0aeee..51f6171 100644
--- a/ash/shell/launcher_delegate_impl.cc
+++ b/ash/shell/launcher_delegate_impl.cc
@@ -64,6 +64,10 @@ void LauncherDelegateImpl::OnLauncherCreated(Launcher* launcher) {
void LauncherDelegateImpl::OnLauncherDestroyed(Launcher* launcher) {
}
+bool LauncherDelegateImpl::IsPerAppLauncher() {
+ return false;
+}
+
LauncherID LauncherDelegateImpl::GetLauncherIDForAppID(
const std::string& app_id) {
return 0;
diff --git a/ash/shell/launcher_delegate_impl.h b/ash/shell/launcher_delegate_impl.h
index 86a3579..4788029 100644
--- a/ash/shell/launcher_delegate_impl.h
+++ b/ash/shell/launcher_delegate_impl.h
@@ -39,6 +39,7 @@ class LauncherDelegateImpl : public ash::LauncherDelegate {
virtual bool ShouldShowTooltip(const LauncherItem& item) OVERRIDE;
virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE;
virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE;
+ virtual bool IsPerAppLauncher() OVERRIDE;
virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE;
virtual void PinAppWithID(const std::string& app_id) OVERRIDE;
virtual bool IsAppPinned(const std::string& app_id) OVERRIDE;
diff --git a/ash/test/test_launcher_delegate.cc b/ash/test/test_launcher_delegate.cc
index ed3e517..0830d32 100644
--- a/ash/test/test_launcher_delegate.cc
+++ b/ash/test/test_launcher_delegate.cc
@@ -126,6 +126,10 @@ void TestLauncherDelegate::OnLauncherCreated(Launcher* launcher) {
void TestLauncherDelegate::OnLauncherDestroyed(Launcher* launcher) {
}
+bool TestLauncherDelegate::IsPerAppLauncher() {
+ return true;
+}
+
LauncherID TestLauncherDelegate::GetLauncherIDForAppID(
const std::string& app_id) {
return 0;
diff --git a/ash/test/test_launcher_delegate.h b/ash/test/test_launcher_delegate.h
index 06d0cde..6214b8d 100644
--- a/ash/test/test_launcher_delegate.h
+++ b/ash/test/test_launcher_delegate.h
@@ -51,6 +51,7 @@ class TestLauncherDelegate : public LauncherDelegate,
virtual bool ShouldShowTooltip(const LauncherItem& item) OVERRIDE;
virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE;
virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE;
+ virtual bool IsPerAppLauncher() OVERRIDE;
virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE;
virtual void PinAppWithID(const std::string& app_id) OVERRIDE;
virtual bool IsAppPinned(const std::string& app_id) OVERRIDE;
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index f7a3caa..ab09e5a 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5971,6 +5971,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_ASH_AUTO_MAXIMIZING_DESCRIPTION" desc="Description for the option to disable the auto window maximizing functionality.">
Disable automatic window maximization for browser / app windows if they are started the first time.
</message>
+ <message name="IDS_FLAGS_ASH_DISABLE_PER_APP_LAUNCHER_NAME" desc="Name for the option to enable/disable the per application sorting launcher functionality.">
+ Disable per application sorting in the launcher.
+ </message>
+ <message name="IDS_FLAGS_ASH_DISABLE_PER_APP_LAUNCHER_DESCRIPTION" desc="Description for the option to enable/disable the per application sorting launcher functionality.">
+ Disable the per application sorting mode of the launcher.
+ </message>
<message name="IDS_FLAGS_DISABLE_GESTURE_REQUIREMENT_FOR_MEDIA_PLAYBACK_NAME" desc="Title for the flag to disable gesture requiment for media playback">
Disable gesture requirement for media playback.
</message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0334972..399d30a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -783,6 +783,13 @@ const Experiment kExperiments[] = {
kOsWin | kOsLinux | kOsCrOS,
SINGLE_VALUE_TYPE(ash::switches::kAshDisableAutoWindowPlacement)
},
+ {
+ "ash-disable-per-app-launcher",
+ IDS_FLAGS_ASH_DISABLE_PER_APP_LAUNCHER_NAME,
+ IDS_FLAGS_ASH_DISABLE_PER_APP_LAUNCHER_DESCRIPTION,
+ kOsWin | kOsLinux | kOsCrOS,
+ SINGLE_VALUE_TYPE(ash::switches::kAshDisablePerAppLauncher)
+ },
#endif
{
"per-tile-painting",
diff --git a/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc
index bdb625f..7cf4554 100644
--- a/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc
@@ -12,6 +12,7 @@
#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
@@ -39,9 +40,9 @@ const int kClickSuppressionInMS = 1000;
// item with the appropriate LauncherItemController type).
AppShortcutLauncherItemController::AppShortcutLauncherItemController(
const std::string& app_id,
- ChromeLauncherController* controller)
+ ChromeLauncherControllerPerApp* controller)
: LauncherItemController(TYPE_SHORTCUT, app_id, controller),
- chrome_launcher_controller_(controller) {
+ app_controller_(controller) {
// To detect V1 applications we use their domain and match them against the
// used URL. This will also work with applications like Google Drive.
const Extension* extension =
@@ -67,7 +68,7 @@ bool AppShortcutLauncherItemController::IsCurrentlyShownInWindow(
browser ? browser->tab_strip_model()->GetActiveWebContents() : NULL;
std::vector<content::WebContents*> content =
- chrome_launcher_controller_->GetV1ApplicationsFromAppId(app_id());
+ app_controller_->GetV1ApplicationsFromAppId(app_id());
std::vector<content::WebContents*>::const_iterator iter =
std::find(content.begin(), content.end(), active_content_of_window);
@@ -76,14 +77,13 @@ bool AppShortcutLauncherItemController::IsCurrentlyShownInWindow(
}
bool AppShortcutLauncherItemController::IsOpen() const {
- return !chrome_launcher_controller_->
- GetV1ApplicationsFromAppId(app_id()).empty();
+ return !app_controller_->GetV1ApplicationsFromAppId(app_id()).empty();
}
bool AppShortcutLauncherItemController::IsVisible() const {
// Return true if any browser window associated with the app is visible.
std::vector<content::WebContents*> content =
- chrome_launcher_controller_->GetV1ApplicationsFromAppId(app_id());
+ app_controller_->GetV1ApplicationsFromAppId(app_id());
for (size_t i = 0; i < content.size(); i++) {
Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
if (browser && browser->window()->GetNativeWindow()->IsVisible())
@@ -93,7 +93,7 @@ bool AppShortcutLauncherItemController::IsVisible() const {
}
void AppShortcutLauncherItemController::Launch(int event_flags) {
- launcher_controller()->LaunchApp(app_id(), event_flags);
+ app_controller_->LaunchApp(app_id(), event_flags);
}
void AppShortcutLauncherItemController::Activate() {
@@ -118,7 +118,7 @@ void AppShortcutLauncherItemController::Activate() {
void AppShortcutLauncherItemController::Close() {
// Close all running 'programs' of this type.
std::vector<content::WebContents*> content =
- launcher_controller()->GetV1ApplicationsFromAppId(app_id());
+ app_controller_->GetV1ApplicationsFromAppId(app_id());
for (size_t i = 0; i < content.size(); i++) {
Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
if (!browser)
@@ -161,8 +161,8 @@ AppShortcutLauncherItemController::GetApplicationList(int event_flags) {
for (size_t i = 0; i < content_list.size(); i++) {
content::WebContents* web_contents = content_list[i];
// Get the icon.
- gfx::Image app_icon = launcher_controller()->GetAppListIcon(web_contents);
- string16 title = launcher_controller()->GetAppListTitle(web_contents);
+ gfx::Image app_icon = app_controller_->GetAppListIcon(web_contents);
+ string16 title = app_controller_->GetAppListTitle(web_contents);
items.push_back(new ChromeLauncherAppMenuItemTab(
title, &app_icon, web_contents, i == 0));
}
@@ -265,8 +265,8 @@ bool AppShortcutLauncherItemController::WebContentMatchesApp(
refocus_pattern.MatchesURL(tab_url)) ||
(extension->OverlapsWithOrigin(tab_url) &&
extension->web_extent().MatchesURL(tab_url)) ||
- launcher_controller()->IsWebContentHandledByApplication(web_contents,
- app_id()));
+ launcher_controller()->GetPerAppInterface()->
+ IsWebContentHandledByApplication(web_contents, app_id()));
}
void AppShortcutLauncherItemController::ActivateContent(
@@ -279,8 +279,7 @@ void AppShortcutLauncherItemController::ActivateContent(
int old_index = tab_strip->active_index();
if (index != old_index)
tab_strip->ActivateTabAt(index, false);
- launcher_controller()->ActivateWindowOrMinimizeIfActive(
- browser->window(),
+ app_controller_->ActivateWindowOrMinimizeIfActive(browser->window(),
index == old_index && GetRunningApplications().size() == 1);
}
@@ -313,8 +312,7 @@ bool AppShortcutLauncherItemController::AdvanceToNextApp() {
}
bool AppShortcutLauncherItemController::IsV2App() {
- const Extension* extension =
- launcher_controller()->GetExtensionForAppID(app_id());
+ const Extension* extension = app_controller_->GetExtensionForAppID(app_id());
return extension && extension->is_platform_app();
}
diff --git a/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h
index bf8ab68..bae7e3c 100644
--- a/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h
@@ -8,10 +8,8 @@
#include <string>
#include "base/time/time.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
-#include "url/gurl.h"
-
-class URLPattern;
namespace aura {
class Window;
@@ -29,7 +27,7 @@ class ChromeLauncherController;
class AppShortcutLauncherItemController : public LauncherItemController {
public:
AppShortcutLauncherItemController(const std::string& app_id,
- ChromeLauncherController* controller);
+ ChromeLauncherControllerPerApp* controller);
virtual ~AppShortcutLauncherItemController();
@@ -81,13 +79,12 @@ class AppShortcutLauncherItemController : public LauncherItemController {
bool AllowNextLaunchAttempt();
GURL refocus_url_;
+ ChromeLauncherControllerPerApp* app_controller_;
// Since V2 applications can be undetectable after launching, this timer is
// keeping track of the last launch attempt.
base::Time last_launch_attempt_;
- ChromeLauncherController* chrome_launcher_controller_;
-
DISALLOW_COPY_AND_ASSIGN(AppShortcutLauncherItemController);
};
diff --git a/chrome/browser/ui/ash/launcher/browser_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/browser_launcher_item_controller.cc
index 04668bb..a2c958e 100644
--- a/chrome/browser/ui/ash/launcher/browser_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/browser_launcher_item_controller.cc
@@ -63,11 +63,17 @@ void BrowserLauncherItemController::Init() {
ash::LauncherItemStatus app_status =
ash::wm::IsActiveWindow(window_) ?
ash::STATUS_ACTIVE : ash::STATUS_RUNNING;
- if (type() != TYPE_TABBED && type() != TYPE_WINDOWED_APP)
+ if (type() != TYPE_TABBED && type() != TYPE_WINDOWED_APP) {
launcher_controller()->CreateAppLauncherItem(this, app_id(), app_status);
- else if (type() == TYPE_WINDOWED_APP)
- launcher_controller()->LockV1AppWithID(LauncherItemController::app_id());
-
+ } else {
+ launcher_controller()->CreateTabbedLauncherItem(
+ this,
+ is_incognito_ ? ChromeLauncherController::STATE_INCOGNITO :
+ ChromeLauncherController::STATE_NOT_INCOGNITO,
+ app_status);
+ if (type() == TYPE_WINDOWED_APP)
+ launcher_controller()->LockV1AppWithID(LauncherItemController::app_id());
+ }
// In testing scenarios we can get tab strips with no active contents.
if (tab_model_->active_index() != TabStripModel::kNoTab)
UpdateLauncher(tab_model_->GetActiveWebContents());
@@ -86,7 +92,8 @@ BrowserLauncherItemController* BrowserLauncherItemController::Create(
type = TYPE_TABBED;
if (!browser->is_type_tabbed() &&
browser->is_type_popup() &&
- browser->is_app()) {
+ browser->is_app() &&
+ ChromeLauncherController::instance()->GetPerAppInterface()) {
app_id = web_app::GetExtensionIdFromApplicationName(
browser->app_name());
// Only allow this for known applications. Some unit tests for example
@@ -173,6 +180,11 @@ void BrowserLauncherItemController::OnRemoved() {
void BrowserLauncherItemController::LauncherItemChanged(
int index,
const ash::LauncherItem& old_item) {
+ if (!launcher_controller()->GetPerAppInterface() &&
+ launcher_model()->items()[index].status == ash::STATUS_ACTIVE &&
+ old_item.status == ash::STATUS_RUNNING) {
+ Activate();
+ }
}
ChromeLauncherAppMenuItems
@@ -189,8 +201,9 @@ void BrowserLauncherItemController::ActiveTabChanged(
int reason) {
// Update immediately on a tab change.
if (old_contents &&
- TabStripModel::kNoTab !=
- tab_model_->GetIndexOfWebContents(old_contents))
+ (!launcher_controller()->GetPerAppInterface() ||
+ TabStripModel::kNoTab !=
+ tab_model_->GetIndexOfWebContents(old_contents)))
UpdateAppState(old_contents);
UpdateAppState(new_contents);
UpdateLauncher(new_contents);
@@ -244,15 +257,60 @@ void BrowserLauncherItemController::OnWindowPropertyChanged(
}
void BrowserLauncherItemController::UpdateItemStatus() {
+ if (launcher_controller()->GetPerAppInterface())
+ return;
+
+ ash::LauncherItemStatus status;
+ if (ash::wm::IsActiveWindow(window_)) {
+ // Clear attention state if active.
+ if (window_->GetProperty(aura::client::kDrawAttentionKey))
+ window_->SetProperty(aura::client::kDrawAttentionKey, false);
+ status = ash::STATUS_ACTIVE;
+ } else if (window_->GetProperty(aura::client::kDrawAttentionKey)) {
+ status = ash::STATUS_ATTENTION;
+ } else {
+ status = ash::STATUS_RUNNING;
+ }
+ launcher_controller()->SetItemStatus(launcher_id(), status);
}
void BrowserLauncherItemController::UpdateLauncher(content::WebContents* tab) {
+ if (launcher_controller()->GetPerAppInterface())
+ return;
+
+ if (type() == TYPE_APP_PANEL)
+ return; // Maintained entirely by ChromeLauncherController.
+
+ if (!tab)
+ return; // Assume the window is going to be closed if there are no tabs.
+
+ int item_index = launcher_model()->ItemIndexByID(launcher_id());
+ if (item_index == -1)
+ return;
+
+ ash::LauncherItem item = launcher_model()->items()[item_index];
+ DCHECK_EQ(TYPE_TABBED, type());
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ FaviconTabHelper* favicon_tab_helper =
+ FaviconTabHelper::FromWebContents(tab);
+ if (favicon_tab_helper->ShouldDisplayFavicon()) {
+ item.image = favicon_tab_helper->GetFavicon().AsImageSkia();
+ if (item.image.isNull()) {
+ item.image = *rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON);
+ }
+ } else {
+ item.image = *rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON);
+ }
+ launcher_model()->Set(item_index, item);
}
void BrowserLauncherItemController::UpdateAppState(content::WebContents* tab) {
ChromeLauncherController::AppState app_state;
- if (tab_model_->GetActiveWebContents() == tab) {
+ if (!launcher_controller()->GetPerAppInterface() &&
+ tab_model_->GetIndexOfWebContents(tab) == TabStripModel::kNoTab) {
+ app_state = ChromeLauncherController::APP_STATE_REMOVED;
+ } else if (tab_model_->GetActiveWebContents() == tab) {
if (ash::wm::IsActiveWindow(window_))
app_state = ChromeLauncherController::APP_STATE_WINDOW_ACTIVE;
else
diff --git a/chrome/browser/ui/ash/launcher/browser_launcher_item_controller_unittest.cc b/chrome/browser/ui/ash/launcher/browser_launcher_item_controller_unittest.cc
new file mode 100644
index 0000000..0f20bc2
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/browser_launcher_item_controller_unittest.cc
@@ -0,0 +1,543 @@
+// Copyright (c) 2012 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/ui/ash/launcher/browser_launcher_item_controller.h"
+
+#include <map>
+#include <string>
+
+#include "ash/ash_switches.h"
+#include "ash/launcher/launcher_model.h"
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/favicon/favicon_tab_helper.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/web_contents.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura/client/activation_change_observer.h"
+#include "ui/aura/client/activation_delegate.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/test/test_activation_client.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/base/events/event.h"
+
+// TODO(skuhne): Remove this module together with the
+// browser_launcher_item_controller.* when the old launcher goes away.
+
+namespace {
+
+// Test implementation of AppTabHelper.
+class AppTabHelperImpl : public ChromeLauncherController::AppTabHelper {
+ public:
+ AppTabHelperImpl() {}
+ virtual ~AppTabHelperImpl() {}
+
+ // Sets the id for the specified tab. The id is removed if Remove() is
+ // invoked.
+ void SetAppID(content::WebContents* tab, const std::string& id) {
+ tab_id_map_[tab] = id;
+ }
+
+ // Returns true if there is an id registered for |tab|.
+ bool HasAppID(content::WebContents* tab) const {
+ return tab_id_map_.find(tab) != tab_id_map_.end();
+ }
+
+ // AppTabHelper implementation:
+ virtual std::string GetAppID(content::WebContents* tab) OVERRIDE {
+ return tab_id_map_.find(tab) != tab_id_map_.end() ? tab_id_map_[tab] :
+ std::string();
+ }
+
+ virtual bool IsValidID(const std::string& id) OVERRIDE {
+ for (TabToStringMap::const_iterator i = tab_id_map_.begin();
+ i != tab_id_map_.end(); ++i) {
+ if (i->second == id)
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ typedef std::map<content::WebContents*, std::string> TabToStringMap;
+
+ TabToStringMap tab_id_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppTabHelperImpl);
+};
+
+// Test implementation of AppIconLoader.
+class AppIconLoaderImpl : public extensions::AppIconLoader {
+ public:
+ AppIconLoaderImpl() : fetch_count_(0) {}
+ virtual ~AppIconLoaderImpl() {}
+
+ // Returns the number of times FetchImage() has been invoked and resets the
+ // count to 0.
+ int GetAndClearFetchCount() {
+ int value = fetch_count_;
+ fetch_count_ = 0;
+ return value;
+ }
+
+ // AppIconLoader implementation:
+ virtual void FetchImage(const std::string& id) OVERRIDE {
+ fetch_count_++;
+ }
+ virtual void ClearImage(const std::string& id) OVERRIDE {
+ }
+ virtual void UpdateImage(const std::string& id) OVERRIDE {
+ }
+
+ private:
+ int fetch_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppIconLoaderImpl);
+};
+
+// Test implementation of TabStripModelDelegate.
+class TabHelperTabStripModelDelegate : public TestTabStripModelDelegate {
+ public:
+ TabHelperTabStripModelDelegate() {}
+ virtual ~TabHelperTabStripModelDelegate() {}
+
+ virtual void WillAddWebContents(content::WebContents* contents) OVERRIDE {
+ // BrowserLauncherItemController assumes that all WebContents passed to it
+ // have attached an extensions::TabHelper and a FaviconTabHelper. The
+ // TestTabStripModelDelegate adds an extensions::TabHelper.
+ TestTabStripModelDelegate::WillAddWebContents(contents);
+ FaviconTabHelper::CreateForWebContents(contents);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TabHelperTabStripModelDelegate);
+};
+
+} // namespace
+
+// TODO(skuhne): Several of these unit tests need to be moved into a new home
+// when the old launcher & the browser launcher item controller are removed
+// (several of these tests are not testing the BrowserLauncherItemController -
+// but the LauncherController framework).
+class LauncherItemControllerPerAppTest
+ : public ChromeRenderViewHostTestHarness {
+ public:
+ virtual void SetUp() OVERRIDE {
+ ChromeRenderViewHostTestHarness::SetUp();
+
+ activation_client_.reset(
+ new aura::test::TestActivationClient(root_window()));
+ launcher_model_.reset(new ash::LauncherModel);
+ launcher_delegate_.reset(
+ ChromeLauncherController::CreateInstance(profile(),
+ launcher_model_.get()));
+ app_tab_helper_ = new AppTabHelperImpl;
+ app_icon_loader_ = new AppIconLoaderImpl;
+ launcher_delegate_->SetAppTabHelperForTest(app_tab_helper_);
+ launcher_delegate_->SetAppIconLoaderForTest(app_icon_loader_);
+ launcher_delegate_->Init();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ launcher_delegate_.reset();
+ ChromeRenderViewHostTestHarness::TearDown();
+ }
+
+ protected:
+ // Contains all the objects needed to create a BrowserLauncherItemController.
+ struct State : public aura::client::ActivationDelegate,
+ public aura::client::ActivationChangeObserver {
+ public:
+ State(LauncherItemControllerPerAppTest* test,
+ const std::string& app_id,
+ BrowserLauncherItemController::Type launcher_type)
+ : launcher_test(test),
+ window(NULL),
+ tab_strip(&tab_strip_delegate, test->profile()),
+ updater(launcher_type,
+ &window,
+ &tab_strip,
+ test->launcher_delegate_.get(),
+ app_id) {
+ window.Init(ui::LAYER_NOT_DRAWN);
+ launcher_test->root_window()->AddChild(&window);
+ launcher_test->activation_client_->ActivateWindow(&window);
+ aura::client::SetActivationDelegate(&window, this);
+ aura::client::SetActivationChangeObserver(&window, this);
+ updater.Init();
+ }
+
+ ash::LauncherItem GetUpdaterItem() {
+ ash::LauncherID launcher_id =
+ BrowserLauncherItemController::TestApi(&updater).item_id();
+ int index = launcher_test->launcher_model_->ItemIndexByID(launcher_id);
+ return launcher_test->launcher_model_->items()[index];
+ }
+
+ // aura::client::ActivationDelegate overrides.
+ virtual bool ShouldActivate() const OVERRIDE {
+ return true;
+ }
+
+ // aura::client::ActivationChangeObserver overrides:
+ virtual void OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) OVERRIDE {
+ DCHECK(&window == gained_active || &window == lost_active);
+ updater.BrowserActivationStateChanged();
+ }
+
+ LauncherItemControllerPerAppTest* launcher_test;
+ aura::Window window;
+ TabHelperTabStripModelDelegate tab_strip_delegate;
+ TabStripModel tab_strip;
+ BrowserLauncherItemController updater;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(State);
+ };
+
+ const std::string& GetAppID(ash::LauncherID id) const {
+ return launcher_delegate_->GetAppIdFromLauncherIdForTest(id);
+ }
+
+ void ResetAppTabHelper() {
+ launcher_delegate_->SetAppTabHelperForTest(app_tab_helper_);
+ }
+
+ void ResetAppIconLoader() {
+ launcher_delegate_->SetAppIconLoaderForTest(app_icon_loader_);
+ }
+
+ void UnpinAppsWithID(const std::string& app_id) {
+ launcher_delegate_->UnpinAppsWithID(app_id);
+ }
+
+ const ash::LauncherItem& GetItem(BrowserLauncherItemController* updater) {
+ int index = launcher_model_->ItemIndexByID(
+ BrowserLauncherItemController::TestApi(updater).item_id());
+ return launcher_model_->items()[index];
+ }
+
+ scoped_ptr<ash::LauncherModel> launcher_model_;
+ scoped_ptr<ChromeLauncherController> launcher_delegate_;
+
+ // Owned by BrowserLauncherItemController.
+ AppTabHelperImpl* app_tab_helper_;
+ AppIconLoaderImpl* app_icon_loader_;
+
+ scoped_ptr<aura::test::TestActivationClient> activation_client_;
+};
+
+// Verify that the launcher item positions are persisted and restored.
+TEST_F(LauncherItemControllerPerAppTest, PersistLauncherItemPositions) {
+ EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT,
+ launcher_model_->items()[0].type);
+ EXPECT_EQ(ash::TYPE_APP_LIST,
+ launcher_model_->items()[1].type);
+ scoped_ptr<content::WebContents> tab1(CreateTestWebContents());
+ scoped_ptr<content::WebContents> tab2(CreateTestWebContents());
+ app_tab_helper_->SetAppID(tab1.get(), "1");
+ app_tab_helper_->SetAppID(tab1.get(), "2");
+
+ EXPECT_FALSE(launcher_delegate_->IsAppPinned("1"));
+ launcher_delegate_->PinAppWithID("1");
+ EXPECT_TRUE(launcher_delegate_->IsAppPinned("1"));
+ launcher_delegate_->PinAppWithID("2");
+
+ EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT,
+ launcher_model_->items()[0].type);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[1].type);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[2].type);
+ EXPECT_EQ(ash::TYPE_APP_LIST,
+ launcher_model_->items()[3].type);
+
+ launcher_model_->Move(0, 2);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[0].type);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[1].type);
+ EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT,
+ launcher_model_->items()[2].type);
+ EXPECT_EQ(ash::TYPE_APP_LIST,
+ launcher_model_->items()[3].type);
+
+ launcher_delegate_.reset();
+ launcher_model_.reset(new ash::LauncherModel);
+ launcher_delegate_.reset(
+ ChromeLauncherController::CreateInstance(profile(),
+ launcher_model_.get()));
+ app_tab_helper_ = new AppTabHelperImpl;
+ app_tab_helper_->SetAppID(tab1.get(), "1");
+ app_tab_helper_->SetAppID(tab2.get(), "2");
+ ResetAppTabHelper();
+
+ launcher_delegate_->Init();
+
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[0].type);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[1].type);
+ EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT,
+ launcher_model_->items()[2].type);
+ EXPECT_EQ(ash::TYPE_APP_LIST,
+ launcher_model_->items()[3].type);
+}
+
+class BrowserLauncherItemControllerTest
+ : public LauncherItemControllerPerAppTest {
+ public:
+ BrowserLauncherItemControllerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ ash::switches::kAshDisablePerAppLauncher);
+
+ LauncherItemControllerPerAppTest::SetUp();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ LauncherItemControllerPerAppTest::TearDown();
+ }
+};
+
+// Verifies a new launcher item is added for TYPE_TABBED.
+TEST_F(BrowserLauncherItemControllerTest, TabbedSetup) {
+ size_t initial_size = launcher_model_->items().size();
+ {
+ scoped_ptr<content::WebContents> web_contents(CreateTestWebContents());
+ State state(this, std::string(),
+ BrowserLauncherItemController::TYPE_TABBED);
+
+ // There should be one more item.
+ ASSERT_EQ(initial_size + 1, launcher_model_->items().size());
+ // New item should be added at the end.
+ EXPECT_EQ(ash::TYPE_TABBED, state.GetUpdaterItem().type);
+ }
+
+ // Deleting the BrowserLauncherItemController should have removed the item.
+ ASSERT_EQ(initial_size, launcher_model_->items().size());
+
+ // Do the same, but this time add the tab first.
+ {
+ scoped_ptr<content::WebContents> web_contents(CreateTestWebContents());
+
+ TabHelperTabStripModelDelegate tab_strip_delegate;
+ TabStripModel tab_strip(&tab_strip_delegate, profile());
+ tab_strip.InsertWebContentsAt(0,
+ web_contents.get(),
+ TabStripModel::ADD_ACTIVE);
+ aura::Window window(NULL);
+ window.Init(ui::LAYER_NOT_DRAWN);
+ root_window()->AddChild(&window);
+ BrowserLauncherItemController updater(
+ LauncherItemController::TYPE_TABBED,
+ &window, &tab_strip, launcher_delegate_.get(),
+ std::string());
+ updater.Init();
+
+ // There should be one more item.
+ ASSERT_EQ(initial_size + 1, launcher_model_->items().size());
+ // New item should be added at the end.
+ EXPECT_EQ(ash::TYPE_TABBED, GetItem(&updater).type);
+ }
+}
+
+// Verifies pinned apps are persisted and restored.
+TEST_F(BrowserLauncherItemControllerTest, PersistPinned) {
+ size_t initial_size = launcher_model_->items().size();
+ scoped_ptr<content::WebContents> tab1(CreateTestWebContents());
+
+ app_tab_helper_->SetAppID(tab1.get(), "1");
+
+ app_icon_loader_->GetAndClearFetchCount();
+ launcher_delegate_->PinAppWithID("1");
+ ash::LauncherID id = launcher_delegate_->GetLauncherIDForAppID("1");
+ int app_index = launcher_model_->ItemIndexByID(id);
+ EXPECT_GT(app_icon_loader_->GetAndClearFetchCount(), 0);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[app_index].type);
+ EXPECT_TRUE(launcher_delegate_->IsAppPinned("1"));
+ EXPECT_FALSE(launcher_delegate_->IsAppPinned("0"));
+ EXPECT_EQ(initial_size + 1, launcher_model_->items().size());
+
+ launcher_delegate_.reset();
+ launcher_model_.reset(new ash::LauncherModel);
+ launcher_delegate_.reset(
+ ChromeLauncherController::CreateInstance(profile(),
+ launcher_model_.get()));
+ app_tab_helper_ = new AppTabHelperImpl;
+ app_tab_helper_->SetAppID(tab1.get(), "1");
+ ResetAppTabHelper();
+ app_icon_loader_ = new AppIconLoaderImpl;
+ ResetAppIconLoader();
+ launcher_delegate_->Init();
+ EXPECT_GT(app_icon_loader_->GetAndClearFetchCount(), 0);
+ ASSERT_EQ(initial_size + 1, launcher_model_->items().size());
+ EXPECT_TRUE(launcher_delegate_->IsAppPinned("1"));
+ EXPECT_FALSE(launcher_delegate_->IsAppPinned("0"));
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[app_index].type);
+
+ UnpinAppsWithID("1");
+ ASSERT_EQ(initial_size, launcher_model_->items().size());
+}
+
+// Verify that launcher item positions are persisted and restored.
+TEST_F(BrowserLauncherItemControllerTest,
+ PersistLauncherItemPositionsPerBrowser) {
+ int browser_shortcut_index = 0;
+ int app_list_index = 1;
+
+ EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT,
+ launcher_model_->items()[browser_shortcut_index].type);
+ EXPECT_EQ(ash::TYPE_APP_LIST,
+ launcher_model_->items()[app_list_index].type);
+
+ scoped_ptr<content::WebContents> tab1(CreateTestWebContents());
+ scoped_ptr<content::WebContents> tab2(CreateTestWebContents());
+
+ app_tab_helper_->SetAppID(tab1.get(), "1");
+ app_tab_helper_->SetAppID(tab2.get(), "2");
+
+ app_icon_loader_->GetAndClearFetchCount();
+ launcher_delegate_->PinAppWithID("1");
+ ash::LauncherID id = launcher_delegate_->GetLauncherIDForAppID("1");
+ int app1_index = launcher_model_->ItemIndexByID(id);
+
+ launcher_delegate_->PinAppWithID("2");
+ id = launcher_delegate_->GetLauncherIDForAppID("2");
+ int app2_index = launcher_model_->ItemIndexByID(id);
+
+ launcher_model_->Move(browser_shortcut_index, app1_index);
+
+ browser_shortcut_index = 1;
+ app1_index = 0;
+
+ EXPECT_GT(app_icon_loader_->GetAndClearFetchCount(), 0);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[app1_index].type);
+ EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT,
+ launcher_model_->items()[browser_shortcut_index].type);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[app2_index].type);
+
+ launcher_delegate_.reset();
+ launcher_model_.reset(new ash::LauncherModel);
+ launcher_delegate_.reset(
+ ChromeLauncherController::CreateInstance(profile(),
+ launcher_model_.get()));
+
+ app_tab_helper_ = new AppTabHelperImpl;
+ app_tab_helper_->SetAppID(tab1.get(), "1");
+ app_tab_helper_->SetAppID(tab2.get(), "2");
+ ResetAppTabHelper();
+ app_icon_loader_ = new AppIconLoaderImpl;
+ ResetAppIconLoader();
+ launcher_delegate_->Init();
+
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[app1_index].type);
+ EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT,
+ launcher_model_->items()[browser_shortcut_index].type);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT,
+ launcher_model_->items()[app2_index].type);
+}
+
+// Confirm that tabbed browsers handle activation correctly.
+TEST_F(BrowserLauncherItemControllerTest, ActivateBrowsers) {
+ State state1(this, std::string(), BrowserLauncherItemController::TYPE_TABBED);
+
+ // First browser is active.
+ EXPECT_EQ(ash::STATUS_ACTIVE, state1.GetUpdaterItem().status);
+
+ {
+ // Both running.
+ State state2(this, std::string(),
+ BrowserLauncherItemController::TYPE_TABBED);
+ EXPECT_EQ(ash::STATUS_ACTIVE, state2.GetUpdaterItem().status);
+ EXPECT_EQ(ash::STATUS_RUNNING, state1.GetUpdaterItem().status);
+
+ // Make first browser active again.
+ activation_client_->ActivateWindow(&state1.window);
+ EXPECT_EQ(ash::STATUS_ACTIVE, state1.GetUpdaterItem().status);
+ EXPECT_EQ(ash::STATUS_RUNNING, state2.GetUpdaterItem().status);
+
+ // And back to second.
+ activation_client_->ActivateWindow(&state2.window);
+ EXPECT_EQ(ash::STATUS_ACTIVE, state2.GetUpdaterItem().status);
+ EXPECT_EQ(ash::STATUS_RUNNING, state1.GetUpdaterItem().status);
+ }
+
+ // First browser should be active again after second is closed.
+ EXPECT_EQ(ash::STATUS_ACTIVE, state1.GetUpdaterItem().status);
+}
+
+// Confirm that window activation works through the model.
+TEST_F(BrowserLauncherItemControllerTest, SwitchDirectlyToApp) {
+ State state1(this, std::string(),
+ BrowserLauncherItemController::TYPE_TABBED);
+ int index1 = launcher_model_->ItemIndexByID(state1.GetUpdaterItem().id);
+
+ // Second app is active and first is inactive.
+ State state2(this, std::string(),
+ BrowserLauncherItemController::TYPE_TABBED);
+ int index2 = launcher_model_->ItemIndexByID(state2.GetUpdaterItem().id);
+
+ EXPECT_EQ(ash::STATUS_RUNNING, state1.GetUpdaterItem().status);
+ EXPECT_EQ(ash::STATUS_ACTIVE, state2.GetUpdaterItem().status);
+ EXPECT_EQ(&state2.window, activation_client_->GetActiveWindow());
+
+ // Test that we can properly switch to the first item.
+ ash::LauncherItem new_item1(launcher_model_->items()[index1]);
+ new_item1.status = ash::STATUS_ACTIVE;
+ launcher_model_->Set(index1, new_item1);
+ EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model_->items()[index1].status);
+ EXPECT_EQ(ash::STATUS_RUNNING, launcher_model_->items()[index2].status);
+ EXPECT_EQ(&state1.window, activation_client_->GetActiveWindow());
+
+ // And to the second item active.
+ ash::LauncherItem new_item2(launcher_model_->items()[index2]);
+ new_item2.status = ash::STATUS_ACTIVE;
+ launcher_model_->Set(index2, new_item2);
+ EXPECT_EQ(ash::STATUS_RUNNING, launcher_model_->items()[index1].status);
+ EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model_->items()[index2].status);
+ EXPECT_EQ(&state2.window, activation_client_->GetActiveWindow());
+}
+
+// Test attention states of windows.
+TEST_F(BrowserLauncherItemControllerTest, FlashWindow) {
+ // App panel first
+ State app_state(this, "1", BrowserLauncherItemController::TYPE_APP_PANEL);
+ EXPECT_EQ(ash::STATUS_ACTIVE, app_state.GetUpdaterItem().status);
+
+ // Active windows don't show attention.
+ app_state.window.SetProperty(aura::client::kDrawAttentionKey, true);
+ EXPECT_EQ(ash::STATUS_ACTIVE, app_state.GetUpdaterItem().status);
+
+ // Then browser window
+ State browser_state(
+ this, std::string(), BrowserLauncherItemController::TYPE_TABBED);
+ // First browser is active.
+ EXPECT_EQ(ash::STATUS_ACTIVE, browser_state.GetUpdaterItem().status);
+ EXPECT_EQ(ash::STATUS_RUNNING, app_state.GetUpdaterItem().status);
+
+ // App window should go to attention state.
+ app_state.window.SetProperty(aura::client::kDrawAttentionKey, true);
+ EXPECT_EQ(ash::STATUS_ATTENTION, app_state.GetUpdaterItem().status);
+
+ // Activating app window should clear attention state.
+ activation_client_->ActivateWindow(&app_state.window);
+ EXPECT_EQ(ash::STATUS_ACTIVE, app_state.GetUpdaterItem().status);
+}
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
index 2f00201..f732149 100644
--- a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
@@ -13,7 +13,7 @@
#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
-#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
@@ -36,11 +36,12 @@
#endif
BrowserShortcutLauncherItemController::BrowserShortcutLauncherItemController(
- ChromeLauncherController* launcher_controller,
+ ChromeLauncherControllerPerApp* launcher_controller,
Profile* profile)
: LauncherItemController(TYPE_SHORTCUT,
extension_misc::kChromeAppId,
launcher_controller),
+ app_controller_(launcher_controller),
profile_(profile) {
}
@@ -157,7 +158,7 @@ BrowserShortcutLauncherItemController::GetApplicationList(int event_flags) {
continue;
if (browser->is_type_tabbed())
found_tabbed_browser = true;
- else if (!launcher_controller()->IsBrowserRepresentedInBrowserList(browser))
+ else if (!app_controller_->IsBrowserRepresentedInBrowserList(browser))
continue;
TabStripModel* tab_strip = browser->tab_strip_model();
if (tab_strip->active_index() == -1)
@@ -173,9 +174,8 @@ BrowserShortcutLauncherItemController::GetApplicationList(int event_flags) {
for (int index = 0; index < tab_strip->count(); ++index) {
content::WebContents* web_contents =
tab_strip->GetWebContentsAt(index);
- gfx::Image app_icon =
- launcher_controller()->GetAppListIcon(web_contents);
- string16 title = launcher_controller()->GetAppListTitle(web_contents);
+ gfx::Image app_icon = app_controller_->GetAppListIcon(web_contents);
+ string16 title = app_controller_->GetAppListTitle(web_contents);
// Check if we need to insert a separator in front.
bool leading_separator = !index;
items.push_back(new ChromeLauncherAppMenuItemTab(
@@ -223,7 +223,7 @@ void BrowserShortcutLauncherItemController::ActivateOrAdvanceToNextBrowser() {
for (BrowserList::const_iterator it =
ash_browser_list->begin();
it != ash_browser_list->end(); ++it) {
- if (launcher_controller()->IsBrowserRepresentedInBrowserList(*it))
+ if (app_controller_->IsBrowserRepresentedInBrowserList(*it))
items.push_back(*it);
}
// If there are no suitable browsers we create a new one.
@@ -254,7 +254,7 @@ void BrowserShortcutLauncherItemController::ActivateOrAdvanceToNextBrowser() {
true,
chrome::HOST_DESKTOP_TYPE_ASH);
if (!browser ||
- !launcher_controller()->IsBrowserRepresentedInBrowserList(browser))
+ !app_controller_->IsBrowserRepresentedInBrowserList(browser))
browser = items[0];
}
}
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h
index c7583f8..3418ba6 100644
--- a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h
@@ -16,14 +16,15 @@ class Image;
}
class Browser;
-class ChromeLauncherController;
+class ChromeLauncherControllerPerApp;
class Profile;
// Item controller for an browser shortcut.
class BrowserShortcutLauncherItemController : public LauncherItemController {
public:
- BrowserShortcutLauncherItemController(ChromeLauncherController* controller,
- Profile* profile);
+ BrowserShortcutLauncherItemController(
+ ChromeLauncherControllerPerApp* controller,
+ Profile* profile);
virtual ~BrowserShortcutLauncherItemController();
@@ -58,6 +59,8 @@ class BrowserShortcutLauncherItemController : public LauncherItemController {
// Activate a browser - or advance to the next one on the list.
void ActivateOrAdvanceToNextBrowser();
+ ChromeLauncherControllerPerApp* app_controller_;
+
Profile* profile_;
DISALLOW_COPY_AND_ASSIGN(BrowserShortcutLauncherItemController);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.cc
index 9ca859d..a2c4907 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.cc
@@ -6,12 +6,12 @@
#include "ash/wm/window_util.h"
#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "content/public/browser/notification_service.h"
-#include "ui/base/events/event_constants.h"
ChromeLauncherAppMenuItemBrowser::ChromeLauncherAppMenuItemBrowser(
const string16 title,
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.cc
index 294c2f1..1aaf17d 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.cc
@@ -5,11 +5,11 @@
#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
#include "ash/wm/window_util.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "ui/base/events/event_constants.h"
ChromeLauncherAppMenuItemTab::ChromeLauncherAppMenuItemTab(
const string16 title,
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.cc
index fe6546c..d85c90b 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.cc
@@ -4,13 +4,13 @@
#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_per_app.h"
ChromeLauncherAppMenuItemV2App::ChromeLauncherAppMenuItemV2App(
const string16 title,
const gfx::Image* icon,
const std::string& app_id,
- ChromeLauncherController* launcher_controller,
+ ChromeLauncherControllerPerApp* launcher_controller,
int app_index,
bool has_leading_separator)
: ChromeLauncherAppMenuItem(title, icon, has_leading_separator),
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.h b/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.h
index c195b0e..391ae26 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.h
@@ -13,7 +13,7 @@ namespace gfx {
class image;
}
-class ChromeLauncherController;
+class ChromeLauncherControllerPerApp;
// A menu item controller for a running V2 application. It gets created when an
// application list gets created. It's main purpose is to add the activation
@@ -24,7 +24,7 @@ class ChromeLauncherAppMenuItemV2App : public ChromeLauncherAppMenuItem {
const string16 title,
const gfx::Image* icon,
const std::string& app_id,
- ChromeLauncherController* launcher_controller,
+ ChromeLauncherControllerPerApp* launcher_controller,
int app_index,
bool has_leading_separator);
virtual bool IsEnabled() const OVERRIDE;
@@ -32,7 +32,7 @@ class ChromeLauncherAppMenuItemV2App : public ChromeLauncherAppMenuItem {
private:
// The owning class which can be used to validate the controller.
- ChromeLauncherController* launcher_controller_;
+ ChromeLauncherControllerPerApp* launcher_controller_;
// The application ID.
const std::string app_id_;
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index 973ded6..442f2e8 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -4,286 +4,14 @@
#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
-#include <vector>
-
#include "ash/ash_switches.h"
-#include "ash/launcher/launcher.h"
-#include "ash/launcher/launcher_model.h"
-#include "ash/launcher/launcher_util.h"
-#include "ash/root_window_controller.h"
-#include "ash/shelf/shelf_layout_manager.h"
-#include "ash/shelf/shelf_widget.h"
-#include "ash/shell.h"
-#include "ash/wm/window_util.h"
#include "base/command_line.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "chrome/browser/app_mode/app_mode_utils.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/defaults.h"
-#include "chrome/browser/extensions/app_icon_loader_impl.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system.h"
-#include "chrome/browser/favicon/favicon_tab_helper.h"
-#include "chrome/browser/prefs/incognito_mode_prefs.h"
-#include "chrome/browser/prefs/pref_service_syncable.h"
-#include "chrome/browser/prefs/scoped_user_pref_update.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/app_sync_ui_state.h"
-#include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
-#include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
-#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.h"
-#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
-#include "chrome/browser/ui/ash/launcher/launcher_app_tab_helper.h"
-#include "chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h"
-#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
-#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/shell_window_launcher_controller.h"
-#include "chrome/browser/ui/ash/launcher/shell_window_launcher_item_controller.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/browser_tabstrip.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/extensions/application_launch.h"
-#include "chrome/browser/ui/extensions/extension_enable_flow.h"
-#include "chrome/browser/ui/host_desktop.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
-#include "content/public/browser/navigation_entry.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/web_contents.h"
-#include "extensions/common/extension_resource.h"
-#include "extensions/common/url_pattern.h"
-#include "grit/ash_resources.h"
-#include "grit/chromium_strings.h"
-#include "grit/generated_resources.h"
-#include "grit/theme_resources.h"
-#include "grit/ui_resources.h"
-#include "ui/aura/root_window.h"
-#include "ui/aura/window.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/views/corewm/window_animations.h"
-
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/default_pinned_apps_field_trial.h"
-#endif
-
-using extensions::Extension;
-using extension_misc::kGmailAppId;
-using content::WebContents;
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser.h"
-// static
+// statics
ChromeLauncherController* ChromeLauncherController::instance_ = NULL;
-namespace {
-
-std::string GetPrefKeyForRootWindow(aura::RootWindow* root_window) {
- gfx::Display display = gfx::Screen::GetScreenFor(
- root_window)->GetDisplayNearestWindow(root_window);
- DCHECK(display.is_valid());
-
- return base::Int64ToString(display.id());
-}
-
-void UpdatePerDisplayPref(PrefService* pref_service,
- aura::RootWindow* root_window,
- const char* pref_key,
- const std::string& value) {
- std::string key = GetPrefKeyForRootWindow(root_window);
- if (key.empty())
- return;
-
- DictionaryPrefUpdate update(pref_service, prefs::kShelfPreferences);
- base::DictionaryValue* shelf_prefs = update.Get();
- base::DictionaryValue* prefs = NULL;
- if (!shelf_prefs->GetDictionary(key, &prefs)) {
- prefs = new base::DictionaryValue();
- shelf_prefs->Set(key, prefs);
- }
- prefs->SetStringWithoutPathExpansion(pref_key, value);
-}
-
-// Returns a pref value in |pref_service| for the display of |root_window|. The
-// pref value is stored in |local_path| and |path|, but |pref_service| may have
-// per-display preferences and the value can be specified by policy. Here is
-// the priority:
-// * A value managed by policy. This is a single value that applies to all
-// displays.
-// * A user-set value for the specified display.
-// * A user-set value in |local_path| or |path|, if no per-display settings are
-// ever specified (see http://crbug.com/173719 for why). |local_path| is
-// preferred. See comment in |kShelfAlignment| as to why we consider two
-// prefs and why |local_path| is preferred.
-// * A value recommended by policy. This is a single value that applies to all
-// root windows.
-// * The default value for |local_path| if the value is not recommended by
-// policy.
-std::string GetPrefForRootWindow(PrefService* pref_service,
- aura::RootWindow* root_window,
- const char* local_path,
- const char* path) {
- const PrefService::Preference* local_pref =
- pref_service->FindPreference(local_path);
- const std::string value(pref_service->GetString(local_path));
- if (local_pref->IsManaged())
- return value;
-
- std::string pref_key = GetPrefKeyForRootWindow(root_window);
- bool has_per_display_prefs = false;
- if (!pref_key.empty()) {
- const base::DictionaryValue* shelf_prefs = pref_service->GetDictionary(
- prefs::kShelfPreferences);
- const base::DictionaryValue* display_pref = NULL;
- std::string per_display_value;
- if (shelf_prefs->GetDictionary(pref_key, &display_pref) &&
- display_pref->GetString(path, &per_display_value))
- return per_display_value;
-
- // If the pref for the specified display is not found, scan the whole prefs
- // and check if the prefs for other display is already specified.
- std::string unused_value;
- for (base::DictionaryValue::Iterator iter(*shelf_prefs);
- !iter.IsAtEnd(); iter.Advance()) {
- const base::DictionaryValue* display_pref = NULL;
- if (iter.value().GetAsDictionary(&display_pref) &&
- display_pref->GetString(path, &unused_value)) {
- has_per_display_prefs = true;
- break;
- }
- }
- }
-
- if (local_pref->IsRecommended() || !has_per_display_prefs)
- return value;
-
- const base::Value* default_value =
- pref_service->GetDefaultPrefValue(local_path);
- std::string default_string;
- default_value->GetAsString(&default_string);
- return default_string;
-}
-
-// If prefs have synced and no user-set value exists at |local_path|, the value
-// from |synced_path| is copied to |local_path|.
-void MaybePropagatePrefToLocal(PrefServiceSyncable* pref_service,
- const char* local_path,
- const char* synced_path) {
- if (!pref_service->FindPreference(local_path)->HasUserSetting() &&
- pref_service->IsSyncing()) {
- // First time the user is using this machine, propagate from remote to
- // local.
- pref_service->SetString(local_path, pref_service->GetString(synced_path));
- }
-}
-
-} // namespace
-
-ChromeLauncherController::ChromeLauncherController(
- Profile* profile,
- ash::LauncherModel* model)
- : model_(model),
- profile_(profile),
- app_sync_ui_state_(NULL),
- ignore_persist_pinned_state_change_(false) {
- if (!profile_) {
- // Use the original profile as on chromeos we may get a temporary off the
- // record profile.
- profile_ = ProfileManager::GetDefaultProfile()->GetOriginalProfile();
-
- app_sync_ui_state_ = AppSyncUIState::Get(profile_);
- if (app_sync_ui_state_)
- app_sync_ui_state_->AddObserver(this);
- }
-
- model_->AddObserver(this);
- BrowserList::AddObserver(this);
- // Right now ash::Shell isn't created for tests.
- // TODO(mukai): Allows it to observe display change and write tests.
- if (ash::Shell::HasInstance())
- ash::Shell::GetInstance()->display_controller()->AddObserver(this);
- // TODO(stevenjb): Find a better owner for shell_window_controller_?
- shell_window_controller_.reset(new ShellWindowLauncherController(this));
- app_tab_helper_.reset(new LauncherAppTabHelper(profile_));
- app_icon_loader_.reset(new extensions::AppIconLoaderImpl(
- profile_, extension_misc::EXTENSION_ICON_SMALL, this));
-
- notification_registrar_.Add(this,
- chrome::NOTIFICATION_EXTENSION_LOADED,
- content::Source<Profile>(profile_));
- notification_registrar_.Add(this,
- chrome::NOTIFICATION_EXTENSION_UNLOADED,
- content::Source<Profile>(profile_));
- pref_change_registrar_.Init(profile_->GetPrefs());
- pref_change_registrar_.Add(
- prefs::kPinnedLauncherApps,
- base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
- base::Unretained(this)));
- pref_change_registrar_.Add(
- prefs::kShelfAlignmentLocal,
- base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs,
- base::Unretained(this)));
- pref_change_registrar_.Add(
- prefs::kShelfAutoHideBehaviorLocal,
- base::Bind(&ChromeLauncherController::
- SetShelfAutoHideBehaviorFromPrefs,
- base::Unretained(this)));
- pref_change_registrar_.Add(
- prefs::kShelfPreferences,
- base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs,
- base::Unretained(this)));
-}
-
-ChromeLauncherController::~ChromeLauncherController() {
- // Reset the shell window controller here since it has a weak pointer to this.
- shell_window_controller_.reset();
-
- for (std::set<ash::Launcher*>::iterator iter = launchers_.begin();
- iter != launchers_.end();
- ++iter)
- (*iter)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
-
- model_->RemoveObserver(this);
- BrowserList::RemoveObserver(this);
- if (ash::Shell::HasInstance())
- ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
- for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
- i != id_to_item_controller_map_.end(); ++i) {
- i->second->OnRemoved();
- // TODO(skuhne): After getting rid of the old launcher, get also rid of the
- // BrowserLauncherItemController (since it is only used for activation
- // tracking at that point.
- int index = model_->ItemIndexByID(i->first);
- // A "browser proxy" is not known to the model and this removal does
- // therefore not need to be propagated to the model.
- if (index != -1 &&
- model_->items()[index].type != ash::TYPE_BROWSER_SHORTCUT)
- model_->RemoveItemAt(index);
- }
-
- if (ash::Shell::HasInstance())
- ash::Shell::GetInstance()->RemoveShellObserver(this);
-
- if (app_sync_ui_state_)
- app_sync_ui_state_->RemoveObserver(this);
-
- PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this);
-
- if (instance_ == this)
- instance_ = NULL;
-}
-
// static
ChromeLauncherController* ChromeLauncherController::CreateInstance(
Profile* profile,
@@ -291,1368 +19,21 @@ ChromeLauncherController* ChromeLauncherController::CreateInstance(
// We do not check here for re-creation of the ChromeLauncherController since
// it appears that it might be intentional that the ChromeLauncherController
// can be re-created.
- instance_ = new ChromeLauncherController(profile, model);
- return instance_;
-}
-
-void ChromeLauncherController::Init() {
- UpdateAppLaunchersFromPref();
- CreateBrowserShortcutLauncherItem();
-
- // TODO(sky): update unit test so that this test isn't necessary.
- if (ash::Shell::HasInstance()) {
- SetShelfAutoHideBehaviorFromPrefs();
- SetShelfAlignmentFromPrefs();
- PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
- if (!prefs->FindPreference(prefs::kShelfAlignmentLocal)->HasUserSetting() ||
- !prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)->
- HasUserSetting()) {
- // This causes OnIsSyncingChanged to be called when the value of
- // PrefService::IsSyncing() changes.
- prefs->AddObserver(this);
- }
- ash::Shell::GetInstance()->AddShellObserver(this);
- }
-}
-
-ash::LauncherID ChromeLauncherController::CreateAppLauncherItem(
- LauncherItemController* controller,
- const std::string& app_id,
- ash::LauncherItemStatus status) {
- CHECK(controller);
- int index = 0;
- // Panels are inserted on the left so as not to push all existing panels over.
- if (controller->GetLauncherItemType() != ash::TYPE_APP_PANEL) {
- index = model_->item_count();
- // For the alternate shelf layout increment the index (after the app icon)
- if (ash::switches::UseAlternateShelfLayout())
- ++index;
- }
- return InsertAppLauncherItem(controller,
- app_id,
- status,
- index,
- controller->GetLauncherItemType());
-}
-
-void ChromeLauncherController::SetItemStatus(
- ash::LauncherID id,
- ash::LauncherItemStatus status) {
- int index = model_->ItemIndexByID(id);
- // Since ordinary browser windows are not registered, we might get a negative
- // index here.
- if (index >= 0) {
- ash::LauncherItem item = model_->items()[index];
- item.status = status;
- model_->Set(index, item);
-
- if (model_->items()[index].type == ash::TYPE_BROWSER_SHORTCUT)
- return;
- }
- UpdateBrowserItemStatus();
-}
-
-void ChromeLauncherController::SetItemController(
- ash::LauncherID id,
- LauncherItemController* controller) {
- CHECK(controller);
- IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
- CHECK(iter != id_to_item_controller_map_.end());
- iter->second->OnRemoved();
- iter->second = controller;
- controller->set_launcher_id(id);
-}
-
-void ChromeLauncherController::CloseLauncherItem(ash::LauncherID id) {
- CHECK(id);
- if (IsPinned(id)) {
- // Create a new shortcut controller.
- IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
- CHECK(iter != id_to_item_controller_map_.end());
- SetItemStatus(id, ash::STATUS_CLOSED);
- std::string app_id = iter->second->app_id();
- iter->second->OnRemoved();
- iter->second = new AppShortcutLauncherItemController(app_id, this);
- iter->second->set_launcher_id(id);
- } else {
- LauncherItemClosed(id);
- }
-}
-
-void ChromeLauncherController::Pin(ash::LauncherID id) {
- DCHECK(HasItemController(id));
-
- int index = model_->ItemIndexByID(id);
- DCHECK_GE(index, 0);
-
- ash::LauncherItem item = model_->items()[index];
-
- if (item.type == ash::TYPE_PLATFORM_APP ||
- item.type == ash::TYPE_WINDOWED_APP) {
- item.type = ash::TYPE_APP_SHORTCUT;
- model_->Set(index, item);
- } else if (item.type != ash::TYPE_APP_SHORTCUT) {
- return;
- }
-
- if (CanPin())
- PersistPinnedState();
-}
-
-void ChromeLauncherController::Unpin(ash::LauncherID id) {
- DCHECK(HasItemController(id));
-
- LauncherItemController* controller = id_to_item_controller_map_[id];
- if (controller->type() == LauncherItemController::TYPE_APP) {
- int index = model_->ItemIndexByID(id);
- DCHECK_GE(index, 0);
- ash::LauncherItem item = model_->items()[index];
- item.type = ash::TYPE_PLATFORM_APP;
- model_->Set(index, item);
- } else {
- // Prevent the removal of items upon unpin if it is locked by a running
- // windowed V1 app.
- if (!controller->locked()) {
- LauncherItemClosed(id);
- } else {
- int index = model_->ItemIndexByID(id);
- DCHECK_GE(index, 0);
- ash::LauncherItem item = model_->items()[index];
- item.type = ash::TYPE_WINDOWED_APP;
- model_->Set(index, item);
- }
- }
- if (CanPin())
- PersistPinnedState();
-}
-
-bool ChromeLauncherController::IsPinned(ash::LauncherID id) {
- int index = model_->ItemIndexByID(id);
- if (index < 0)
- return false;
- ash::LauncherItemType type = model_->items()[index].type;
- return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT);
-}
-
-void ChromeLauncherController::TogglePinned(ash::LauncherID id) {
- if (!HasItemController(id))
- return; // May happen if item closed with menu open.
-
- if (IsPinned(id))
- Unpin(id);
- else
- Pin(id);
-}
-
-bool ChromeLauncherController::IsPinnable(ash::LauncherID id) const {
- int index = model_->ItemIndexByID(id);
- if (index == -1)
- return false;
-
- ash::LauncherItemType type = model_->items()[index].type;
- return ((type == ash::TYPE_APP_SHORTCUT ||
- type == ash::TYPE_PLATFORM_APP ||
- type == ash::TYPE_WINDOWED_APP) &&
- CanPin());
-}
-
-void ChromeLauncherController::LockV1AppWithID(
- const std::string& app_id) {
- ash::LauncherID id = GetLauncherIDForAppID(app_id);
- if (!IsPinned(id) && !IsWindowedAppInLauncher(app_id)) {
- CreateAppShortcutLauncherItemWithType(app_id,
- model_->item_count(),
- ash::TYPE_WINDOWED_APP);
- id = GetLauncherIDForAppID(app_id);
- }
- CHECK(id);
- id_to_item_controller_map_[id]->lock();
-}
-
-void ChromeLauncherController::UnlockV1AppWithID(
- const std::string& app_id) {
- ash::LauncherID id = GetLauncherIDForAppID(app_id);
- CHECK(IsPinned(id) || IsWindowedAppInLauncher(app_id));
- CHECK(id);
- LauncherItemController* controller = id_to_item_controller_map_[id];
- controller->unlock();
- if (!controller->locked() && !IsPinned(id))
- CloseLauncherItem(id);
-}
-
-void ChromeLauncherController::Launch(ash::LauncherID id,
- int event_flags) {
- if (!HasItemController(id))
- return; // In case invoked from menu and item closed while menu up.
- id_to_item_controller_map_[id]->Launch(event_flags);
-}
-
-void ChromeLauncherController::Close(ash::LauncherID id) {
- if (!HasItemController(id))
- return; // May happen if menu closed.
- id_to_item_controller_map_[id]->Close();
-}
-
-bool ChromeLauncherController::IsOpen(ash::LauncherID id) {
- if (!HasItemController(id))
- return false;
- return id_to_item_controller_map_[id]->IsOpen();
-}
-
-bool ChromeLauncherController::IsPlatformApp(ash::LauncherID id) {
- if (!HasItemController(id))
- return false;
-
- std::string app_id = GetAppIDForLauncherID(id);
- const Extension* extension = GetExtensionForAppID(app_id);
- // An extension can be synced / updated at any time and therefore not be
- // available.
- return extension ? extension->is_platform_app() : false;
-}
-
-void ChromeLauncherController::LaunchApp(const std::string& app_id,
- int event_flags) {
- // |extension| could be NULL when it is being unloaded for updating.
- const Extension* extension = GetExtensionForAppID(app_id);
- if (!extension)
- return;
-
- const ExtensionService* service =
- extensions::ExtensionSystem::Get(profile_)->extension_service();
- if (!service->IsExtensionEnabledForLauncher(app_id)) {
- // Do nothing if there is already a running enable flow.
- if (extension_enable_flow_)
- return;
-
- extension_enable_flow_.reset(
- new ExtensionEnableFlow(profile_, app_id, this));
- extension_enable_flow_->StartForNativeWindow(NULL);
- return;
- }
-
- chrome::OpenApplication(chrome::AppLaunchParams(GetProfileForNewWindows(),
- extension,
- event_flags));
-}
-
-void ChromeLauncherController::ActivateApp(const std::string& app_id,
- int event_flags) {
- // If there is an existing non-shortcut controller for this app, open it.
- ash::LauncherID id = GetLauncherIDForAppID(app_id);
- if (id) {
- LauncherItemController* controller = id_to_item_controller_map_[id];
- controller->Activate();
- return;
- }
-
- // Create a temporary application launcher item and use it to see if there are
- // running instances.
- scoped_ptr<AppShortcutLauncherItemController> app_controller(
- new AppShortcutLauncherItemController(app_id, this));
- if (!app_controller->GetRunningApplications().empty())
- app_controller->Activate();
- else
- LaunchApp(app_id, event_flags);
-}
-
-extensions::ExtensionPrefs::LaunchType
- ChromeLauncherController::GetLaunchType(ash::LauncherID id) {
- DCHECK(HasItemController(id));
-
- const Extension* extension = GetExtensionForAppID(
- id_to_item_controller_map_[id]->app_id());
-
- // An extension can be unloaded/updated/unavailable at any time.
- if (!extension)
- return extensions::ExtensionPrefs::LAUNCH_DEFAULT;
-
- return profile_->GetExtensionService()->extension_prefs()->GetLaunchType(
- extension,
- extensions::ExtensionPrefs::LAUNCH_DEFAULT);
-}
-
-std::string ChromeLauncherController::GetAppID(content::WebContents* tab) {
- return app_tab_helper_->GetAppID(tab);
-}
-
-ash::LauncherID ChromeLauncherController::GetLauncherIDForAppID(
- const std::string& app_id) {
- for (IDToItemControllerMap::const_iterator i =
- id_to_item_controller_map_.begin();
- i != id_to_item_controller_map_.end(); ++i) {
- if (i->second->type() == LauncherItemController::TYPE_APP_PANEL)
- continue; // Don't include panels
- if (i->second->app_id() == app_id)
- return i->first;
- }
- return 0;
-}
-
-std::string ChromeLauncherController::GetAppIDForLauncherID(
- ash::LauncherID id) {
- CHECK(HasItemController(id));
- return id_to_item_controller_map_[id]->app_id();
-}
-
-void ChromeLauncherController::SetAppImage(const std::string& id,
- const gfx::ImageSkia& image) {
- // TODO: need to get this working for shortcuts.
-
- for (IDToItemControllerMap::const_iterator i =
- id_to_item_controller_map_.begin();
- i != id_to_item_controller_map_.end(); ++i) {
- LauncherItemController* controller = i->second;
- if (controller->app_id() != id)
- continue;
- if (controller->image_set_by_controller())
- continue;
- int index = model_->ItemIndexByID(i->first);
- if (index == -1)
- continue;
- ash::LauncherItem item = model_->items()[index];
- item.image = image;
- model_->Set(index, item);
- // It's possible we're waiting on more than one item, so don't break.
- }
-}
-
-void ChromeLauncherController::OnAutoHideBehaviorChanged(
- aura::RootWindow* root_window,
- ash::ShelfAutoHideBehavior new_behavior) {
- SetShelfAutoHideBehaviorPrefs(new_behavior, root_window);
-}
-
-void ChromeLauncherController::SetLauncherItemImage(
- ash::LauncherID launcher_id,
- const gfx::ImageSkia& image) {
- int index = model_->ItemIndexByID(launcher_id);
- if (index == -1)
- return;
- ash::LauncherItem item = model_->items()[index];
- item.image = image;
- model_->Set(index, item);
-}
-
-bool ChromeLauncherController::IsAppPinned(const std::string& app_id) {
- for (IDToItemControllerMap::const_iterator i =
- id_to_item_controller_map_.begin();
- i != id_to_item_controller_map_.end(); ++i) {
- if (IsPinned(i->first) && i->second->app_id() == app_id)
- return true;
- }
- return false;
-}
-
-bool ChromeLauncherController::IsWindowedAppInLauncher(
- const std::string& app_id) {
- int index = model_->ItemIndexByID(GetLauncherIDForAppID(app_id));
- if (index < 0)
- return false;
-
- ash::LauncherItemType type = model_->items()[index].type;
- return type == ash::TYPE_WINDOWED_APP;
-}
-
-void ChromeLauncherController::PinAppWithID(const std::string& app_id) {
- if (CanPin())
- DoPinAppWithID(app_id);
- else
- NOTREACHED();
-}
-
-void ChromeLauncherController::SetLaunchType(
- ash::LauncherID id,
- extensions::ExtensionPrefs::LaunchType launch_type) {
- if (!HasItemController(id))
- return;
-
- profile_->GetExtensionService()->extension_prefs()->SetLaunchType(
- id_to_item_controller_map_[id]->app_id(), launch_type);
-}
-
-void ChromeLauncherController::UnpinAppsWithID(const std::string& app_id) {
- if (CanPin())
- DoUnpinAppsWithID(app_id);
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ ash::switches::kAshDisablePerAppLauncher))
+ instance_ = new ChromeLauncherControllerPerApp(profile, model);
else
- NOTREACHED();
-}
-
-bool ChromeLauncherController::IsLoggedInAsGuest() {
- return ProfileManager::GetDefaultProfileOrOffTheRecord()->IsOffTheRecord();
-}
-
-void ChromeLauncherController::CreateNewWindow() {
- chrome::NewEmptyWindow(
- GetProfileForNewWindows(), chrome::HOST_DESKTOP_TYPE_ASH);
-}
-
-void ChromeLauncherController::CreateNewIncognitoWindow() {
- chrome::NewEmptyWindow(GetProfileForNewWindows()->GetOffTheRecordProfile(),
- chrome::HOST_DESKTOP_TYPE_ASH);
-}
-
-bool ChromeLauncherController::CanPin() const {
- const PrefService::Preference* pref =
- profile_->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps);
- return pref && pref->IsUserModifiable();
-}
-
-void ChromeLauncherController::PersistPinnedState() {
- if (ignore_persist_pinned_state_change_)
- return;
- // It is a coding error to call PersistPinnedState() if the pinned apps are
- // not user-editable. The code should check earlier and not perform any
- // modification actions that trigger persisting the state.
- if (!CanPin()) {
- NOTREACHED() << "Can't pin but pinned state being updated";
- return;
- }
-
- // Mutating kPinnedLauncherApps is going to notify us and trigger us to
- // process the change. We don't want that to happen so remove ourselves as a
- // listener.
- pref_change_registrar_.Remove(prefs::kPinnedLauncherApps);
- {
- ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
- updater->Clear();
- for (size_t i = 0; i < model_->items().size(); ++i) {
- if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) {
- ash::LauncherID id = model_->items()[i].id;
- if (HasItemController(id) && IsPinned(id)) {
- base::DictionaryValue* app_value = ash::CreateAppDict(
- id_to_item_controller_map_[id]->app_id());
- if (app_value)
- updater->Append(app_value);
- }
- } else if (model_->items()[i].type == ash::TYPE_BROWSER_SHORTCUT) {
- PersistChromeItemIndex(i);
- }
- }
- }
- pref_change_registrar_.Add(
- prefs::kPinnedLauncherApps,
- base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
- base::Unretained(this)));
-}
-
-ash::LauncherModel* ChromeLauncherController::model() {
- return model_;
-}
-
-Profile* ChromeLauncherController::profile() {
- return profile_;
-}
-
-ash::ShelfAutoHideBehavior ChromeLauncherController::GetShelfAutoHideBehavior(
- aura::RootWindow* root_window) const {
- // Don't show the shelf in app mode.
- if (chrome::IsRunningInAppMode())
- return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN;
-
- // See comment in |kShelfAlignment| as to why we consider two prefs.
- const std::string behavior_value(
- GetPrefForRootWindow(profile_->GetPrefs(),
- root_window,
- prefs::kShelfAutoHideBehaviorLocal,
- prefs::kShelfAutoHideBehavior));
-
- // Note: To maintain sync compatibility with old images of chrome/chromeos
- // the set of values that may be encountered includes the now-extinct
- // "Default" as well as "Never" and "Always", "Default" should now
- // be treated as "Never" (http://crbug.com/146773).
- if (behavior_value == ash::kShelfAutoHideBehaviorAlways)
- return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
- return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER;
-}
-
-bool ChromeLauncherController::CanUserModifyShelfAutoHideBehavior(
- aura::RootWindow* root_window) const {
- return profile_->GetPrefs()->
- FindPreference(prefs::kShelfAutoHideBehaviorLocal)->IsUserModifiable();
-}
-
-void ChromeLauncherController::ToggleShelfAutoHideBehavior(
- aura::RootWindow* root_window) {
- ash::ShelfAutoHideBehavior behavior = GetShelfAutoHideBehavior(root_window) ==
- ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS ?
- ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER :
- ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
- SetShelfAutoHideBehaviorPrefs(behavior, root_window);
- return;
-}
-
-void ChromeLauncherController::RemoveTabFromRunningApp(
- WebContents* tab,
- const std::string& app_id) {
- web_contents_to_app_id_.erase(tab);
- AppIDToWebContentsListMap::iterator i_app_id =
- app_id_to_web_contents_list_.find(app_id);
- if (i_app_id != app_id_to_web_contents_list_.end()) {
- WebContentsList* tab_list = &i_app_id->second;
- tab_list->remove(tab);
- if (tab_list->empty()) {
- app_id_to_web_contents_list_.erase(i_app_id);
- i_app_id = app_id_to_web_contents_list_.end();
- ash::LauncherID id = GetLauncherIDForAppID(app_id);
- if (id)
- SetItemStatus(id, ash::STATUS_CLOSED);
- }
- }
-}
-
-void ChromeLauncherController::UpdateAppState(content::WebContents* contents,
- AppState app_state) {
- std::string app_id = GetAppID(contents);
-
- // Check if the gMail app is loaded and it matches the given content.
- // This special treatment is needed to address crbug.com/234268.
- if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
- app_id = kGmailAppId;
-
- // Check the old |app_id| for a tab. If the contents has changed we need to
- // remove it from the previous app.
- if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) {
- std::string last_app_id = web_contents_to_app_id_[contents];
- if (last_app_id != app_id)
- RemoveTabFromRunningApp(contents, last_app_id);
- }
-
- if (app_id.empty()) {
- // Even if there is no application running, we should update the activation
- // state of the associated browser.
- UpdateBrowserItemStatus();
- return;
- }
-
- web_contents_to_app_id_[contents] = app_id;
-
- if (app_state == APP_STATE_REMOVED) {
- // The tab has gone away.
- RemoveTabFromRunningApp(contents, app_id);
- } else {
- WebContentsList& tab_list(app_id_to_web_contents_list_[app_id]);
-
- if (app_state == APP_STATE_INACTIVE) {
- WebContentsList::const_iterator i_tab =
- std::find(tab_list.begin(), tab_list.end(), contents);
- if (i_tab == tab_list.end())
- tab_list.push_back(contents);
- if (i_tab != tab_list.begin()) {
- // Going inactive, but wasn't the front tab, indicating that a new
- // tab has already become active.
- return;
- }
- } else {
- tab_list.remove(contents);
- tab_list.push_front(contents);
- }
- ash::LauncherID id = GetLauncherIDForAppID(app_id);
- if (id) {
- // If the window is active, mark the app as active.
- SetItemStatus(id, app_state == APP_STATE_WINDOW_ACTIVE ?
- ash::STATUS_ACTIVE : ash::STATUS_RUNNING);
- }
- }
- UpdateBrowserItemStatus();
-}
-
-void ChromeLauncherController::SetRefocusURLPatternForTest(ash::LauncherID id,
- const GURL& url) {
- DCHECK(HasItemController(id));
- LauncherItemController* controller = id_to_item_controller_map_[id];
-
- int index = model_->ItemIndexByID(id);
- if (index == -1) {
- NOTREACHED() << "Invalid launcher id";
- return;
- }
-
- ash::LauncherItemType type = model_->items()[index].type;
- if (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_WINDOWED_APP) {
- AppShortcutLauncherItemController* app_controller =
- static_cast<AppShortcutLauncherItemController*>(controller);
- app_controller->set_refocus_url(url);
- } else {
- NOTREACHED() << "Invalid launcher type";
- }
-}
-
-const Extension* ChromeLauncherController::GetExtensionForAppID(
- const std::string& app_id) const {
- // Some unit tests do not have a real extension.
- return (profile_->GetExtensionService()) ?
- profile_->GetExtensionService()->GetInstalledExtension(app_id) : NULL;
-}
-
-void ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
- ui::BaseWindow* window,
- bool allow_minimize) {
- if (window->IsActive() && allow_minimize) {
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableMinimizeOnSecondLauncherItemClick)) {
- AnimateWindow(window->GetNativeWindow(),
- views::corewm::WINDOW_ANIMATION_TYPE_BOUNCE);
- } else {
- window->Minimize();
- }
- } else {
- window->Show();
- window->Activate();
- }
-}
-
-void ChromeLauncherController::ItemSelected(const ash::LauncherItem& item,
- const ui::Event& event) {
- DCHECK(HasItemController(item.id));
- LauncherItemController* item_controller = id_to_item_controller_map_[item.id];
-#if defined(OS_CHROMEOS)
- if (!item_controller->app_id().empty()) {
- chromeos::default_pinned_apps_field_trial::RecordShelfAppClick(
- item_controller->app_id());
- }
-#endif
- item_controller->Clicked(event);
-}
-
-string16 ChromeLauncherController::GetTitle(const ash::LauncherItem& item) {
- DCHECK(HasItemController(item.id));
- return id_to_item_controller_map_[item.id]->GetTitle();
-}
-
-ui::MenuModel* ChromeLauncherController::CreateContextMenu(
- const ash::LauncherItem& item,
- aura::RootWindow* root_window) {
- return new LauncherContextMenu(this, &item, root_window);
-}
-
-ash::LauncherMenuModel* ChromeLauncherController::CreateApplicationMenu(
- const ash::LauncherItem& item,
- int event_flags) {
- return new LauncherApplicationMenuItemModel(GetApplicationList(item,
- event_flags));
-}
-
-ash::LauncherID ChromeLauncherController::GetIDByWindow(aura::Window* window) {
- int browser_index = ash::launcher::GetBrowserItemIndex(*model_);
- DCHECK_GE(browser_index, 0);
- ash::LauncherID browser_id = model_->items()[browser_index].id;
-
- IDToItemControllerMap::const_iterator i = id_to_item_controller_map_.begin();
- for (; i != id_to_item_controller_map_.end(); ++i) {
- // Since a |window| can be used by multiple applications, an explicit
- // application always gets chosen over the generic browser.
- if (i->first != browser_id && i->second->IsCurrentlyShownInWindow(window))
- return i->first;
- }
-
- if (i == id_to_item_controller_map_.end() &&
- GetBrowserShortcutLauncherItemController()->
- IsCurrentlyShownInWindow(window))
- return browser_id;
-
- return 0;
+ instance_ = new ChromeLauncherControllerPerBrowser(profile, model);
+ return instance_;
}
-bool ChromeLauncherController::IsDraggable(const ash::LauncherItem& item) {
- return (item.type == ash::TYPE_APP_SHORTCUT ||
- item.type == ash::TYPE_WINDOWED_APP) ? CanPin() : true;
+ChromeLauncherController::~ChromeLauncherController() {
+ if (instance_ == this)
+ instance_ = NULL;
}
-bool ChromeLauncherController::ShouldShowTooltip(
- const ash::LauncherItem& item) {
- if (item.type == ash::TYPE_APP_PANEL &&
- id_to_item_controller_map_[item.id]->IsVisible())
+bool ChromeLauncherController::IsPerAppLauncher() {
+ if (!instance_)
return false;
- return true;
-}
-
-void ChromeLauncherController::OnLauncherCreated(ash::Launcher* launcher) {
- launchers_.insert(launcher);
- launcher->shelf_widget()->shelf_layout_manager()->AddObserver(this);
-}
-
-void ChromeLauncherController::OnLauncherDestroyed(ash::Launcher* launcher) {
- launchers_.erase(launcher);
- // RemoveObserver is not called here, since by the time this method is called
- // Launcher is already in its destructor.
-}
-
-void ChromeLauncherController::LauncherItemAdded(int index) {
-}
-
-void ChromeLauncherController::LauncherItemRemoved(int index,
- ash::LauncherID id) {
-}
-
-void ChromeLauncherController::LauncherItemMoved(int start_index,
- int target_index) {
- ash::LauncherID id = model_->items()[target_index].id;
- if (HasItemController(id) && IsPinned(id))
- PersistPinnedState();
-}
-
-void ChromeLauncherController::LauncherItemChanged(
- int index,
- const ash::LauncherItem& old_item) {
- ash::LauncherID id = model_->items()[index].id;
- DCHECK(HasItemController(id));
- id_to_item_controller_map_[id]->LauncherItemChanged(index, old_item);
-}
-
-void ChromeLauncherController::LauncherStatusChanged() {
-}
-
-void ChromeLauncherController::Observe(
- int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
- switch (type) {
- case chrome::NOTIFICATION_EXTENSION_LOADED: {
- const Extension* extension =
- content::Details<const Extension>(details).ptr();
- if (IsAppPinned(extension->id())) {
- // Clear and re-fetch to ensure icon is up-to-date.
- app_icon_loader_->ClearImage(extension->id());
- app_icon_loader_->FetchImage(extension->id());
- }
-
- UpdateAppLaunchersFromPref();
- break;
- }
- case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
- const content::Details<extensions::UnloadedExtensionInfo>& unload_info(
- details);
- const Extension* extension = unload_info->extension;
- const std::string& id = extension->id();
- // Since we might have windowed apps of this type which might have
- // outstanding locks which needs to be removed.
- if (GetLauncherIDForAppID(id) &&
- unload_info->reason == extension_misc::UNLOAD_REASON_UNINSTALL) {
- CloseWindowedAppsFromRemovedExtension(id);
- }
-
- if (IsAppPinned(id)) {
- if (unload_info->reason == extension_misc::UNLOAD_REASON_UNINSTALL) {
- DoUnpinAppsWithID(id);
- app_icon_loader_->ClearImage(id);
- } else {
- app_icon_loader_->UpdateImage(id);
- }
- }
- break;
- }
- default:
- NOTREACHED() << "Unexpected notification type=" << type;
- }
-}
-
-void ChromeLauncherController::OnShelfAlignmentChanged(
- aura::RootWindow* root_window) {
- const char* pref_value = NULL;
- switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window)) {
- case ash::SHELF_ALIGNMENT_BOTTOM:
- pref_value = ash::kShelfAlignmentBottom;
- break;
- case ash::SHELF_ALIGNMENT_LEFT:
- pref_value = ash::kShelfAlignmentLeft;
- break;
- case ash::SHELF_ALIGNMENT_RIGHT:
- pref_value = ash::kShelfAlignmentRight;
- break;
- case ash::SHELF_ALIGNMENT_TOP:
- pref_value = ash::kShelfAlignmentTop;
- }
-
- UpdatePerDisplayPref(
- profile_->GetPrefs(), root_window, prefs::kShelfAlignment, pref_value);
-
- if (root_window == ash::Shell::GetPrimaryRootWindow()) {
- // See comment in |kShelfAlignment| about why we have two prefs here.
- profile_->GetPrefs()->SetString(prefs::kShelfAlignmentLocal, pref_value);
- profile_->GetPrefs()->SetString(prefs::kShelfAlignment, pref_value);
- }
-}
-
-void ChromeLauncherController::OnDisplayConfigurationChanging() {
-}
-
-void ChromeLauncherController::OnDisplayConfigurationChanged() {
- SetShelfBehaviorsFromPrefs();
-}
-
-void ChromeLauncherController::OnIsSyncingChanged() {
- PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
- MaybePropagatePrefToLocal(prefs,
- prefs::kShelfAlignmentLocal,
- prefs::kShelfAlignment);
- MaybePropagatePrefToLocal(prefs,
- prefs::kShelfAutoHideBehaviorLocal,
- prefs::kShelfAutoHideBehavior);
-}
-
-void ChromeLauncherController::OnAppSyncUIStatusChanged() {
- if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING)
- model_->SetStatus(ash::LauncherModel::STATUS_LOADING);
- else
- model_->SetStatus(ash::LauncherModel::STATUS_NORMAL);
-}
-
-void ChromeLauncherController::ExtensionEnableFlowFinished() {
- LaunchApp(extension_enable_flow_->extension_id(), ui::EF_NONE);
- extension_enable_flow_.reset();
-}
-
-void ChromeLauncherController::ExtensionEnableFlowAborted(bool user_initiated) {
- extension_enable_flow_.reset();
-}
-
-ChromeLauncherAppMenuItems ChromeLauncherController::GetApplicationList(
- const ash::LauncherItem& item,
- int event_flags) {
- // Make sure that there is a controller associated with the id and that the
- // extension itself is a valid application and not a panel.
- if (!HasItemController(item.id) ||
- !GetLauncherIDForAppID(id_to_item_controller_map_[item.id]->app_id()))
- return ChromeLauncherAppMenuItems().Pass();
-
- return id_to_item_controller_map_[item.id]->GetApplicationList(event_flags);
-}
-
-std::vector<content::WebContents*>
-ChromeLauncherController::GetV1ApplicationsFromAppId(std::string app_id) {
- ash::LauncherID id = GetLauncherIDForAppID(app_id);
-
- // If there is no such an item pinned to the launcher, no menu gets created.
- if (id) {
- LauncherItemController* controller = id_to_item_controller_map_[id];
- DCHECK(controller);
- if (controller->type() == LauncherItemController::TYPE_SHORTCUT)
- return GetV1ApplicationsFromController(controller);
- }
- return std::vector<content::WebContents*>();
-}
-
-void ChromeLauncherController::ActivateShellApp(const std::string& app_id,
- int index) {
- ash::LauncherID id = GetLauncherIDForAppID(app_id);
- if (id) {
- LauncherItemController* controller = id_to_item_controller_map_[id];
- if (controller->type() == LauncherItemController::TYPE_APP) {
- ShellWindowLauncherItemController* shell_window_controller =
- static_cast<ShellWindowLauncherItemController*>(controller);
- shell_window_controller->ActivateIndexedApp(index);
- }
- }
-}
-
-bool ChromeLauncherController::IsWebContentHandledByApplication(
- content::WebContents* web_contents,
- const std::string& app_id) {
- if ((web_contents_to_app_id_.find(web_contents) !=
- web_contents_to_app_id_.end()) &&
- (web_contents_to_app_id_[web_contents] == app_id))
- return true;
- return (app_id == kGmailAppId && ContentCanBeHandledByGmailApp(web_contents));
-}
-
-bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
- content::WebContents* web_contents) {
- ash::LauncherID id = GetLauncherIDForAppID(kGmailAppId);
- if (id) {
- const GURL url = web_contents->GetURL();
- // We need to extend the application matching for the gMail app beyond the
- // manifest file's specification. This is required because of the namespace
- // overlap with the offline app ("/mail/mu/").
- if (!MatchPattern(url.path(), "/mail/mu/*") &&
- MatchPattern(url.path(), "/mail/*") &&
- GetExtensionForAppID(kGmailAppId) &&
- GetExtensionForAppID(kGmailAppId)->OverlapsWithOrigin(url))
- return true;
- }
- return false;
-}
-
-gfx::Image ChromeLauncherController::GetAppListIcon(
- content::WebContents* web_contents) const {
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- if (IsIncognito(web_contents))
- return rb.GetImageNamed(IDR_AURA_LAUNCHER_LIST_INCOGNITO_BROWSER);
- FaviconTabHelper* favicon_tab_helper =
- FaviconTabHelper::FromWebContents(web_contents);
- gfx::Image result = favicon_tab_helper->GetFavicon();
- if (result.IsEmpty())
- return rb.GetImageNamed(IDR_DEFAULT_FAVICON);
- return result;
-}
-
-string16 ChromeLauncherController::GetAppListTitle(
- content::WebContents* web_contents) const {
- string16 title = web_contents->GetTitle();
- if (!title.empty())
- return title;
- WebContentsToAppIDMap::const_iterator iter =
- web_contents_to_app_id_.find(web_contents);
- if (iter != web_contents_to_app_id_.end()) {
- std::string app_id = iter->second;
- const extensions::Extension* extension = GetExtensionForAppID(app_id);
- if (extension)
- return UTF8ToUTF16(extension->name());
- }
- return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
-}
-
-void ChromeLauncherController::OnBrowserRemoved(Browser* browser) {
- // When called by a unit test it is possible that there is no shell.
- // In that case, the following function should not get called.
- if (ash::Shell::HasInstance())
- UpdateBrowserItemStatus();
-}
-
-ash::LauncherID ChromeLauncherController::CreateAppShortcutLauncherItem(
- const std::string& app_id,
- int index) {
- return CreateAppShortcutLauncherItemWithType(app_id,
- index,
- ash::TYPE_APP_SHORTCUT);
-}
-
-void ChromeLauncherController::SetAppTabHelperForTest(AppTabHelper* helper) {
- app_tab_helper_.reset(helper);
-}
-
-void ChromeLauncherController::SetAppIconLoaderForTest(
- extensions::AppIconLoader* loader) {
- app_icon_loader_.reset(loader);
-}
-
-const std::string& ChromeLauncherController::GetAppIdFromLauncherIdForTest(
- ash::LauncherID id) {
- return id_to_item_controller_map_[id]->app_id();
-}
-
-ash::LauncherID ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
- const std::string& app_id,
- int index,
- ash::LauncherItemType launcher_item_type) {
- AppShortcutLauncherItemController* controller =
- new AppShortcutLauncherItemController(app_id, this);
- ash::LauncherID launcher_id = InsertAppLauncherItem(
- controller, app_id, ash::STATUS_CLOSED, index, launcher_item_type);
- return launcher_id;
-}
-
-void ChromeLauncherController::UpdateBrowserItemStatus() {
- // Determine the new browser's active state and change if necessary.
- size_t browser_index = ash::launcher::GetBrowserItemIndex(*model_);
- DCHECK_GE(browser_index, 0u);
- ash::LauncherItem browser_item = model_->items()[browser_index];
- ash::LauncherItemStatus browser_status = ash::STATUS_CLOSED;
-
- aura::Window* window = ash::wm::GetActiveWindow();
- if (window) {
- // Check if the active browser / tab is a browser which is not an app,
- // a windowed app, a popup or any other item which is not a browser of
- // interest.
- Browser* browser = chrome::FindBrowserWithWindow(window);
- if (IsBrowserRepresentedInBrowserList(browser)) {
- browser_status = ash::STATUS_ACTIVE;
- const ash::LauncherItems& items = model_->items();
- // If another launcher item has claimed to be active, we don't.
- for (size_t i = 0;
- i < items.size() && browser_status == ash::STATUS_ACTIVE; ++i) {
- if (i != browser_index && items[i].status == ash::STATUS_ACTIVE)
- browser_status = ash::STATUS_RUNNING;
- }
- }
- }
-
- if (browser_status == ash::STATUS_CLOSED) {
- const BrowserList* ash_browser_list =
- BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
- for (BrowserList::const_reverse_iterator it =
- ash_browser_list->begin_last_active();
- it != ash_browser_list->end_last_active() &&
- browser_status == ash::STATUS_CLOSED; ++it) {
- if (IsBrowserRepresentedInBrowserList(*it))
- browser_status = ash::STATUS_RUNNING;
- }
- }
-
- if (browser_status != browser_item.status) {
- browser_item.status = browser_status;
- model_->Set(browser_index, browser_item);
- }
-}
-
-Profile* ChromeLauncherController::GetProfileForNewWindows() {
- return ProfileManager::GetDefaultProfileOrOffTheRecord();
-}
-
-void ChromeLauncherController::LauncherItemClosed(ash::LauncherID id) {
- IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
- CHECK(iter != id_to_item_controller_map_.end());
- CHECK(iter->second);
- app_icon_loader_->ClearImage(iter->second->app_id());
- iter->second->OnRemoved();
- id_to_item_controller_map_.erase(iter);
- int index = model_->ItemIndexByID(id);
- // A "browser proxy" is not known to the model and this removal does
- // therefore not need to be propagated to the model.
- if (index != -1)
- model_->RemoveItemAt(index);
-}
-
-void ChromeLauncherController::DoPinAppWithID(const std::string& app_id) {
- // If there is an item, do nothing and return.
- if (IsAppPinned(app_id))
- return;
-
- ash::LauncherID launcher_id = GetLauncherIDForAppID(app_id);
- if (launcher_id) {
- // App item exists, pin it
- Pin(launcher_id);
- } else {
- // Otherwise, create a shortcut item for it.
- CreateAppShortcutLauncherItem(app_id, model_->item_count());
- if (CanPin())
- PersistPinnedState();
- }
-}
-
-void ChromeLauncherController::DoUnpinAppsWithID(const std::string& app_id) {
- for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
- i != id_to_item_controller_map_.end(); ) {
- IDToItemControllerMap::iterator current(i);
- ++i;
- if (current->second->app_id() == app_id && IsPinned(current->first))
- Unpin(current->first);
- }
-}
-
-void ChromeLauncherController::UpdateAppLaunchersFromPref() {
- // Construct a vector representation of to-be-pinned apps from the pref.
- std::vector<std::string> pinned_apps;
- int chrome_icon_index = GetChromeIconIndexFromPref();
- int index = 0;
- int max_index = model_->item_count();
- // Using the alternate shelf layout the App Icon should be the first item in
- // the list thus start adding items at slot 1 (instead of slot 0).
- if (ash::switches::UseAlternateShelfLayout()) {
- ++index;
- ++max_index;
- // The alternate shelf layout's icon position will always include the
- // AppLauncher which needs to be subtracted here.
- if (chrome_icon_index > 0)
- --chrome_icon_index;
- }
- const base::ListValue* pinned_apps_pref =
- profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
- for (base::ListValue::const_iterator it(pinned_apps_pref->begin());
- it != pinned_apps_pref->end(); ++it) {
- // To preserve the Chrome icon position, we insert a dummy slot for it - if
- // the model has a Chrome item. While initializing we can come here with no
- // item in which case the count would be 1 or below.
- if (it - pinned_apps_pref->begin() == chrome_icon_index &&
- model_->item_count() > 1) {
- pinned_apps.push_back(extension_misc::kChromeAppId);
- }
-
- DictionaryValue* app = NULL;
- std::string app_id;
- if ((*it)->GetAsDictionary(&app) &&
- app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id) &&
- std::find(pinned_apps.begin(), pinned_apps.end(), app_id) ==
- pinned_apps.end() &&
- app_tab_helper_->IsValidID(app_id)) {
- pinned_apps.push_back(app_id);
- }
- }
-
- // Walk the model and |pinned_apps| from the pref lockstep, adding and
- // removing items as necessary. NB: This code uses plain old indexing instead
- // of iterators because of model mutations as part of the loop.
- std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin());
- for (; index < max_index && pref_app_id != pinned_apps.end(); ++index) {
- // If the next app launcher according to the pref is present in the model,
- // delete all app launcher entries in between.
- if (*pref_app_id == extension_misc::kChromeAppId ||
- IsAppPinned(*pref_app_id)) {
- for (; index < max_index; ++index) {
- const ash::LauncherItem& item(model_->items()[index]);
- if (item.type != ash::TYPE_APP_SHORTCUT &&
- item.type != ash::TYPE_BROWSER_SHORTCUT)
- continue;
-
- IDToItemControllerMap::const_iterator entry =
- id_to_item_controller_map_.find(item.id);
- if ((extension_misc::kChromeAppId == *pref_app_id &&
- item.type == ash::TYPE_BROWSER_SHORTCUT) ||
- (entry != id_to_item_controller_map_.end() &&
- entry->second->app_id() == *pref_app_id)) {
- ++pref_app_id;
- break;
- } else {
- if (item.type == ash::TYPE_BROWSER_SHORTCUT) {
- // We cannot delete the browser shortcut. As such we move it up by
- // one. To avoid any side effects from our pinned state observer, we
- // do not call the model directly.
- MoveItemWithoutPinnedStateChangeNotification(index, index + 1);
- } else {
- LauncherItemClosed(item.id);
- --max_index;
- }
- --index;
- }
- }
- // If the item wasn't found, that means id_to_item_controller_map_
- // is out of sync.
- DCHECK(index < max_index);
- } else {
- // This app wasn't pinned before, insert a new entry.
- ash::LauncherID id = CreateAppShortcutLauncherItem(*pref_app_id, index);
- index = model_->ItemIndexByID(id);
- ++pref_app_id;
- }
- }
-
- // Remove any trailing existing items.
- while (index < model_->item_count()) {
- const ash::LauncherItem& item(model_->items()[index]);
- if (item.type == ash::TYPE_APP_SHORTCUT)
- LauncherItemClosed(item.id);
- else
- ++index;
- }
-
- // Append unprocessed items from the pref to the end of the model.
- for (; pref_app_id != pinned_apps.end(); ++pref_app_id) {
- // Ignore the chrome icon.
- if (*pref_app_id != extension_misc::kChromeAppId)
- DoPinAppWithID(*pref_app_id);
- }
-
-}
-
-void ChromeLauncherController::SetShelfAutoHideBehaviorPrefs(
- ash::ShelfAutoHideBehavior behavior,
- aura::RootWindow* root_window) {
- const char* value = NULL;
- switch (behavior) {
- case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
- value = ash::kShelfAutoHideBehaviorAlways;
- break;
- case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
- value = ash::kShelfAutoHideBehaviorNever;
- break;
- case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
- // This one should not be a valid preference option for now. We only want
- // to completely hide it when we run app mode.
- NOTREACHED();
- return;
- }
-
- UpdatePerDisplayPref(
- profile_->GetPrefs(), root_window, prefs::kShelfAutoHideBehavior, value);
-
- if (root_window == ash::Shell::GetPrimaryRootWindow()) {
- // See comment in |kShelfAlignment| about why we have two prefs here.
- profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal, value);
- profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior, value);
- }
-}
-
-void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
- ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
-
- for (ash::Shell::RootWindowList::const_iterator iter = root_windows.begin();
- iter != root_windows.end(); ++iter) {
- ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
- GetShelfAutoHideBehavior(*iter), *iter);
- }
-}
-
-void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
- if (!ash::ShelfWidget::ShelfAlignmentAllowed())
- return;
-
- ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
-
- for (ash::Shell::RootWindowList::const_iterator iter = root_windows.begin();
- iter != root_windows.end(); ++iter) {
- // See comment in |kShelfAlignment| as to why we consider two prefs.
- const std::string alignment_value(
- GetPrefForRootWindow(profile_->GetPrefs(),
- *iter,
- prefs::kShelfAlignmentLocal,
- prefs::kShelfAlignment));
- ash::ShelfAlignment alignment = ash::SHELF_ALIGNMENT_BOTTOM;
- if (alignment_value == ash::kShelfAlignmentLeft)
- alignment = ash::SHELF_ALIGNMENT_LEFT;
- else if (alignment_value == ash::kShelfAlignmentRight)
- alignment = ash::SHELF_ALIGNMENT_RIGHT;
- else if (alignment_value == ash::kShelfAlignmentTop)
- alignment = ash::SHELF_ALIGNMENT_TOP;
- ash::Shell::GetInstance()->SetShelfAlignment(alignment, *iter);
- }
-}
-
-void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
- SetShelfAutoHideBehaviorFromPrefs();
- SetShelfAlignmentFromPrefs();
-}
-
-WebContents* ChromeLauncherController::GetLastActiveWebContents(
- const std::string& app_id) {
- AppIDToWebContentsListMap::const_iterator i =
- app_id_to_web_contents_list_.find(app_id);
- if (i == app_id_to_web_contents_list_.end())
- return NULL;
- DCHECK_GT(i->second.size(), 0u);
- return *i->second.begin();
-}
-
-ash::LauncherID ChromeLauncherController::InsertAppLauncherItem(
- LauncherItemController* controller,
- const std::string& app_id,
- ash::LauncherItemStatus status,
- int index,
- ash::LauncherItemType launcher_item_type) {
- ash::LauncherID id = model_->next_id();
- CHECK(!HasItemController(id));
- CHECK(controller);
- id_to_item_controller_map_[id] = controller;
- controller->set_launcher_id(id);
-
- ash::LauncherItem item;
- item.type = launcher_item_type;
- item.is_incognito = false;
- item.image = extensions::IconsInfo::GetDefaultAppIcon();
-
- WebContents* active_tab = GetLastActiveWebContents(app_id);
- if (active_tab) {
- Browser* browser = chrome::FindBrowserWithWebContents(active_tab);
- DCHECK(browser);
- if (browser->window()->IsActive())
- status = ash::STATUS_ACTIVE;
- else
- status = ash::STATUS_RUNNING;
- }
- item.status = status;
-
- model_->AddAt(index, item);
-
- app_icon_loader_->FetchImage(app_id);
-
- return id;
-}
-
-bool ChromeLauncherController::HasItemController(ash::LauncherID id) const {
- return id_to_item_controller_map_.find(id) !=
- id_to_item_controller_map_.end();
-}
-
-std::vector<content::WebContents*>
-ChromeLauncherController::GetV1ApplicationsFromController(
- LauncherItemController* controller) {
- DCHECK(controller->type() == LauncherItemController::TYPE_SHORTCUT);
- AppShortcutLauncherItemController* app_controller =
- static_cast<AppShortcutLauncherItemController*>(controller);
- return app_controller->GetRunningApplications();
-}
-
-bool ChromeLauncherController::IsBrowserRepresentedInBrowserList(
- Browser* browser) {
- return (browser &&
- (browser->is_type_tabbed() ||
- !browser->is_app() ||
- !browser->is_type_popup() ||
- GetLauncherIDForAppID(web_app::GetExtensionIdFromApplicationName(
- browser->app_name())) <= 0));
-}
-
-LauncherItemController*
-ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
- for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
- i != id_to_item_controller_map_.end(); ++i) {
- int index = model_->ItemIndexByID(i->first);
- const ash::LauncherItem& item = model_->items()[index];
- if (item.type == ash::TYPE_BROWSER_SHORTCUT)
- return i->second;
- }
- // LauncerItemController For Browser Shortcut must be existed. If it does not
- // existe create it.
- ash::LauncherID id = CreateBrowserShortcutLauncherItem();
- DCHECK(id_to_item_controller_map_[id]);
- return id_to_item_controller_map_[id];
-}
-
-ash::LauncherID ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
- ash::LauncherItem browser_shortcut;
- browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT;
- browser_shortcut.is_incognito = false;
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
- ash::LauncherID id = model_->next_id();
- size_t index = GetChromeIconIndexFromPref();
- model_->AddAt(index, browser_shortcut);
- browser_item_controller_.reset(
- new BrowserShortcutLauncherItemController(this, profile_));
- id_to_item_controller_map_[id] = browser_item_controller_.get();
- id_to_item_controller_map_[id]->set_launcher_id(id);
- return id;
-}
-
-void ChromeLauncherController::PersistChromeItemIndex(int index) {
- profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index);
-}
-
-int ChromeLauncherController::GetChromeIconIndexFromPref() const {
- size_t index = profile_->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex);
- const base::ListValue* pinned_apps_pref =
- profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
- if (ash::switches::UseAlternateShelfLayout())
- return std::max(static_cast<size_t>(1),
- std::min(pinned_apps_pref->GetSize() + 1, index));
- return std::max(static_cast<size_t>(0),
- std::min(pinned_apps_pref->GetSize(), index));
-}
-
-bool ChromeLauncherController::IsIncognito(
- content::WebContents* web_contents) const {
- const Profile* profile =
- Profile::FromBrowserContext(web_contents->GetBrowserContext());
- return profile->IsOffTheRecord() && !profile->IsGuestSession();
-}
-
-void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
- const std::string& app_id) {
- // This function cannot rely on the controller's enumeration functionality
- // since the extension has already be unloaded.
- const BrowserList* ash_browser_list =
- BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
- std::vector<Browser*> browser_to_close;
- for (BrowserList::const_reverse_iterator
- it = ash_browser_list->begin_last_active();
- it != ash_browser_list->end_last_active(); ++it) {
- Browser* browser = *it;
- if (!browser->is_type_tabbed() &&
- browser->is_type_popup() &&
- browser->is_app() &&
- app_id == web_app::GetExtensionIdFromApplicationName(
- browser->app_name())) {
- browser_to_close.push_back(browser);
- }
- }
- while (!browser_to_close.empty()) {
- TabStripModel* tab_strip = browser_to_close.back()->tab_strip_model();
- tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
- browser_to_close.pop_back();
- }
-}
-
-void
-ChromeLauncherController::MoveItemWithoutPinnedStateChangeNotification(
- int source_index, int target_index) {
- base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true);
- model_->Move(source_index, target_index);
+ return instance_->GetPerAppInterface() != NULL;
}
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
index 7f4cc73..dafc18d 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -5,43 +5,20 @@
#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_LAUNCHER_CONTROLLER_H_
#define CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_LAUNCHER_CONTROLLER_H_
-#include <list>
-#include <map>
-#include <set>
#include <string>
-#include <vector>
-#include "ash/display/display_controller.h"
#include "ash/launcher/launcher_delegate.h"
-#include "ash/launcher/launcher_model_observer.h"
#include "ash/launcher/launcher_types.h"
-#include "ash/shelf/shelf_layout_manager_observer.h"
#include "ash/shelf/shelf_types.h"
-#include "ash/shell_observer.h"
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
-#include "base/prefs/pref_change_registrar.h"
#include "chrome/browser/extensions/app_icon_loader.h"
#include "chrome/browser/extensions/extension_prefs.h"
-#include "chrome/browser/prefs/pref_service_syncable_observer.h"
-#include "chrome/browser/ui/ash/app_sync_ui_state_observer.h"
-#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
-#include "chrome/browser/ui/browser_list_observer.h"
-#include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
-#include "content/public/browser/notification_observer.h"
-#include "ui/aura/window_observer.h"
-
-class AppSyncUIState;
-class Browser;
-class BrowserShortcutLauncherItemController;
-class ExtensionEnableFlow;
-class GURL;
+
+class LauncherItemControllerPerAppTest;
class LauncherItemController;
class Profile;
-class ShellWindowLauncherController;
-class TabContents;
+class ChromeLauncherAppMenuItem;
+class ChromeLauncherControllerPerApp;
namespace ash {
class LauncherModel;
@@ -49,10 +26,10 @@ class LauncherModel;
namespace aura {
class Window;
+class RootWindow;
}
namespace content {
-class NotificationRegistrar;
class WebContents;
}
@@ -65,23 +42,17 @@ typedef ScopedVector<ChromeLauncherAppMenuItem> ChromeLauncherAppMenuItems;
// ChromeLauncherController manages the launcher items needed for content
// windows. Launcher items have a type, an optional app id, and a controller.
-// This incarnation groups running tabs/windows in application specific lists.
-// * Browser app windows have BrowserLauncherItemController, owned by the
-// BrowserView instance.
+// ChromeLauncherController will furthermore create the particular
+// implementation of interest - either sorting by application (new) or sorting
+// by browser (old).
+// * Tabbed browsers and browser app windows have BrowserLauncherItemController,
+// owned by the BrowserView instance.
// * App shell windows have ShellWindowLauncherItemController, owned by
// ShellWindowLauncherController.
// * Shortcuts have no LauncherItemController.
-class ChromeLauncherController : public ash::LauncherDelegate,
- public ash::LauncherModelObserver,
- public ash::ShellObserver,
- public ash::DisplayController::Observer,
- public content::NotificationObserver,
- public extensions::AppIconLoader::Delegate,
- public PrefServiceSyncableObserver,
- public AppSyncUIStateObserver,
- public ExtensionEnableFlowDelegate,
- public chrome::BrowserListObserver,
- public ash::ShelfLayoutManagerObserver {
+class ChromeLauncherController
+ : public ash::LauncherDelegate,
+ public extensions::AppIconLoader::Delegate {
public:
// Indicates if a launcher item is incognito or not.
enum IncognitoState {
@@ -111,11 +82,16 @@ class ChromeLauncherController : public ash::LauncherDelegate,
virtual bool IsValidID(const std::string& id) = 0;
};
- ChromeLauncherController(Profile* profile, ash::LauncherModel* model);
+ ChromeLauncherController() {}
virtual ~ChromeLauncherController();
// Initializes this ChromeLauncherController.
- void Init();
+ virtual void Init() = 0;
+
+ // Returns the new per application interface of the given launcher. If it is
+ // a per browser (old) controller, it will return NULL;
+ // TODO(skuhne): Remove when we rip out the old launcher.
+ virtual ChromeLauncherControllerPerApp* GetPerAppInterface() = 0;
// Creates an instance.
static ChromeLauncherController* CreateInstance(Profile* profile,
@@ -124,391 +100,211 @@ class ChromeLauncherController : public ash::LauncherDelegate,
// Returns the single ChromeLauncherController instance.
static ChromeLauncherController* instance() { return instance_; }
+ // Creates a new tabbed item on the launcher for |controller|.
+ virtual ash::LauncherID CreateTabbedLauncherItem(
+ LauncherItemController* controller,
+ IncognitoState is_incognito,
+ ash::LauncherItemStatus status) = 0;
+
// Creates a new app item on the launcher for |controller|.
- ash::LauncherID CreateAppLauncherItem(LauncherItemController* controller,
- const std::string& app_id,
- ash::LauncherItemStatus status);
+ virtual ash::LauncherID CreateAppLauncherItem(
+ LauncherItemController* controller,
+ const std::string& app_id,
+ ash::LauncherItemStatus status) = 0;
- // Updates the running status of an item. It will also update the status of
- // browsers launcher item if needed.
- void SetItemStatus(ash::LauncherID id, ash::LauncherItemStatus status);
+ // Updates the running status of an item.
+ virtual void SetItemStatus(ash::LauncherID id,
+ ash::LauncherItemStatus status) = 0;
// Updates the controller associated with id (which should be a shortcut).
// |controller| remains owned by caller.
- void SetItemController(ash::LauncherID id,
- LauncherItemController* controller);
+ virtual void SetItemController(ash::LauncherID id,
+ LauncherItemController* controller) = 0;
// Closes or unpins the launcher item.
- void CloseLauncherItem(ash::LauncherID id);
+ virtual void CloseLauncherItem(ash::LauncherID id) = 0;
// Pins the specified id. Currently only supports platform apps.
- void Pin(ash::LauncherID id);
+ virtual void Pin(ash::LauncherID id) = 0;
// Unpins the specified id, closing if not running.
- void Unpin(ash::LauncherID id);
+ virtual void Unpin(ash::LauncherID id) = 0;
// Returns true if the item identified by |id| is pinned.
- bool IsPinned(ash::LauncherID id);
+ virtual bool IsPinned(ash::LauncherID id) = 0;
// Pins/unpins the specified id.
- void TogglePinned(ash::LauncherID id);
+ virtual void TogglePinned(ash::LauncherID id) = 0;
// Returns true if the specified item can be pinned or unpinned. Only apps can
// be pinned.
- bool IsPinnable(ash::LauncherID id) const;
+ virtual bool IsPinnable(ash::LauncherID id) const = 0;
// If there is no launcher item in the launcher for application |app_id|, one
// gets created. The (existing or created) launcher items get then locked
// against a users un-pinning removal.
- void LockV1AppWithID(const std::string& app_id);
+ virtual void LockV1AppWithID(const std::string& app_id) = 0;
// A previously locked launcher item of type |app_id| gets unlocked. If the
// lock count reaches 0 and the item is not pinned it will go away.
- void UnlockV1AppWithID(const std::string& app_id);
+ virtual void UnlockV1AppWithID(const std::string& app_id) = 0;
// Requests that the launcher item controller specified by |id| open a new
// instance of the app. |event_flags| holds the flags of the event which
// triggered this command.
- void Launch(ash::LauncherID id, int event_flags);
+ virtual void Launch(ash::LauncherID id, int event_flags) = 0;
// Closes the specified item.
- void Close(ash::LauncherID id);
+ virtual void Close(ash::LauncherID id) = 0;
// Returns true if the specified item is open.
- bool IsOpen(ash::LauncherID id);
+ virtual bool IsOpen(ash::LauncherID id) = 0;
// Returns true if the specified item is for a platform app.
- bool IsPlatformApp(ash::LauncherID id);
+ virtual bool IsPlatformApp(ash::LauncherID id) = 0;
// Opens a new instance of the application identified by |app_id|.
// Used by the app-list, and by pinned-app launcher items.
- void LaunchApp(const std::string& app_id, int event_flags);
+ virtual void LaunchApp(const std::string& app_id, int event_flags) = 0;
// If |app_id| is running, reactivates the app's most recently active window,
// otherwise launches and activates the app.
// Used by the app-list, and by pinned-app launcher items.
- void ActivateApp(const std::string& app_id, int event_flags);
+ virtual void ActivateApp(const std::string& app_id, int event_flags) = 0;
// Returns the launch type of app for the specified id.
- extensions::ExtensionPrefs::LaunchType GetLaunchType(ash::LauncherID id);
+ virtual extensions::ExtensionPrefs::LaunchType GetLaunchType(
+ ash::LauncherID id) = 0;
// Returns the id of the app for the specified tab.
- std::string GetAppID(content::WebContents* tab);
+ virtual std::string GetAppID(content::WebContents* tab) = 0;
+
+ // Returns the launcherID of the first non-panel item whose app_id
+ // matches |app_id| or 0 if none match.
+ virtual ash::LauncherID GetLauncherIDForAppID(const std::string& app_id) = 0;
- std::string GetAppIDForLauncherID(ash::LauncherID id);
+ // Returns the id of the app for the specified id (which must exist).
+ virtual std::string GetAppIDForLauncherID(ash::LauncherID id) = 0;
// Set the image for a specific launcher item (e.g. when set by the app).
- void SetLauncherItemImage(ash::LauncherID launcher_id,
- const gfx::ImageSkia& image);
+ virtual void SetLauncherItemImage(ash::LauncherID launcher_id,
+ const gfx::ImageSkia& image) = 0;
- // Find out if the given application |id| is a windowed app item and not a
- // pinned item in the launcher.
- bool IsWindowedAppInLauncher(const std::string& app_id);
+ // Returns true if a pinned launcher item with given |app_id| could be found.
+ virtual bool IsAppPinned(const std::string& app_id) = 0;
+
+ // Pins an app with |app_id| to launcher. If there is a running instance in
+ // launcher, the running instance is pinned. If there is no running instance,
+ // a new launcher item is created and pinned.
+ virtual void PinAppWithID(const std::string& app_id) = 0;
// Updates the launche type of the app for the specified id to |launch_type|.
- void SetLaunchType(ash::LauncherID id,
- extensions::ExtensionPrefs::LaunchType launch_type);
+ virtual void SetLaunchType(
+ ash::LauncherID id,
+ extensions::ExtensionPrefs::LaunchType launch_type) = 0;
+
+ // Unpins any app items whose id is |app_id|.
+ virtual void UnpinAppsWithID(const std::string& app_id) = 0;
// Returns true if the user is currently logged in as a guest.
- // Makes virtual for unittest in LauncherContextMenuTest.
- virtual bool IsLoggedInAsGuest();
+ virtual bool IsLoggedInAsGuest() = 0;
// Invoked when user clicks on button in the launcher and there is no last
// used window (or CTRL is held with the click).
- void CreateNewWindow();
+ virtual void CreateNewWindow() = 0;
// Invoked when the user clicks on button in the launcher to create a new
// incognito window.
- void CreateNewIncognitoWindow();
+ virtual void CreateNewIncognitoWindow() = 0;
// Checks whether the user is allowed to pin apps. Pinning may be disallowed
// by policy in case there is a pre-defined set of pinned apps.
- bool CanPin() const;
+ virtual bool CanPin() const = 0;
// Updates the pinned pref state. The pinned state consists of a list pref.
// Each item of the list is a dictionary. The key |kAppIDPath| gives the
// id of the app.
- void PersistPinnedState();
+ virtual void PersistPinnedState() = 0;
- ash::LauncherModel* model();
+ virtual ash::LauncherModel* model() = 0;
- Profile* profile();
+ virtual Profile* profile() = 0;
// Gets the shelf auto-hide behavior on |root_window|.
- ash::ShelfAutoHideBehavior GetShelfAutoHideBehavior(
- aura::RootWindow* root_window) const;
+ virtual ash::ShelfAutoHideBehavior GetShelfAutoHideBehavior(
+ aura::RootWindow* root_window) const = 0;
// Returns |true| if the user is allowed to modify the shelf auto-hide
// behavior on |root_window|.
- bool CanUserModifyShelfAutoHideBehavior(aura::RootWindow* root_window) const;
+ virtual bool CanUserModifyShelfAutoHideBehavior(
+ aura::RootWindow* root_window) const = 0;
// Toggles the shelf auto-hide behavior on |root_window|. Does nothing if the
// user is not allowed to modify the auto-hide behavior.
- void ToggleShelfAutoHideBehavior(aura::RootWindow* root_window);
+ virtual void ToggleShelfAutoHideBehavior(aura::RootWindow* root_window) = 0;
// The tab no longer represents its previously identified application.
- void RemoveTabFromRunningApp(content::WebContents* tab,
- const std::string& app_id);
+ virtual void RemoveTabFromRunningApp(content::WebContents* tab,
+ const std::string& app_id) = 0;
// Notify the controller that the state of an non platform app's tabs
// have changed,
- void UpdateAppState(content::WebContents* contents, AppState app_state);
+ virtual void UpdateAppState(content::WebContents* contents,
+ AppState app_state) = 0;
// Limits application refocusing to urls that match |url| for |id|.
- void SetRefocusURLPatternForTest(ash::LauncherID id, const GURL& url);
+ virtual void SetRefocusURLPatternForTest(ash::LauncherID id,
+ const GURL& url) = 0;
// Returns the extension identified by |app_id|.
- const extensions::Extension* GetExtensionForAppID(
- const std::string& app_id) const;
+ virtual const extensions::Extension* GetExtensionForAppID(
+ const std::string& app_id) const = 0;
// Activates a |window|. If |allow_minimize| is true and the system allows
// it, the the window will get minimized instead.
- void ActivateWindowOrMinimizeIfActive(ui::BaseWindow* window,
- bool allow_minimize);
-
+ virtual void ActivateWindowOrMinimizeIfActive(ui::BaseWindow* window,
+ bool allow_minimize) = 0;
// ash::LauncherDelegate overrides:
virtual void ItemSelected(const ash::LauncherItem& item,
- const ui::Event& event) OVERRIDE;
- virtual string16 GetTitle(const ash::LauncherItem& item) OVERRIDE;
+ const ui::Event& event) OVERRIDE = 0;
+ virtual string16 GetTitle(const ash::LauncherItem& item) OVERRIDE = 0;
virtual ui::MenuModel* CreateContextMenu(
- const ash::LauncherItem& item, aura::RootWindow* root) OVERRIDE;
+ const ash::LauncherItem& item, aura::RootWindow* root) OVERRIDE = 0;
virtual ash::LauncherMenuModel* CreateApplicationMenu(
const ash::LauncherItem& item,
- int event_flags) OVERRIDE;
- virtual ash::LauncherID GetIDByWindow(aura::Window* window) OVERRIDE;
- virtual bool IsDraggable(const ash::LauncherItem& item) OVERRIDE;
- virtual bool ShouldShowTooltip(const ash::LauncherItem& item) OVERRIDE;
- virtual void OnLauncherCreated(ash::Launcher* launcher) OVERRIDE;
- virtual void OnLauncherDestroyed(ash::Launcher* launcher) OVERRIDE;
- virtual ash::LauncherID GetLauncherIDForAppID(
- const std::string& app_id) OVERRIDE;
- virtual void PinAppWithID(const std::string& app_id) OVERRIDE;
- virtual bool IsAppPinned(const std::string& app_id) OVERRIDE;
- virtual void UnpinAppsWithID(const std::string& app_id) OVERRIDE;
-
- // ash::LauncherModelObserver overrides:
- virtual void LauncherItemAdded(int index) OVERRIDE;
- virtual void LauncherItemRemoved(int index, ash::LauncherID id) OVERRIDE;
- virtual void LauncherItemMoved(int start_index, int target_index) OVERRIDE;
- virtual void LauncherItemChanged(int index,
- const ash::LauncherItem& old_item) OVERRIDE;
- virtual void LauncherStatusChanged() OVERRIDE;
-
- // content::NotificationObserver overrides:
- virtual void Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) OVERRIDE;
-
- // ash::ShellObserver overrides:
- virtual void OnShelfAlignmentChanged(aura::RootWindow* root_window) OVERRIDE;
-
- // ash::DisplayController::Observer overrides:
- virtual void OnDisplayConfigurationChanging() OVERRIDE;
- virtual void OnDisplayConfigurationChanged() OVERRIDE;
-
- // PrefServiceSyncableObserver overrides:
- virtual void OnIsSyncingChanged() OVERRIDE;
-
- // AppSyncUIStateObserver overrides:
- virtual void OnAppSyncUIStatusChanged() OVERRIDE;
-
- // ExtensionEnableFlowDelegate overrides:
- virtual void ExtensionEnableFlowFinished() OVERRIDE;
- virtual void ExtensionEnableFlowAborted(bool user_initiated) OVERRIDE;
+ int event_flags) OVERRIDE = 0;
+ virtual ash::LauncherID GetIDByWindow(aura::Window* window) OVERRIDE = 0;
+ virtual bool IsDraggable(const ash::LauncherItem& item) OVERRIDE = 0;
+ virtual bool ShouldShowTooltip(const ash::LauncherItem& item) OVERRIDE = 0;
+ virtual bool IsPerAppLauncher() OVERRIDE;
// extensions::AppIconLoader overrides:
virtual void SetAppImage(const std::string& app_id,
- const gfx::ImageSkia& image) OVERRIDE;
-
- // ash::ShelfLayoutManagerObserver overrides:
- virtual void OnAutoHideBehaviorChanged(
- aura::RootWindow* root_window,
- ash::ShelfAutoHideBehavior new_behavior) OVERRIDE;
-
- // Get the list of all running incarnations of this item.
- // |event_flags| specifies the flags which were set by the event which
- // triggered this menu generation. It can be used to generate different lists.
- ChromeLauncherAppMenuItems GetApplicationList(const ash::LauncherItem& item,
- int event_flags);
-
- // Get the list of all tabs which belong to a certain application type.
- std::vector<content::WebContents*> GetV1ApplicationsFromAppId(
- std::string app_id);
-
- // Activates a specified shell application.
- void ActivateShellApp(const std::string& app_id, int index);
-
- // Checks if a given |web_contents| is known to be associated with an
- // application of type |app_id|.
- bool IsWebContentHandledByApplication(content::WebContents* web_contents,
- const std::string& app_id);
-
- // Check if the gMail app is loaded and it can handle the given web content.
- // This special treatment is required to address crbug.com/234268.
- bool ContentCanBeHandledByGmailApp(content::WebContents* web_contents);
-
- // Get the favicon for the application list entry for |web_contents|.
- // Note that for incognito windows the incognito icon will be returned.
- // If |web_contents| has not loaded, returns the default favicon.
- gfx::Image GetAppListIcon(content::WebContents* web_contents) const;
-
- // Get the title for the applicatoin list entry for |web_contents|.
- // If |web_contents| has not loaded, returns "Net Tab".
- string16 GetAppListTitle(content::WebContents* web_contents) const;
-
- // Overridden from chrome::BrowserListObserver.
- virtual void OnBrowserRemoved(Browser* browser) OVERRIDE;
-
- // Returns true when the given |browser| is listed in the browser application
- // list.
- bool IsBrowserRepresentedInBrowserList(Browser* browser);
-
- // Returns the LauncherItemController of BrowserShortcut.
- LauncherItemController* GetBrowserShortcutLauncherItemController();
+ const gfx::ImageSkia& image) OVERRIDE = 0;
protected:
- // Creates a new app shortcut item and controller on the launcher at |index|.
- // Use kInsertItemAtEnd to add a shortcut as the last item.
- ash::LauncherID CreateAppShortcutLauncherItem(const std::string& app_id,
- int index);
-
- // Sets the AppTabHelper/AppIconLoader, taking ownership of the helper class.
- // These are intended for testing.
- void SetAppTabHelperForTest(AppTabHelper* helper);
- void SetAppIconLoaderForTest(extensions::AppIconLoader* loader);
- const std::string& GetAppIdFromLauncherIdForTest(ash::LauncherID id);
-
- private:
- friend class ChromeLauncherControllerTest;
- friend class LauncherAppBrowserTest;
+ friend class LauncherItemControllerPerAppTest;
friend class LauncherPlatformAppBrowserTest;
-
- typedef std::map<ash::LauncherID, LauncherItemController*>
- IDToItemControllerMap;
- typedef std::list<content::WebContents*> WebContentsList;
- typedef std::map<std::string, WebContentsList> AppIDToWebContentsListMap;
- typedef std::map<content::WebContents*, std::string> WebContentsToAppIDMap;
+ friend class LauncherAppBrowserTest;
+ // TODO(skuhne): Remove these when the old launcher get removed.
+ friend class LauncherPlatformPerAppAppBrowserTest;
+ friend class LauncherPerAppAppBrowserTest;
// Creates a new app shortcut item and controller on the launcher at |index|.
// Use kInsertItemAtEnd to add a shortcut as the last item.
- ash::LauncherID CreateAppShortcutLauncherItemWithType(
- const std::string& app_id,
- int index,
- ash::LauncherItemType launcher_item_type);
-
- // Updates the activation state of the Broswer item.
- void UpdateBrowserItemStatus();
-
- // Returns the profile used for new windows.
- Profile* GetProfileForNewWindows();
-
- // Invoked when the associated browser or app is closed.
- void LauncherItemClosed(ash::LauncherID id);
-
- // Internal helpers for pinning and unpinning that handle both
- // client-triggered and internal pinning operations.
- void DoPinAppWithID(const std::string& app_id);
- void DoUnpinAppsWithID(const std::string& app_id);
-
- // Re-syncs launcher model with prefs::kPinnedLauncherApps.
- void UpdateAppLaunchersFromPref();
-
- // Persists the shelf auto-hide behavior to prefs.
- void SetShelfAutoHideBehaviorPrefs(ash::ShelfAutoHideBehavior behavior,
- aura::RootWindow* root_window);
-
- // Sets the shelf auto-hide behavior from prefs.
- void SetShelfAutoHideBehaviorFromPrefs();
-
- // Sets the shelf alignment from prefs.
- void SetShelfAlignmentFromPrefs();
-
- // Sets both of auto-hide behavior and alignment from prefs.
- void SetShelfBehaviorsFromPrefs();
-
- // Returns the most recently active web contents for an app.
- content::WebContents* GetLastActiveWebContents(const std::string& app_id);
-
- // Creates an app launcher to insert at |index|. Note that |index| may be
- // adjusted by the model to meet ordering constraints.
- // The |launcher_item_type| will be set into the LauncherModel.
- ash::LauncherID InsertAppLauncherItem(
- LauncherItemController* controller,
+ virtual ash::LauncherID CreateAppShortcutLauncherItem(
const std::string& app_id,
- ash::LauncherItemStatus status,
- int index,
- ash::LauncherItemType launcher_item_type);
+ int index) = 0;
- bool HasItemController(ash::LauncherID id) const;
-
- // Enumerate all Web contents which match a given shortcut |controller|.
- std::vector<content::WebContents*> GetV1ApplicationsFromController(
- LauncherItemController* controller);
-
- // Create LauncherItem for Browser Shortcut.
- ash::LauncherID CreateBrowserShortcutLauncherItem();
-
- // Check if the given |web_contents| is in incognito mode.
- bool IsIncognito(content::WebContents* web_contents) const;
-
- // Update browser shortcut's index.
- void PersistChromeItemIndex(int index);
-
- // Get browser shortcut's index from pref.
- int GetChromeIconIndexFromPref() const;
-
- // Close all windowed V1 applications of a certain extension which was already
- // deleted.
- void CloseWindowedAppsFromRemovedExtension(const std::string& app_id);
-
- // Move a launcher item ignoring the pinned state changes from |index| to
- // |target_index|.
- void MoveItemWithoutPinnedStateChangeNotification(int source_index,
- int target_index);
+ // Sets the AppTabHelper/AppIconLoader, taking ownership of the helper class.
+ // These are intended for testing.
+ virtual void SetAppTabHelperForTest(AppTabHelper* helper) = 0;
+ virtual void SetAppIconLoaderForTest(extensions::AppIconLoader* loader) = 0;
+ virtual const std::string& GetAppIdFromLauncherIdForTest(
+ ash::LauncherID id) = 0;
+ private:
static ChromeLauncherController* instance_;
-
- ash::LauncherModel* model_;
-
- // Profile used for prefs and loading extensions. This is NOT necessarily the
- // profile new windows are created with.
- Profile* profile_;
-
- IDToItemControllerMap id_to_item_controller_map_;
-
- // Maintains activation order of web contents for each app.
- AppIDToWebContentsListMap app_id_to_web_contents_list_;
-
- // Direct access to app_id for a web contents.
- WebContentsToAppIDMap web_contents_to_app_id_;
-
- // Used to track shell windows.
- scoped_ptr<ShellWindowLauncherController> shell_window_controller_;
-
- // Used to get app info for tabs.
- scoped_ptr<AppTabHelper> app_tab_helper_;
-
- // Used to load the image for an app item.
- scoped_ptr<extensions::AppIconLoader> app_icon_loader_;
-
- content::NotificationRegistrar notification_registrar_;
-
- PrefChangeRegistrar pref_change_registrar_;
-
- AppSyncUIState* app_sync_ui_state_;
-
- scoped_ptr<ExtensionEnableFlow> extension_enable_flow_;
-
- // Launchers that are currently being observed.
- std::set<ash::Launcher*> launchers_;
-
- // The owned browser shortcut item.
- scoped_ptr<BrowserShortcutLauncherItemController> browser_item_controller_;
-
- // If true, incoming pinned state changes should be ignored.
- bool ignore_persist_pinned_state_change_;
-
- DISALLOW_COPY_AND_ASSIGN(ChromeLauncherController);
};
#endif // CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_LAUNCHER_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index abf0408..c68476c 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -8,15 +8,13 @@
#include "apps/shell_window.h"
#include "apps/shell_window_registry.h"
#include "ash/ash_switches.h"
-#include "ash/display/display_controller.h"
#include "ash/launcher/launcher.h"
#include "ash/launcher/launcher_model.h"
-#include "ash/launcher/launcher_util.h"
-#include "ash/launcher/launcher_view.h"
#include "ash/shell.h"
#include "ash/test/launcher_view_test_api.h"
#include "ash/test/shell_test_api.h"
#include "ash/wm/window_util.h"
+#include "base/command_line.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/apps/app_browsertest_util.h"
@@ -29,13 +27,8 @@
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/extension_test_message_listener.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/app_list/app_list_service.h"
-#include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/browser/ui/host_desktop.h"
@@ -46,81 +39,37 @@
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/web_contents.h"
-#include "extensions/common/switches.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/app_list/views/apps_grid_view.h"
#include "ui/aura/client/aura_constants.h"
-#include "ui/aura/test/event_generator.h"
#include "ui/aura/window.h"
-#include "ui/base/events/event.h"
using apps::ShellWindow;
using extensions::Extension;
using content::WebContents;
-namespace {
-
-class TestEvent : public ui::Event {
- public:
- explicit TestEvent(ui::EventType type)
- : ui::Event(type, base::TimeDelta(), 0) {
- }
- virtual ~TestEvent() {
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestEvent);
-};
-
-class TestShellWindowRegistryObserver
- : public apps::ShellWindowRegistry::Observer {
- public:
- explicit TestShellWindowRegistryObserver(Profile* profile)
- : profile_(profile),
- icon_updates_(0) {
- apps::ShellWindowRegistry::Get(profile_)->AddObserver(this);
- }
-
- virtual ~TestShellWindowRegistryObserver() {
- apps::ShellWindowRegistry::Get(profile_)->RemoveObserver(this);
- }
-
- // Overridden from ShellWindowRegistry::Observer:
- virtual void OnShellWindowAdded(ShellWindow* shell_window) OVERRIDE {}
-
- virtual void OnShellWindowIconChanged(ShellWindow* shell_window) OVERRIDE {
- ++icon_updates_;
- }
-
- virtual void OnShellWindowRemoved(ShellWindow* shell_window) OVERRIDE {}
-
- int icon_updates() { return icon_updates_; }
-
- private:
- Profile* profile_;
- int icon_updates_;
-
- DISALLOW_COPY_AND_ASSIGN(TestShellWindowRegistryObserver);
-};
-
-} // namespace
-
class LauncherPlatformAppBrowserTest
: public extensions::PlatformAppBrowserTest {
protected:
- LauncherPlatformAppBrowserTest() : launcher_(NULL), controller_(NULL) {
+ LauncherPlatformAppBrowserTest()
+ : launcher_(NULL),
+ controller_(NULL) {
}
virtual ~LauncherPlatformAppBrowserTest() {}
+ ash::LauncherModel* launcher_model() {
+ return ash::test::ShellTestApi(ash::Shell::GetInstance()).launcher_model();
+ }
+
virtual void RunTestOnMainThreadLoop() OVERRIDE {
launcher_ = ash::Launcher::ForPrimaryDisplay();
- controller_ = ChromeLauncherController::instance();
+ controller_ = static_cast<ChromeLauncherController*>(launcher_->delegate());
return extensions::PlatformAppBrowserTest::RunTestOnMainThreadLoop();
}
- ash::LauncherModel* launcher_model() {
- return ash::test::ShellTestApi(ash::Shell::GetInstance()).launcher_model();
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ PlatformAppBrowserTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitch(ash::switches::kAshDisablePerAppLauncher);
}
ash::LauncherID CreateAppShortcutLauncherItem(const std::string& name) {
@@ -130,7 +79,7 @@ class LauncherPlatformAppBrowserTest
const ash::LauncherItem& GetLastLauncherItem() {
// Unless there are any panels, the item at index [count - 1] will be
- // the app list, and the item at [count - 2] will be the desired item.
+ // the app list, and the item at [count - 2] will be the desited item.
return launcher_model()->items()[launcher_model()->item_count() - 2];
}
@@ -140,39 +89,15 @@ class LauncherPlatformAppBrowserTest
return launcher_model()->items()[launcher_model()->item_count() - 1];
}
- LauncherItemController* GetItemController(ash::LauncherID id) {
- return controller_->id_to_item_controller_map_[id];
- }
-
- // Returns the number of menu items, ignoring separators.
- int GetNumApplicationMenuItems(const ash::LauncherItem& item) {
- const int event_flags = 0;
- scoped_ptr<ash::LauncherMenuModel> menu(
- controller_->CreateApplicationMenu(item, event_flags));
- int num_items = 0;
- for (int i = 0; i < menu->GetItemCount(); ++i) {
- if (menu->GetTypeAt(i) != ui::MenuModel::TYPE_SEPARATOR)
- ++num_items;
- }
- return num_items;
- }
-
- // Activate the launcher item with the given |id|.
- void ActivateLauncherItem(int id) {
- launcher_->ActivateLauncherItem(id);
- }
-
ash::Launcher* launcher_;
ChromeLauncherController* controller_;
-
- private:
-
- DISALLOW_COPY_AND_ASSIGN(LauncherPlatformAppBrowserTest);
};
class LauncherAppBrowserTest : public ExtensionBrowserTest {
protected:
- LauncherAppBrowserTest() : launcher_(NULL), model_(NULL), controller_(NULL) {
+ LauncherAppBrowserTest()
+ : launcher_(NULL),
+ model_(NULL) {
}
virtual ~LauncherAppBrowserTest() {}
@@ -181,17 +106,12 @@ class LauncherAppBrowserTest : public ExtensionBrowserTest {
launcher_ = ash::Launcher::ForPrimaryDisplay();
model_ =
ash::test::ShellTestApi(ash::Shell::GetInstance()).launcher_model();
- controller_ = ChromeLauncherController::instance();
return ExtensionBrowserTest::RunTestOnMainThreadLoop();
}
- size_t NumberOfDetectedLauncherBrowsers(bool show_all_tabs) {
- LauncherItemController* item_controller =
- controller_->GetBrowserShortcutLauncherItemController();
- int items = item_controller->GetApplicationList(
- show_all_tabs ? ui::EF_SHIFT_DOWN : 0).size();
- // If we have at least one item, we have also a title which we remove here.
- return items ? (items - 1) : 0;
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ ExtensionBrowserTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitch(ash::switches::kAshDisablePerAppLauncher);
}
const Extension* LoadAndLaunchExtension(
@@ -201,21 +121,19 @@ class LauncherAppBrowserTest : public ExtensionBrowserTest {
EXPECT_TRUE(LoadExtension(test_data_dir_.AppendASCII(name)));
ExtensionService* service = extensions::ExtensionSystem::Get(
- profile())->extension_service();
+ browser()->profile())->extension_service();
const Extension* extension =
service->GetExtensionById(last_loaded_extension_id_, false);
EXPECT_TRUE(extension);
- chrome::OpenApplication(chrome::AppLaunchParams(profile(),
- extension,
- container,
- disposition));
+ chrome::OpenApplication(chrome::AppLaunchParams(
+ browser()->profile(), extension, container, disposition));
return extension;
}
ash::LauncherID CreateShortcut(const char* name) {
ExtensionService* service = extensions::ExtensionSystem::Get(
- profile())->extension_service();
+ browser()->profile())->extension_service();
LoadExtension(test_data_dir_.AppendASCII(name));
// First get app_id.
@@ -224,72 +142,29 @@ class LauncherAppBrowserTest : public ExtensionBrowserTest {
const std::string app_id = extension->id();
// Then create a shortcut.
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
int item_count = model_->item_count();
- ash::LauncherID shortcut_id = controller_->CreateAppShortcutLauncherItem(
- app_id,
- item_count);
- controller_->PersistPinnedState();
+ ash::LauncherID shortcut_id = controller->CreateAppShortcutLauncherItem(
+ app_id, item_count);
+ controller->PersistPinnedState();
EXPECT_EQ(++item_count, model_->item_count());
- const ash::LauncherItem& item = *model_->ItemByID(shortcut_id);
+ ash::LauncherItem item = *model_->ItemByID(shortcut_id);
EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type);
return item.id;
}
- // Activate the launcher item with the given |id|.
- void ActivateLauncherItem(int id) {
- launcher_->ActivateLauncherItem(id);
- }
-
ash::LauncherID PinFakeApp(const std::string& name) {
- return controller_->CreateAppShortcutLauncherItem(
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
+ return controller->CreateAppShortcutLauncherItem(
name, model_->item_count());
}
ash::Launcher* launcher_;
ash::LauncherModel* model_;
- ChromeLauncherController* controller_;
-
- private:
-
- DISALLOW_COPY_AND_ASSIGN(LauncherAppBrowserTest);
};
-class LauncherAppBrowserTestNoDefaultBrowser : public LauncherAppBrowserTest {
- protected:
- LauncherAppBrowserTestNoDefaultBrowser() {}
- virtual ~LauncherAppBrowserTestNoDefaultBrowser() {}
-
- virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- LauncherAppBrowserTest::SetUpCommandLine(command_line);
- command_line->AppendSwitch(switches::kNoStartupWindow);
- }
-
- private:
-
- DISALLOW_COPY_AND_ASSIGN(LauncherAppBrowserTestNoDefaultBrowser);
-};
-
-// Since the default for minimizing on click might change, I added both classes
-// to either get the minimize on click or not.
-class LauncherAppBrowserNoMinimizeOnClick
- : public LauncherPlatformAppBrowserTest {
- protected:
- LauncherAppBrowserNoMinimizeOnClick() {}
- virtual ~LauncherAppBrowserNoMinimizeOnClick() {}
-
- virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- LauncherPlatformAppBrowserTest::SetUpCommandLine(command_line);
- command_line->AppendSwitch(
- switches::kDisableMinimizeOnSecondLauncherItemClick);
- }
-
- private:
-
- DISALLOW_COPY_AND_ASSIGN(LauncherAppBrowserNoMinimizeOnClick);
-};
-
-typedef LauncherPlatformAppBrowserTest LauncherAppBrowserMinimizeOnClick;
-
// Test that we can launch a platform app and get a running item.
IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, LaunchUnpinned) {
int item_count = launcher_model()->item_count();
@@ -435,7 +310,6 @@ IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, MultipleWindows) {
ash::LauncherID item_id = item1.id;
EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type);
EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
- EXPECT_EQ(2, GetNumApplicationMenuItems(item1)); // Title + 1 window
// Add second window.
ShellWindow* window2 = CreateShellWindow(extension);
@@ -443,7 +317,6 @@ IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, MultipleWindows) {
ASSERT_EQ(item_count, launcher_model()->item_count());
const ash::LauncherItem& item2 = *launcher_model()->ItemByID(item_id);
EXPECT_EQ(ash::STATUS_ACTIVE, item2.status);
- EXPECT_EQ(3, GetNumApplicationMenuItems(item2)); // Title + 2 windows
// Close second window.
CloseShellWindow(window2);
@@ -451,7 +324,6 @@ IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, MultipleWindows) {
ASSERT_EQ(item_count, launcher_model()->item_count());
const ash::LauncherItem& item3 = *launcher_model()->ItemByID(item_id);
EXPECT_EQ(ash::STATUS_ACTIVE, item3.status);
- EXPECT_EQ(2, GetNumApplicationMenuItems(item3)); // Title + 1 window
// Close first window.
CloseShellWindow(window1);
@@ -501,9 +373,33 @@ IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, MultipleApps) {
ASSERT_EQ(item_count, launcher_model()->item_count());
}
+// Test that we can launch a platform app panel and get a running item.
+IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, LaunchPanelWindow) {
+ int item_count = launcher_model()->item_count();
+ const Extension* extension = LoadAndLaunchPlatformApp("launch");
+ ShellWindow::CreateParams params;
+ params.window_type = ShellWindow::WINDOW_TYPE_PANEL;
+ params.focused = false;
+ ShellWindow* window = CreateShellWindowFromParams(extension, params);
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item = GetLastLauncherPanelItem();
+ EXPECT_EQ(ash::TYPE_APP_PANEL, item.type);
+ // Opening a panel does not activate it.
+ EXPECT_EQ(ash::STATUS_RUNNING, item.status);
+ CloseShellWindow(window);
+ --item_count;
+ EXPECT_EQ(item_count, launcher_model()->item_count());
+}
+
+#if defined(OS_CHROMEOS)
+#define MAYBE_WindowActivation DISABLED_WindowActivation
+#else
+#define MAYBE_WindowActivation WindowActivation
+#endif
// Confirm that app windows can be reactivated by clicking their icons and that
// the correct activation order is maintained.
-IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, WindowActivation) {
+IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, MAYBE_WindowActivation) {
int item_count = launcher_model()->item_count();
// First run app.
@@ -531,7 +427,7 @@ IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, WindowActivation) {
launcher_model()->ItemByID(item_id1)->status);
// Activate first one.
- ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+ launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model()->ItemByID(item_id1)->status);
EXPECT_EQ(ash::STATUS_RUNNING,
launcher_model()->ItemByID(item_id2)->status);
@@ -539,7 +435,7 @@ IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, WindowActivation) {
EXPECT_FALSE(ash::wm::IsActiveWindow(window2->GetNativeWindow()));
// Activate second one.
- ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2));
+ launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2));
EXPECT_EQ(ash::STATUS_RUNNING,
launcher_model()->ItemByID(item_id1)->status);
EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model()->ItemByID(item_id2)->status);
@@ -554,23 +450,23 @@ IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, WindowActivation) {
EXPECT_TRUE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
// Activate launcher item for app1, this will activate the first app window.
- ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+ launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
EXPECT_FALSE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
- ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
- EXPECT_TRUE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
+ launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+ EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
// Activate the second app again
- ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2));
+ launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2));
EXPECT_FALSE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
EXPECT_TRUE(ash::wm::IsActiveWindow(window2->GetNativeWindow()));
EXPECT_FALSE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
// Activate the first app again
- ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
- EXPECT_TRUE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
+ launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+ EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
EXPECT_FALSE(ash::wm::IsActiveWindow(window2->GetNativeWindow()));
- EXPECT_FALSE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
+ EXPECT_FALSE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
// Close second app.
CloseShellWindow(window2);
@@ -586,138 +482,6 @@ IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, WindowActivation) {
EXPECT_EQ(item_count, launcher_model()->item_count());
}
-// Confirm that Click behavior for app windows is correnct.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserNoMinimizeOnClick,
- AppClickBehavior) {
- // Launch a platform app and create a window for it.
- const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
- ShellWindow* window1 = CreateShellWindow(extension1);
- EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
- // Confirm that a controller item was created and is the correct state.
- const ash::LauncherItem& item1 = GetLastLauncherItem();
- LauncherItemController* item1_controller = GetItemController(item1.id);
- EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type);
- EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
- EXPECT_EQ(LauncherItemController::TYPE_APP, item1_controller->type());
- // Clicking the item should have no effect.
- TestEvent click_event(ui::ET_MOUSE_PRESSED);
- item1_controller->Clicked(click_event);
- EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
- // Minimize the window and confirm that the controller item is updated.
- window1->GetBaseWindow()->Minimize();
- EXPECT_FALSE(window1->GetNativeWindow()->IsVisible());
- EXPECT_FALSE(window1->GetBaseWindow()->IsActive());
- EXPECT_EQ(ash::STATUS_RUNNING, item1.status);
- // Clicking the item should activate the window.
- item1_controller->Clicked(click_event);
- EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
- EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
- // Maximizing a window should preserve state after minimize + click.
- window1->GetBaseWindow()->Maximize();
- window1->GetBaseWindow()->Minimize();
- item1_controller->Clicked(click_event);
- EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
- EXPECT_TRUE(window1->GetBaseWindow()->IsMaximized());
-}
-
-// Confirm the minimizing click behavior for apps.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserMinimizeOnClick,
- PackagedAppClickBehaviorInMinimizeMode) {
- // Launch one platform app and create a window for it.
- const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
- ShellWindow* window1 = CreateShellWindow(extension1);
- EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
-
- // Confirm that a controller item was created and is the correct state.
- const ash::LauncherItem& item1 = GetLastLauncherItem();
- LauncherItemController* item1_controller = GetItemController(item1.id);
- EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type);
- EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
- EXPECT_EQ(LauncherItemController::TYPE_APP, item1_controller->type());
- // Since it is already active, clicking it should minimize.
- TestEvent click_event(ui::ET_MOUSE_PRESSED);
- item1_controller->Clicked(click_event);
- EXPECT_FALSE(window1->GetNativeWindow()->IsVisible());
- EXPECT_FALSE(window1->GetBaseWindow()->IsActive());
- EXPECT_TRUE(window1->GetBaseWindow()->IsMinimized());
- EXPECT_EQ(ash::STATUS_RUNNING, item1.status);
- // Clicking the item again should activate the window again.
- item1_controller->Clicked(click_event);
- EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
- EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
- // Maximizing a window should preserve state after minimize + click.
- window1->GetBaseWindow()->Maximize();
- window1->GetBaseWindow()->Minimize();
- item1_controller->Clicked(click_event);
- EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
- EXPECT_TRUE(window1->GetBaseWindow()->IsMaximized());
- window1->GetBaseWindow()->Restore();
- EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
- EXPECT_FALSE(window1->GetBaseWindow()->IsMaximized());
-
- // Creating a second window of the same type should change the behavior so
- // that a click does not change the activation state.
- ShellWindow* window1a = CreateShellWindow(extension1);
- EXPECT_TRUE(window1a->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1a->GetBaseWindow()->IsActive());
- // The first click does nothing.
- item1_controller->Clicked(click_event);
- EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1a->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
- EXPECT_FALSE(window1a->GetBaseWindow()->IsActive());
- // The second neither.
- item1_controller->Clicked(click_event);
- EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1a->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
- EXPECT_FALSE(window1a->GetBaseWindow()->IsActive());
-}
-
-// Confirm that click behavior for app panels is correct.
-IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, AppPanelClickBehavior) {
- // Enable experimental APIs to allow panel creation.
- CommandLine::ForCurrentProcess()->AppendSwitch(
- extensions::switches::kEnableExperimentalExtensionApis);
- // Launch a platform app and create a panel window for it.
- const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
- ShellWindow::CreateParams params;
- params.window_type = ShellWindow::WINDOW_TYPE_PANEL;
- params.focused = false;
- ShellWindow* panel = CreateShellWindowFromParams(extension1, params);
- EXPECT_TRUE(panel->GetNativeWindow()->IsVisible());
- // Panels should not be active by default.
- EXPECT_FALSE(panel->GetBaseWindow()->IsActive());
- // Confirm that a controller item was created and is the correct state.
- const ash::LauncherItem& item1 = GetLastLauncherPanelItem();
- LauncherItemController* item1_controller = GetItemController(item1.id);
- EXPECT_EQ(ash::TYPE_APP_PANEL, item1.type);
- EXPECT_EQ(ash::STATUS_RUNNING, item1.status);
- EXPECT_EQ(LauncherItemController::TYPE_APP_PANEL, item1_controller->type());
- // Click the item and confirm that the panel is activated.
- TestEvent click_event(ui::ET_MOUSE_PRESSED);
- item1_controller->Clicked(click_event);
- EXPECT_TRUE(panel->GetBaseWindow()->IsActive());
- EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
- // Click the item again and confirm that the panel is minimized.
- item1_controller->Clicked(click_event);
- EXPECT_TRUE(panel->GetBaseWindow()->IsMinimized());
- EXPECT_EQ(ash::STATUS_RUNNING, item1.status);
- // Click the item again and confirm that the panel is activated.
- item1_controller->Clicked(click_event);
- EXPECT_TRUE(panel->GetNativeWindow()->IsVisible());
- EXPECT_TRUE(panel->GetBaseWindow()->IsActive());
- EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
-}
-
IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, BrowserActivation) {
int item_count = launcher_model()->item_count();
@@ -736,49 +500,24 @@ IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, BrowserActivation) {
launcher_model()->ItemByID(item_id1)->status);
}
-// Test that opening an app sets the correct icon
-IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, SetIcon) {
- TestShellWindowRegistryObserver test_observer(browser()->profile());
-
- // Enable experimental APIs to allow panel creation.
- CommandLine::ForCurrentProcess()->AppendSwitch(
- extensions::switches::kEnableExperimentalExtensionApis);
-
- int base_launcher_item_count = launcher_model()->item_count();
- ExtensionTestMessageListener launched_listener("Launched", false);
- ExtensionTestMessageListener completed_listener("Completed", false);
- LoadAndLaunchPlatformApp("app_icon");
- ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
- ASSERT_TRUE(completed_listener.WaitUntilSatisfied());
-
- // Now wait until the WebContent has decoded the icons and chrome has
- // processed it. This needs to be in a loop since the renderer runs in a
- // different process.
- while (test_observer.icon_updates() < 3) {
- base::RunLoop run_loop;
- run_loop.RunUntilIdle();
- }
-
- // This test creates one shell window and one panel window.
- int launcher_item_count = launcher_model()->item_count();
- ASSERT_EQ(base_launcher_item_count + 2, launcher_item_count);
- // The Panel will be the last item, the app list second-to-last, the app
- // third from last.
- const ash::LauncherItem& app_item =
- launcher_model()->items()[launcher_item_count - 3];
- const ash::LauncherItem& panel_item =
- launcher_model()->items()[launcher_item_count - 1];
- const LauncherItemController* app_item_controller =
- GetItemController(app_item.id);
- const LauncherItemController* panel_item_controller =
- GetItemController(panel_item.id);
- // Icons for Apps are set by the ShellWindowLauncherController, so
- // image_set_by_controller() should be set.
- EXPECT_TRUE(app_item_controller->image_set_by_controller());
- EXPECT_TRUE(panel_item_controller->image_set_by_controller());
- // Ensure icon heights are correct (see test.js in app_icon/ test directory)
- EXPECT_EQ(48, app_item.image.height());
- EXPECT_EQ(64, panel_item.image.height());
+// Test that draw attention sets the launcher item status.
+IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, DrawAttention) {
+ const Extension* extension = LoadAndLaunchPlatformApp("launch");
+ ShellWindow* shell_window = CreateShellWindow(extension);
+ const ash::LauncherItem& item = GetLastLauncherItem();
+ EXPECT_EQ(ash::STATUS_ACTIVE, item.status);
+ // Set Minimize window to deactivate the launcher item.
+ shell_window->GetBaseWindow()->Minimize();
+ EXPECT_EQ(ash::STATUS_RUNNING, item.status);
+ // Set DrawAttention property.
+ shell_window->GetNativeWindow()->SetProperty(
+ aura::client::kDrawAttentionKey, true);
+ EXPECT_EQ(ash::STATUS_ATTENTION, item.status);
+ // Activate window, should clear DrawAttention.
+ shell_window->GetBaseWindow()->Activate();
+ EXPECT_EQ(ash::STATUS_ACTIVE, item.status);
+ EXPECT_FALSE(shell_window->GetNativeWindow()->GetProperty(
+ aura::client::kDrawAttentionKey));
}
// Test that we can launch an app with a shortcut.
@@ -787,7 +526,7 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, LaunchPinned) {
int tab_count = tab_strip->count();
ash::LauncherID shortcut_id = CreateShortcut("app1");
EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status);
- ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
EXPECT_EQ(++tab_count, tab_strip->count());
EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
WebContents* tab = tab_strip->GetActiveWebContents();
@@ -847,7 +586,7 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, LaunchMaximized) {
ash::wm::MaximizeWindow(window2);
ash::LauncherID shortcut_id = CreateShortcut("app1");
- ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
EXPECT_EQ(++tab_count, tab_strip->count());
EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
@@ -855,7 +594,7 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, LaunchMaximized) {
ash::wm::ActivateWindow(window1);
EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut_id)).status);
- ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
}
@@ -896,13 +635,13 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, MultipleApps) {
EXPECT_EQ(++item_count, model_->item_count());
// Launch first app.
- ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
EXPECT_EQ(++tab_count, tab_strip->count());
WebContents* tab1 = tab_strip->GetActiveWebContents();
EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status);
// Launch second app.
- ActivateLauncherItem(model_->ItemIndexByID(shortcut2));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut2));
EXPECT_EQ(++tab_count, tab_strip->count());
WebContents* tab2 = tab_strip->GetActiveWebContents();
ASSERT_NE(tab1, tab2);
@@ -910,7 +649,7 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, MultipleApps) {
EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut2)).status);
// Reactivate first app.
- ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
EXPECT_EQ(tab_count, tab_strip->count());
EXPECT_EQ(tab_strip->GetActiveWebContents(), tab1);
EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status);
@@ -923,20 +662,21 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, MultipleApps) {
NEW_FOREGROUND_TAB,
0);
EXPECT_EQ(++tab_count, tab_strip->count());
+ WebContents* tab3 = tab_strip->GetActiveWebContents();
EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut1)).status);
EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut2)).status);
// Reactivate first app.
- ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
EXPECT_EQ(tab_count, tab_strip->count());
EXPECT_EQ(tab_strip->GetActiveWebContents(), tab1);
EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status);
EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut2)).status);
// And second again. This time the second tab should become active.
- ActivateLauncherItem(model_->ItemIndexByID(shortcut2));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut2));
EXPECT_EQ(tab_count, tab_strip->count());
- EXPECT_EQ(tab_strip->GetActiveWebContents(), tab2);
+ EXPECT_EQ(tab_strip->GetActiveWebContents(), tab3);
EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut1)).status);
EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut2)).status);
}
@@ -946,7 +686,7 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, MultipleApps) {
IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, Navigation) {
ash::LauncherID shortcut_id = CreateShortcut("app1");
EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status);
- ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
// Navigate away.
@@ -964,10 +704,9 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, MultipleOwnedTabs) {
TabStripModel* tab_strip = browser()->tab_strip_model();
int tab_count = tab_strip->count();
ash::LauncherID shortcut_id = CreateShortcut("app1");
- ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
EXPECT_EQ(++tab_count, tab_strip->count());
EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
- WebContents* first_tab = tab_strip->GetActiveWebContents();
// Create new tab owned by app.
ui_test_utils::NavigateToURLWithDisposition(
@@ -978,6 +717,7 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, MultipleOwnedTabs) {
EXPECT_EQ(++tab_count, tab_strip->count());
// Confirm app is still active.
EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
+ WebContents* second_tab = tab_strip->GetActiveWebContents();
// Create new tab not owned by app.
ui_test_utils::NavigateToURLWithDisposition(
@@ -989,22 +729,24 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, MultipleOwnedTabs) {
// No longer active.
EXPECT_EQ(ash::STATUS_RUNNING, model_->ItemByID(shortcut_id)->status);
- // Activating app makes first tab active again.
- ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ // Activating app makes second tab active again.
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
- EXPECT_EQ(tab_strip->GetActiveWebContents(), first_tab);
+ EXPECT_EQ(tab_strip->GetActiveWebContents(), second_tab);
}
IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, RefocusFilter) {
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
TabStripModel* tab_strip = browser()->tab_strip_model();
int tab_count = tab_strip->count();
ash::LauncherID shortcut_id = CreateShortcut("app1");
- ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
EXPECT_EQ(++tab_count, tab_strip->count());
EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
WebContents* first_tab = tab_strip->GetActiveWebContents();
- controller_->SetRefocusURLPatternForTest(
+ controller->SetRefocusURLPatternForTest(
shortcut_id, GURL("http://www.example.com/path1/*"));
// Create new tab owned by app.
ui_test_utils::NavigateToURLWithDisposition(
@@ -1028,32 +770,34 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, RefocusFilter) {
// Activating app makes first tab active again, because second tab isn't
// in its refocus url path.
- ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
EXPECT_EQ(tab_strip->GetActiveWebContents(), first_tab);
}
IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, RefocusFilterLaunch) {
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
TabStripModel* tab_strip = browser()->tab_strip_model();
int tab_count = tab_strip->count();
ash::LauncherID shortcut_id = CreateShortcut("app1");
- controller_->SetRefocusURLPatternForTest(
+ controller->SetRefocusURLPatternForTest(
shortcut_id, GURL("http://www.example.com/path1/*"));
- // Create new tab.
+ // Create new tab owned by app.
ui_test_utils::NavigateToURLWithDisposition(
browser(),
- GURL("http://www.example2.com/path2/bar.html"),
+ GURL("http://www.example.com/path2/bar.html"),
NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
EXPECT_EQ(++tab_count, tab_strip->count());
WebContents* first_tab = tab_strip->GetActiveWebContents();
- // Confirm app is not active.
- EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
+ // Confirm app is active.
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
// Activating app should launch new tab, because second tab isn't
// in its refocus url path.
- ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
EXPECT_EQ(++tab_count, tab_strip->count());
WebContents* second_tab = tab_strip->GetActiveWebContents();
EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
@@ -1061,580 +805,6 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, RefocusFilterLaunch) {
EXPECT_EQ(tab_strip->GetActiveWebContents(), second_tab);
}
-// Check the launcher activation state for applications and browser.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, ActivationStateCheck) {
- TabStripModel* tab_strip = browser()->tab_strip_model();
- // Get the browser item index
- int browser_index = ash::launcher::GetBrowserItemIndex(*controller_->model());
- EXPECT_TRUE(browser_index >= 0);
-
- // Even though we are just comming up, the browser should be active.
- EXPECT_EQ(ash::STATUS_ACTIVE, model_->items()[browser_index].status);
-
- ash::LauncherID shortcut_id = CreateShortcut("app1");
- controller_->SetRefocusURLPatternForTest(
- shortcut_id, GURL("http://www.example.com/path1/*"));
-
- EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
- EXPECT_EQ(ash::STATUS_ACTIVE, model_->items()[browser_index].status);
-
- // Create new tab which would be the running app.
- ui_test_utils::NavigateToURLWithDisposition(
- browser(),
- GURL("http://www.example.com/path1/bar.html"),
- NEW_FOREGROUND_TAB,
- ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-
- // There should never be two items active at the same time.
- EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
- EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[browser_index].status);
-
- tab_strip->ActivateTabAt(0, false);
- EXPECT_EQ(ash::STATUS_RUNNING, model_->ItemByID(shortcut_id)->status);
- EXPECT_EQ(ash::STATUS_ACTIVE, model_->items()[browser_index].status);
-
- tab_strip->CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
- EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
- EXPECT_EQ(ash::STATUS_ACTIVE, model_->items()[browser_index].status);
-
- ash::wm::DeactivateWindow(browser()->window()->GetNativeWindow());
- EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
- EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[browser_index].status);
-}
-
-// Check that the launcher activation state for a V1 application stays closed
-// even after an asynchronous browser event comes in after the tab got
-// destroyed.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, AsyncActivationStateCheck) {
- TabStripModel* tab_strip = browser()->tab_strip_model();
-
- ash::LauncherID shortcut_id = CreateShortcut("app1");
- controller_->SetRefocusURLPatternForTest(
- shortcut_id, GURL("http://www.example.com/path1/*"));
-
- EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
-
- // Create new tab which would be the running app.
- ui_test_utils::NavigateToURLWithDisposition(
- browser(),
- GURL("http://www.example.com/path1/bar.html"),
- NEW_FOREGROUND_TAB,
- ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-
- EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
- // To address the issue of crbug.com/174050, the tab we are about to close
- // has to be active.
- tab_strip->ActivateTabAt(1, false);
- EXPECT_EQ(1, tab_strip->active_index());
-
- // Close the web contents.
- tab_strip->CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
- // The status should now be set to closed.
- EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
-}
-
-// Checks that a windowed application does not add an item to the browser list.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTestNoDefaultBrowser,
- WindowedAppDoesNotAddToBrowser) {
- // Get the number of items in the browser menu.
- size_t items = NumberOfDetectedLauncherBrowsers(false);
- size_t running_browser = chrome::GetTotalBrowserCount();
- EXPECT_EQ(0u, items);
- EXPECT_EQ(0u, running_browser);
-
- LoadAndLaunchExtension("app1", extension_misc::LAUNCH_WINDOW, NEW_WINDOW);
-
- // No new browser should get detected, even though one more is running.
- EXPECT_EQ(0u, NumberOfDetectedLauncherBrowsers(false));
- EXPECT_EQ(++running_browser, chrome::GetTotalBrowserCount());
-
- LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB, NEW_WINDOW);
-
- // A new browser should get detected and one more should be running.
- EXPECT_EQ(NumberOfDetectedLauncherBrowsers(false), 1u);
- EXPECT_EQ(++running_browser, chrome::GetTotalBrowserCount());
-}
-
-// Checks the functionality to enumerate all browsers vs. all tabs.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTestNoDefaultBrowser,
- EnumerateALlBrowsersAndTabs) {
- // Create at least one browser.
- LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB, NEW_WINDOW);
- size_t browsers = NumberOfDetectedLauncherBrowsers(false);
- size_t tabs = NumberOfDetectedLauncherBrowsers(true);
-
- // Create a second browser.
- LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB, NEW_WINDOW);
-
- EXPECT_EQ(++browsers, NumberOfDetectedLauncherBrowsers(false));
- EXPECT_EQ(++tabs, NumberOfDetectedLauncherBrowsers(true));
-
- // Create only a tab.
- LoadAndLaunchExtension("app1",
- extension_misc::LAUNCH_TAB,
- NEW_FOREGROUND_TAB);
-
- EXPECT_EQ(browsers, NumberOfDetectedLauncherBrowsers(false));
- EXPECT_EQ(++tabs, NumberOfDetectedLauncherBrowsers(true));
-}
-
-// Check that the keyboard activation of a launcher item tabs properly through
-// the items at hand.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, AltNumberTabsTabbing) {
- TabStripModel* tab_strip = browser()->tab_strip_model();
-
- ash::LauncherID shortcut_id = CreateShortcut("app");
- controller_->SetRefocusURLPatternForTest(
- shortcut_id, GURL("http://www.example.com/path/*"));
- std::string url = "http://www.example.com/path/bla";
-
- int shortcut_index = model_->ItemIndexByID(shortcut_id);
-
- // Create an application handled browser tab.
- ui_test_utils::NavigateToURLWithDisposition(
- browser(),
- GURL(url),
- NEW_FOREGROUND_TAB,
- ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-
- content::WebContents* content1 = tab_strip->GetActiveWebContents();
-
- // Create some other browser tab.
- ui_test_utils::NavigateToURLWithDisposition(
- browser(),
- GURL("http://www.test.com"),
- NEW_FOREGROUND_TAB,
- ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
- content::WebContents* content1a = tab_strip->GetActiveWebContents();
-
- // Make sure that the active tab is now our handled tab.
- EXPECT_NE(content1a, content1);
-
- // The active tab should still be the unnamed tab. Then we switch and reach
- // the first app and stay there.
- EXPECT_EQ(content1a, tab_strip->GetActiveWebContents());
- ActivateLauncherItem(shortcut_index);
- EXPECT_EQ(content1, tab_strip->GetActiveWebContents());
- ActivateLauncherItem(shortcut_index);
- EXPECT_EQ(content1, tab_strip->GetActiveWebContents());
-
- ui_test_utils::NavigateToURLWithDisposition(
- browser(),
- GURL(url),
- NEW_FOREGROUND_TAB,
- ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
- content::WebContents* content2 = tab_strip->GetActiveWebContents();
-
- EXPECT_EQ(content2, browser()->tab_strip_model()->GetActiveWebContents());
- ActivateLauncherItem(shortcut_index);
- EXPECT_EQ(content1, browser()->tab_strip_model()->GetActiveWebContents());
- ActivateLauncherItem(shortcut_index);
- EXPECT_EQ(content2, browser()->tab_strip_model()->GetActiveWebContents());
-}
-
-// Check that the keyboard activation of a launcher item tabs properly through
-// the items at hand.
-IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest,
- AltNumberAppsTabbing) {
- // First run app.
- const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
- ui::BaseWindow* window1 = CreateShellWindow(extension1)->GetBaseWindow();
- const ash::LauncherItem& item1 = GetLastLauncherItem();
- ash::LauncherID app_id = item1.id;
- int app_index = launcher_model()->ItemIndexByID(app_id);
-
- EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type);
- EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
-
- const Extension* extension2 = LoadAndLaunchPlatformApp("launch_2");
- ui::BaseWindow* window2 = CreateShellWindow(extension2)->GetBaseWindow();
-
- // By now the browser should be active. Issue Alt keystrokes several times to
- // see that we stay on that application.
- EXPECT_TRUE(window2->IsActive());
- ActivateLauncherItem(app_index);
- EXPECT_TRUE(window1->IsActive());
- ActivateLauncherItem(app_index);
- EXPECT_TRUE(window1->IsActive());
-
- ui::BaseWindow* window1a = CreateShellWindow(extension1)->GetBaseWindow();
-
- EXPECT_TRUE(window1a->IsActive());
- EXPECT_FALSE(window1->IsActive());
- ActivateLauncherItem(app_index);
- EXPECT_TRUE(window1->IsActive());
- ActivateLauncherItem(app_index);
- EXPECT_TRUE(window1a->IsActive());
-}
-
-// Test that we can launch a platform app panel and get a running item.
-IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest,
- LaunchPanelWindow) {
- int item_count = launcher_model()->item_count();
- const Extension* extension = LoadAndLaunchPlatformApp("launch");
- ShellWindow::CreateParams params;
- params.window_type = ShellWindow::WINDOW_TYPE_PANEL;
- params.focused = false;
- ShellWindow* window = CreateShellWindowFromParams(extension, params);
- ++item_count;
- ASSERT_EQ(item_count, launcher_model()->item_count());
- const ash::LauncherItem& item = GetLastLauncherPanelItem();
- EXPECT_EQ(ash::TYPE_APP_PANEL, item.type);
- // Opening a panel does not activate it.
- EXPECT_EQ(ash::STATUS_RUNNING, item.status);
- CloseShellWindow(window);
- --item_count;
- EXPECT_EQ(item_count, launcher_model()->item_count());
-}
-
-// Test attention states of windows.
-IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, WindowAttentionStatus) {
- const Extension* extension = LoadAndLaunchPlatformApp("launch");
- ShellWindow::CreateParams params;
- params.window_type = ShellWindow::WINDOW_TYPE_PANEL;
- params.focused = false;
- ShellWindow* panel = CreateShellWindowFromParams(extension, params);
- EXPECT_TRUE(panel->GetNativeWindow()->IsVisible());
- // Panels should not be active by default.
- EXPECT_FALSE(panel->GetBaseWindow()->IsActive());
- // Confirm that a controller item was created and is the correct state.
- const ash::LauncherItem& item = GetLastLauncherPanelItem();
- LauncherItemController* item_controller = GetItemController(item.id);
- EXPECT_EQ(ash::TYPE_APP_PANEL, item.type);
- EXPECT_EQ(ash::STATUS_RUNNING, item.status);
- EXPECT_EQ(LauncherItemController::TYPE_APP_PANEL, item_controller->type());
-
- // App windows should go to attention state.
- panel->GetNativeWindow()->SetProperty(aura::client::kDrawAttentionKey, true);
- EXPECT_EQ(ash::STATUS_ATTENTION, item.status);
-
- // Click the item and confirm that the panel is activated.
- TestEvent click_event(ui::ET_MOUSE_PRESSED);
- item_controller->Clicked(click_event);
- EXPECT_TRUE(panel->GetBaseWindow()->IsActive());
- EXPECT_EQ(ash::STATUS_ACTIVE, item.status);
-
- // Active windows don't show attention.
- panel->GetNativeWindow()->SetProperty(aura::client::kDrawAttentionKey, true);
- EXPECT_EQ(ash::STATUS_ACTIVE, item.status);
-}
-
-// Checks that the browser Alt "tabbing" is properly done.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTestNoDefaultBrowser,
- AltNumberBrowserTabbing) {
- // Get the number of items in the browser menu.
- EXPECT_EQ(0u, chrome::GetTotalBrowserCount());
- // The first activation should create a browser.
- launcher_->ActivateLauncherItem(0);
- EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
- // A second activation should not create a new instance.
- launcher_->ActivateLauncherItem(0);
- Browser* browser1 = chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow());
- EXPECT_TRUE(browser1);
- aura::Window* window1 = browser1->window()->GetNativeWindow();
- Browser* browser2 = CreateBrowser(profile());
- aura::Window* window2 = browser2->window()->GetNativeWindow();
-
- EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
- EXPECT_NE(window1, window2);
- EXPECT_EQ(window2, ash::wm::GetActiveWindow());
-
- // Activate multiple times the switcher to see that the windows get activated.
- launcher_->ActivateLauncherItem(0);
- EXPECT_EQ(window1, ash::wm::GetActiveWindow());
- launcher_->ActivateLauncherItem(0);
- EXPECT_EQ(window2, ash::wm::GetActiveWindow());
-
- // Create a third browser - make sure that we do not toggle simply between
- // two windows.
- Browser* browser3 = CreateBrowser(profile());
- aura::Window* window3 = browser3->window()->GetNativeWindow();
-
- EXPECT_EQ(3u, chrome::GetTotalBrowserCount());
- EXPECT_NE(window1, window3);
- EXPECT_NE(window2, window3);
- EXPECT_EQ(window3, ash::wm::GetActiveWindow());
-
- launcher_->ActivateLauncherItem(0);
- EXPECT_EQ(window1, ash::wm::GetActiveWindow());
- launcher_->ActivateLauncherItem(0);
- EXPECT_EQ(window2, ash::wm::GetActiveWindow());
- launcher_->ActivateLauncherItem(0);
- EXPECT_EQ(window3, ash::wm::GetActiveWindow());
- launcher_->ActivateLauncherItem(0);
- EXPECT_EQ(window1, ash::wm::GetActiveWindow());
-
- // Create anther app and make sure that none of our browsers is active.
- LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB, NEW_WINDOW);
- EXPECT_NE(window1, ash::wm::GetActiveWindow());
- EXPECT_NE(window2, ash::wm::GetActiveWindow());
-
- // After activation our browser should be active again.
- launcher_->ActivateLauncherItem(0);
- EXPECT_EQ(window1, ash::wm::GetActiveWindow());
-}
-
-// Checks that after a session restore, we do not start applications on an
-// activation.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, ActivateAfterSessionRestore) {
- EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
-
- // Create a known application.
- ash::LauncherID shortcut_id = CreateShortcut("app1");
-
- // Create a new browser - without activating it - and load an "app" into it.
- Browser::CreateParams params =
- Browser::CreateParams(profile(), chrome::GetActiveDesktop());
- params.initial_show_state = ui::SHOW_STATE_INACTIVE;
- Browser* browser2 = new Browser(params);
- controller_->SetRefocusURLPatternForTest(
- shortcut_id, GURL("http://www.example.com/path/*"));
- std::string url = "http://www.example.com/path/bla";
- ui_test_utils::NavigateToURLWithDisposition(
- browser2,
- GURL(url),
- NEW_FOREGROUND_TAB,
- ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-
- // Remember the number of tabs for each browser.
- TabStripModel* tab_strip = browser()->tab_strip_model();
- int tab_count1 = tab_strip->count();
- TabStripModel* tab_strip2 = browser2->tab_strip_model();
- int tab_count2 = tab_strip2->count();
-
- // Check that we have two browsers and the inactive browser remained inactive.
- EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
- EXPECT_EQ(chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow()),
- browser());
- // Check that the LRU browser list does only contain the original browser.
- BrowserList* ash_browser_list =
- BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
- BrowserList::const_reverse_iterator it =
- ash_browser_list->begin_last_active();
- EXPECT_EQ(*it, browser());
- ++it;
- EXPECT_EQ(it, ash_browser_list->end_last_active());
-
- // Now request to either activate an existing app or create a new one.
- controller_->ItemSelected(*model_->ItemByID(shortcut_id),
- ui::KeyEvent(ui::ET_KEY_RELEASED,
- ui::VKEY_RETURN,
- 0,
- false));
-
- // Check that we have set focus on the existing application and nothing new
- // was created.
- EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
- EXPECT_EQ(tab_count1, tab_strip->count());
- EXPECT_EQ(tab_count2, tab_strip2->count());
- EXPECT_EQ(chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow()),
- browser2);
-}
-
-// Do various drag and drop interaction tests between the application list and
-// the launcher.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, DragAndDrop) {
- // Get a number of interfaces we need.
- aura::test::EventGenerator generator(
- ash::Shell::GetPrimaryRootWindow(), gfx::Point());
- ash::test::LauncherViewTestAPI test(launcher_->GetLauncherViewForTest());
- AppListService* service = AppListService::Get();
-
- // There should be two items in our launcher by this time.
- EXPECT_EQ(2, model_->item_count());
- EXPECT_FALSE(service->IsAppListVisible());
-
- // Open the app list menu and check that the drag and drop host was set.
- gfx::Rect app_list_bounds =
- test.launcher_view()->GetAppListButtonView()->GetBoundsInScreen();
- generator.MoveMouseTo(app_list_bounds.CenterPoint().x(),
- app_list_bounds.CenterPoint().y());
- base::MessageLoop::current()->RunUntilIdle();
- generator.ClickLeftButton();
-
- EXPECT_TRUE(service->IsAppListVisible());
- app_list::AppsGridView* grid_view =
- app_list::AppsGridView::GetLastGridViewForTest();
- ASSERT_TRUE(grid_view);
- ASSERT_TRUE(grid_view->has_drag_and_drop_host_for_test());
-
- // There should be 2 items in our application list.
- const views::ViewModel* vm_grid = grid_view->view_model_for_test();
- EXPECT_EQ(2, vm_grid->view_size());
-
- // Test #1: Drag an app list which does not exist yet item into the
- // launcher. Keeping it dragged, see that a new item gets created. Continuing
- // to drag it out should remove it again.
-
- // Get over item #1 of the application list and press the mouse button.
- views::View* item1 = vm_grid->view_at(1);
- gfx::Rect bounds_grid_1 = item1->GetBoundsInScreen();
- generator.MoveMouseTo(bounds_grid_1.CenterPoint().x(),
- bounds_grid_1.CenterPoint().y());
- base::MessageLoop::current()->RunUntilIdle();
- generator.PressLeftButton();
-
- EXPECT_FALSE(grid_view->forward_events_to_drag_and_drop_host_for_test());
-
- // Drag the item into the launcher and check that a new item gets created.
- const views::ViewModel* vm_launcher =
- test.launcher_view()->view_model_for_test();
- views::View* launcher1 = vm_launcher->view_at(1);
- gfx::Rect bounds_launcher_1 = launcher1->GetBoundsInScreen();
- generator.MoveMouseTo(bounds_launcher_1.CenterPoint().x(),
- bounds_launcher_1.CenterPoint().y());
- base::MessageLoop::current()->RunUntilIdle();
-
- // Check that a new item got created.
- EXPECT_EQ(3, model_->item_count());
- EXPECT_TRUE(grid_view->forward_events_to_drag_and_drop_host_for_test());
-
- // Move it where the item originally was and check that it disappears again.
- generator.MoveMouseTo(bounds_grid_1.CenterPoint().x(),
- bounds_grid_1.CenterPoint().y());
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(2, model_->item_count());
- EXPECT_FALSE(grid_view->forward_events_to_drag_and_drop_host_for_test());
-
- // Dropping it should keep the launcher as it originally was.
- generator.ReleaseLeftButton();
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(2, model_->item_count());
- // There are a few animations which need finishing before we can continue.
- test.RunMessageLoopUntilAnimationsDone();
- // Move the mouse outside of the launcher.
- generator.MoveMouseTo(0, 0);
-
- // Test #2: Check that the unknown item dropped into the launcher will
- // create a new item.
- generator.MoveMouseTo(bounds_grid_1.CenterPoint().x(),
- bounds_grid_1.CenterPoint().y());
- generator.PressLeftButton();
- generator.MoveMouseTo(bounds_launcher_1.CenterPoint().x(),
- bounds_launcher_1.CenterPoint().y());
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(3, model_->item_count());
- EXPECT_TRUE(grid_view->forward_events_to_drag_and_drop_host_for_test());
- generator.ReleaseLeftButton();
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_FALSE(grid_view->forward_events_to_drag_and_drop_host_for_test());
- EXPECT_EQ(3, model_->item_count()); // It should be still there.
- test.RunMessageLoopUntilAnimationsDone();
-
- // Test #3: Check that the now known item dropped into the launcher will
- // not create a new item.
- generator.MoveMouseTo(bounds_grid_1.CenterPoint().x(),
- bounds_grid_1.CenterPoint().y());
- generator.PressLeftButton();
- generator.MoveMouseTo(bounds_launcher_1.CenterPoint().x(),
- bounds_launcher_1.CenterPoint().y());
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(3, model_->item_count()); // No new item got added.
- EXPECT_TRUE(grid_view->forward_events_to_drag_and_drop_host_for_test());
- generator.ReleaseLeftButton();
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_FALSE(grid_view->forward_events_to_drag_and_drop_host_for_test());
- EXPECT_EQ(3, model_->item_count()); // And it remains that way.
-}
-
-// Check that clicking on an app launcher item launches a new browser.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, ClickItem) {
- // Get a number of interfaces we need.
- aura::test::EventGenerator generator(
- ash::Shell::GetPrimaryRootWindow(), gfx::Point());
- ash::test::LauncherViewTestAPI test(launcher_->GetLauncherViewForTest());
- AppListService* service = AppListService::Get();
- // There should be two items in our launcher by this time.
- EXPECT_EQ(2, model_->item_count());
- EXPECT_FALSE(service->IsAppListVisible());
-
- // Open the app list menu and check that the drag and drop host was set.
- gfx::Rect app_list_bounds =
- test.launcher_view()->GetAppListButtonView()->GetBoundsInScreen();
- generator.MoveMouseTo(app_list_bounds.CenterPoint().x(),
- app_list_bounds.CenterPoint().y());
- generator.ClickLeftButton();
- base::MessageLoop::current()->RunUntilIdle();
-
- EXPECT_TRUE(service->IsAppListVisible());
- app_list::AppsGridView* grid_view =
- app_list::AppsGridView::GetLastGridViewForTest();
- ASSERT_TRUE(grid_view);
- const views::ViewModel* vm_grid = grid_view->view_model_for_test();
- EXPECT_EQ(2, vm_grid->view_size());
- gfx::Rect bounds_grid_1 = vm_grid->view_at(1)->GetBoundsInScreen();
- // Test now that a click does create a new application tab.
- TabStripModel* tab_strip = browser()->tab_strip_model();
- int tab_count = tab_strip->count();
- generator.MoveMouseTo(bounds_grid_1.CenterPoint().x(),
- bounds_grid_1.CenterPoint().y());
- generator.ClickLeftButton();
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(tab_count + 1, tab_strip->count());
-}
-
-// Check LauncherItemController of Browser Shortcut functionality.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTestNoDefaultBrowser,
- BrowserShortcutLauncherItemController) {
- LauncherItemController* item_controller =
- controller_->GetBrowserShortcutLauncherItemController();
-
- // Get the number of browsers.
- size_t running_browser = chrome::GetTotalBrowserCount();
- EXPECT_EQ(0u, running_browser);
- EXPECT_FALSE(item_controller->IsOpen());
-
- // Activate. This creates new browser
- item_controller->Activate();
- // New Window is created.
- running_browser = chrome::GetTotalBrowserCount();
- EXPECT_EQ(1u, running_browser);
- EXPECT_TRUE(item_controller->IsOpen());
-
- // Minimize Window.
- aura::Window* window = ash::wm::GetActiveWindow();
- ash::wm::MinimizeWindow(window);
- EXPECT_TRUE(ash::wm::IsWindowMinimized(window));
-
- // Activate again. This doesn't create new browser.
- // It activates window.
- item_controller->Activate();
- running_browser = chrome::GetTotalBrowserCount();
- EXPECT_EQ(1u, running_browser);
- EXPECT_TRUE(item_controller->IsOpen());
- EXPECT_FALSE(ash::wm::IsWindowMinimized(window));
-}
-
-// Check that GetIDByWindow() returns |LauncherID| of the active tab.
-IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, MatchingLauncherIDandActiveTab) {
- EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
- EXPECT_EQ(1, browser()->tab_strip_model()->count());
- EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
- EXPECT_EQ(2, model_->item_count());
-
- aura::Window* window = browser()->window()->GetNativeWindow();
-
- int browser_index = ash::launcher::GetBrowserItemIndex(*model_);
- ash::LauncherID browser_id = model_->items()[browser_index].id;
- EXPECT_EQ(browser_id, controller_->GetIDByWindow(window));
-
- ash::LauncherID app_id = CreateShortcut("app1");
- EXPECT_EQ(3, model_->item_count());
-
- // Creates a new tab for "app1" and checks that GetIDByWindow() returns
- // |LauncherID| of "app1".
- ActivateLauncherItem(model_->ItemIndexByID(app_id));
- EXPECT_EQ(2, browser()->tab_strip_model()->count());
- EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
- EXPECT_EQ(app_id, controller_->GetIDByWindow(window));
-
- // Makes tab at index 0(NTP) as an active tab and checks that GetIDByWindow()
- // returns |LauncherID| of browser shortcut.
- browser()->tab_strip_model()->ActivateTabAt(0, false);
- EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
- EXPECT_EQ(browser_id, controller_->GetIDByWindow(window));
-}
-
IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, OverflowBubble) {
// Make sure to have a browser window
chrome::NewTab(browser());
@@ -1658,7 +828,9 @@ IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, OverflowBubble) {
EXPECT_TRUE(launcher_->IsShowingOverflowBubble());
// Unpin first pinned app and there should be no crash.
- controller_->UnpinAppsWithID(std::string("fake_app_0"));
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
+ controller->UnpinAppsWithID(std::string("fake_app_0"));
test.RunMessageLoopUntilAnimationsDone();
EXPECT_FALSE(launcher_->IsShowingOverflowBubble());
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.cc
new file mode 100644
index 0000000..3137581
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.cc
@@ -0,0 +1,1678 @@
+// Copyright (c) 2012 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/ui/ash/launcher/chrome_launcher_controller_per_app.h"
+
+#include <vector>
+
+#include "ash/ash_switches.h"
+#include "ash/launcher/launcher.h"
+#include "ash/launcher/launcher_model.h"
+#include "ash/launcher/launcher_util.h"
+#include "ash/root_window_controller.h"
+#include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shelf/shelf_widget.h"
+#include "ash/shell.h"
+#include "ash/wm/window_util.h"
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/app_mode/app_mode_utils.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/defaults.h"
+#include "chrome/browser/extensions/app_icon_loader_impl.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/favicon/favicon_tab_helper.h"
+#include "chrome/browser/prefs/incognito_mode_prefs.h"
+#include "chrome/browser/prefs/pref_service_syncable.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/app_sync_ui_state.h"
+#include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
+#include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h"
+#include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
+#include "chrome/browser/ui/ash/launcher/launcher_app_tab_helper.h"
+#include "chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h"
+#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
+#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
+#include "chrome/browser/ui/ash/launcher/shell_window_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/shell_window_launcher_item_controller.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/browser/ui/extensions/extension_enable_flow.h"
+#include "chrome/browser/ui/host_desktop.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/common/extension_resource.h"
+#include "extensions/common/url_pattern.h"
+#include "grit/ash_resources.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "grit/ui_resources.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/corewm/window_animations.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/default_pinned_apps_field_trial.h"
+#endif
+
+using extensions::Extension;
+using extension_misc::kGmailAppId;
+using content::WebContents;
+
+namespace {
+
+std::string GetPrefKeyForRootWindow(aura::RootWindow* root_window) {
+ gfx::Display display = gfx::Screen::GetScreenFor(
+ root_window)->GetDisplayNearestWindow(root_window);
+ DCHECK(display.is_valid());
+
+ return base::Int64ToString(display.id());
+}
+
+void UpdatePerDisplayPref(PrefService* pref_service,
+ aura::RootWindow* root_window,
+ const char* pref_key,
+ const std::string& value) {
+ std::string key = GetPrefKeyForRootWindow(root_window);
+ if (key.empty())
+ return;
+
+ DictionaryPrefUpdate update(pref_service, prefs::kShelfPreferences);
+ base::DictionaryValue* shelf_prefs = update.Get();
+ base::DictionaryValue* prefs = NULL;
+ if (!shelf_prefs->GetDictionary(key, &prefs)) {
+ prefs = new base::DictionaryValue();
+ shelf_prefs->Set(key, prefs);
+ }
+ prefs->SetStringWithoutPathExpansion(pref_key, value);
+}
+
+// Returns a pref value in |pref_service| for the display of |root_window|. The
+// pref value is stored in |local_path| and |path|, but |pref_service| may have
+// per-display preferences and the value can be specified by policy. Here is
+// the priority:
+// * A value managed by policy. This is a single value that applies to all
+// displays.
+// * A user-set value for the specified display.
+// * A user-set value in |local_path| or |path|, if no per-display settings are
+// ever specified (see http://crbug.com/173719 for why). |local_path| is
+// preferred. See comment in |kShelfAlignment| as to why we consider two
+// prefs and why |local_path| is preferred.
+// * A value recommended by policy. This is a single value that applies to all
+// root windows.
+// * The default value for |local_path| if the value is not recommended by
+// policy.
+std::string GetPrefForRootWindow(PrefService* pref_service,
+ aura::RootWindow* root_window,
+ const char* local_path,
+ const char* path) {
+ const PrefService::Preference* local_pref =
+ pref_service->FindPreference(local_path);
+ const std::string value(pref_service->GetString(local_path));
+ if (local_pref->IsManaged())
+ return value;
+
+ std::string pref_key = GetPrefKeyForRootWindow(root_window);
+ bool has_per_display_prefs = false;
+ if (!pref_key.empty()) {
+ const base::DictionaryValue* shelf_prefs = pref_service->GetDictionary(
+ prefs::kShelfPreferences);
+ const base::DictionaryValue* display_pref = NULL;
+ std::string per_display_value;
+ if (shelf_prefs->GetDictionary(pref_key, &display_pref) &&
+ display_pref->GetString(path, &per_display_value))
+ return per_display_value;
+
+ // If the pref for the specified display is not found, scan the whole prefs
+ // and check if the prefs for other display is already specified.
+ std::string unused_value;
+ for (base::DictionaryValue::Iterator iter(*shelf_prefs);
+ !iter.IsAtEnd(); iter.Advance()) {
+ const base::DictionaryValue* display_pref = NULL;
+ if (iter.value().GetAsDictionary(&display_pref) &&
+ display_pref->GetString(path, &unused_value)) {
+ has_per_display_prefs = true;
+ break;
+ }
+ }
+ }
+
+ if (local_pref->IsRecommended() || !has_per_display_prefs)
+ return value;
+
+ const base::Value* default_value =
+ pref_service->GetDefaultPrefValue(local_path);
+ std::string default_string;
+ default_value->GetAsString(&default_string);
+ return default_string;
+}
+
+// If prefs have synced and no user-set value exists at |local_path|, the value
+// from |synced_path| is copied to |local_path|.
+void MaybePropagatePrefToLocal(PrefServiceSyncable* pref_service,
+ const char* local_path,
+ const char* synced_path) {
+ if (!pref_service->FindPreference(local_path)->HasUserSetting() &&
+ pref_service->IsSyncing()) {
+ // First time the user is using this machine, propagate from remote to
+ // local.
+ pref_service->SetString(local_path, pref_service->GetString(synced_path));
+ }
+}
+
+} // namespace
+
+ChromeLauncherControllerPerApp::ChromeLauncherControllerPerApp(
+ Profile* profile,
+ ash::LauncherModel* model)
+ : model_(model),
+ profile_(profile),
+ app_sync_ui_state_(NULL),
+ ignore_persist_pinned_state_change_(false) {
+ if (!profile_) {
+ // Use the original profile as on chromeos we may get a temporary off the
+ // record profile.
+ profile_ = ProfileManager::GetDefaultProfile()->GetOriginalProfile();
+
+ app_sync_ui_state_ = AppSyncUIState::Get(profile_);
+ if (app_sync_ui_state_)
+ app_sync_ui_state_->AddObserver(this);
+ }
+
+ model_->AddObserver(this);
+ BrowserList::AddObserver(this);
+ // Right now ash::Shell isn't created for tests.
+ // TODO(mukai): Allows it to observe display change and write tests.
+ if (ash::Shell::HasInstance())
+ ash::Shell::GetInstance()->display_controller()->AddObserver(this);
+ // TODO(stevenjb): Find a better owner for shell_window_controller_?
+ shell_window_controller_.reset(new ShellWindowLauncherController(this));
+ app_tab_helper_.reset(new LauncherAppTabHelper(profile_));
+ app_icon_loader_.reset(new extensions::AppIconLoaderImpl(
+ profile_, extension_misc::EXTENSION_ICON_SMALL, this));
+
+ notification_registrar_.Add(this,
+ chrome::NOTIFICATION_EXTENSION_LOADED,
+ content::Source<Profile>(profile_));
+ notification_registrar_.Add(this,
+ chrome::NOTIFICATION_EXTENSION_UNLOADED,
+ content::Source<Profile>(profile_));
+ pref_change_registrar_.Init(profile_->GetPrefs());
+ pref_change_registrar_.Add(
+ prefs::kPinnedLauncherApps,
+ base::Bind(&ChromeLauncherControllerPerApp::UpdateAppLaunchersFromPref,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kShelfAlignmentLocal,
+ base::Bind(&ChromeLauncherControllerPerApp::SetShelfAlignmentFromPrefs,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kShelfAutoHideBehaviorLocal,
+ base::Bind(&ChromeLauncherControllerPerApp::
+ SetShelfAutoHideBehaviorFromPrefs,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kShelfPreferences,
+ base::Bind(&ChromeLauncherControllerPerApp::SetShelfBehaviorsFromPrefs,
+ base::Unretained(this)));
+}
+
+ChromeLauncherControllerPerApp::~ChromeLauncherControllerPerApp() {
+ // Reset the shell window controller here since it has a weak pointer to this.
+ shell_window_controller_.reset();
+
+ for (std::set<ash::Launcher*>::iterator iter = launchers_.begin();
+ iter != launchers_.end();
+ ++iter)
+ (*iter)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
+
+ model_->RemoveObserver(this);
+ BrowserList::RemoveObserver(this);
+ if (ash::Shell::HasInstance())
+ ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
+ for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ++i) {
+ i->second->OnRemoved();
+ // TODO(skuhne): After getting rid of the old launcher, get also rid of the
+ // BrowserLauncherItemController (since it is only used for activation
+ // tracking at that point.
+ int index = model_->ItemIndexByID(i->first);
+ // A "browser proxy" is not known to the model and this removal does
+ // therefore not need to be propagated to the model.
+ if (index != -1 &&
+ model_->items()[index].type != ash::TYPE_BROWSER_SHORTCUT)
+ model_->RemoveItemAt(index);
+ }
+
+ if (ash::Shell::HasInstance())
+ ash::Shell::GetInstance()->RemoveShellObserver(this);
+
+ if (app_sync_ui_state_)
+ app_sync_ui_state_->RemoveObserver(this);
+
+ PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this);
+}
+
+void ChromeLauncherControllerPerApp::Init() {
+ UpdateAppLaunchersFromPref();
+ CreateBrowserShortcutLauncherItem();
+
+ // TODO(sky): update unit test so that this test isn't necessary.
+ if (ash::Shell::HasInstance()) {
+ SetShelfAutoHideBehaviorFromPrefs();
+ SetShelfAlignmentFromPrefs();
+ PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
+ if (!prefs->FindPreference(prefs::kShelfAlignmentLocal)->HasUserSetting() ||
+ !prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)->
+ HasUserSetting()) {
+ // This causes OnIsSyncingChanged to be called when the value of
+ // PrefService::IsSyncing() changes.
+ prefs->AddObserver(this);
+ }
+ ash::Shell::GetInstance()->AddShellObserver(this);
+ }
+}
+
+ChromeLauncherControllerPerApp*
+ChromeLauncherControllerPerApp::GetPerAppInterface() {
+ return this;
+}
+
+ash::LauncherID ChromeLauncherControllerPerApp::CreateTabbedLauncherItem(
+ LauncherItemController* controller,
+ IncognitoState is_incognito,
+ ash::LauncherItemStatus status) {
+ // The PerAppLauncher doesn't need an empty slot for TabbedLauncherItem and
+ // its LauncherItemController.
+ // TODO(skuhne): Remove function when PerBrowser launcher gets removed.
+ return 0;
+}
+
+ash::LauncherID ChromeLauncherControllerPerApp::CreateAppLauncherItem(
+ LauncherItemController* controller,
+ const std::string& app_id,
+ ash::LauncherItemStatus status) {
+ CHECK(controller);
+ int index = 0;
+ // Panels are inserted on the left so as not to push all existing panels over.
+ if (controller->GetLauncherItemType() != ash::TYPE_APP_PANEL) {
+ index = model_->item_count();
+ // For the alternate shelf layout increment the index (after the app icon)
+ if (ash::switches::UseAlternateShelfLayout())
+ ++index;
+ }
+ return InsertAppLauncherItem(controller,
+ app_id,
+ status,
+ index,
+ controller->GetLauncherItemType());
+}
+
+void ChromeLauncherControllerPerApp::SetItemStatus(
+ ash::LauncherID id,
+ ash::LauncherItemStatus status) {
+ int index = model_->ItemIndexByID(id);
+ // Since ordinary browser windows are not registered, we might get a negative
+ // index here.
+ if (index >= 0) {
+ ash::LauncherItem item = model_->items()[index];
+ item.status = status;
+ model_->Set(index, item);
+
+ if (model_->items()[index].type == ash::TYPE_BROWSER_SHORTCUT)
+ return;
+ }
+ UpdateBrowserItemStatus();
+}
+
+void ChromeLauncherControllerPerApp::SetItemController(
+ ash::LauncherID id,
+ LauncherItemController* controller) {
+ CHECK(controller);
+ IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
+ CHECK(iter != id_to_item_controller_map_.end());
+ iter->second->OnRemoved();
+ iter->second = controller;
+ controller->set_launcher_id(id);
+}
+
+void ChromeLauncherControllerPerApp::CloseLauncherItem(ash::LauncherID id) {
+ CHECK(id);
+ if (IsPinned(id)) {
+ // Create a new shortcut controller.
+ IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
+ CHECK(iter != id_to_item_controller_map_.end());
+ SetItemStatus(id, ash::STATUS_CLOSED);
+ std::string app_id = iter->second->app_id();
+ iter->second->OnRemoved();
+ iter->second = new AppShortcutLauncherItemController(app_id, this);
+ iter->second->set_launcher_id(id);
+ } else {
+ LauncherItemClosed(id);
+ }
+}
+
+void ChromeLauncherControllerPerApp::Pin(ash::LauncherID id) {
+ DCHECK(HasItemController(id));
+
+ int index = model_->ItemIndexByID(id);
+ DCHECK_GE(index, 0);
+
+ ash::LauncherItem item = model_->items()[index];
+
+ if (item.type == ash::TYPE_PLATFORM_APP ||
+ item.type == ash::TYPE_WINDOWED_APP) {
+ item.type = ash::TYPE_APP_SHORTCUT;
+ model_->Set(index, item);
+ } else if (item.type != ash::TYPE_APP_SHORTCUT) {
+ return;
+ }
+
+ if (CanPin())
+ PersistPinnedState();
+}
+
+void ChromeLauncherControllerPerApp::Unpin(ash::LauncherID id) {
+ DCHECK(HasItemController(id));
+
+ LauncherItemController* controller = id_to_item_controller_map_[id];
+ if (controller->type() == LauncherItemController::TYPE_APP) {
+ int index = model_->ItemIndexByID(id);
+ DCHECK_GE(index, 0);
+ ash::LauncherItem item = model_->items()[index];
+ item.type = ash::TYPE_PLATFORM_APP;
+ model_->Set(index, item);
+ } else {
+ // Prevent the removal of items upon unpin if it is locked by a running
+ // windowed V1 app.
+ if (!controller->locked()) {
+ LauncherItemClosed(id);
+ } else {
+ int index = model_->ItemIndexByID(id);
+ DCHECK_GE(index, 0);
+ ash::LauncherItem item = model_->items()[index];
+ item.type = ash::TYPE_WINDOWED_APP;
+ model_->Set(index, item);
+ }
+ }
+ if (CanPin())
+ PersistPinnedState();
+}
+
+bool ChromeLauncherControllerPerApp::IsPinned(ash::LauncherID id) {
+ int index = model_->ItemIndexByID(id);
+ if (index < 0)
+ return false;
+ ash::LauncherItemType type = model_->items()[index].type;
+ return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT);
+}
+
+void ChromeLauncherControllerPerApp::TogglePinned(ash::LauncherID id) {
+ if (!HasItemController(id))
+ return; // May happen if item closed with menu open.
+
+ if (IsPinned(id))
+ Unpin(id);
+ else
+ Pin(id);
+}
+
+bool ChromeLauncherControllerPerApp::IsPinnable(ash::LauncherID id) const {
+ int index = model_->ItemIndexByID(id);
+ if (index == -1)
+ return false;
+
+ ash::LauncherItemType type = model_->items()[index].type;
+ return ((type == ash::TYPE_APP_SHORTCUT ||
+ type == ash::TYPE_PLATFORM_APP ||
+ type == ash::TYPE_WINDOWED_APP) &&
+ CanPin());
+}
+
+void ChromeLauncherControllerPerApp::LockV1AppWithID(
+ const std::string& app_id) {
+ ash::LauncherID id = GetLauncherIDForAppID(app_id);
+ if (!IsPinned(id) && !IsWindowedAppInLauncher(app_id)) {
+ CreateAppShortcutLauncherItemWithType(app_id,
+ model_->item_count(),
+ ash::TYPE_WINDOWED_APP);
+ id = GetLauncherIDForAppID(app_id);
+ }
+ CHECK(id);
+ id_to_item_controller_map_[id]->lock();
+}
+
+void ChromeLauncherControllerPerApp::UnlockV1AppWithID(
+ const std::string& app_id) {
+ ash::LauncherID id = GetLauncherIDForAppID(app_id);
+ CHECK(IsPinned(id) || IsWindowedAppInLauncher(app_id));
+ CHECK(id);
+ LauncherItemController* controller = id_to_item_controller_map_[id];
+ controller->unlock();
+ if (!controller->locked() && !IsPinned(id))
+ CloseLauncherItem(id);
+}
+
+void ChromeLauncherControllerPerApp::Launch(ash::LauncherID id,
+ int event_flags) {
+ if (!HasItemController(id))
+ return; // In case invoked from menu and item closed while menu up.
+ id_to_item_controller_map_[id]->Launch(event_flags);
+}
+
+void ChromeLauncherControllerPerApp::Close(ash::LauncherID id) {
+ if (!HasItemController(id))
+ return; // May happen if menu closed.
+ id_to_item_controller_map_[id]->Close();
+}
+
+bool ChromeLauncherControllerPerApp::IsOpen(ash::LauncherID id) {
+ if (!HasItemController(id))
+ return false;
+ return id_to_item_controller_map_[id]->IsOpen();
+}
+
+bool ChromeLauncherControllerPerApp::IsPlatformApp(ash::LauncherID id) {
+ if (!HasItemController(id))
+ return false;
+
+ std::string app_id = GetAppIDForLauncherID(id);
+ const Extension* extension = GetExtensionForAppID(app_id);
+ // An extension can be synced / updated at any time and therefore not be
+ // available.
+ return extension ? extension->is_platform_app() : false;
+}
+
+void ChromeLauncherControllerPerApp::LaunchApp(const std::string& app_id,
+ int event_flags) {
+ // |extension| could be NULL when it is being unloaded for updating.
+ const Extension* extension = GetExtensionForAppID(app_id);
+ if (!extension)
+ return;
+
+ const ExtensionService* service =
+ extensions::ExtensionSystem::Get(profile_)->extension_service();
+ if (!service->IsExtensionEnabledForLauncher(app_id)) {
+ // Do nothing if there is already a running enable flow.
+ if (extension_enable_flow_)
+ return;
+
+ extension_enable_flow_.reset(
+ new ExtensionEnableFlow(profile_, app_id, this));
+ extension_enable_flow_->StartForNativeWindow(NULL);
+ return;
+ }
+
+ chrome::OpenApplication(chrome::AppLaunchParams(GetProfileForNewWindows(),
+ extension,
+ event_flags));
+}
+
+void ChromeLauncherControllerPerApp::ActivateApp(const std::string& app_id,
+ int event_flags) {
+ // If there is an existing non-shortcut controller for this app, open it.
+ ash::LauncherID id = GetLauncherIDForAppID(app_id);
+ if (id) {
+ LauncherItemController* controller = id_to_item_controller_map_[id];
+ controller->Activate();
+ return;
+ }
+
+ // Create a temporary application launcher item and use it to see if there are
+ // running instances.
+ scoped_ptr<AppShortcutLauncherItemController> app_controller(
+ new AppShortcutLauncherItemController(app_id, this));
+ if (!app_controller->GetRunningApplications().empty())
+ app_controller->Activate();
+ else
+ LaunchApp(app_id, event_flags);
+}
+
+extensions::ExtensionPrefs::LaunchType
+ ChromeLauncherControllerPerApp::GetLaunchType(ash::LauncherID id) {
+ DCHECK(HasItemController(id));
+
+ const Extension* extension = GetExtensionForAppID(
+ id_to_item_controller_map_[id]->app_id());
+
+ // An extension can be unloaded/updated/unavailable at any time.
+ if (!extension)
+ return extensions::ExtensionPrefs::LAUNCH_DEFAULT;
+
+ return profile_->GetExtensionService()->extension_prefs()->GetLaunchType(
+ extension,
+ extensions::ExtensionPrefs::LAUNCH_DEFAULT);
+}
+
+std::string ChromeLauncherControllerPerApp::GetAppID(
+ content::WebContents* tab) {
+ return app_tab_helper_->GetAppID(tab);
+}
+
+ash::LauncherID ChromeLauncherControllerPerApp::GetLauncherIDForAppID(
+ const std::string& app_id) {
+ for (IDToItemControllerMap::const_iterator i =
+ id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ++i) {
+ if (i->second->type() == LauncherItemController::TYPE_APP_PANEL)
+ continue; // Don't include panels
+ if (i->second->app_id() == app_id)
+ return i->first;
+ }
+ return 0;
+}
+
+std::string ChromeLauncherControllerPerApp::GetAppIDForLauncherID(
+ ash::LauncherID id) {
+ CHECK(HasItemController(id));
+ return id_to_item_controller_map_[id]->app_id();
+}
+
+void ChromeLauncherControllerPerApp::SetAppImage(
+ const std::string& id,
+ const gfx::ImageSkia& image) {
+ // TODO: need to get this working for shortcuts.
+
+ for (IDToItemControllerMap::const_iterator i =
+ id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ++i) {
+ LauncherItemController* controller = i->second;
+ if (controller->app_id() != id)
+ continue;
+ if (controller->image_set_by_controller())
+ continue;
+ int index = model_->ItemIndexByID(i->first);
+ if (index == -1)
+ continue;
+ ash::LauncherItem item = model_->items()[index];
+ item.image = image;
+ model_->Set(index, item);
+ // It's possible we're waiting on more than one item, so don't break.
+ }
+}
+
+void ChromeLauncherControllerPerApp::OnAutoHideBehaviorChanged(
+ aura::RootWindow* root_window,
+ ash::ShelfAutoHideBehavior new_behavior) {
+ SetShelfAutoHideBehaviorPrefs(new_behavior, root_window);
+}
+
+void ChromeLauncherControllerPerApp::SetLauncherItemImage(
+ ash::LauncherID launcher_id,
+ const gfx::ImageSkia& image) {
+ int index = model_->ItemIndexByID(launcher_id);
+ if (index == -1)
+ return;
+ ash::LauncherItem item = model_->items()[index];
+ item.image = image;
+ model_->Set(index, item);
+}
+
+bool ChromeLauncherControllerPerApp::IsAppPinned(const std::string& app_id) {
+ for (IDToItemControllerMap::const_iterator i =
+ id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ++i) {
+ if (IsPinned(i->first) && i->second->app_id() == app_id)
+ return true;
+ }
+ return false;
+}
+
+bool ChromeLauncherControllerPerApp::IsWindowedAppInLauncher(
+ const std::string& app_id) {
+ int index = model_->ItemIndexByID(GetLauncherIDForAppID(app_id));
+ if (index < 0)
+ return false;
+
+ ash::LauncherItemType type = model_->items()[index].type;
+ return type == ash::TYPE_WINDOWED_APP;
+}
+
+void ChromeLauncherControllerPerApp::PinAppWithID(const std::string& app_id) {
+ if (CanPin())
+ DoPinAppWithID(app_id);
+ else
+ NOTREACHED();
+}
+
+void ChromeLauncherControllerPerApp::SetLaunchType(
+ ash::LauncherID id,
+ extensions::ExtensionPrefs::LaunchType launch_type) {
+ if (!HasItemController(id))
+ return;
+
+ profile_->GetExtensionService()->extension_prefs()->SetLaunchType(
+ id_to_item_controller_map_[id]->app_id(), launch_type);
+}
+
+void ChromeLauncherControllerPerApp::UnpinAppsWithID(
+ const std::string& app_id) {
+ if (CanPin())
+ DoUnpinAppsWithID(app_id);
+ else
+ NOTREACHED();
+}
+
+bool ChromeLauncherControllerPerApp::IsLoggedInAsGuest() {
+ return ProfileManager::GetDefaultProfileOrOffTheRecord()->IsOffTheRecord();
+}
+
+void ChromeLauncherControllerPerApp::CreateNewWindow() {
+ chrome::NewEmptyWindow(
+ GetProfileForNewWindows(), chrome::HOST_DESKTOP_TYPE_ASH);
+}
+
+void ChromeLauncherControllerPerApp::CreateNewIncognitoWindow() {
+ chrome::NewEmptyWindow(GetProfileForNewWindows()->GetOffTheRecordProfile(),
+ chrome::HOST_DESKTOP_TYPE_ASH);
+}
+
+bool ChromeLauncherControllerPerApp::CanPin() const {
+ const PrefService::Preference* pref =
+ profile_->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps);
+ return pref && pref->IsUserModifiable();
+}
+
+void ChromeLauncherControllerPerApp::PersistPinnedState() {
+ if (ignore_persist_pinned_state_change_)
+ return;
+ // It is a coding error to call PersistPinnedState() if the pinned apps are
+ // not user-editable. The code should check earlier and not perform any
+ // modification actions that trigger persisting the state.
+ if (!CanPin()) {
+ NOTREACHED() << "Can't pin but pinned state being updated";
+ return;
+ }
+
+ // Mutating kPinnedLauncherApps is going to notify us and trigger us to
+ // process the change. We don't want that to happen so remove ourselves as a
+ // listener.
+ pref_change_registrar_.Remove(prefs::kPinnedLauncherApps);
+ {
+ ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
+ updater->Clear();
+ for (size_t i = 0; i < model_->items().size(); ++i) {
+ if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) {
+ ash::LauncherID id = model_->items()[i].id;
+ if (HasItemController(id) && IsPinned(id)) {
+ base::DictionaryValue* app_value = ash::CreateAppDict(
+ id_to_item_controller_map_[id]->app_id());
+ if (app_value)
+ updater->Append(app_value);
+ }
+ } else if (model_->items()[i].type == ash::TYPE_BROWSER_SHORTCUT) {
+ PersistChromeItemIndex(i);
+ }
+ }
+ }
+ pref_change_registrar_.Add(
+ prefs::kPinnedLauncherApps,
+ base::Bind(&ChromeLauncherControllerPerApp::UpdateAppLaunchersFromPref,
+ base::Unretained(this)));
+}
+
+ash::LauncherModel* ChromeLauncherControllerPerApp::model() {
+ return model_;
+}
+
+Profile* ChromeLauncherControllerPerApp::profile() {
+ return profile_;
+}
+
+ash::ShelfAutoHideBehavior
+ ChromeLauncherControllerPerApp::GetShelfAutoHideBehavior(
+ aura::RootWindow* root_window) const {
+ // Don't show the shelf in app mode.
+ if (chrome::IsRunningInAppMode())
+ return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN;
+
+ // See comment in |kShelfAlignment| as to why we consider two prefs.
+ const std::string behavior_value(
+ GetPrefForRootWindow(profile_->GetPrefs(),
+ root_window,
+ prefs::kShelfAutoHideBehaviorLocal,
+ prefs::kShelfAutoHideBehavior));
+
+ // Note: To maintain sync compatibility with old images of chrome/chromeos
+ // the set of values that may be encountered includes the now-extinct
+ // "Default" as well as "Never" and "Always", "Default" should now
+ // be treated as "Never" (http://crbug.com/146773).
+ if (behavior_value == ash::kShelfAutoHideBehaviorAlways)
+ return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
+ return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER;
+}
+
+bool ChromeLauncherControllerPerApp::CanUserModifyShelfAutoHideBehavior(
+ aura::RootWindow* root_window) const {
+ return profile_->GetPrefs()->
+ FindPreference(prefs::kShelfAutoHideBehaviorLocal)->IsUserModifiable();
+}
+
+void ChromeLauncherControllerPerApp::ToggleShelfAutoHideBehavior(
+ aura::RootWindow* root_window) {
+ ash::ShelfAutoHideBehavior behavior = GetShelfAutoHideBehavior(root_window) ==
+ ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS ?
+ ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER :
+ ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
+ SetShelfAutoHideBehaviorPrefs(behavior, root_window);
+ return;
+}
+
+void ChromeLauncherControllerPerApp::RemoveTabFromRunningApp(
+ WebContents* tab,
+ const std::string& app_id) {
+ web_contents_to_app_id_.erase(tab);
+ AppIDToWebContentsListMap::iterator i_app_id =
+ app_id_to_web_contents_list_.find(app_id);
+ if (i_app_id != app_id_to_web_contents_list_.end()) {
+ WebContentsList* tab_list = &i_app_id->second;
+ tab_list->remove(tab);
+ if (tab_list->empty()) {
+ app_id_to_web_contents_list_.erase(i_app_id);
+ i_app_id = app_id_to_web_contents_list_.end();
+ ash::LauncherID id = GetLauncherIDForAppID(app_id);
+ if (id)
+ SetItemStatus(id, ash::STATUS_CLOSED);
+ }
+ }
+}
+
+void ChromeLauncherControllerPerApp::UpdateAppState(
+ content::WebContents* contents,
+ AppState app_state) {
+ std::string app_id = GetAppID(contents);
+
+ // Check if the gMail app is loaded and it matches the given content.
+ // This special treatment is needed to address crbug.com/234268.
+ if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
+ app_id = kGmailAppId;
+
+ // Check the old |app_id| for a tab. If the contents has changed we need to
+ // remove it from the previous app.
+ if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) {
+ std::string last_app_id = web_contents_to_app_id_[contents];
+ if (last_app_id != app_id)
+ RemoveTabFromRunningApp(contents, last_app_id);
+ }
+
+ if (app_id.empty()) {
+ // Even if there is no application running, we should update the activation
+ // state of the associated browser.
+ UpdateBrowserItemStatus();
+ return;
+ }
+
+ web_contents_to_app_id_[contents] = app_id;
+
+ if (app_state == APP_STATE_REMOVED) {
+ // The tab has gone away.
+ RemoveTabFromRunningApp(contents, app_id);
+ } else {
+ WebContentsList& tab_list(app_id_to_web_contents_list_[app_id]);
+
+ if (app_state == APP_STATE_INACTIVE) {
+ WebContentsList::const_iterator i_tab =
+ std::find(tab_list.begin(), tab_list.end(), contents);
+ if (i_tab == tab_list.end())
+ tab_list.push_back(contents);
+ if (i_tab != tab_list.begin()) {
+ // Going inactive, but wasn't the front tab, indicating that a new
+ // tab has already become active.
+ return;
+ }
+ } else {
+ tab_list.remove(contents);
+ tab_list.push_front(contents);
+ }
+ ash::LauncherID id = GetLauncherIDForAppID(app_id);
+ if (id) {
+ // If the window is active, mark the app as active.
+ SetItemStatus(id, app_state == APP_STATE_WINDOW_ACTIVE ?
+ ash::STATUS_ACTIVE : ash::STATUS_RUNNING);
+ }
+ }
+ UpdateBrowserItemStatus();
+}
+
+void ChromeLauncherControllerPerApp::SetRefocusURLPatternForTest(
+ ash::LauncherID id,
+ const GURL& url) {
+ DCHECK(HasItemController(id));
+ LauncherItemController* controller = id_to_item_controller_map_[id];
+
+ int index = model_->ItemIndexByID(id);
+ if (index == -1) {
+ NOTREACHED() << "Invalid launcher id";
+ return;
+ }
+
+ ash::LauncherItemType type = model_->items()[index].type;
+ if (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_WINDOWED_APP) {
+ AppShortcutLauncherItemController* app_controller =
+ static_cast<AppShortcutLauncherItemController*>(controller);
+ app_controller->set_refocus_url(url);
+ } else {
+ NOTREACHED() << "Invalid launcher type";
+ }
+}
+
+const Extension* ChromeLauncherControllerPerApp::GetExtensionForAppID(
+ const std::string& app_id) const {
+ // Some unit tests do not have a real extension.
+ return (profile_->GetExtensionService()) ?
+ profile_->GetExtensionService()->GetInstalledExtension(app_id) : NULL;
+}
+
+void ChromeLauncherControllerPerApp::ActivateWindowOrMinimizeIfActive(
+ ui::BaseWindow* window,
+ bool allow_minimize) {
+ if (window->IsActive() && allow_minimize) {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableMinimizeOnSecondLauncherItemClick)) {
+ AnimateWindow(window->GetNativeWindow(),
+ views::corewm::WINDOW_ANIMATION_TYPE_BOUNCE);
+ } else {
+ window->Minimize();
+ }
+ } else {
+ window->Show();
+ window->Activate();
+ }
+}
+
+void ChromeLauncherControllerPerApp::ItemSelected(const ash::LauncherItem& item,
+ const ui::Event& event) {
+ DCHECK(HasItemController(item.id));
+ LauncherItemController* item_controller = id_to_item_controller_map_[item.id];
+#if defined(OS_CHROMEOS)
+ if (!item_controller->app_id().empty()) {
+ chromeos::default_pinned_apps_field_trial::RecordShelfAppClick(
+ item_controller->app_id());
+ }
+#endif
+ item_controller->Clicked(event);
+}
+
+string16 ChromeLauncherControllerPerApp::GetTitle(
+ const ash::LauncherItem& item) {
+ DCHECK(HasItemController(item.id));
+ return id_to_item_controller_map_[item.id]->GetTitle();
+}
+
+ui::MenuModel* ChromeLauncherControllerPerApp::CreateContextMenu(
+ const ash::LauncherItem& item,
+ aura::RootWindow* root_window) {
+ return new LauncherContextMenu(this, &item, root_window);
+}
+
+ash::LauncherMenuModel* ChromeLauncherControllerPerApp::CreateApplicationMenu(
+ const ash::LauncherItem& item,
+ int event_flags) {
+ return new LauncherApplicationMenuItemModel(GetApplicationList(item,
+ event_flags));
+}
+
+ash::LauncherID ChromeLauncherControllerPerApp::GetIDByWindow(
+ aura::Window* window) {
+ int browser_index = ash::launcher::GetBrowserItemIndex(*model_);
+ DCHECK_GE(browser_index, 0);
+ ash::LauncherID browser_id = model_->items()[browser_index].id;
+
+ IDToItemControllerMap::const_iterator i = id_to_item_controller_map_.begin();
+ for (; i != id_to_item_controller_map_.end(); ++i) {
+ // Since a |window| can be used by multiple applications, an explicit
+ // application always gets chosen over the generic browser.
+ if (i->first != browser_id && i->second->IsCurrentlyShownInWindow(window))
+ return i->first;
+ }
+
+ if (i == id_to_item_controller_map_.end() &&
+ GetBrowserShortcutLauncherItemController()->
+ IsCurrentlyShownInWindow(window))
+ return browser_id;
+
+ return 0;
+}
+
+bool ChromeLauncherControllerPerApp::IsDraggable(
+ const ash::LauncherItem& item) {
+ return (item.type == ash::TYPE_APP_SHORTCUT ||
+ item.type == ash::TYPE_WINDOWED_APP) ? CanPin() : true;
+}
+
+bool ChromeLauncherControllerPerApp::ShouldShowTooltip(
+ const ash::LauncherItem& item) {
+ if (item.type == ash::TYPE_APP_PANEL &&
+ id_to_item_controller_map_[item.id]->IsVisible())
+ return false;
+ return true;
+}
+
+void ChromeLauncherControllerPerApp::OnLauncherCreated(
+ ash::Launcher* launcher) {
+ launchers_.insert(launcher);
+ launcher->shelf_widget()->shelf_layout_manager()->AddObserver(this);
+}
+
+void ChromeLauncherControllerPerApp::OnLauncherDestroyed(
+ ash::Launcher* launcher) {
+ launchers_.erase(launcher);
+ // RemoveObserver is not called here, since by the time this method is called
+ // Launcher is already in its destructor.
+}
+
+void ChromeLauncherControllerPerApp::LauncherItemAdded(int index) {
+}
+
+void ChromeLauncherControllerPerApp::LauncherItemRemoved(
+ int index,
+ ash::LauncherID id) {
+}
+
+void ChromeLauncherControllerPerApp::LauncherItemMoved(
+ int start_index,
+ int target_index) {
+ ash::LauncherID id = model_->items()[target_index].id;
+ if (HasItemController(id) && IsPinned(id))
+ PersistPinnedState();
+}
+
+void ChromeLauncherControllerPerApp::LauncherItemChanged(
+ int index,
+ const ash::LauncherItem& old_item) {
+ ash::LauncherID id = model_->items()[index].id;
+ DCHECK(HasItemController(id));
+ id_to_item_controller_map_[id]->LauncherItemChanged(index, old_item);
+}
+
+void ChromeLauncherControllerPerApp::LauncherStatusChanged() {
+}
+
+void ChromeLauncherControllerPerApp::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case chrome::NOTIFICATION_EXTENSION_LOADED: {
+ const Extension* extension =
+ content::Details<const Extension>(details).ptr();
+ if (IsAppPinned(extension->id())) {
+ // Clear and re-fetch to ensure icon is up-to-date.
+ app_icon_loader_->ClearImage(extension->id());
+ app_icon_loader_->FetchImage(extension->id());
+ }
+
+ UpdateAppLaunchersFromPref();
+ break;
+ }
+ case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
+ const content::Details<extensions::UnloadedExtensionInfo>& unload_info(
+ details);
+ const Extension* extension = unload_info->extension;
+ const std::string& id = extension->id();
+ // Since we might have windowed apps of this type which might have
+ // outstanding locks which needs to be removed.
+ if (GetLauncherIDForAppID(id) &&
+ unload_info->reason == extension_misc::UNLOAD_REASON_UNINSTALL) {
+ CloseWindowedAppsFromRemovedExtension(id);
+ }
+
+ if (IsAppPinned(id)) {
+ if (unload_info->reason == extension_misc::UNLOAD_REASON_UNINSTALL) {
+ DoUnpinAppsWithID(id);
+ app_icon_loader_->ClearImage(id);
+ } else {
+ app_icon_loader_->UpdateImage(id);
+ }
+ }
+ break;
+ }
+ default:
+ NOTREACHED() << "Unexpected notification type=" << type;
+ }
+}
+
+void ChromeLauncherControllerPerApp::OnShelfAlignmentChanged(
+ aura::RootWindow* root_window) {
+ const char* pref_value = NULL;
+ switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window)) {
+ case ash::SHELF_ALIGNMENT_BOTTOM:
+ pref_value = ash::kShelfAlignmentBottom;
+ break;
+ case ash::SHELF_ALIGNMENT_LEFT:
+ pref_value = ash::kShelfAlignmentLeft;
+ break;
+ case ash::SHELF_ALIGNMENT_RIGHT:
+ pref_value = ash::kShelfAlignmentRight;
+ break;
+ case ash::SHELF_ALIGNMENT_TOP:
+ pref_value = ash::kShelfAlignmentTop;
+ }
+
+ UpdatePerDisplayPref(
+ profile_->GetPrefs(), root_window, prefs::kShelfAlignment, pref_value);
+
+ if (root_window == ash::Shell::GetPrimaryRootWindow()) {
+ // See comment in |kShelfAlignment| about why we have two prefs here.
+ profile_->GetPrefs()->SetString(prefs::kShelfAlignmentLocal, pref_value);
+ profile_->GetPrefs()->SetString(prefs::kShelfAlignment, pref_value);
+ }
+}
+
+void ChromeLauncherControllerPerApp::OnDisplayConfigurationChanging() {
+}
+
+void ChromeLauncherControllerPerApp::OnDisplayConfigurationChanged() {
+ SetShelfBehaviorsFromPrefs();
+}
+
+void ChromeLauncherControllerPerApp::OnIsSyncingChanged() {
+ PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
+ MaybePropagatePrefToLocal(prefs,
+ prefs::kShelfAlignmentLocal,
+ prefs::kShelfAlignment);
+ MaybePropagatePrefToLocal(prefs,
+ prefs::kShelfAutoHideBehaviorLocal,
+ prefs::kShelfAutoHideBehavior);
+}
+
+void ChromeLauncherControllerPerApp::OnAppSyncUIStatusChanged() {
+ if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING)
+ model_->SetStatus(ash::LauncherModel::STATUS_LOADING);
+ else
+ model_->SetStatus(ash::LauncherModel::STATUS_NORMAL);
+}
+
+void ChromeLauncherControllerPerApp::ExtensionEnableFlowFinished() {
+ LaunchApp(extension_enable_flow_->extension_id(), ui::EF_NONE);
+ extension_enable_flow_.reset();
+}
+
+void ChromeLauncherControllerPerApp::ExtensionEnableFlowAborted(
+ bool user_initiated) {
+ extension_enable_flow_.reset();
+}
+
+ChromeLauncherAppMenuItems ChromeLauncherControllerPerApp::GetApplicationList(
+ const ash::LauncherItem& item,
+ int event_flags) {
+ // Make sure that there is a controller associated with the id and that the
+ // extension itself is a valid application and not a panel.
+ if (!HasItemController(item.id) ||
+ !GetLauncherIDForAppID(id_to_item_controller_map_[item.id]->app_id()))
+ return ChromeLauncherAppMenuItems().Pass();
+
+ return id_to_item_controller_map_[item.id]->GetApplicationList(event_flags);
+}
+
+std::vector<content::WebContents*>
+ChromeLauncherControllerPerApp::GetV1ApplicationsFromAppId(
+ std::string app_id) {
+ ash::LauncherID id = GetLauncherIDForAppID(app_id);
+
+ // If there is no such an item pinned to the launcher, no menu gets created.
+ if (id) {
+ LauncherItemController* controller = id_to_item_controller_map_[id];
+ DCHECK(controller);
+ if (controller->type() == LauncherItemController::TYPE_SHORTCUT)
+ return GetV1ApplicationsFromController(controller);
+ }
+ return std::vector<content::WebContents*>();
+}
+
+void ChromeLauncherControllerPerApp::ActivateShellApp(
+ const std::string& app_id,
+ int index) {
+ ash::LauncherID id = GetLauncherIDForAppID(app_id);
+ if (id) {
+ LauncherItemController* controller = id_to_item_controller_map_[id];
+ if (controller->type() == LauncherItemController::TYPE_APP) {
+ ShellWindowLauncherItemController* shell_window_controller =
+ static_cast<ShellWindowLauncherItemController*>(controller);
+ shell_window_controller->ActivateIndexedApp(index);
+ }
+ }
+}
+
+bool ChromeLauncherControllerPerApp::IsWebContentHandledByApplication(
+ content::WebContents* web_contents,
+ const std::string& app_id) {
+ if ((web_contents_to_app_id_.find(web_contents) !=
+ web_contents_to_app_id_.end()) &&
+ (web_contents_to_app_id_[web_contents] == app_id))
+ return true;
+ return (app_id == kGmailAppId && ContentCanBeHandledByGmailApp(web_contents));
+}
+
+bool ChromeLauncherControllerPerApp::ContentCanBeHandledByGmailApp(
+ content::WebContents* web_contents) {
+ ash::LauncherID id = GetLauncherIDForAppID(kGmailAppId);
+ if (id) {
+ const GURL url = web_contents->GetURL();
+ // We need to extend the application matching for the gMail app beyond the
+ // manifest file's specification. This is required because of the namespace
+ // overlap with the offline app ("/mail/mu/").
+ if (!MatchPattern(url.path(), "/mail/mu/*") &&
+ MatchPattern(url.path(), "/mail/*") &&
+ GetExtensionForAppID(kGmailAppId) &&
+ GetExtensionForAppID(kGmailAppId)->OverlapsWithOrigin(url))
+ return true;
+ }
+ return false;
+}
+
+gfx::Image ChromeLauncherControllerPerApp::GetAppListIcon(
+ content::WebContents* web_contents) const {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ if (IsIncognito(web_contents))
+ return rb.GetImageNamed(IDR_AURA_LAUNCHER_LIST_INCOGNITO_BROWSER);
+ FaviconTabHelper* favicon_tab_helper =
+ FaviconTabHelper::FromWebContents(web_contents);
+ gfx::Image result = favicon_tab_helper->GetFavicon();
+ if (result.IsEmpty())
+ return rb.GetImageNamed(IDR_DEFAULT_FAVICON);
+ return result;
+}
+
+string16 ChromeLauncherControllerPerApp::GetAppListTitle(
+ content::WebContents* web_contents) const {
+ string16 title = web_contents->GetTitle();
+ if (!title.empty())
+ return title;
+ WebContentsToAppIDMap::const_iterator iter =
+ web_contents_to_app_id_.find(web_contents);
+ if (iter != web_contents_to_app_id_.end()) {
+ std::string app_id = iter->second;
+ const extensions::Extension* extension = GetExtensionForAppID(app_id);
+ if (extension)
+ return UTF8ToUTF16(extension->name());
+ }
+ return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
+}
+
+void ChromeLauncherControllerPerApp::OnBrowserRemoved(Browser* browser) {
+ // When called by a unit test it is possible that there is no shell.
+ // In that case, the following function should not get called.
+ if (ash::Shell::HasInstance())
+ UpdateBrowserItemStatus();
+}
+
+ash::LauncherID ChromeLauncherControllerPerApp::CreateAppShortcutLauncherItem(
+ const std::string& app_id,
+ int index) {
+ return CreateAppShortcutLauncherItemWithType(app_id,
+ index,
+ ash::TYPE_APP_SHORTCUT);
+}
+
+void ChromeLauncherControllerPerApp::SetAppTabHelperForTest(
+ AppTabHelper* helper) {
+ app_tab_helper_.reset(helper);
+}
+
+void ChromeLauncherControllerPerApp::SetAppIconLoaderForTest(
+ extensions::AppIconLoader* loader) {
+ app_icon_loader_.reset(loader);
+}
+
+const std::string&
+ChromeLauncherControllerPerApp::GetAppIdFromLauncherIdForTest(
+ ash::LauncherID id) {
+ return id_to_item_controller_map_[id]->app_id();
+}
+
+ash::LauncherID
+ChromeLauncherControllerPerApp::CreateAppShortcutLauncherItemWithType(
+ const std::string& app_id,
+ int index,
+ ash::LauncherItemType launcher_item_type) {
+ AppShortcutLauncherItemController* controller =
+ new AppShortcutLauncherItemController(app_id, this);
+ ash::LauncherID launcher_id = InsertAppLauncherItem(
+ controller, app_id, ash::STATUS_CLOSED, index, launcher_item_type);
+ return launcher_id;
+}
+
+void ChromeLauncherControllerPerApp::UpdateBrowserItemStatus() {
+ // Determine the new browser's active state and change if necessary.
+ size_t browser_index = ash::launcher::GetBrowserItemIndex(*model_);
+ DCHECK_GE(browser_index, 0u);
+ ash::LauncherItem browser_item = model_->items()[browser_index];
+ ash::LauncherItemStatus browser_status = ash::STATUS_CLOSED;
+
+ aura::Window* window = ash::wm::GetActiveWindow();
+ if (window) {
+ // Check if the active browser / tab is a browser which is not an app,
+ // a windowed app, a popup or any other item which is not a browser of
+ // interest.
+ Browser* browser = chrome::FindBrowserWithWindow(window);
+ if (IsBrowserRepresentedInBrowserList(browser)) {
+ browser_status = ash::STATUS_ACTIVE;
+ const ash::LauncherItems& items = model_->items();
+ // If another launcher item has claimed to be active, we don't.
+ for (size_t i = 0;
+ i < items.size() && browser_status == ash::STATUS_ACTIVE; ++i) {
+ if (i != browser_index && items[i].status == ash::STATUS_ACTIVE)
+ browser_status = ash::STATUS_RUNNING;
+ }
+ }
+ }
+
+ if (browser_status == ash::STATUS_CLOSED) {
+ const BrowserList* ash_browser_list =
+ BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
+ for (BrowserList::const_reverse_iterator it =
+ ash_browser_list->begin_last_active();
+ it != ash_browser_list->end_last_active() &&
+ browser_status == ash::STATUS_CLOSED; ++it) {
+ if (IsBrowserRepresentedInBrowserList(*it))
+ browser_status = ash::STATUS_RUNNING;
+ }
+ }
+
+ if (browser_status != browser_item.status) {
+ browser_item.status = browser_status;
+ model_->Set(browser_index, browser_item);
+ }
+}
+
+Profile* ChromeLauncherControllerPerApp::GetProfileForNewWindows() {
+ return ProfileManager::GetDefaultProfileOrOffTheRecord();
+}
+
+void ChromeLauncherControllerPerApp::LauncherItemClosed(ash::LauncherID id) {
+ IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
+ CHECK(iter != id_to_item_controller_map_.end());
+ CHECK(iter->second);
+ app_icon_loader_->ClearImage(iter->second->app_id());
+ iter->second->OnRemoved();
+ id_to_item_controller_map_.erase(iter);
+ int index = model_->ItemIndexByID(id);
+ // A "browser proxy" is not known to the model and this removal does
+ // therefore not need to be propagated to the model.
+ if (index != -1)
+ model_->RemoveItemAt(index);
+}
+
+void ChromeLauncherControllerPerApp::DoPinAppWithID(
+ const std::string& app_id) {
+ // If there is an item, do nothing and return.
+ if (IsAppPinned(app_id))
+ return;
+
+ ash::LauncherID launcher_id = GetLauncherIDForAppID(app_id);
+ if (launcher_id) {
+ // App item exists, pin it
+ Pin(launcher_id);
+ } else {
+ // Otherwise, create a shortcut item for it.
+ CreateAppShortcutLauncherItem(app_id, model_->item_count());
+ if (CanPin())
+ PersistPinnedState();
+ }
+}
+
+void ChromeLauncherControllerPerApp::DoUnpinAppsWithID(
+ const std::string& app_id) {
+ for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ) {
+ IDToItemControllerMap::iterator current(i);
+ ++i;
+ if (current->second->app_id() == app_id && IsPinned(current->first))
+ Unpin(current->first);
+ }
+}
+
+void ChromeLauncherControllerPerApp::UpdateAppLaunchersFromPref() {
+ // Construct a vector representation of to-be-pinned apps from the pref.
+ std::vector<std::string> pinned_apps;
+ int chrome_icon_index = GetChromeIconIndexFromPref();
+ int index = 0;
+ int max_index = model_->item_count();
+ // Using the alternate shelf layout the App Icon should be the first item in
+ // the list thus start adding items at slot 1 (instead of slot 0).
+ if (ash::switches::UseAlternateShelfLayout()) {
+ ++index;
+ ++max_index;
+ // The alternate shelf layout's icon position will always include the
+ // AppLauncher which needs to be subtracted here.
+ if (chrome_icon_index > 0)
+ --chrome_icon_index;
+ }
+ const base::ListValue* pinned_apps_pref =
+ profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
+ for (base::ListValue::const_iterator it(pinned_apps_pref->begin());
+ it != pinned_apps_pref->end(); ++it) {
+ // To preserve the Chrome icon position, we insert a dummy slot for it - if
+ // the model has a Chrome item. While initializing we can come here with no
+ // item in which case the count would be 1 or below.
+ if (it - pinned_apps_pref->begin() == chrome_icon_index &&
+ model_->item_count() > 1) {
+ pinned_apps.push_back(extension_misc::kChromeAppId);
+ }
+
+ DictionaryValue* app = NULL;
+ std::string app_id;
+ if ((*it)->GetAsDictionary(&app) &&
+ app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id) &&
+ std::find(pinned_apps.begin(), pinned_apps.end(), app_id) ==
+ pinned_apps.end() &&
+ app_tab_helper_->IsValidID(app_id)) {
+ pinned_apps.push_back(app_id);
+ }
+ }
+
+ // Walk the model and |pinned_apps| from the pref lockstep, adding and
+ // removing items as necessary. NB: This code uses plain old indexing instead
+ // of iterators because of model mutations as part of the loop.
+ std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin());
+ for (; index < max_index && pref_app_id != pinned_apps.end(); ++index) {
+ // If the next app launcher according to the pref is present in the model,
+ // delete all app launcher entries in between.
+ if (*pref_app_id == extension_misc::kChromeAppId ||
+ IsAppPinned(*pref_app_id)) {
+ for (; index < max_index; ++index) {
+ const ash::LauncherItem& item(model_->items()[index]);
+ if (item.type != ash::TYPE_APP_SHORTCUT &&
+ item.type != ash::TYPE_BROWSER_SHORTCUT)
+ continue;
+
+ IDToItemControllerMap::const_iterator entry =
+ id_to_item_controller_map_.find(item.id);
+ if ((extension_misc::kChromeAppId == *pref_app_id &&
+ item.type == ash::TYPE_BROWSER_SHORTCUT) ||
+ (entry != id_to_item_controller_map_.end() &&
+ entry->second->app_id() == *pref_app_id)) {
+ ++pref_app_id;
+ break;
+ } else {
+ if (item.type == ash::TYPE_BROWSER_SHORTCUT) {
+ // We cannot delete the browser shortcut. As such we move it up by
+ // one. To avoid any side effects from our pinned state observer, we
+ // do not call the model directly.
+ MoveItemWithoutPinnedStateChangeNotification(index, index + 1);
+ } else {
+ LauncherItemClosed(item.id);
+ --max_index;
+ }
+ --index;
+ }
+ }
+ // If the item wasn't found, that means id_to_item_controller_map_
+ // is out of sync.
+ DCHECK(index < max_index);
+ } else {
+ // This app wasn't pinned before, insert a new entry.
+ ash::LauncherID id = CreateAppShortcutLauncherItem(*pref_app_id, index);
+ index = model_->ItemIndexByID(id);
+ ++pref_app_id;
+ }
+ }
+
+ // Remove any trailing existing items.
+ while (index < model_->item_count()) {
+ const ash::LauncherItem& item(model_->items()[index]);
+ if (item.type == ash::TYPE_APP_SHORTCUT)
+ LauncherItemClosed(item.id);
+ else
+ ++index;
+ }
+
+ // Append unprocessed items from the pref to the end of the model.
+ for (; pref_app_id != pinned_apps.end(); ++pref_app_id) {
+ // Ignore the chrome icon.
+ if (*pref_app_id != extension_misc::kChromeAppId)
+ DoPinAppWithID(*pref_app_id);
+ }
+
+}
+
+void ChromeLauncherControllerPerApp::SetShelfAutoHideBehaviorPrefs(
+ ash::ShelfAutoHideBehavior behavior,
+ aura::RootWindow* root_window) {
+ const char* value = NULL;
+ switch (behavior) {
+ case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
+ value = ash::kShelfAutoHideBehaviorAlways;
+ break;
+ case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
+ value = ash::kShelfAutoHideBehaviorNever;
+ break;
+ case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
+ // This one should not be a valid preference option for now. We only want
+ // to completely hide it when we run app mode.
+ NOTREACHED();
+ return;
+ }
+
+ UpdatePerDisplayPref(
+ profile_->GetPrefs(), root_window, prefs::kShelfAutoHideBehavior, value);
+
+ if (root_window == ash::Shell::GetPrimaryRootWindow()) {
+ // See comment in |kShelfAlignment| about why we have two prefs here.
+ profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal, value);
+ profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior, value);
+ }
+}
+
+void ChromeLauncherControllerPerApp::SetShelfAutoHideBehaviorFromPrefs() {
+ ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
+
+ for (ash::Shell::RootWindowList::const_iterator iter = root_windows.begin();
+ iter != root_windows.end(); ++iter) {
+ ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
+ GetShelfAutoHideBehavior(*iter), *iter);
+ }
+}
+
+void ChromeLauncherControllerPerApp::SetShelfAlignmentFromPrefs() {
+ if (!ash::ShelfWidget::ShelfAlignmentAllowed())
+ return;
+
+ ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
+
+ for (ash::Shell::RootWindowList::const_iterator iter = root_windows.begin();
+ iter != root_windows.end(); ++iter) {
+ // See comment in |kShelfAlignment| as to why we consider two prefs.
+ const std::string alignment_value(
+ GetPrefForRootWindow(profile_->GetPrefs(),
+ *iter,
+ prefs::kShelfAlignmentLocal,
+ prefs::kShelfAlignment));
+ ash::ShelfAlignment alignment = ash::SHELF_ALIGNMENT_BOTTOM;
+ if (alignment_value == ash::kShelfAlignmentLeft)
+ alignment = ash::SHELF_ALIGNMENT_LEFT;
+ else if (alignment_value == ash::kShelfAlignmentRight)
+ alignment = ash::SHELF_ALIGNMENT_RIGHT;
+ else if (alignment_value == ash::kShelfAlignmentTop)
+ alignment = ash::SHELF_ALIGNMENT_TOP;
+ ash::Shell::GetInstance()->SetShelfAlignment(alignment, *iter);
+ }
+}
+
+void ChromeLauncherControllerPerApp::SetShelfBehaviorsFromPrefs() {
+ SetShelfAutoHideBehaviorFromPrefs();
+ SetShelfAlignmentFromPrefs();
+}
+
+WebContents* ChromeLauncherControllerPerApp::GetLastActiveWebContents(
+ const std::string& app_id) {
+ AppIDToWebContentsListMap::const_iterator i =
+ app_id_to_web_contents_list_.find(app_id);
+ if (i == app_id_to_web_contents_list_.end())
+ return NULL;
+ DCHECK_GT(i->second.size(), 0u);
+ return *i->second.begin();
+}
+
+ash::LauncherID ChromeLauncherControllerPerApp::InsertAppLauncherItem(
+ LauncherItemController* controller,
+ const std::string& app_id,
+ ash::LauncherItemStatus status,
+ int index,
+ ash::LauncherItemType launcher_item_type) {
+ ash::LauncherID id = model_->next_id();
+ CHECK(!HasItemController(id));
+ CHECK(controller);
+ id_to_item_controller_map_[id] = controller;
+ controller->set_launcher_id(id);
+
+ ash::LauncherItem item;
+ item.type = launcher_item_type;
+ item.is_incognito = false;
+ item.image = extensions::IconsInfo::GetDefaultAppIcon();
+
+ WebContents* active_tab = GetLastActiveWebContents(app_id);
+ if (active_tab) {
+ Browser* browser = chrome::FindBrowserWithWebContents(active_tab);
+ DCHECK(browser);
+ if (browser->window()->IsActive())
+ status = ash::STATUS_ACTIVE;
+ else
+ status = ash::STATUS_RUNNING;
+ }
+ item.status = status;
+
+ model_->AddAt(index, item);
+
+ app_icon_loader_->FetchImage(app_id);
+
+ return id;
+}
+
+bool ChromeLauncherControllerPerApp::HasItemController(
+ ash::LauncherID id) const {
+ return id_to_item_controller_map_.find(id) !=
+ id_to_item_controller_map_.end();
+}
+
+std::vector<content::WebContents*>
+ChromeLauncherControllerPerApp::GetV1ApplicationsFromController(
+ LauncherItemController* controller) {
+ DCHECK(controller->type() == LauncherItemController::TYPE_SHORTCUT);
+ AppShortcutLauncherItemController* app_controller =
+ static_cast<AppShortcutLauncherItemController*>(controller);
+ return app_controller->GetRunningApplications();
+}
+
+bool ChromeLauncherControllerPerApp::IsBrowserRepresentedInBrowserList(
+ Browser* browser) {
+ return (browser &&
+ (browser->is_type_tabbed() ||
+ !browser->is_app() ||
+ !browser->is_type_popup() ||
+ GetLauncherIDForAppID(web_app::GetExtensionIdFromApplicationName(
+ browser->app_name())) <= 0));
+}
+
+LauncherItemController*
+ChromeLauncherControllerPerApp::GetBrowserShortcutLauncherItemController() {
+ for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ++i) {
+ int index = model_->ItemIndexByID(i->first);
+ const ash::LauncherItem& item = model_->items()[index];
+ if (item.type == ash::TYPE_BROWSER_SHORTCUT)
+ return i->second;
+ }
+ // LauncerItemController For Browser Shortcut must be existed. If it does not
+ // existe create it.
+ ash::LauncherID id = CreateBrowserShortcutLauncherItem();
+ DCHECK(id_to_item_controller_map_[id]);
+ return id_to_item_controller_map_[id];
+}
+
+ash::LauncherID
+ChromeLauncherControllerPerApp::CreateBrowserShortcutLauncherItem() {
+ ash::LauncherItem browser_shortcut;
+ browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT;
+ browser_shortcut.is_incognito = false;
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
+ ash::LauncherID id = model_->next_id();
+ size_t index = GetChromeIconIndexFromPref();
+ model_->AddAt(index, browser_shortcut);
+ browser_item_controller_.reset(
+ new BrowserShortcutLauncherItemController(this, profile_));
+ id_to_item_controller_map_[id] = browser_item_controller_.get();
+ id_to_item_controller_map_[id]->set_launcher_id(id);
+ return id;
+}
+
+void ChromeLauncherControllerPerApp::PersistChromeItemIndex(int index) {
+ profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index);
+}
+
+int ChromeLauncherControllerPerApp::GetChromeIconIndexFromPref() const {
+ size_t index = profile_->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex);
+ const base::ListValue* pinned_apps_pref =
+ profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
+ if (ash::switches::UseAlternateShelfLayout())
+ return std::max(static_cast<size_t>(1),
+ std::min(pinned_apps_pref->GetSize() + 1, index));
+ return std::max(static_cast<size_t>(0),
+ std::min(pinned_apps_pref->GetSize(), index));
+}
+
+bool ChromeLauncherControllerPerApp::IsIncognito(
+ content::WebContents* web_contents) const {
+ const Profile* profile =
+ Profile::FromBrowserContext(web_contents->GetBrowserContext());
+ return profile->IsOffTheRecord() && !profile->IsGuestSession();
+}
+
+void ChromeLauncherControllerPerApp::CloseWindowedAppsFromRemovedExtension(
+ const std::string& app_id) {
+ // This function cannot rely on the controller's enumeration functionality
+ // since the extension has already be unloaded.
+ const BrowserList* ash_browser_list =
+ BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
+ std::vector<Browser*> browser_to_close;
+ for (BrowserList::const_reverse_iterator
+ it = ash_browser_list->begin_last_active();
+ it != ash_browser_list->end_last_active(); ++it) {
+ Browser* browser = *it;
+ if (!browser->is_type_tabbed() &&
+ browser->is_type_popup() &&
+ browser->is_app() &&
+ app_id == web_app::GetExtensionIdFromApplicationName(
+ browser->app_name())) {
+ browser_to_close.push_back(browser);
+ }
+ }
+ while (!browser_to_close.empty()) {
+ TabStripModel* tab_strip = browser_to_close.back()->tab_strip_model();
+ tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
+ browser_to_close.pop_back();
+ }
+}
+
+void
+ChromeLauncherControllerPerApp::MoveItemWithoutPinnedStateChangeNotification(
+ int source_index, int target_index) {
+ base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true);
+ model_->Move(source_index, target_index);
+}
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h
new file mode 100644
index 0000000..951589c
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h
@@ -0,0 +1,508 @@
+// Copyright (c) 2012 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_UI_ASH_LAUNCHER_CHROME_LAUNCHER_CONTROLLER_PER_APP_H_
+#define CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_LAUNCHER_CONTROLLER_PER_APP_H_
+
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "ash/display/display_controller.h"
+#include "ash/launcher/launcher_model_observer.h"
+#include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_layout_manager_observer.h"
+#include "ash/shelf/shelf_types.h"
+#include "ash/shell_observer.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/prefs/pref_change_registrar.h"
+#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/browser/prefs/pref_service_syncable_observer.h"
+#include "chrome/browser/ui/ash/app_sync_ui_state_observer.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/shell_window_launcher_controller.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "ui/aura/window_observer.h"
+
+class AppSyncUIState;
+class Browser;
+class BrowserLauncherItemControllerTest;
+class BrowserShortcutLauncherItemController;
+class ExtensionEnableFlow;
+class LauncherItemController;
+class Profile;
+class TabContents;
+
+namespace ash {
+class LauncherModel;
+}
+
+namespace aura {
+class Window;
+}
+
+namespace content {
+class WebContents;
+}
+
+namespace ui {
+class BaseWindow;
+}
+
+// ChromeLauncherControllerPerApp manages the launcher items needed for content
+// content windows. Launcher items have a type, an optional app id, and a
+// controller. This incarnation groups running tabs/windows in application
+// specific lists.
+// * Tabbed browsers and browser app windows have BrowserLauncherItemController,
+// owned by the BrowserView instance.
+// * App shell windows have ShellWindowLauncherItemController, owned by
+// ShellWindowLauncherController.
+// * Shortcuts have no LauncherItemController.
+class ChromeLauncherControllerPerApp
+ : public ash::LauncherModelObserver,
+ public ash::ShellObserver,
+ public ash::DisplayController::Observer,
+ public ChromeLauncherController,
+ public content::NotificationObserver,
+ public PrefServiceSyncableObserver,
+ public AppSyncUIStateObserver,
+ public ExtensionEnableFlowDelegate,
+ public chrome::BrowserListObserver,
+ public ash::ShelfLayoutManagerObserver {
+ public:
+ ChromeLauncherControllerPerApp(Profile* profile, ash::LauncherModel* model);
+ virtual ~ChromeLauncherControllerPerApp();
+
+ // ChromeLauncherController overrides:
+
+ // Initializes this ChromeLauncherControllerPerApp.
+ virtual void Init() OVERRIDE;
+
+ // Returns the new per application interface of the given launcher. If it is
+ // a per browser (old) controller, it will return NULL;
+ // TODO(skuhne): Remove when we rip out the old launcher.
+ virtual ChromeLauncherControllerPerApp* GetPerAppInterface() OVERRIDE;
+
+ // Creates a new tabbed item on the launcher for |controller|.
+ virtual ash::LauncherID CreateTabbedLauncherItem(
+ LauncherItemController* controller,
+ IncognitoState is_incognito,
+ ash::LauncherItemStatus status) OVERRIDE;
+
+ // Creates a new app item on the launcher for |controller|.
+ virtual ash::LauncherID CreateAppLauncherItem(
+ LauncherItemController* controller,
+ const std::string& app_id,
+ ash::LauncherItemStatus status) OVERRIDE;
+
+ // Updates the running status of an item. It will also update the status of
+ // browsers launcher item if needed.
+ virtual void SetItemStatus(ash::LauncherID id,
+ ash::LauncherItemStatus status) OVERRIDE;
+
+ // Updates the controller associated with id (which should be a shortcut).
+ // |controller| remains owned by caller.
+ virtual void SetItemController(ash::LauncherID id,
+ LauncherItemController* controller) OVERRIDE;
+
+ // Closes or unpins the launcher item.
+ virtual void CloseLauncherItem(ash::LauncherID id) OVERRIDE;
+
+ // Pins the specified id. Currently only supports platform apps.
+ virtual void Pin(ash::LauncherID id) OVERRIDE;
+
+ // Unpins the specified id, closing if not running.
+ virtual void Unpin(ash::LauncherID id) OVERRIDE;
+
+ // Returns true if the item identified by |id| is pinned.
+ virtual bool IsPinned(ash::LauncherID id) OVERRIDE;
+
+ // Pins/unpins the specified id.
+ virtual void TogglePinned(ash::LauncherID id) OVERRIDE;
+
+ // Returns true if the specified item can be pinned or unpinned. Only apps can
+ // be pinned.
+ virtual bool IsPinnable(ash::LauncherID id) const OVERRIDE;
+
+ // If there is no launcher item in the launcher for application |app_id|, one
+ // gets created. The (existing or created) launcher items get then locked
+ // against a users un-pinning removal.
+ virtual void LockV1AppWithID(const std::string& app_id) OVERRIDE;
+
+ // A previously locked launcher item of type |app_id| gets unlocked. If the
+ // lock count reaches 0 and the item is not pinned it will go away.
+ virtual void UnlockV1AppWithID(const std::string& app_id) OVERRIDE;
+
+ // Requests that the launcher item controller specified by |id| open a new
+ // instance of the app. |event_flags| holds the flags of the event which
+ // triggered this command.
+ virtual void Launch(ash::LauncherID id, int event_flags) OVERRIDE;
+
+ // Closes the specified item.
+ virtual void Close(ash::LauncherID id) OVERRIDE;
+
+ // Returns true if the specified item is open.
+ virtual bool IsOpen(ash::LauncherID id) OVERRIDE;
+
+ // Returns true if the specified item is for a platform app.
+ virtual bool IsPlatformApp(ash::LauncherID id) OVERRIDE;
+
+ // Opens a new instance of the application identified by |app_id|.
+ // Used by the app-list, and by pinned-app launcher items.
+ virtual void LaunchApp(const std::string& app_id, int event_flags) OVERRIDE;
+
+ // If |app_id| is running, reactivates the app's most recently active window,
+ // otherwise launches and activates the app.
+ // Used by the app-list, and by pinned-app launcher items.
+ virtual void ActivateApp(const std::string& app_id, int event_flags) OVERRIDE;
+
+ // Returns the launch type of app for the specified id.
+ virtual extensions::ExtensionPrefs::LaunchType GetLaunchType(
+ ash::LauncherID id) OVERRIDE;
+
+ // Returns the id of the app for the specified tab.
+ virtual std::string GetAppID(content::WebContents* tab) OVERRIDE;
+
+ // Returns the |LauncherModel|'s ID or 0 if the AppId was not found.
+ virtual ash::LauncherID GetLauncherIDForAppID(
+ const std::string& app_id) OVERRIDE;
+ virtual std::string GetAppIDForLauncherID(ash::LauncherID id) OVERRIDE;
+
+ // Set the image for a specific launcher item (e.g. when set by the app).
+ virtual void SetLauncherItemImage(ash::LauncherID launcher_id,
+ const gfx::ImageSkia& image) OVERRIDE;
+
+ // Returns true if a pinned launcher item with given |app_id| could be found.
+ virtual bool IsAppPinned(const std::string& app_id) OVERRIDE;
+
+ // Find out if the given application |id| is a windowed app item and not a
+ // pinned item in the launcher.
+ bool IsWindowedAppInLauncher(const std::string& app_id);
+
+ // Pins an app with |app_id| to launcher. If there is a running instance in
+ // launcher, the running instance is pinned. If there is no running instance,
+ // a new launcher item is created and pinned.
+ virtual void PinAppWithID(const std::string& app_id) OVERRIDE;
+
+ // Updates the launche type of the app for the specified id to |launch_type|.
+ virtual void SetLaunchType(
+ ash::LauncherID id,
+ extensions::ExtensionPrefs::LaunchType launch_type) OVERRIDE;
+
+ // Unpins any app items whose id is |app_id|.
+ virtual void UnpinAppsWithID(const std::string& app_id) OVERRIDE;
+
+ // Returns true if the user is currently logged in as a guest.
+ virtual bool IsLoggedInAsGuest() OVERRIDE;
+
+ // Invoked when user clicks on button in the launcher and there is no last
+ // used window (or CTRL is held with the click).
+ virtual void CreateNewWindow() OVERRIDE;
+
+ // Invoked when the user clicks on button in the launcher to create a new
+ // incognito window.
+ virtual void CreateNewIncognitoWindow() OVERRIDE;
+
+ // Checks whether the user is allowed to pin apps. Pinning may be disallowed
+ // by policy in case there is a pre-defined set of pinned apps.
+ virtual bool CanPin() const OVERRIDE;
+
+ // Updates the pinned pref state. The pinned state consists of a list pref.
+ // Each item of the list is a dictionary. The key |kAppIDPath| gives the
+ // id of the app.
+ virtual void PersistPinnedState() OVERRIDE;
+
+ virtual ash::LauncherModel* model() OVERRIDE;
+
+ virtual Profile* profile() OVERRIDE;
+
+ // Gets the shelf auto-hide behavior on |root_window|.
+ virtual ash::ShelfAutoHideBehavior GetShelfAutoHideBehavior(
+ aura::RootWindow* root_window) const OVERRIDE;
+
+ // Returns |true| if the user is allowed to modify the shelf auto-hide
+ // behavior on |root_window|.
+ virtual bool CanUserModifyShelfAutoHideBehavior(
+ aura::RootWindow* root_window) const OVERRIDE;
+
+ // Toggles the shelf auto-hide behavior on |root_window|. Does nothing if the
+ // user is not allowed to modify the auto-hide behavior.
+ virtual void ToggleShelfAutoHideBehavior(
+ aura::RootWindow* root_window) OVERRIDE;
+
+ // The tab no longer represents its previously identified application.
+ virtual void RemoveTabFromRunningApp(content::WebContents* tab,
+ const std::string& app_id) OVERRIDE;
+
+ // Notify the controller that the state of an non platform app's tabs
+ // have changed,
+ virtual void UpdateAppState(content::WebContents* contents,
+ AppState app_state) OVERRIDE;
+
+ // Limits application refocusing to urls that match |url| for |id|.
+ virtual void SetRefocusURLPatternForTest(ash::LauncherID id,
+ const GURL& url) OVERRIDE;
+
+ // Returns the extension identified by |app_id|.
+ virtual const extensions::Extension* GetExtensionForAppID(
+ const std::string& app_id) const OVERRIDE;
+
+ // Activates a |window|. If |allow_minimize| is true and the system allows
+ // it, the the window will get minimized instead.
+ virtual void ActivateWindowOrMinimizeIfActive(ui::BaseWindow* window,
+ bool allow_minimize) OVERRIDE;
+
+ // ash::LauncherDelegate overrides:
+ virtual void ItemSelected(const ash::LauncherItem& item,
+ const ui::Event& event) OVERRIDE;
+ virtual string16 GetTitle(const ash::LauncherItem& item) OVERRIDE;
+ virtual ui::MenuModel* CreateContextMenu(
+ const ash::LauncherItem& item, aura::RootWindow* root) OVERRIDE;
+ virtual ash::LauncherMenuModel* CreateApplicationMenu(
+ const ash::LauncherItem& item,
+ int event_flags) OVERRIDE;
+ virtual ash::LauncherID GetIDByWindow(aura::Window* window) OVERRIDE;
+ virtual bool IsDraggable(const ash::LauncherItem& item) OVERRIDE;
+ virtual bool ShouldShowTooltip(const ash::LauncherItem& item) OVERRIDE;
+ virtual void OnLauncherCreated(ash::Launcher* launcher) OVERRIDE;
+ virtual void OnLauncherDestroyed(ash::Launcher* launcher) OVERRIDE;
+
+ // ash::LauncherModelObserver overrides:
+ virtual void LauncherItemAdded(int index) OVERRIDE;
+ virtual void LauncherItemRemoved(int index, ash::LauncherID id) OVERRIDE;
+ virtual void LauncherItemMoved(int start_index, int target_index) OVERRIDE;
+ virtual void LauncherItemChanged(int index,
+ const ash::LauncherItem& old_item) OVERRIDE;
+ virtual void LauncherStatusChanged() OVERRIDE;
+
+ // content::NotificationObserver overrides:
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // ash::ShellObserver overrides:
+ virtual void OnShelfAlignmentChanged(aura::RootWindow* root_window) OVERRIDE;
+
+ // ash::DisplayController::Observer overrides:
+ virtual void OnDisplayConfigurationChanging() OVERRIDE;
+ virtual void OnDisplayConfigurationChanged() OVERRIDE;
+
+ // PrefServiceSyncableObserver overrides:
+ virtual void OnIsSyncingChanged() OVERRIDE;
+
+ // AppSyncUIStateObserver overrides:
+ virtual void OnAppSyncUIStatusChanged() OVERRIDE;
+
+ // ExtensionEnableFlowDelegate overrides:
+ virtual void ExtensionEnableFlowFinished() OVERRIDE;
+ virtual void ExtensionEnableFlowAborted(bool user_initiated) OVERRIDE;
+
+ // extensions::AppIconLoader overrides:
+ virtual void SetAppImage(const std::string& app_id,
+ const gfx::ImageSkia& image) OVERRIDE;
+
+ // ash::ShelfLayoutManagerObserver overrides:
+ virtual void OnAutoHideBehaviorChanged(
+ aura::RootWindow* root_window,
+ ash::ShelfAutoHideBehavior new_behavior) OVERRIDE;
+
+ // Get the list of all running incarnations of this item.
+ // |event_flags| specifies the flags which were set by the event which
+ // triggered this menu generation. It can be used to generate different lists.
+ ChromeLauncherAppMenuItems GetApplicationList(const ash::LauncherItem& item,
+ int event_flags);
+
+ // Get the list of all tabs which belong to a certain application type.
+ std::vector<content::WebContents*> GetV1ApplicationsFromAppId(
+ std::string app_id);
+
+ // Activates a specified shell application.
+ void ActivateShellApp(const std::string& app_id, int index);
+
+ // Checks if a given |web_contents| is known to be associated with an
+ // application of type |app_id|.
+ bool IsWebContentHandledByApplication(content::WebContents* web_contents,
+ const std::string& app_id);
+
+ // Check if the gMail app is loaded and it can handle the given web content.
+ // This special treatment is required to address crbug.com/234268.
+ bool ContentCanBeHandledByGmailApp(content::WebContents* web_contents);
+
+ // Get the favicon for the application list entry for |web_contents|.
+ // Note that for incognito windows the incognito icon will be returned.
+ // If |web_contents| has not loaded, returns the default favicon.
+ gfx::Image GetAppListIcon(content::WebContents* web_contents) const;
+
+ // Get the title for the applicatoin list entry for |web_contents|.
+ // If |web_contents| has not loaded, returns "Net Tab".
+ string16 GetAppListTitle(content::WebContents* web_contents) const;
+
+ // Overridden from chrome::BrowserListObserver.
+ virtual void OnBrowserRemoved(Browser* browser) OVERRIDE;
+
+ // Returns true when the given |browser| is listed in the browser application
+ // list.
+ bool IsBrowserRepresentedInBrowserList(Browser* browser);
+
+ // Returns the LauncherItemController of BrowserShortcut.
+ LauncherItemController* GetBrowserShortcutLauncherItemController();
+
+ protected:
+ // ChromeLauncherController overrides:
+
+ // Creates a new app shortcut item and controller on the launcher at |index|.
+ // Use kInsertItemAtEnd to add a shortcut as the last item.
+ virtual ash::LauncherID CreateAppShortcutLauncherItem(
+ const std::string& app_id,
+ int index) OVERRIDE;
+
+ // Sets the AppTabHelper/AppIconLoader, taking ownership of the helper class.
+ // These are intended for testing.
+ virtual void SetAppTabHelperForTest(AppTabHelper* helper) OVERRIDE;
+ virtual void SetAppIconLoaderForTest(
+ extensions::AppIconLoader* loader) OVERRIDE;
+ virtual const std::string& GetAppIdFromLauncherIdForTest(
+ ash::LauncherID id) OVERRIDE;
+
+ private:
+ friend class ChromeLauncherControllerPerAppTest;
+ friend class LauncherPerAppAppBrowserTest;
+ friend class LauncherPlatformPerAppAppBrowserTest;
+
+ // Creates a new app shortcut item and controller on the launcher at |index|.
+ // Use kInsertItemAtEnd to add a shortcut as the last item.
+ virtual ash::LauncherID CreateAppShortcutLauncherItemWithType(
+ const std::string& app_id,
+ int index,
+ ash::LauncherItemType launcher_item_type);
+
+ // Updates the activation state of the Broswer item.
+ void UpdateBrowserItemStatus();
+
+ typedef std::map<ash::LauncherID, LauncherItemController*>
+ IDToItemControllerMap;
+ typedef std::list<content::WebContents*> WebContentsList;
+ typedef std::map<std::string, WebContentsList> AppIDToWebContentsListMap;
+ typedef std::map<content::WebContents*, std::string> WebContentsToAppIDMap;
+
+ // Returns the profile used for new windows.
+ Profile* GetProfileForNewWindows();
+
+ // Invoked when the associated browser or app is closed.
+ void LauncherItemClosed(ash::LauncherID id);
+
+ // Internal helpers for pinning and unpinning that handle both
+ // client-triggered and internal pinning operations.
+ void DoPinAppWithID(const std::string& app_id);
+ void DoUnpinAppsWithID(const std::string& app_id);
+
+ // Re-syncs launcher model with prefs::kPinnedLauncherApps.
+ void UpdateAppLaunchersFromPref();
+
+ // Persists the shelf auto-hide behavior to prefs.
+ void SetShelfAutoHideBehaviorPrefs(ash::ShelfAutoHideBehavior behavior,
+ aura::RootWindow* root_window);
+
+ // Sets the shelf auto-hide behavior from prefs.
+ void SetShelfAutoHideBehaviorFromPrefs();
+
+ // Sets the shelf alignment from prefs.
+ void SetShelfAlignmentFromPrefs();
+
+ // Sets both of auto-hide behavior and alignment from prefs.
+ void SetShelfBehaviorsFromPrefs();
+
+ // Returns the most recently active web contents for an app.
+ content::WebContents* GetLastActiveWebContents(const std::string& app_id);
+
+ // Creates an app launcher to insert at |index|. Note that |index| may be
+ // adjusted by the model to meet ordering constraints.
+ // The |launcher_item_type| will be set into the LauncherModel.
+ ash::LauncherID InsertAppLauncherItem(
+ LauncherItemController* controller,
+ const std::string& app_id,
+ ash::LauncherItemStatus status,
+ int index,
+ ash::LauncherItemType launcher_item_type);
+
+ bool HasItemController(ash::LauncherID id) const;
+
+ // Enumerate all Web contents which match a given shortcut |controller|.
+ std::vector<content::WebContents*> GetV1ApplicationsFromController(
+ LauncherItemController* controller);
+
+ // Create LauncherItem for Browser Shortcut.
+ ash::LauncherID CreateBrowserShortcutLauncherItem();
+
+ // Check if the given |web_contents| is in incognito mode.
+ bool IsIncognito(content::WebContents* web_contents) const;
+
+ // Update browser shortcut's index.
+ void PersistChromeItemIndex(int index);
+
+ // Get browser shortcut's index from pref.
+ int GetChromeIconIndexFromPref() const;
+
+ // Close all windowed V1 applications of a certain extension which was already
+ // deleted.
+ void CloseWindowedAppsFromRemovedExtension(const std::string& app_id);
+
+ // Move a launcher item ignoring the pinned state changes from |index| to
+ // |target_index|.
+ void MoveItemWithoutPinnedStateChangeNotification(int source_index,
+ int target_index);
+
+ ash::LauncherModel* model_;
+
+ // Profile used for prefs and loading extensions. This is NOT necessarily the
+ // profile new windows are created with.
+ Profile* profile_;
+
+ IDToItemControllerMap id_to_item_controller_map_;
+
+ // Maintains activation order of web contents for each app.
+ AppIDToWebContentsListMap app_id_to_web_contents_list_;
+
+ // Direct access to app_id for a web contents.
+ WebContentsToAppIDMap web_contents_to_app_id_;
+
+ // Used to track shell windows.
+ scoped_ptr<ShellWindowLauncherController> shell_window_controller_;
+
+ // Used to get app info for tabs.
+ scoped_ptr<AppTabHelper> app_tab_helper_;
+
+ // Used to load the image for an app item.
+ scoped_ptr<extensions::AppIconLoader> app_icon_loader_;
+
+ content::NotificationRegistrar notification_registrar_;
+
+ PrefChangeRegistrar pref_change_registrar_;
+
+ AppSyncUIState* app_sync_ui_state_;
+
+ scoped_ptr<ExtensionEnableFlow> extension_enable_flow_;
+
+ // Launchers that are currently being observed.
+ std::set<ash::Launcher*> launchers_;
+
+ // The owned browser shortcut item.
+ scoped_ptr<BrowserShortcutLauncherItemController> browser_item_controller_;
+
+ // If true, incoming pinned state changes should be ignored.
+ bool ignore_persist_pinned_state_change_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerPerApp);
+};
+
+#endif // CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_LAUNCHER_CONTROLLER_PER_APP_H_
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app_browsertest.cc
new file mode 100644
index 0000000..23b41f7
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app_browsertest.cc
@@ -0,0 +1,1616 @@
+// Copyright (c) 2012 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/ui/ash/launcher/chrome_launcher_controller.h"
+
+#include "apps/native_app_window.h"
+#include "apps/shell_window.h"
+#include "apps/shell_window_registry.h"
+#include "ash/ash_switches.h"
+#include "ash/display/display_controller.h"
+#include "ash/launcher/launcher.h"
+#include "ash/launcher/launcher_model.h"
+#include "ash/launcher/launcher_util.h"
+#include "ash/launcher/launcher_view.h"
+#include "ash/shell.h"
+#include "ash/test/launcher_view_test_api.h"
+#include "ash/test/shell_test_api.h"
+#include "ash/wm/window_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/apps/app_browsertest_util.h"
+#include "chrome/browser/automation/automation_util.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/extensions/extension_test_message_listener.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/app_list_service.h"
+#include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
+#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/browser/ui/host_desktop.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/common/switches.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/app_list/views/apps_grid_view.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/aura/window.h"
+#include "ui/base/events/event.h"
+
+using apps::ShellWindow;
+using extensions::Extension;
+using content::WebContents;
+
+namespace {
+
+class TestEvent : public ui::Event {
+ public:
+ explicit TestEvent(ui::EventType type)
+ : ui::Event(type, base::TimeDelta(), 0) {
+ }
+ virtual ~TestEvent() {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestEvent);
+};
+
+class TestShellWindowRegistryObserver
+ : public apps::ShellWindowRegistry::Observer {
+ public:
+ explicit TestShellWindowRegistryObserver(Profile* profile)
+ : profile_(profile),
+ icon_updates_(0) {
+ apps::ShellWindowRegistry::Get(profile_)->AddObserver(this);
+ }
+
+ virtual ~TestShellWindowRegistryObserver() {
+ apps::ShellWindowRegistry::Get(profile_)->RemoveObserver(this);
+ }
+
+ // Overridden from ShellWindowRegistry::Observer:
+ virtual void OnShellWindowAdded(ShellWindow* shell_window) OVERRIDE {}
+
+ virtual void OnShellWindowIconChanged(ShellWindow* shell_window) OVERRIDE {
+ ++icon_updates_;
+ }
+
+ virtual void OnShellWindowRemoved(ShellWindow* shell_window) OVERRIDE {}
+
+ int icon_updates() { return icon_updates_; }
+
+ private:
+ Profile* profile_;
+ int icon_updates_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestShellWindowRegistryObserver);
+};
+
+} // namespace
+
+// TODO(skuhne): Change name back to LauncherPlatformAppBrowserTest when the
+// old launcher gets ripped out.
+class LauncherPlatformPerAppAppBrowserTest
+ : public extensions::PlatformAppBrowserTest {
+ protected:
+ LauncherPlatformPerAppAppBrowserTest()
+ : launcher_(NULL),
+ controller_(NULL) {
+ }
+
+ virtual ~LauncherPlatformPerAppAppBrowserTest() {}
+
+ virtual void RunTestOnMainThreadLoop() OVERRIDE {
+ launcher_ = ash::Launcher::ForPrimaryDisplay();
+ controller_ =
+ static_cast<ChromeLauncherControllerPerApp*>(launcher_->delegate());
+ return extensions::PlatformAppBrowserTest::RunTestOnMainThreadLoop();
+ }
+
+ ash::LauncherModel* launcher_model() {
+ return ash::test::ShellTestApi(ash::Shell::GetInstance()).launcher_model();
+ }
+
+ ash::LauncherID CreateAppShortcutLauncherItem(const std::string& name) {
+ return controller_->CreateAppShortcutLauncherItem(
+ name, controller_->model()->item_count());
+ }
+
+ const ash::LauncherItem& GetLastLauncherItem() {
+ // Unless there are any panels, the item at index [count - 1] will be
+ // the app list, and the item at [count - 2] will be the desired item.
+ return launcher_model()->items()[launcher_model()->item_count() - 2];
+ }
+
+ const ash::LauncherItem& GetLastLauncherPanelItem() {
+ // Panels show up on the right side of the launcher, so the desired item
+ // will be the last one.
+ return launcher_model()->items()[launcher_model()->item_count() - 1];
+ }
+
+ LauncherItemController* GetItemController(ash::LauncherID id) {
+ return controller_->id_to_item_controller_map_[id];
+ }
+
+ // Returns the number of menu items, ignoring separators.
+ int GetNumApplicationMenuItems(const ash::LauncherItem& item) {
+ const int event_flags = 0;
+ scoped_ptr<ash::LauncherMenuModel> menu(
+ controller_->CreateApplicationMenu(item, event_flags));
+ int num_items = 0;
+ for (int i = 0; i < menu->GetItemCount(); ++i) {
+ if (menu->GetTypeAt(i) != ui::MenuModel::TYPE_SEPARATOR)
+ ++num_items;
+ }
+ return num_items;
+ }
+
+ // Activate the launcher item with the given |id|.
+ void ActivateLauncherItem(int id) {
+ launcher_->ActivateLauncherItem(id);
+ }
+
+ ash::Launcher* launcher_;
+ ChromeLauncherControllerPerApp* controller_;
+
+ private:
+
+ DISALLOW_COPY_AND_ASSIGN(LauncherPlatformPerAppAppBrowserTest);
+};
+
+// TODO(skuhne): Change name back to LauncherAppBrowserTest when the
+// old launcher gets ripped out.
+class LauncherPerAppAppBrowserTest : public ExtensionBrowserTest {
+ protected:
+ LauncherPerAppAppBrowserTest()
+ : launcher_(NULL),
+ model_(NULL) {
+ }
+
+ virtual ~LauncherPerAppAppBrowserTest() {}
+
+ virtual void RunTestOnMainThreadLoop() OVERRIDE {
+ launcher_ = ash::Launcher::ForPrimaryDisplay();
+ model_ =
+ ash::test::ShellTestApi(ash::Shell::GetInstance()).launcher_model();
+ return ExtensionBrowserTest::RunTestOnMainThreadLoop();
+ }
+
+ size_t NumberOfDetectedLauncherBrowsers(bool show_all_tabs) {
+ ChromeLauncherControllerPerApp* controller =
+ static_cast<ChromeLauncherControllerPerApp*>(launcher_->delegate());
+ LauncherItemController* item_controller =
+ controller->GetBrowserShortcutLauncherItemController();
+ int items = item_controller->GetApplicationList(
+ show_all_tabs ? ui::EF_SHIFT_DOWN : 0).size();
+ // If we have at least one item, we have also a title which we remove here.
+ return items ? (items - 1) : 0;
+ }
+
+ const Extension* LoadAndLaunchExtension(
+ const char* name,
+ extension_misc::LaunchContainer container,
+ WindowOpenDisposition disposition) {
+ EXPECT_TRUE(LoadExtension(test_data_dir_.AppendASCII(name)));
+
+ ExtensionService* service = extensions::ExtensionSystem::Get(
+ profile())->extension_service();
+ const Extension* extension =
+ service->GetExtensionById(last_loaded_extension_id_, false);
+ EXPECT_TRUE(extension);
+
+ chrome::OpenApplication(chrome::AppLaunchParams(profile(),
+ extension,
+ container,
+ disposition));
+ return extension;
+ }
+
+ ash::LauncherID CreateShortcut(const char* name) {
+ ExtensionService* service = extensions::ExtensionSystem::Get(
+ profile())->extension_service();
+ LoadExtension(test_data_dir_.AppendASCII(name));
+
+ // First get app_id.
+ const Extension* extension =
+ service->GetExtensionById(last_loaded_extension_id_, false);
+ const std::string app_id = extension->id();
+
+ // Then create a shortcut.
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
+ int item_count = model_->item_count();
+ ash::LauncherID shortcut_id = controller->CreateAppShortcutLauncherItem(
+ app_id,
+ item_count);
+ controller->PersistPinnedState();
+ EXPECT_EQ(++item_count, model_->item_count());
+ const ash::LauncherItem& item = *model_->ItemByID(shortcut_id);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type);
+ return item.id;
+ }
+
+ // Activate the launcher item with the given |id|.
+ void ActivateLauncherItem(int id) {
+ launcher_->ActivateLauncherItem(id);
+ }
+
+ ash::Launcher* launcher_;
+ ash::LauncherModel* model_;
+
+ private:
+
+ DISALLOW_COPY_AND_ASSIGN(LauncherPerAppAppBrowserTest);
+};
+
+// TODO(skuhne): Change name to LauncherAppBrowserTestNoBrowser when the
+// old launcher gets ripped out.
+class LauncherPerAppAppBrowserTestNoDefaultBrowser
+ : public LauncherPerAppAppBrowserTest {
+ protected:
+ LauncherPerAppAppBrowserTestNoDefaultBrowser() {}
+ virtual ~LauncherPerAppAppBrowserTestNoDefaultBrowser() {}
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ LauncherPerAppAppBrowserTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitch(switches::kNoStartupWindow);
+ }
+
+ private:
+
+ DISALLOW_COPY_AND_ASSIGN(LauncherPerAppAppBrowserTestNoDefaultBrowser);
+};
+
+// Since the default for minimizing on click might change, I added both classes
+// to either get the minimize on click or not.
+class LauncherPerAppAppBrowserNoMinimizeOnClick
+ : public LauncherPlatformPerAppAppBrowserTest {
+ protected:
+ LauncherPerAppAppBrowserNoMinimizeOnClick() {}
+ virtual ~LauncherPerAppAppBrowserNoMinimizeOnClick() {}
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ LauncherPlatformPerAppAppBrowserTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitch(
+ switches::kDisableMinimizeOnSecondLauncherItemClick);
+ }
+
+ private:
+
+ DISALLOW_COPY_AND_ASSIGN(LauncherPerAppAppBrowserNoMinimizeOnClick);
+};
+
+typedef LauncherPlatformPerAppAppBrowserTest
+ LauncherPerAppAppBrowserMinimizeOnClick;
+
+// Test that we can launch a platform app and get a running item.
+IN_PROC_BROWSER_TEST_F(LauncherPlatformPerAppAppBrowserTest, LaunchUnpinned) {
+ int item_count = launcher_model()->item_count();
+ const Extension* extension = LoadAndLaunchPlatformApp("launch");
+ ShellWindow* window = CreateShellWindow(extension);
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item = GetLastLauncherItem();
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item.status);
+ CloseShellWindow(window);
+ --item_count;
+ EXPECT_EQ(item_count, launcher_model()->item_count());
+}
+
+// Test that we can launch a platform app that already has a shortcut.
+IN_PROC_BROWSER_TEST_F(LauncherPlatformPerAppAppBrowserTest, LaunchPinned) {
+ int item_count = launcher_model()->item_count();
+
+ // First get app_id.
+ const Extension* extension = LoadAndLaunchPlatformApp("launch");
+ const std::string app_id = extension->id();
+
+ // Then create a shortcut.
+ ash::LauncherID shortcut_id = CreateAppShortcutLauncherItem(app_id);
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ ash::LauncherItem item = *launcher_model()->ItemByID(shortcut_id);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type);
+ EXPECT_EQ(ash::STATUS_CLOSED, item.status);
+
+ // Open a window. Confirm the item is now running.
+ ShellWindow* window = CreateShellWindow(extension);
+ ash::wm::ActivateWindow(window->GetNativeWindow());
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ item = *launcher_model()->ItemByID(shortcut_id);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item.status);
+
+ // Then close it, make sure there's still an item.
+ CloseShellWindow(window);
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ item = *launcher_model()->ItemByID(shortcut_id);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type);
+ EXPECT_EQ(ash::STATUS_CLOSED, item.status);
+}
+
+IN_PROC_BROWSER_TEST_F(LauncherPlatformPerAppAppBrowserTest, PinRunning) {
+ // Run.
+ int item_count = launcher_model()->item_count();
+ const Extension* extension = LoadAndLaunchPlatformApp("launch");
+ ShellWindow* window = CreateShellWindow(extension);
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item1 = GetLastLauncherItem();
+ ash::LauncherID id = item1.id;
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
+
+ // Create a shortcut. The app item should be after it.
+ ash::LauncherID foo_id = CreateAppShortcutLauncherItem("foo");
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ EXPECT_LT(launcher_model()->ItemIndexByID(foo_id),
+ launcher_model()->ItemIndexByID(id));
+
+ // Pin the app. The item should remain.
+ controller_->Pin(id);
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item2 = *launcher_model()->ItemByID(id);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item2.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item2.status);
+
+ // New shortcuts should come after the item.
+ ash::LauncherID bar_id = CreateAppShortcutLauncherItem("bar");
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ EXPECT_LT(launcher_model()->ItemIndexByID(id),
+ launcher_model()->ItemIndexByID(bar_id));
+
+ // Then close it, make sure the item remains.
+ CloseShellWindow(window);
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+}
+
+IN_PROC_BROWSER_TEST_F(LauncherPlatformPerAppAppBrowserTest, UnpinRunning) {
+ int item_count = launcher_model()->item_count();
+
+ // First get app_id.
+ const Extension* extension = LoadAndLaunchPlatformApp("launch");
+ const std::string app_id = extension->id();
+
+ // Then create a shortcut.
+ ash::LauncherID shortcut_id = CreateAppShortcutLauncherItem(app_id);
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ ash::LauncherItem item = *launcher_model()->ItemByID(shortcut_id);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type);
+ EXPECT_EQ(ash::STATUS_CLOSED, item.status);
+
+ // Create a second shortcut. This will be needed to force the first one to
+ // move once it gets unpinned.
+ ash::LauncherID foo_id = CreateAppShortcutLauncherItem("foo");
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ EXPECT_LT(launcher_model()->ItemIndexByID(shortcut_id),
+ launcher_model()->ItemIndexByID(foo_id));
+
+ // Open a window. Confirm the item is now running.
+ ShellWindow* window = CreateShellWindow(extension);
+ ash::wm::ActivateWindow(window->GetNativeWindow());
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ item = *launcher_model()->ItemByID(shortcut_id);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item.status);
+
+ // Unpin the app. The item should remain.
+ controller_->Unpin(shortcut_id);
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ item = *launcher_model()->ItemByID(shortcut_id);
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item.status);
+ // The item should have moved after the other shortcuts.
+ EXPECT_GT(launcher_model()->ItemIndexByID(shortcut_id),
+ launcher_model()->ItemIndexByID(foo_id));
+
+ // Then close it, make sure the item's gone.
+ CloseShellWindow(window);
+ --item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+}
+
+// Test that we can launch a platform app with more than one window.
+IN_PROC_BROWSER_TEST_F(LauncherPlatformPerAppAppBrowserTest, MultipleWindows) {
+ int item_count = launcher_model()->item_count();
+
+ // First run app.
+ const Extension* extension = LoadAndLaunchPlatformApp("launch");
+ ShellWindow* window1 = CreateShellWindow(extension);
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item = GetLastLauncherItem();
+ ash::LauncherID item_id = item.id;
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item.status);
+ EXPECT_EQ(2, GetNumApplicationMenuItems(item)); // Title + 1 window
+
+ // Add second window.
+ ShellWindow* window2 = CreateShellWindow(extension);
+ // Confirm item stays.
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item2 = *launcher_model()->ItemByID(item_id);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item2.status);
+ EXPECT_EQ(3, GetNumApplicationMenuItems(item2)); // Title + 2 windows
+
+ // Close second window.
+ CloseShellWindow(window2);
+ // Confirm item stays.
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item3 = *launcher_model()->ItemByID(item_id);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item3.status);
+ EXPECT_EQ(2, GetNumApplicationMenuItems(item3)); // Title + 1 window
+
+ // Close first window.
+ CloseShellWindow(window1);
+ // Confirm item is removed.
+ --item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+}
+
+IN_PROC_BROWSER_TEST_F(LauncherPlatformPerAppAppBrowserTest, MultipleApps) {
+ int item_count = launcher_model()->item_count();
+
+ // First run app.
+ const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
+ ShellWindow* window1 = CreateShellWindow(extension1);
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item1 = GetLastLauncherItem();
+ ash::LauncherID item_id1 = item1.id;
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
+
+ // Then run second app.
+ const Extension* extension2 = LoadAndLaunchPlatformApp("launch_2");
+ ShellWindow* window2 = CreateShellWindow(extension2);
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item2 = GetLastLauncherItem();
+ ash::LauncherID item_id2 = item2.id;
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item2.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item2.status);
+
+ EXPECT_NE(item_id1, item_id2);
+ EXPECT_EQ(ash::STATUS_RUNNING,
+ launcher_model()->ItemByID(item_id1)->status);
+
+ // Close second app.
+ CloseShellWindow(window2);
+ --item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ // First app should be active again.
+ EXPECT_EQ(ash::STATUS_ACTIVE,
+ launcher_model()->ItemByID(item_id1)->status);
+
+ // Close first app.
+ CloseShellWindow(window1);
+ --item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+
+}
+
+// Confirm that app windows can be reactivated by clicking their icons and that
+// the correct activation order is maintained.
+IN_PROC_BROWSER_TEST_F(LauncherPlatformPerAppAppBrowserTest, WindowActivation) {
+ int item_count = launcher_model()->item_count();
+
+ // First run app.
+ const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
+ ShellWindow* window1 = CreateShellWindow(extension1);
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item1 = GetLastLauncherItem();
+ ash::LauncherID item_id1 = item1.id;
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
+
+ // Then run second app.
+ const Extension* extension2 = LoadAndLaunchPlatformApp("launch_2");
+ ShellWindow* window2 = CreateShellWindow(extension2);
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item2 = GetLastLauncherItem();
+ ash::LauncherID item_id2 = item2.id;
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item2.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item2.status);
+
+ EXPECT_NE(item_id1, item_id2);
+ EXPECT_EQ(ash::STATUS_RUNNING,
+ launcher_model()->ItemByID(item_id1)->status);
+
+ // Activate first one.
+ ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+ EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model()->ItemByID(item_id1)->status);
+ EXPECT_EQ(ash::STATUS_RUNNING,
+ launcher_model()->ItemByID(item_id2)->status);
+ EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
+ EXPECT_FALSE(ash::wm::IsActiveWindow(window2->GetNativeWindow()));
+
+ // Activate second one.
+ ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2));
+ EXPECT_EQ(ash::STATUS_RUNNING,
+ launcher_model()->ItemByID(item_id1)->status);
+ EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model()->ItemByID(item_id2)->status);
+ EXPECT_FALSE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
+ EXPECT_TRUE(ash::wm::IsActiveWindow(window2->GetNativeWindow()));
+
+ // Add window for app1. This will activate it.
+ ShellWindow* window1b = CreateShellWindow(extension1);
+ ash::wm::ActivateWindow(window1b->GetNativeWindow());
+ EXPECT_FALSE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
+ EXPECT_FALSE(ash::wm::IsActiveWindow(window2->GetNativeWindow()));
+ EXPECT_TRUE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
+
+ // Activate launcher item for app1, this will activate the first app window.
+ ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+ EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
+ EXPECT_FALSE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
+ ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+ EXPECT_TRUE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
+
+ // Activate the second app again
+ ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2));
+ EXPECT_FALSE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
+ EXPECT_TRUE(ash::wm::IsActiveWindow(window2->GetNativeWindow()));
+ EXPECT_FALSE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
+
+ // Activate the first app again
+ ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+ EXPECT_TRUE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
+ EXPECT_FALSE(ash::wm::IsActiveWindow(window2->GetNativeWindow()));
+ EXPECT_FALSE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
+
+ // Close second app.
+ CloseShellWindow(window2);
+ --item_count;
+ EXPECT_EQ(item_count, launcher_model()->item_count());
+ // First app should be active again.
+ EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model()->ItemByID(item_id1)->status);
+
+ // Close first app.
+ CloseShellWindow(window1b);
+ CloseShellWindow(window1);
+ --item_count;
+ EXPECT_EQ(item_count, launcher_model()->item_count());
+}
+
+// Confirm that Click behavior for app windows is correnct.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserNoMinimizeOnClick,
+ AppClickBehavior) {
+ // Launch a platform app and create a window for it.
+ const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
+ ShellWindow* window1 = CreateShellWindow(extension1);
+ EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
+ // Confirm that a controller item was created and is the correct state.
+ const ash::LauncherItem& item1 = GetLastLauncherItem();
+ LauncherItemController* item1_controller = GetItemController(item1.id);
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
+ EXPECT_EQ(LauncherItemController::TYPE_APP, item1_controller->type());
+ // Clicking the item should have no effect.
+ TestEvent click_event(ui::ET_MOUSE_PRESSED);
+ item1_controller->Clicked(click_event);
+ EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
+ // Minimize the window and confirm that the controller item is updated.
+ window1->GetBaseWindow()->Minimize();
+ EXPECT_FALSE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_FALSE(window1->GetBaseWindow()->IsActive());
+ EXPECT_EQ(ash::STATUS_RUNNING, item1.status);
+ // Clicking the item should activate the window.
+ item1_controller->Clicked(click_event);
+ EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
+ EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
+ // Maximizing a window should preserve state after minimize + click.
+ window1->GetBaseWindow()->Maximize();
+ window1->GetBaseWindow()->Minimize();
+ item1_controller->Clicked(click_event);
+ EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsMaximized());
+}
+
+// Confirm the minimizing click behavior for apps.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserMinimizeOnClick,
+ PackagedAppClickBehaviorInMinimizeMode) {
+ // Launch one platform app and create a window for it.
+ const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
+ ShellWindow* window1 = CreateShellWindow(extension1);
+ EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
+
+ // Confirm that a controller item was created and is the correct state.
+ const ash::LauncherItem& item1 = GetLastLauncherItem();
+ LauncherItemController* item1_controller = GetItemController(item1.id);
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
+ EXPECT_EQ(LauncherItemController::TYPE_APP, item1_controller->type());
+ // Since it is already active, clicking it should minimize.
+ TestEvent click_event(ui::ET_MOUSE_PRESSED);
+ item1_controller->Clicked(click_event);
+ EXPECT_FALSE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_FALSE(window1->GetBaseWindow()->IsActive());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsMinimized());
+ EXPECT_EQ(ash::STATUS_RUNNING, item1.status);
+ // Clicking the item again should activate the window again.
+ item1_controller->Clicked(click_event);
+ EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
+ EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
+ // Maximizing a window should preserve state after minimize + click.
+ window1->GetBaseWindow()->Maximize();
+ window1->GetBaseWindow()->Minimize();
+ item1_controller->Clicked(click_event);
+ EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsMaximized());
+ window1->GetBaseWindow()->Restore();
+ EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
+ EXPECT_FALSE(window1->GetBaseWindow()->IsMaximized());
+
+ // Creating a second window of the same type should change the behavior so
+ // that a click does not change the activation state.
+ ShellWindow* window1a = CreateShellWindow(extension1);
+ EXPECT_TRUE(window1a->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1a->GetBaseWindow()->IsActive());
+ // The first click does nothing.
+ item1_controller->Clicked(click_event);
+ EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1a->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
+ EXPECT_FALSE(window1a->GetBaseWindow()->IsActive());
+ // The second neither.
+ item1_controller->Clicked(click_event);
+ EXPECT_TRUE(window1->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1a->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(window1->GetBaseWindow()->IsActive());
+ EXPECT_FALSE(window1a->GetBaseWindow()->IsActive());
+}
+
+// Confirm that click behavior for app panels is correct.
+IN_PROC_BROWSER_TEST_F(LauncherPlatformPerAppAppBrowserTest,
+ AppPanelClickBehavior) {
+ // Enable experimental APIs to allow panel creation.
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ extensions::switches::kEnableExperimentalExtensionApis);
+ // Launch a platform app and create a panel window for it.
+ const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
+ ShellWindow::CreateParams params;
+ params.window_type = ShellWindow::WINDOW_TYPE_PANEL;
+ params.focused = false;
+ ShellWindow* panel = CreateShellWindowFromParams(extension1, params);
+ EXPECT_TRUE(panel->GetNativeWindow()->IsVisible());
+ // Panels should not be active by default.
+ EXPECT_FALSE(panel->GetBaseWindow()->IsActive());
+ // Confirm that a controller item was created and is the correct state.
+ const ash::LauncherItem& item1 = GetLastLauncherPanelItem();
+ LauncherItemController* item1_controller = GetItemController(item1.id);
+ EXPECT_EQ(ash::TYPE_APP_PANEL, item1.type);
+ EXPECT_EQ(ash::STATUS_RUNNING, item1.status);
+ EXPECT_EQ(LauncherItemController::TYPE_APP_PANEL, item1_controller->type());
+ // Click the item and confirm that the panel is activated.
+ TestEvent click_event(ui::ET_MOUSE_PRESSED);
+ item1_controller->Clicked(click_event);
+ EXPECT_TRUE(panel->GetBaseWindow()->IsActive());
+ EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
+ // Click the item again and confirm that the panel is minimized.
+ item1_controller->Clicked(click_event);
+ EXPECT_TRUE(panel->GetBaseWindow()->IsMinimized());
+ EXPECT_EQ(ash::STATUS_RUNNING, item1.status);
+ // Click the item again and confirm that the panel is activated.
+ item1_controller->Clicked(click_event);
+ EXPECT_TRUE(panel->GetNativeWindow()->IsVisible());
+ EXPECT_TRUE(panel->GetBaseWindow()->IsActive());
+ EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
+}
+
+IN_PROC_BROWSER_TEST_F(LauncherPlatformPerAppAppBrowserTest,
+ BrowserActivation) {
+ int item_count = launcher_model()->item_count();
+
+ // First run app.
+ const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
+ CreateShellWindow(extension1);
+ ++item_count;
+ ASSERT_EQ(item_count, launcher_model()->item_count());
+ const ash::LauncherItem& item1 = GetLastLauncherItem();
+ ash::LauncherID item_id1 = item1.id;
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
+
+ ash::wm::ActivateWindow(browser()->window()->GetNativeWindow());
+ EXPECT_EQ(ash::STATUS_RUNNING,
+ launcher_model()->ItemByID(item_id1)->status);
+}
+
+// Test that opening an app sets the correct icon
+IN_PROC_BROWSER_TEST_F(LauncherPlatformPerAppAppBrowserTest, SetIcon) {
+ TestShellWindowRegistryObserver test_observer(browser()->profile());
+
+ // Enable experimental APIs to allow panel creation.
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ extensions::switches::kEnableExperimentalExtensionApis);
+
+ int base_launcher_item_count = launcher_model()->item_count();
+ ExtensionTestMessageListener launched_listener("Launched", false);
+ ExtensionTestMessageListener completed_listener("Completed", false);
+ LoadAndLaunchPlatformApp("app_icon");
+ ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
+ ASSERT_TRUE(completed_listener.WaitUntilSatisfied());
+
+ // Now wait until the WebContent has decoded the icons and chrome has
+ // processed it. This needs to be in a loop since the renderer runs in a
+ // different process.
+ while (test_observer.icon_updates() < 3) {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+
+ // This test creates one shell window and one panel window.
+ int launcher_item_count = launcher_model()->item_count();
+ ASSERT_EQ(base_launcher_item_count + 2, launcher_item_count);
+ // The Panel will be the last item, the app list second-to-last, the app
+ // third from last.
+ const ash::LauncherItem& app_item =
+ launcher_model()->items()[launcher_item_count - 3];
+ const ash::LauncherItem& panel_item =
+ launcher_model()->items()[launcher_item_count - 1];
+ const LauncherItemController* app_item_controller =
+ GetItemController(app_item.id);
+ const LauncherItemController* panel_item_controller =
+ GetItemController(panel_item.id);
+ // Icons for Apps are set by the ShellWindowLauncherController, so
+ // image_set_by_controller() should be set.
+ EXPECT_TRUE(app_item_controller->image_set_by_controller());
+ EXPECT_TRUE(panel_item_controller->image_set_by_controller());
+ // Ensure icon heights are correct (see test.js in app_icon/ test directory)
+ EXPECT_EQ(48, app_item.image.height());
+ EXPECT_EQ(64, panel_item.image.height());
+}
+
+// Test that we can launch an app with a shortcut.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, LaunchPinned) {
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ int tab_count = tab_strip->count();
+ ash::LauncherID shortcut_id = CreateShortcut("app1");
+ EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status);
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
+ WebContents* tab = tab_strip->GetActiveWebContents();
+ content::WindowedNotificationObserver close_observer(
+ content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+ content::Source<WebContents>(tab));
+ browser()->tab_strip_model()->CloseSelectedTabs();
+ close_observer.Wait();
+ EXPECT_EQ(--tab_count, tab_strip->count());
+ EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status);
+}
+
+// Launch the app first and then create the shortcut.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, LaunchUnpinned) {
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ int tab_count = tab_strip->count();
+ LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB,
+ NEW_FOREGROUND_TAB);
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ ash::LauncherID shortcut_id = CreateShortcut("app1");
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
+ WebContents* tab = tab_strip->GetActiveWebContents();
+ content::WindowedNotificationObserver close_observer(
+ content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+ content::Source<WebContents>(tab));
+ browser()->tab_strip_model()->CloseSelectedTabs();
+ close_observer.Wait();
+ EXPECT_EQ(--tab_count, tab_strip->count());
+ EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status);
+}
+
+// Launches an app in the background and then tries to open it. This is test for
+// a crash we had.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, LaunchInBackground) {
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ int tab_count = tab_strip->count();
+ LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB,
+ NEW_BACKGROUND_TAB);
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ ChromeLauncherController::instance()->LaunchApp(last_loaded_extension_id_, 0);
+}
+
+// Confirm that clicking a icon for an app running in one of 2 maxmized windows
+// activates the right window.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, LaunchMaximized) {
+ aura::Window* window1 = browser()->window()->GetNativeWindow();
+ ash::wm::MaximizeWindow(window1);
+ content::WindowedNotificationObserver open_observer(
+ chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+ content::NotificationService::AllSources());
+ chrome::NewEmptyWindow(browser()->profile(), chrome::HOST_DESKTOP_TYPE_ASH);
+ open_observer.Wait();
+ Browser* browser2 = content::Source<Browser>(open_observer.source()).ptr();
+ aura::Window* window2 = browser2->window()->GetNativeWindow();
+ TabStripModel* tab_strip = browser2->tab_strip_model();
+ int tab_count = tab_strip->count();
+ ash::wm::MaximizeWindow(window2);
+
+ ash::LauncherID shortcut_id = CreateShortcut("app1");
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
+
+ window1->Show();
+ ash::wm::ActivateWindow(window1);
+ EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut_id)).status);
+
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
+}
+
+// Activating the same app multiple times should launch only a single copy.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, ActivateApp) {
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ int tab_count = tab_strip->count();
+ const Extension* extension =
+ LoadExtension(test_data_dir_.AppendASCII("app1"));
+
+ ChromeLauncherController::instance()->ActivateApp(extension->id(), 0);
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ ChromeLauncherController::instance()->ActivateApp(extension->id(), 0);
+ EXPECT_EQ(tab_count, tab_strip->count());
+}
+
+// Launching the same app multiple times should launch a copy for each call.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, LaunchApp) {
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ int tab_count = tab_strip->count();
+ const Extension* extension =
+ LoadExtension(test_data_dir_.AppendASCII("app1"));
+
+ ChromeLauncherController::instance()->LaunchApp(extension->id(), 0);
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ ChromeLauncherController::instance()->LaunchApp(extension->id(), 0);
+ EXPECT_EQ(++tab_count, tab_strip->count());
+}
+
+// Launch 2 apps and toggle which is active.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, MultipleApps) {
+ int item_count = model_->item_count();
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ int tab_count = tab_strip->count();
+ ash::LauncherID shortcut1 = CreateShortcut("app1");
+ EXPECT_EQ(++item_count, model_->item_count());
+ ash::LauncherID shortcut2 = CreateShortcut("app2");
+ EXPECT_EQ(++item_count, model_->item_count());
+
+ // Launch first app.
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ WebContents* tab1 = tab_strip->GetActiveWebContents();
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status);
+
+ // Launch second app.
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut2));
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ WebContents* tab2 = tab_strip->GetActiveWebContents();
+ ASSERT_NE(tab1, tab2);
+ EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut1)).status);
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut2)).status);
+
+ // Reactivate first app.
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
+ EXPECT_EQ(tab_count, tab_strip->count());
+ EXPECT_EQ(tab_strip->GetActiveWebContents(), tab1);
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status);
+ EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut2)).status);
+
+ // Open second tab for second app. This should activate it.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL("http://www.example.com/path3/foo.html"),
+ NEW_FOREGROUND_TAB,
+ 0);
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut1)).status);
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut2)).status);
+
+ // Reactivate first app.
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
+ EXPECT_EQ(tab_count, tab_strip->count());
+ EXPECT_EQ(tab_strip->GetActiveWebContents(), tab1);
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status);
+ EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut2)).status);
+
+ // And second again. This time the second tab should become active.
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut2));
+ EXPECT_EQ(tab_count, tab_strip->count());
+ EXPECT_EQ(tab_strip->GetActiveWebContents(), tab2);
+ EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut1)).status);
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut2)).status);
+}
+
+// Confirm that a page can be navigated from and to while maintaining the
+// correct running state.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, Navigation) {
+ ash::LauncherID shortcut_id = CreateShortcut("app1");
+ EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status);
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
+
+ // Navigate away.
+ ui_test_utils::NavigateToURL(
+ browser(), GURL("http://www.example.com/path0/bar.html"));
+ EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status);
+
+ // Navigate back.
+ ui_test_utils::NavigateToURL(
+ browser(), GURL("http://www.example.com/path1/foo.html"));
+ EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
+}
+
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, MultipleOwnedTabs) {
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ int tab_count = tab_strip->count();
+ ash::LauncherID shortcut_id = CreateShortcut("app1");
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
+ WebContents* first_tab = tab_strip->GetActiveWebContents();
+
+ // Create new tab owned by app.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL("http://www.example.com/path2/bar.html"),
+ NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ // Confirm app is still active.
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
+
+ // Create new tab not owned by app.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL("http://www.example.com/path3/foo.html"),
+ NEW_FOREGROUND_TAB,
+ 0);
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ // No longer active.
+ EXPECT_EQ(ash::STATUS_RUNNING, model_->ItemByID(shortcut_id)->status);
+
+ // Activating app makes first tab active again.
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
+ EXPECT_EQ(tab_strip->GetActiveWebContents(), first_tab);
+}
+
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, RefocusFilter) {
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ int tab_count = tab_strip->count();
+ ash::LauncherID shortcut_id = CreateShortcut("app1");
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
+ WebContents* first_tab = tab_strip->GetActiveWebContents();
+
+ controller->SetRefocusURLPatternForTest(
+ shortcut_id, GURL("http://www.example.com/path1/*"));
+ // Create new tab owned by app.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL("http://www.example.com/path2/bar.html"),
+ NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ // Confirm app is still active.
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
+
+ // Create new tab not owned by app.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL("http://www.example.com/path3/foo.html"),
+ NEW_FOREGROUND_TAB,
+ 0);
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ // No longer active.
+ EXPECT_EQ(ash::STATUS_RUNNING, model_->ItemByID(shortcut_id)->status);
+
+ // Activating app makes first tab active again, because second tab isn't
+ // in its refocus url path.
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
+ EXPECT_EQ(tab_strip->GetActiveWebContents(), first_tab);
+}
+
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, RefocusFilterLaunch) {
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ int tab_count = tab_strip->count();
+ ash::LauncherID shortcut_id = CreateShortcut("app1");
+ controller->SetRefocusURLPatternForTest(
+ shortcut_id, GURL("http://www.example.com/path1/*"));
+
+ // Create new tab.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL("http://www.example2.com/path2/bar.html"),
+ NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ WebContents* first_tab = tab_strip->GetActiveWebContents();
+ // Confirm app is not active.
+ EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
+
+ // Activating app should launch new tab, because second tab isn't
+ // in its refocus url path.
+ ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+ EXPECT_EQ(++tab_count, tab_strip->count());
+ WebContents* second_tab = tab_strip->GetActiveWebContents();
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
+ EXPECT_NE(first_tab, second_tab);
+ EXPECT_EQ(tab_strip->GetActiveWebContents(), second_tab);
+}
+
+// Check the launcher activation state for applications and browser.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, ActivationStateCheck) {
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ // Get the browser item index
+ int browser_index = ash::launcher::GetBrowserItemIndex(*controller->model());
+ EXPECT_TRUE(browser_index >= 0);
+
+ // Even though we are just comming up, the browser should be active.
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->items()[browser_index].status);
+
+ ash::LauncherID shortcut_id = CreateShortcut("app1");
+ controller->SetRefocusURLPatternForTest(
+ shortcut_id, GURL("http://www.example.com/path1/*"));
+
+ EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->items()[browser_index].status);
+
+ // Create new tab which would be the running app.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL("http://www.example.com/path1/bar.html"),
+ NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+ // There should never be two items active at the same time.
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
+ EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[browser_index].status);
+
+ tab_strip->ActivateTabAt(0, false);
+ EXPECT_EQ(ash::STATUS_RUNNING, model_->ItemByID(shortcut_id)->status);
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->items()[browser_index].status);
+
+ tab_strip->CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
+ EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->items()[browser_index].status);
+
+ ash::wm::DeactivateWindow(browser()->window()->GetNativeWindow());
+ EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
+ EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[browser_index].status);
+}
+
+// Check that the launcher activation state for a V1 application stays closed
+// even after an asynchronous browser event comes in after the tab got
+// destroyed.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest,
+ AsyncActivationStateCheck) {
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+
+ ash::LauncherID shortcut_id = CreateShortcut("app1");
+ controller->SetRefocusURLPatternForTest(
+ shortcut_id, GURL("http://www.example.com/path1/*"));
+
+ EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
+
+ // Create new tab which would be the running app.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL("http://www.example.com/path1/bar.html"),
+ NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+ EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
+ // To address the issue of crbug.com/174050, the tab we are about to close
+ // has to be active.
+ tab_strip->ActivateTabAt(1, false);
+ EXPECT_EQ(1, tab_strip->active_index());
+
+ // Close the web contents.
+ tab_strip->CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
+ // The status should now be set to closed.
+ EXPECT_EQ(ash::STATUS_CLOSED, model_->ItemByID(shortcut_id)->status);
+}
+
+// Checks that a windowed application does not add an item to the browser list.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTestNoDefaultBrowser,
+ WindowedAppDoesNotAddToBrowser) {
+ // Get the number of items in the browser menu.
+ size_t items = NumberOfDetectedLauncherBrowsers(false);
+ size_t running_browser = chrome::GetTotalBrowserCount();
+ EXPECT_EQ(0u, items);
+ EXPECT_EQ(0u, running_browser);
+
+ LoadAndLaunchExtension("app1", extension_misc::LAUNCH_WINDOW, NEW_WINDOW);
+
+ // No new browser should get detected, even though one more is running.
+ EXPECT_EQ(0u, NumberOfDetectedLauncherBrowsers(false));
+ EXPECT_EQ(++running_browser, chrome::GetTotalBrowserCount());
+
+ LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB, NEW_WINDOW);
+
+ // A new browser should get detected and one more should be running.
+ EXPECT_EQ(NumberOfDetectedLauncherBrowsers(false), 1u);
+ EXPECT_EQ(++running_browser, chrome::GetTotalBrowserCount());
+}
+
+// Checks the functionality to enumerate all browsers vs. all tabs.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTestNoDefaultBrowser,
+ EnumerateALlBrowsersAndTabs) {
+ // Create at least one browser.
+ LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB, NEW_WINDOW);
+ size_t browsers = NumberOfDetectedLauncherBrowsers(false);
+ size_t tabs = NumberOfDetectedLauncherBrowsers(true);
+
+ // Create a second browser.
+ LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB, NEW_WINDOW);
+
+ EXPECT_EQ(++browsers, NumberOfDetectedLauncherBrowsers(false));
+ EXPECT_EQ(++tabs, NumberOfDetectedLauncherBrowsers(true));
+
+ // Create only a tab.
+ LoadAndLaunchExtension("app1",
+ extension_misc::LAUNCH_TAB,
+ NEW_FOREGROUND_TAB);
+
+ EXPECT_EQ(browsers, NumberOfDetectedLauncherBrowsers(false));
+ EXPECT_EQ(++tabs, NumberOfDetectedLauncherBrowsers(true));
+}
+
+// Check that the keyboard activation of a launcher item tabs properly through
+// the items at hand.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, AltNumberTabsTabbing) {
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
+
+ ash::LauncherID shortcut_id = CreateShortcut("app");
+ controller->SetRefocusURLPatternForTest(
+ shortcut_id, GURL("http://www.example.com/path/*"));
+ std::string url = "http://www.example.com/path/bla";
+
+ int shortcut_index = model_->ItemIndexByID(shortcut_id);
+
+ // Create an application handled browser tab.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL(url),
+ NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+ content::WebContents* content1 = tab_strip->GetActiveWebContents();
+
+ // Create some other browser tab.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL("http://www.test.com"),
+ NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ content::WebContents* content1a = tab_strip->GetActiveWebContents();
+
+ // Make sure that the active tab is now our handled tab.
+ EXPECT_NE(content1a, content1);
+
+ // The active tab should still be the unnamed tab. Then we switch and reach
+ // the first app and stay there.
+ EXPECT_EQ(content1a, tab_strip->GetActiveWebContents());
+ ActivateLauncherItem(shortcut_index);
+ EXPECT_EQ(content1, tab_strip->GetActiveWebContents());
+ ActivateLauncherItem(shortcut_index);
+ EXPECT_EQ(content1, tab_strip->GetActiveWebContents());
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL(url),
+ NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ content::WebContents* content2 = tab_strip->GetActiveWebContents();
+
+ EXPECT_EQ(content2, browser()->tab_strip_model()->GetActiveWebContents());
+ ActivateLauncherItem(shortcut_index);
+ EXPECT_EQ(content1, browser()->tab_strip_model()->GetActiveWebContents());
+ ActivateLauncherItem(shortcut_index);
+ EXPECT_EQ(content2, browser()->tab_strip_model()->GetActiveWebContents());
+}
+
+// Check that the keyboard activation of a launcher item tabs properly through
+// the items at hand.
+IN_PROC_BROWSER_TEST_F(LauncherPlatformPerAppAppBrowserTest,
+ AltNumberAppsTabbing) {
+ // First run app.
+ const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
+ ui::BaseWindow* window1 = CreateShellWindow(extension1)->GetBaseWindow();
+ const ash::LauncherItem& item1 = GetLastLauncherItem();
+ ash::LauncherID app_id = item1.id;
+ int app_index = launcher_model()->ItemIndexByID(app_id);
+
+ EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type);
+ EXPECT_EQ(ash::STATUS_ACTIVE, item1.status);
+
+ const Extension* extension2 = LoadAndLaunchPlatformApp("launch_2");
+ ui::BaseWindow* window2 = CreateShellWindow(extension2)->GetBaseWindow();
+
+ // By now the browser should be active. Issue Alt keystrokes several times to
+ // see that we stay on that application.
+ EXPECT_TRUE(window2->IsActive());
+ ActivateLauncherItem(app_index);
+ EXPECT_TRUE(window1->IsActive());
+ ActivateLauncherItem(app_index);
+ EXPECT_TRUE(window1->IsActive());
+
+ ui::BaseWindow* window1a = CreateShellWindow(extension1)->GetBaseWindow();
+
+ EXPECT_TRUE(window1a->IsActive());
+ EXPECT_FALSE(window1->IsActive());
+ ActivateLauncherItem(app_index);
+ EXPECT_TRUE(window1->IsActive());
+ ActivateLauncherItem(app_index);
+ EXPECT_TRUE(window1a->IsActive());
+}
+
+// Checks that the browser Alt "tabbing" is properly done.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTestNoDefaultBrowser,
+ AltNumberBrowserTabbing) {
+ // Get the number of items in the browser menu.
+ EXPECT_EQ(0u, chrome::GetTotalBrowserCount());
+ // The first activation should create a browser.
+ launcher_->ActivateLauncherItem(0);
+ EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+ // A second activation should not create a new instance.
+ launcher_->ActivateLauncherItem(0);
+ Browser* browser1 = chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow());
+ EXPECT_TRUE(browser1);
+ aura::Window* window1 = browser1->window()->GetNativeWindow();
+ Browser* browser2 = CreateBrowser(profile());
+ aura::Window* window2 = browser2->window()->GetNativeWindow();
+
+ EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
+ EXPECT_NE(window1, window2);
+ EXPECT_EQ(window2, ash::wm::GetActiveWindow());
+
+ // Activate multiple times the switcher to see that the windows get activated.
+ launcher_->ActivateLauncherItem(0);
+ EXPECT_EQ(window1, ash::wm::GetActiveWindow());
+ launcher_->ActivateLauncherItem(0);
+ EXPECT_EQ(window2, ash::wm::GetActiveWindow());
+
+ // Create a third browser - make sure that we do not toggle simply between
+ // two windows.
+ Browser* browser3 = CreateBrowser(profile());
+ aura::Window* window3 = browser3->window()->GetNativeWindow();
+
+ EXPECT_EQ(3u, chrome::GetTotalBrowserCount());
+ EXPECT_NE(window1, window3);
+ EXPECT_NE(window2, window3);
+ EXPECT_EQ(window3, ash::wm::GetActiveWindow());
+
+ launcher_->ActivateLauncherItem(0);
+ EXPECT_EQ(window1, ash::wm::GetActiveWindow());
+ launcher_->ActivateLauncherItem(0);
+ EXPECT_EQ(window2, ash::wm::GetActiveWindow());
+ launcher_->ActivateLauncherItem(0);
+ EXPECT_EQ(window3, ash::wm::GetActiveWindow());
+ launcher_->ActivateLauncherItem(0);
+ EXPECT_EQ(window1, ash::wm::GetActiveWindow());
+
+ // Create anther app and make sure that none of our browsers is active.
+ LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB, NEW_WINDOW);
+ EXPECT_NE(window1, ash::wm::GetActiveWindow());
+ EXPECT_NE(window2, ash::wm::GetActiveWindow());
+
+ // After activation our browser should be active again.
+ launcher_->ActivateLauncherItem(0);
+ EXPECT_EQ(window1, ash::wm::GetActiveWindow());
+}
+
+// Checks that after a session restore, we do not start applications on an
+// activation.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest,
+ ActivateAfterSessionRestore) {
+ EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+
+ // Create a known application.
+ ChromeLauncherController* controller =
+ static_cast<ChromeLauncherController*>(launcher_->delegate());
+ ash::LauncherID shortcut_id = CreateShortcut("app1");
+
+ // Create a new browser - without activating it - and load an "app" into it.
+ Browser::CreateParams params =
+ Browser::CreateParams(profile(), chrome::GetActiveDesktop());
+ params.initial_show_state = ui::SHOW_STATE_INACTIVE;
+ Browser* browser2 = new Browser(params);
+ controller->SetRefocusURLPatternForTest(
+ shortcut_id, GURL("http://www.example.com/path/*"));
+ std::string url = "http://www.example.com/path/bla";
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser2,
+ GURL(url),
+ NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+ // Remember the number of tabs for each browser.
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ int tab_count1 = tab_strip->count();
+ TabStripModel* tab_strip2 = browser2->tab_strip_model();
+ int tab_count2 = tab_strip2->count();
+
+ // Check that we have two browsers and the inactive browser remained inactive.
+ EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
+ EXPECT_EQ(chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow()),
+ browser());
+ // Check that the LRU browser list does only contain the original browser.
+ BrowserList* ash_browser_list =
+ BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
+ BrowserList::const_reverse_iterator it =
+ ash_browser_list->begin_last_active();
+ EXPECT_EQ(*it, browser());
+ ++it;
+ EXPECT_EQ(it, ash_browser_list->end_last_active());
+
+ // Now request to either activate an existing app or create a new one.
+ controller->ItemSelected(*model_->ItemByID(shortcut_id),
+ ui::KeyEvent(ui::ET_KEY_RELEASED,
+ ui::VKEY_RETURN,
+ 0,
+ false));
+
+ // Check that we have set focus on the existing application and nothing new
+ // was created.
+ EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
+ EXPECT_EQ(tab_count1, tab_strip->count());
+ EXPECT_EQ(tab_count2, tab_strip2->count());
+ EXPECT_EQ(chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow()),
+ browser2);
+}
+
+// Do various drag and drop interaction tests between the application list and
+// the launcher.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, DragAndDrop) {
+ // Get a number of interfaces we need.
+ aura::test::EventGenerator generator(
+ ash::Shell::GetPrimaryRootWindow(), gfx::Point());
+ ash::test::LauncherViewTestAPI test(launcher_->GetLauncherViewForTest());
+ AppListService* service = AppListService::Get();
+
+ // There should be two items in our launcher by this time.
+ EXPECT_EQ(2, model_->item_count());
+ EXPECT_FALSE(service->IsAppListVisible());
+
+ // Open the app list menu and check that the drag and drop host was set.
+ gfx::Rect app_list_bounds =
+ test.launcher_view()->GetAppListButtonView()->GetBoundsInScreen();
+ generator.MoveMouseTo(app_list_bounds.CenterPoint().x(),
+ app_list_bounds.CenterPoint().y());
+ base::MessageLoop::current()->RunUntilIdle();
+ generator.ClickLeftButton();
+
+ EXPECT_TRUE(service->IsAppListVisible());
+ app_list::AppsGridView* grid_view =
+ app_list::AppsGridView::GetLastGridViewForTest();
+ ASSERT_TRUE(grid_view);
+ ASSERT_TRUE(grid_view->has_drag_and_drop_host_for_test());
+
+ // There should be 2 items in our application list.
+ const views::ViewModel* vm_grid = grid_view->view_model_for_test();
+ EXPECT_EQ(2, vm_grid->view_size());
+
+ // Test #1: Drag an app list which does not exist yet item into the
+ // launcher. Keeping it dragged, see that a new item gets created. Continuing
+ // to drag it out should remove it again.
+
+ // Get over item #1 of the application list and press the mouse button.
+ views::View* item1 = vm_grid->view_at(1);
+ gfx::Rect bounds_grid_1 = item1->GetBoundsInScreen();
+ generator.MoveMouseTo(bounds_grid_1.CenterPoint().x(),
+ bounds_grid_1.CenterPoint().y());
+ base::MessageLoop::current()->RunUntilIdle();
+ generator.PressLeftButton();
+
+ EXPECT_FALSE(grid_view->forward_events_to_drag_and_drop_host_for_test());
+
+ // Drag the item into the launcher and check that a new item gets created.
+ const views::ViewModel* vm_launcher =
+ test.launcher_view()->view_model_for_test();
+ views::View* launcher1 = vm_launcher->view_at(1);
+ gfx::Rect bounds_launcher_1 = launcher1->GetBoundsInScreen();
+ generator.MoveMouseTo(bounds_launcher_1.CenterPoint().x(),
+ bounds_launcher_1.CenterPoint().y());
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Check that a new item got created.
+ EXPECT_EQ(3, model_->item_count());
+ EXPECT_TRUE(grid_view->forward_events_to_drag_and_drop_host_for_test());
+
+ // Move it where the item originally was and check that it disappears again.
+ generator.MoveMouseTo(bounds_grid_1.CenterPoint().x(),
+ bounds_grid_1.CenterPoint().y());
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(2, model_->item_count());
+ EXPECT_FALSE(grid_view->forward_events_to_drag_and_drop_host_for_test());
+
+ // Dropping it should keep the launcher as it originally was.
+ generator.ReleaseLeftButton();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(2, model_->item_count());
+ // There are a few animations which need finishing before we can continue.
+ test.RunMessageLoopUntilAnimationsDone();
+ // Move the mouse outside of the launcher.
+ generator.MoveMouseTo(0, 0);
+
+ // Test #2: Check that the unknown item dropped into the launcher will
+ // create a new item.
+ generator.MoveMouseTo(bounds_grid_1.CenterPoint().x(),
+ bounds_grid_1.CenterPoint().y());
+ generator.PressLeftButton();
+ generator.MoveMouseTo(bounds_launcher_1.CenterPoint().x(),
+ bounds_launcher_1.CenterPoint().y());
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(3, model_->item_count());
+ EXPECT_TRUE(grid_view->forward_events_to_drag_and_drop_host_for_test());
+ generator.ReleaseLeftButton();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_FALSE(grid_view->forward_events_to_drag_and_drop_host_for_test());
+ EXPECT_EQ(3, model_->item_count()); // It should be still there.
+ test.RunMessageLoopUntilAnimationsDone();
+
+ // Test #3: Check that the now known item dropped into the launcher will
+ // not create a new item.
+ generator.MoveMouseTo(bounds_grid_1.CenterPoint().x(),
+ bounds_grid_1.CenterPoint().y());
+ generator.PressLeftButton();
+ generator.MoveMouseTo(bounds_launcher_1.CenterPoint().x(),
+ bounds_launcher_1.CenterPoint().y());
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(3, model_->item_count()); // No new item got added.
+ EXPECT_TRUE(grid_view->forward_events_to_drag_and_drop_host_for_test());
+ generator.ReleaseLeftButton();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_FALSE(grid_view->forward_events_to_drag_and_drop_host_for_test());
+ EXPECT_EQ(3, model_->item_count()); // And it remains that way.
+}
+
+// Check that clicking on an app launcher item launches a new browser.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest, ClickItem) {
+ // Get a number of interfaces we need.
+ aura::test::EventGenerator generator(
+ ash::Shell::GetPrimaryRootWindow(), gfx::Point());
+ ash::test::LauncherViewTestAPI test(launcher_->GetLauncherViewForTest());
+ AppListService* service = AppListService::Get();
+ // There should be two items in our launcher by this time.
+ EXPECT_EQ(2, model_->item_count());
+ EXPECT_FALSE(service->IsAppListVisible());
+
+ // Open the app list menu and check that the drag and drop host was set.
+ gfx::Rect app_list_bounds =
+ test.launcher_view()->GetAppListButtonView()->GetBoundsInScreen();
+ generator.MoveMouseTo(app_list_bounds.CenterPoint().x(),
+ app_list_bounds.CenterPoint().y());
+ generator.ClickLeftButton();
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_TRUE(service->IsAppListVisible());
+ app_list::AppsGridView* grid_view =
+ app_list::AppsGridView::GetLastGridViewForTest();
+ ASSERT_TRUE(grid_view);
+ const views::ViewModel* vm_grid = grid_view->view_model_for_test();
+ EXPECT_EQ(2, vm_grid->view_size());
+ gfx::Rect bounds_grid_1 = vm_grid->view_at(1)->GetBoundsInScreen();
+ // Test now that a click does create a new application tab.
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ int tab_count = tab_strip->count();
+ generator.MoveMouseTo(bounds_grid_1.CenterPoint().x(),
+ bounds_grid_1.CenterPoint().y());
+ generator.ClickLeftButton();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(tab_count + 1, tab_strip->count());
+}
+
+// Check LauncherItemController of Browser Shortcut functionality.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTestNoDefaultBrowser,
+ BrowserShortcutLauncherItemController) {
+ ChromeLauncherControllerPerApp* controller =
+ static_cast<ChromeLauncherControllerPerApp*>(launcher_->delegate());
+ LauncherItemController* item_controller =
+ controller->GetBrowserShortcutLauncherItemController();
+
+ // Get the number of browsers.
+ size_t running_browser = chrome::GetTotalBrowserCount();
+ EXPECT_EQ(0u, running_browser);
+ EXPECT_FALSE(item_controller->IsOpen());
+
+ // Activate. This creates new browser
+ item_controller->Activate();
+ // New Window is created.
+ running_browser = chrome::GetTotalBrowserCount();
+ EXPECT_EQ(1u, running_browser);
+ EXPECT_TRUE(item_controller->IsOpen());
+
+ // Minimize Window.
+ aura::Window* window = ash::wm::GetActiveWindow();
+ ash::wm::MinimizeWindow(window);
+ EXPECT_TRUE(ash::wm::IsWindowMinimized(window));
+
+ // Activate again. This doesn't create new browser.
+ // It activates window.
+ item_controller->Activate();
+ running_browser = chrome::GetTotalBrowserCount();
+ EXPECT_EQ(1u, running_browser);
+ EXPECT_TRUE(item_controller->IsOpen());
+ EXPECT_FALSE(ash::wm::IsWindowMinimized(window));
+}
+
+// Check that GetIDByWindow() returns |LauncherID| of the active tab.
+IN_PROC_BROWSER_TEST_F(LauncherPerAppAppBrowserTest,
+ MatchingLauncherIDandActiveTab) {
+ ChromeLauncherControllerPerApp* controller =
+ static_cast<ChromeLauncherControllerPerApp*>(launcher_->delegate());
+
+ EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+ EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
+ EXPECT_EQ(2, model_->item_count());
+
+ aura::Window* window = browser()->window()->GetNativeWindow();
+
+ int browser_index = ash::launcher::GetBrowserItemIndex(*model_);
+ ash::LauncherID browser_id = model_->items()[browser_index].id;
+ EXPECT_EQ(browser_id, controller->GetIDByWindow(window));
+
+ ash::LauncherID app_id = CreateShortcut("app1");
+ EXPECT_EQ(3, model_->item_count());
+
+ // Creates a new tab for "app1" and checks that GetIDByWindow() returns
+ // |LauncherID| of "app1".
+ ActivateLauncherItem(model_->ItemIndexByID(app_id));
+ EXPECT_EQ(2, browser()->tab_strip_model()->count());
+ EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
+ EXPECT_EQ(app_id, controller->GetIDByWindow(window));
+
+ // Makes tab at index 0(NTP) as an active tab and checks that GetIDByWindow()
+ // returns |LauncherID| of browser shortcut.
+ browser()->tab_strip_model()->ActivateTabAt(0, false);
+ EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
+ EXPECT_EQ(browser_id, controller->GetIDByWindow(window));
+}
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app_unittest.cc
index c08d2f6d..ea3861f 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app_unittest.cc
@@ -1,8 +1,8 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
#include <algorithm>
#include <string>
@@ -35,7 +35,6 @@
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_pref_service_syncable.h"
#include "chrome/test/base/testing_profile.h"
-#include "content/public/browser/web_contents.h"
#include "content/public/test/test_browser_thread.h"
#include "extensions/common/manifest_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -136,55 +135,16 @@ class TestAppIconLoaderImpl : public extensions::AppIconLoader {
DISALLOW_COPY_AND_ASSIGN(TestAppIconLoaderImpl);
};
-// Test implementation of AppTabHelper.
-class TestAppTabHelperImpl : public ChromeLauncherController::AppTabHelper {
- public:
- TestAppTabHelperImpl() {}
- virtual ~TestAppTabHelperImpl() {}
-
- // Sets the id for the specified tab. The id is removed if Remove() is
- // invoked.
- void SetAppID(content::WebContents* tab, const std::string& id) {
- tab_id_map_[tab] = id;
- }
-
- // Returns true if there is an id registered for |tab|.
- bool HasAppID(content::WebContents* tab) const {
- return tab_id_map_.find(tab) != tab_id_map_.end();
- }
-
- // AppTabHelper implementation:
- virtual std::string GetAppID(content::WebContents* tab) OVERRIDE {
- return tab_id_map_.find(tab) != tab_id_map_.end() ? tab_id_map_[tab] :
- std::string();
- }
-
- virtual bool IsValidID(const std::string& id) OVERRIDE {
- for (TabToStringMap::const_iterator i = tab_id_map_.begin();
- i != tab_id_map_.end(); ++i) {
- if (i->second == id)
- return true;
- }
- return false;
- }
-
- private:
- typedef std::map<content::WebContents*, std::string> TabToStringMap;
-
- TabToStringMap tab_id_map_;
-
- DISALLOW_COPY_AND_ASSIGN(TestAppTabHelperImpl);
-};
-
} // namespace
-class ChromeLauncherControllerTest : public BrowserWithTestWindowTest {
+class ChromeLauncherControllerPerAppTest : public BrowserWithTestWindowTest {
protected:
- ChromeLauncherControllerTest() : extension_service_(NULL) {
+ ChromeLauncherControllerPerAppTest()
+ : extension_service_(NULL) {
SetHostDesktopType(chrome::HOST_DESKTOP_TYPE_ASH);
}
- virtual ~ChromeLauncherControllerTest() {
+ virtual ~ChromeLauncherControllerPerAppTest() {
}
virtual void SetUp() OVERRIDE {
@@ -256,7 +216,7 @@ class ChromeLauncherControllerTest : public BrowserWithTestWindowTest {
void InitLauncherController() {
launcher_controller_.reset(
- new ChromeLauncherController(profile(), model_.get()));
+ new ChromeLauncherControllerPerApp(profile(), model_.get()));
launcher_controller_->Init();
}
@@ -270,10 +230,6 @@ class ChromeLauncherControllerTest : public BrowserWithTestWindowTest {
launcher_controller_->SetAppIconLoaderForTest(loader);
}
- void SetAppTabHelper(ChromeLauncherController::AppTabHelper* helper) {
- launcher_controller_->SetAppTabHelperForTest(helper);
- }
-
void InsertPrefValue(base::ListValue* pref_value,
int index,
const std::string& extension_id) {
@@ -283,12 +239,12 @@ class ChromeLauncherControllerTest : public BrowserWithTestWindowTest {
}
// Gets the currently configured app launchers from the controller.
- void GetAppLaunchers(ChromeLauncherController* controller,
+ void GetAppLaunchers(ChromeLauncherControllerPerApp* controller,
std::vector<std::string>* launchers) {
launchers->clear();
for (ash::LauncherItems::const_iterator iter(model_->items().begin());
iter != model_->items().end(); ++iter) {
- ChromeLauncherController::IDToItemControllerMap::const_iterator
+ ChromeLauncherControllerPerApp::IDToItemControllerMap::const_iterator
entry(controller->id_to_item_controller_map_.find(iter->id));
if (iter->type == ash::TYPE_APP_SHORTCUT &&
entry != controller->id_to_item_controller_map_.end()) {
@@ -341,30 +297,30 @@ class ChromeLauncherControllerTest : public BrowserWithTestWindowTest {
scoped_refptr<Extension> extension2_;
scoped_refptr<Extension> extension3_;
scoped_refptr<Extension> extension4_;
- scoped_ptr<ChromeLauncherController> launcher_controller_;
+ scoped_ptr<ChromeLauncherControllerPerApp> launcher_controller_;
scoped_ptr<TestLauncherModelObserver> model_observer_;
scoped_ptr<ash::LauncherModel> model_;
ExtensionService* extension_service_;
- DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerTest);
+ DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerPerAppTest);
};
// The testing framework to test the alternate shelf layout.
-class AlternateLayoutChromeLauncherControllerTest
- : public ChromeLauncherControllerTest {
+class AlternateLayoutChromeLauncherControllerPerAppTest
+ : public ChromeLauncherControllerPerAppTest {
protected:
- AlternateLayoutChromeLauncherControllerTest() {
+ AlternateLayoutChromeLauncherControllerPerAppTest() {
}
- virtual ~AlternateLayoutChromeLauncherControllerTest() {
+ virtual ~AlternateLayoutChromeLauncherControllerPerAppTest() {
}
// Overwrite the Setup function to add the Alternate Shelf layout option.
virtual void SetUp() OVERRIDE {
CommandLine::ForCurrentProcess()->AppendSwitch(
ash::switches::kAshUseAlternateShelfLayout);
- ChromeLauncherControllerTest::SetUp();
+ ChromeLauncherControllerPerAppTest::SetUp();
}
// Set the index at which the chrome icon should be.
@@ -375,11 +331,11 @@ class AlternateLayoutChromeLauncherControllerTest
private:
- DISALLOW_COPY_AND_ASSIGN(AlternateLayoutChromeLauncherControllerTest);
+ DISALLOW_COPY_AND_ASSIGN(AlternateLayoutChromeLauncherControllerPerAppTest);
};
-TEST_F(ChromeLauncherControllerTest, DefaultApps) {
+TEST_F(ChromeLauncherControllerPerAppTest, DefaultApps) {
InitLauncherController();
// Model should only contain the browser shortcut and app list items.
EXPECT_EQ(2, model_->item_count());
@@ -398,7 +354,7 @@ TEST_F(ChromeLauncherControllerTest, DefaultApps) {
// Check that the restauration of launcher items is happening in the same order
// as the user has pinned them (on another system) when they are synced reverse
// order.
-TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsReverseOrder) {
+TEST_F(ChromeLauncherControllerPerAppTest, RestoreDefaultAppsReverseOrder) {
InitLauncherController();
base::ListValue policy_value;
@@ -437,7 +393,7 @@ TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsReverseOrder) {
// Check that the restauration of launcher items is happening in the same order
// as the user has pinned them (on another system) when they are synced random
// order.
-TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsRandomOrder) {
+TEST_F(ChromeLauncherControllerPerAppTest, RestoreDefaultAppsRandomOrder) {
InitLauncherController();
base::ListValue policy_value;
@@ -475,7 +431,8 @@ TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsRandomOrder) {
// Check that the restauration of launcher items is happening in the same order
// as the user has pinned / moved them (on another system) when they are synced
// random order - including the chrome icon.
-TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsRandomOrderChromeMoved) {
+TEST_F(ChromeLauncherControllerPerAppTest,
+ RestoreDefaultAppsRandomOrderChromeMoved) {
InitLauncherController();
base::ListValue policy_value;
@@ -513,7 +470,7 @@ TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsRandomOrderChromeMoved) {
}
// Check that syncing to a different state does the correct thing.
-TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsResyncOrder) {
+TEST_F(ChromeLauncherControllerPerAppTest, RestoreDefaultAppsResyncOrder) {
InitLauncherController();
base::ListValue policy_value;
InsertPrefValue(&policy_value, 0, extension1_->id());
@@ -548,7 +505,7 @@ TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsResyncOrder) {
EXPECT_EQ("App2, Chrome, App3, App1, AppList, ", GetPinnedAppStatus());
}
-TEST_F(AlternateLayoutChromeLauncherControllerTest, DefaultApps) {
+TEST_F(AlternateLayoutChromeLauncherControllerPerAppTest, DefaultApps) {
InitLauncherController();
// Model should only contain the browser shortcut and app list items.
EXPECT_EQ(2, model_->item_count());
@@ -567,7 +524,7 @@ TEST_F(AlternateLayoutChromeLauncherControllerTest, DefaultApps) {
// Check that the restauration of launcher items is happening in the same order
// as the user has pinned them (on another system) when they are synced reverse
// order.
-TEST_F(AlternateLayoutChromeLauncherControllerTest,
+TEST_F(AlternateLayoutChromeLauncherControllerPerAppTest,
RestoreDefaultAppsReverseOrder) {
InitLauncherController();
@@ -607,7 +564,7 @@ TEST_F(AlternateLayoutChromeLauncherControllerTest,
// Check that the restauration of launcher items is happening in the same order
// as the user has pinned them (on another system) when they are synced random
// order.
-TEST_F(AlternateLayoutChromeLauncherControllerTest,
+TEST_F(AlternateLayoutChromeLauncherControllerPerAppTest,
RestoreDefaultAppsRandomOrder) {
InitLauncherController();
@@ -646,7 +603,7 @@ TEST_F(AlternateLayoutChromeLauncherControllerTest,
// Check that the restauration of launcher items is happening in the same order
// as the user has pinned / moved them (on another system) when they are synced
// random order - including the chrome icon - using the alternate shelf layout.
-TEST_F(AlternateLayoutChromeLauncherControllerTest,
+TEST_F(AlternateLayoutChromeLauncherControllerPerAppTest,
RestoreDefaultAppsRandomOrderChromeMoved) {
InitLauncherController();
@@ -685,7 +642,7 @@ TEST_F(AlternateLayoutChromeLauncherControllerTest,
// Check that syncing to a different state does the correct thing with the
// alternate shelf layout.
-TEST_F(AlternateLayoutChromeLauncherControllerTest,
+TEST_F(AlternateLayoutChromeLauncherControllerPerAppTest,
RestoreDefaultAppsResyncOrder) {
InitLauncherController();
base::ListValue policy_value;
@@ -747,7 +704,7 @@ TEST_F(AlternateLayoutChromeLauncherControllerTest,
}
// Check that simple locking of an application will 'create' a launcher item.
-TEST_F(ChromeLauncherControllerTest, CheckLockApps) {
+TEST_F(ChromeLauncherControllerPerAppTest, CheckLockApps) {
InitLauncherController();
// Model should only contain the browser shortcut and app list items.
EXPECT_EQ(2, model_->item_count());
@@ -780,7 +737,7 @@ TEST_F(ChromeLauncherControllerTest, CheckLockApps) {
}
// Check that multiple locks of an application will be properly handled.
-TEST_F(ChromeLauncherControllerTest, CheckMukltiLockApps) {
+TEST_F(ChromeLauncherControllerPerAppTest, CheckMukltiLockApps) {
InitLauncherController();
// Model should only contain the browser shortcut and app list items.
EXPECT_EQ(2, model_->item_count());
@@ -817,7 +774,7 @@ TEST_F(ChromeLauncherControllerTest, CheckMukltiLockApps) {
}
// Check that already pinned items are not effected by locks.
-TEST_F(ChromeLauncherControllerTest, CheckAlreadyPinnedLockApps) {
+TEST_F(ChromeLauncherControllerPerAppTest, CheckAlreadyPinnedLockApps) {
InitLauncherController();
// Model should only contain the browser shortcut and app list items.
EXPECT_EQ(2, model_->item_count());
@@ -857,7 +814,7 @@ TEST_F(ChromeLauncherControllerTest, CheckAlreadyPinnedLockApps) {
}
// Check that already pinned items which get locked stay after unpinning.
-TEST_F(ChromeLauncherControllerTest, CheckPinnedAppsStayAfterUnlock) {
+TEST_F(ChromeLauncherControllerPerAppTest, CheckPinnedAppsStayAfterUnlock) {
InitLauncherController();
// Model should only contain the browser shortcut and app list items.
EXPECT_EQ(2, model_->item_count());
@@ -894,7 +851,7 @@ TEST_F(ChromeLauncherControllerTest, CheckPinnedAppsStayAfterUnlock) {
}
// Check that lock -> pin -> unlock -> unpin does properly transition.
-TEST_F(ChromeLauncherControllerTest, CheckLockPinUnlockUnpin) {
+TEST_F(ChromeLauncherControllerPerAppTest, CheckLockPinUnlockUnpin) {
InitLauncherController();
// Model should only contain the browser shortcut and app list items.
EXPECT_EQ(2, model_->item_count());
@@ -930,7 +887,7 @@ TEST_F(ChromeLauncherControllerTest, CheckLockPinUnlockUnpin) {
EXPECT_EQ(2, model_->item_count());
}
-TEST_F(ChromeLauncherControllerTest, Policy) {
+TEST_F(ChromeLauncherControllerPerAppTest, Policy) {
extension_service_->AddExtension(extension1_.get());
extension_service_->AddExtension(extension3_.get());
@@ -970,7 +927,7 @@ TEST_F(ChromeLauncherControllerTest, Policy) {
EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
}
-TEST_F(ChromeLauncherControllerTest, UnpinWithUninstall) {
+TEST_F(ChromeLauncherControllerPerAppTest, UnpinWithUninstall) {
extension_service_->AddExtension(extension3_.get());
extension_service_->AddExtension(extension4_.get());
@@ -986,7 +943,7 @@ TEST_F(ChromeLauncherControllerTest, UnpinWithUninstall) {
EXPECT_TRUE(launcher_controller_->IsAppPinned(extension4_->id()));
}
-TEST_F(ChromeLauncherControllerTest, PrefUpdates) {
+TEST_F(ChromeLauncherControllerPerAppTest, PrefUpdates) {
extension_service_->AddExtension(extension2_.get());
extension_service_->AddExtension(extension3_.get());
extension_service_->AddExtension(extension4_.get());
@@ -1042,7 +999,7 @@ TEST_F(ChromeLauncherControllerTest, PrefUpdates) {
EXPECT_EQ(expected_launchers, actual_launchers);
}
-TEST_F(ChromeLauncherControllerTest, PendingInsertionOrder) {
+TEST_F(ChromeLauncherControllerPerAppTest, PendingInsertionOrder) {
extension_service_->AddExtension(extension1_.get());
extension_service_->AddExtension(extension3_.get());
@@ -1075,7 +1032,7 @@ TEST_F(ChromeLauncherControllerTest, PendingInsertionOrder) {
// found item count against the |expected_items|. The |title| list contains the
// menu titles in the order of their appearance in the menu (not including the
// application name).
-bool CheckMenuCreation(ChromeLauncherController* controller,
+bool CheckMenuCreation(ChromeLauncherControllerPerApp* controller,
const ash::LauncherItem& item,
size_t expected_items,
string16 title[],
@@ -1117,7 +1074,7 @@ bool CheckMenuCreation(ChromeLauncherController* controller,
}
// Check that browsers get reflected correctly in the launcher menu.
-TEST_F(ChromeLauncherControllerTest, BrowserMenuGeneration) {
+TEST_F(ChromeLauncherControllerPerAppTest, BrowserMenuGeneration) {
EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
chrome::NewTab(browser());
@@ -1165,7 +1122,7 @@ TEST_F(ChromeLauncherControllerTest, BrowserMenuGeneration) {
// refocus logic.
// Note that the extension matching logic is tested by the extension system
// and does not need a separate test here.
-TEST_F(ChromeLauncherControllerTest, V1AppMenuGeneration) {
+TEST_F(ChromeLauncherControllerPerAppTest, V1AppMenuGeneration) {
EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
EXPECT_EQ(0, browser()->tab_strip_model()->count());
@@ -1238,7 +1195,7 @@ TEST_F(ChromeLauncherControllerTest, V1AppMenuGeneration) {
}
// Checks that the generated menu list properly activates items.
-TEST_F(ChromeLauncherControllerTest, V1AppMenuExecution) {
+TEST_F(ChromeLauncherControllerPerAppTest, V1AppMenuExecution) {
InitLauncherControllerWithBrowser();
// Add |extension3_| to the launcher and add two items.
@@ -1287,7 +1244,7 @@ TEST_F(ChromeLauncherControllerTest, V1AppMenuExecution) {
}
// Checks that the generated menu list properly deletes items.
-TEST_F(ChromeLauncherControllerTest, V1AppMenuDeletionExecution) {
+TEST_F(ChromeLauncherControllerPerAppTest, V1AppMenuDeletionExecution) {
InitLauncherControllerWithBrowser();
// Add |extension3_| to the launcher and add two items.
@@ -1328,9 +1285,8 @@ TEST_F(ChromeLauncherControllerTest, V1AppMenuDeletionExecution) {
}
// Tests that panels create launcher items correctly
-TEST_F(ChromeLauncherControllerTest, AppPanels) {
+TEST_F(ChromeLauncherControllerPerAppTest, AppPanels) {
InitLauncherControllerWithBrowser();
- // Browser shortcut LauncherItem is added.
EXPECT_EQ(1, model_observer_->added());
TestAppIconLoaderImpl* app_icon_loader = new TestAppIconLoaderImpl();
@@ -1374,7 +1330,7 @@ TEST_F(ChromeLauncherControllerTest, AppPanels) {
// Tests that the Gmail extension matches more then the app itself claims with
// the manifest file.
-TEST_F(ChromeLauncherControllerTest, GmailMatching) {
+TEST_F(ChromeLauncherControllerPerAppTest, GmailMatching) {
InitLauncherControllerWithBrowser();
// Create a Gmail browser tab.
@@ -1406,7 +1362,7 @@ TEST_F(ChromeLauncherControllerTest, GmailMatching) {
}
// Tests that the Gmail extension does not match the offline verison.
-TEST_F(ChromeLauncherControllerTest, GmailOfflineMatching) {
+TEST_F(ChromeLauncherControllerPerAppTest, GmailOfflineMatching) {
InitLauncherControllerWithBrowser();
// Create a Gmail browser tab.
@@ -1429,102 +1385,3 @@ TEST_F(ChromeLauncherControllerTest, GmailOfflineMatching) {
// The content should not be able to be handled by the app.
EXPECT_FALSE(launcher_controller_->ContentCanBeHandledByGmailApp(content));
}
-
-// Verify that the launcher item positions are persisted and restored.
-TEST_F(ChromeLauncherControllerTest, PersistLauncherItemPositions) {
- InitLauncherController();
-
- TestAppTabHelperImpl* app_tab_helper = new TestAppTabHelperImpl;
- SetAppTabHelper(app_tab_helper);
-
- EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[0].type);
- EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[1].type);
-
- TabStripModel* tab_strip_model = browser()->tab_strip_model();
- EXPECT_EQ(0, tab_strip_model->count());
- chrome::NewTab(browser());
- chrome::NewTab(browser());
- EXPECT_EQ(2, tab_strip_model->count());
- app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1");
- app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(1), "2");
-
- EXPECT_FALSE(launcher_controller_->IsAppPinned("1"));
- launcher_controller_->PinAppWithID("1");
- EXPECT_TRUE(launcher_controller_->IsAppPinned("1"));
- launcher_controller_->PinAppWithID("2");
-
- EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[0].type);
- EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
- EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type);
- EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[3].type);
-
- // Move browser shortcut item from index 0 to index 2.
- model_->Move(0, 2);
- EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[0].type);
- EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
- EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[2].type);
- EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[3].type);
-
- launcher_controller_.reset();
- model_.reset(new ash::LauncherModel);
- launcher_controller_.reset(
- ChromeLauncherController::CreateInstance(profile(), model_.get()));
- app_tab_helper = new TestAppTabHelperImpl;
- app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1");
- app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(1), "2");
- SetAppTabHelper(app_tab_helper);
-
- launcher_controller_->Init();
-
- // Check LauncherItems are restored after resetting ChromeLauncherController.
- EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[0].type);
- EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
- EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[2].type);
- EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[3].type);
-}
-
-// Verifies pinned apps are persisted and restored.
-TEST_F(ChromeLauncherControllerTest, PersistPinned) {
- InitLauncherControllerWithBrowser();
- size_t initial_size = model_->items().size();
-
- TabStripModel* tab_strip_model = browser()->tab_strip_model();
- EXPECT_EQ(1, tab_strip_model->count());
-
- TestAppTabHelperImpl* app_tab_helper = new TestAppTabHelperImpl;
- app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1");
- SetAppTabHelper(app_tab_helper);
-
- TestAppIconLoaderImpl* app_icon_loader = new TestAppIconLoaderImpl;
- SetAppIconLoader(app_icon_loader);
- EXPECT_EQ(0, app_icon_loader->fetch_count());
-
- launcher_controller_->PinAppWithID("1");
- ash::LauncherID id = launcher_controller_->GetLauncherIDForAppID("1");
- int app_index = model_->ItemIndexByID(id);
- EXPECT_EQ(1, app_icon_loader->fetch_count());
- EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[app_index].type);
- EXPECT_TRUE(launcher_controller_->IsAppPinned("1"));
- EXPECT_FALSE(launcher_controller_->IsAppPinned("0"));
- EXPECT_EQ(initial_size + 1, model_->items().size());
-
- launcher_controller_.reset();
- model_.reset(new ash::LauncherModel);
- launcher_controller_.reset(
- ChromeLauncherController::CreateInstance(profile(), model_.get()));
- app_tab_helper = new TestAppTabHelperImpl;
- app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1");
- SetAppTabHelper(app_tab_helper);
- app_icon_loader = new TestAppIconLoaderImpl;
- SetAppIconLoader(app_icon_loader);
- launcher_controller_->Init();
-
- EXPECT_EQ(1, app_icon_loader->fetch_count());
- ASSERT_EQ(initial_size + 1, model_->items().size());
- EXPECT_TRUE(launcher_controller_->IsAppPinned("1"));
- EXPECT_FALSE(launcher_controller_->IsAppPinned("0"));
- EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[app_index].type);
-
- launcher_controller_->UnpinAppsWithID("1");
- ASSERT_EQ(initial_size, model_->items().size());
-}
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser.cc
new file mode 100644
index 0000000..4836677
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser.cc
@@ -0,0 +1,1476 @@
+// Copyright (c) 2012 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/ui/ash/launcher/chrome_launcher_controller_per_browser.h"
+
+#include <vector>
+
+#include "ash/ash_switches.h"
+#include "ash/launcher/launcher_model.h"
+#include "ash/root_window_controller.h"
+#include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shelf/shelf_widget.h"
+#include "ash/shell.h"
+#include "ash/wm/window_util.h"
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/app_mode/app_mode_utils.h"
+#include "chrome/browser/defaults.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/app_icon_loader_impl.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/prefs/incognito_mode_prefs.h"
+#include "chrome/browser/prefs/pref_service_syncable.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/app_sync_ui_state.h"
+#include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
+#include "chrome/browser/ui/ash/launcher/launcher_app_tab_helper.h"
+#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
+#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
+#include "chrome/browser/ui/ash/launcher/shell_window_launcher_controller.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/browser/ui/extensions/extension_enable_flow.h"
+#include "chrome/browser/ui/host_desktop.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/common/url_pattern.h"
+#include "grit/chromium_strings.h"
+#include "grit/theme_resources.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/default_pinned_apps_field_trial.h"
+#endif
+
+using content::WebContents;
+using extensions::Extension;
+
+namespace {
+
+// Item controller for an app shortcut. Shortcuts track app and launcher ids,
+// but do not have any associated windows (opening a shortcut will replace the
+// item with the appropriate LauncherItemController type).
+class AppShortcutLauncherItemController : public LauncherItemController {
+ public:
+ AppShortcutLauncherItemController(
+ const std::string& app_id,
+ ChromeLauncherControllerPerBrowser* controller)
+ : LauncherItemController(TYPE_SHORTCUT, app_id, controller) {
+ // Google Drive should just refocus to it's main app UI.
+ // TODO(davemoore): Generalize this for other applications.
+ if (app_id == "apdfllckaahabafndbhieahigkjlhalf") {
+ const Extension* extension =
+ launcher_controller()->GetExtensionForAppID(app_id);
+ refocus_url_ = GURL(
+ extensions::AppLaunchInfo::GetLaunchWebURL(extension).spec() + "*");
+ }
+ }
+
+ virtual ~AppShortcutLauncherItemController() {}
+
+ // LauncherItemController overrides:
+ virtual string16 GetTitle() OVERRIDE {
+ return GetAppTitle();
+ }
+
+ virtual bool IsCurrentlyShownInWindow(aura::Window* window) const OVERRIDE {
+ return false;
+ }
+
+ virtual bool IsOpen() const OVERRIDE {
+ return false;
+ }
+
+ virtual bool IsVisible() const OVERRIDE {
+ return false;
+ }
+
+ virtual void Launch(int event_flags) OVERRIDE {
+ launcher_controller()->LaunchApp(app_id(), event_flags);
+ }
+
+ virtual void Activate() OVERRIDE {
+ launcher_controller()->ActivateApp(app_id(), ui::EF_NONE);
+ }
+
+ virtual void Close() OVERRIDE {
+ // TODO: maybe should treat as unpin?
+ }
+
+ virtual void Clicked(const ui::Event& event) OVERRIDE {
+ Activate();
+ }
+
+ virtual void OnRemoved() OVERRIDE {
+ // AppShortcutLauncherItemController is unowned; delete on removal.
+ delete this;
+ }
+
+ virtual void LauncherItemChanged(
+ int model_index,
+ const ash::LauncherItem& old_item) OVERRIDE {
+ }
+
+ virtual ChromeLauncherAppMenuItems GetApplicationList(
+ int event_flags) OVERRIDE {
+ ChromeLauncherAppMenuItems items;
+ return items.Pass();
+ }
+
+ // Stores the optional refocus url pattern for this item.
+ const GURL& refocus_url() const { return refocus_url_; }
+ void set_refocus_url(const GURL& refocus_url) { refocus_url_ = refocus_url; }
+
+ private:
+ GURL refocus_url_;
+ DISALLOW_COPY_AND_ASSIGN(AppShortcutLauncherItemController);
+};
+
+std::string GetPrefKeyForRootWindow(aura::RootWindow* root_window) {
+ gfx::Display display = gfx::Screen::GetScreenFor(
+ root_window)->GetDisplayNearestWindow(root_window);
+ DCHECK(display.is_valid());
+
+ return base::Int64ToString(display.id());
+}
+
+void UpdatePerDisplayPref(PrefService* pref_service,
+ aura::RootWindow* root_window,
+ const char* pref_key,
+ const std::string& value) {
+ std::string key = GetPrefKeyForRootWindow(root_window);
+ if (key.empty())
+ return;
+
+ DictionaryPrefUpdate update(pref_service, prefs::kShelfPreferences);
+ base::DictionaryValue* shelf_prefs = update.Get();
+ base::DictionaryValue* prefs = NULL;
+ if (!shelf_prefs->GetDictionary(key, &prefs)) {
+ prefs = new base::DictionaryValue();
+ shelf_prefs->Set(key, prefs);
+ }
+ prefs->SetStringWithoutPathExpansion(pref_key, value);
+}
+
+// Returns a pref value in |pref_service| for the display of |root_window|. The
+// pref value is stored in |local_path| and |path|, but |pref_service| may have
+// per-display preferences and the value can be specified by policy. Here is
+// the priority:
+// * A value managed by policy. This is a single value that applies to all
+// displays.
+// * A user-set value for the specified display.
+// * A user-set value in |local_path| or |path|, if no per-display settings are
+// ever specified (see http://crbug.com/173719 for why). |local_path| is
+// preferred. See comment in |kShelfAlignment| as to why we consider two
+// prefs and why |local_path| is preferred.
+// * A value recommended by policy. This is a single value that applies to all
+// root windows.
+// * The default value for |local_path| if the value is not recommended by
+// policy.
+std::string GetPrefForRootWindow(PrefService* pref_service,
+ aura::RootWindow* root_window,
+ const char* local_path,
+ const char* path) {
+ const PrefService::Preference* local_pref =
+ pref_service->FindPreference(local_path);
+ const std::string value(pref_service->GetString(local_path));
+ if (local_pref->IsManaged())
+ return value;
+
+ std::string pref_key = GetPrefKeyForRootWindow(root_window);
+ bool has_per_display_prefs = false;
+ if (!pref_key.empty()) {
+ const base::DictionaryValue* shelf_prefs = pref_service->GetDictionary(
+ prefs::kShelfPreferences);
+ const base::DictionaryValue* display_pref = NULL;
+ std::string per_display_value;
+ if (shelf_prefs->GetDictionary(pref_key, &display_pref) &&
+ display_pref->GetString(path, &per_display_value))
+ return per_display_value;
+
+ // If the pref for the specified display is not found, scan the whole prefs
+ // and check if the prefs for other display is already specified.
+ std::string unused_value;
+ for (base::DictionaryValue::Iterator iter(*shelf_prefs);
+ !iter.IsAtEnd(); iter.Advance()) {
+ const base::DictionaryValue* display_pref = NULL;
+ if (iter.value().GetAsDictionary(&display_pref) &&
+ display_pref->GetString(path, &unused_value)) {
+ has_per_display_prefs = true;
+ break;
+ }
+ }
+ }
+
+ if (local_pref->IsRecommended() || !has_per_display_prefs)
+ return value;
+
+ const base::Value* default_value =
+ pref_service->GetDefaultPrefValue(local_path);
+ std::string default_string;
+ default_value->GetAsString(&default_string);
+ return default_string;
+}
+
+// If prefs have synced and no user-set value exists at |local_path|, the value
+// from |synced_path| is copied to |local_path|.
+void MaybePropagatePrefToLocal(PrefServiceSyncable* pref_service,
+ const char* local_path,
+ const char* synced_path) {
+ if (!pref_service->FindPreference(local_path)->HasUserSetting() &&
+ pref_service->IsSyncing()) {
+ // First time the user is using this machine, propagate from remote to
+ // local.
+ pref_service->SetString(local_path, pref_service->GetString(synced_path));
+ }
+}
+
+} // namespace
+
+// ChromeLauncherControllerPerBrowser -----------------------------------------
+
+ChromeLauncherControllerPerBrowser::ChromeLauncherControllerPerBrowser(
+ Profile* profile,
+ ash::LauncherModel* model)
+ : model_(model),
+ profile_(profile),
+ app_sync_ui_state_(NULL),
+ ignore_persist_pinned_state_change_(false) {
+ if (!profile_) {
+ // Use the original profile as on chromeos we may get a temporary off the
+ // record profile.
+ profile_ = ProfileManager::GetDefaultProfile()->GetOriginalProfile();
+
+ app_sync_ui_state_ = AppSyncUIState::Get(profile_);
+ if (app_sync_ui_state_)
+ app_sync_ui_state_->AddObserver(this);
+ }
+
+ model_->AddObserver(this);
+ // Right now ash::Shell isn't created for tests.
+ // TODO(mukai): Allows it to observe display change and write tests.
+ if (ash::Shell::HasInstance())
+ ash::Shell::GetInstance()->display_controller()->AddObserver(this);
+ // TODO(stevenjb): Find a better owner for shell_window_controller_?
+ shell_window_controller_.reset(new ShellWindowLauncherController(this));
+ app_tab_helper_.reset(new LauncherAppTabHelper(profile_));
+ app_icon_loader_.reset(new extensions::AppIconLoaderImpl(
+ profile_, extension_misc::EXTENSION_ICON_SMALL, this));
+
+ notification_registrar_.Add(this,
+ chrome::NOTIFICATION_EXTENSION_LOADED,
+ content::Source<Profile>(profile_));
+ notification_registrar_.Add(this,
+ chrome::NOTIFICATION_EXTENSION_UNLOADED,
+ content::Source<Profile>(profile_));
+ pref_change_registrar_.Init(profile_->GetPrefs());
+ pref_change_registrar_.Add(
+ prefs::kPinnedLauncherApps,
+ base::Bind(&ChromeLauncherControllerPerBrowser::
+ UpdateAppLaunchersFromPref,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kShelfAlignmentLocal,
+ base::Bind(&ChromeLauncherControllerPerBrowser::
+ SetShelfAlignmentFromPrefs,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kShelfAutoHideBehaviorLocal,
+ base::Bind(&ChromeLauncherControllerPerBrowser::
+ SetShelfAutoHideBehaviorFromPrefs,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kShelfPreferences,
+ base::Bind(&ChromeLauncherControllerPerBrowser::
+ SetShelfBehaviorsFromPrefs,
+ base::Unretained(this)));
+}
+
+ChromeLauncherControllerPerBrowser::~ChromeLauncherControllerPerBrowser() {
+ // Reset the shell window controller here since it has a weak pointer to
+ // this.
+ shell_window_controller_.reset();
+
+ for (std::set<ash::Launcher*>::iterator iter = launchers_.begin();
+ iter != launchers_.end();
+ ++iter)
+ (*iter)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
+
+ model_->RemoveObserver(this);
+ if (ash::Shell::HasInstance())
+ ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
+ for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ++i) {
+ i->second->OnRemoved();
+ model_->RemoveItemAt(model_->ItemIndexByID(i->first));
+ }
+
+ if (ash::Shell::HasInstance())
+ ash::Shell::GetInstance()->RemoveShellObserver(this);
+
+ if (app_sync_ui_state_)
+ app_sync_ui_state_->RemoveObserver(this);
+
+ PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this);
+}
+
+void ChromeLauncherControllerPerBrowser::Init() {
+ UpdateAppLaunchersFromPref();
+ CreateBrowserShortcutLauncherItem();
+
+ // TODO(sky): update unit test so that this test isn't necessary.
+ if (ash::Shell::HasInstance()) {
+ SetShelfAutoHideBehaviorFromPrefs();
+ SetShelfAlignmentFromPrefs();
+ PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
+ if (!prefs->FindPreference(prefs::kShelfAlignmentLocal)->HasUserSetting() ||
+ !prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)->
+ HasUserSetting()) {
+ // This causes OnIsSyncingChanged to be called when the value of
+ // PrefService::IsSyncing() changes.
+ prefs->AddObserver(this);
+ }
+ ash::Shell::GetInstance()->AddShellObserver(this);
+ }
+}
+
+ChromeLauncherControllerPerApp*
+ChromeLauncherControllerPerBrowser::GetPerAppInterface() {
+ return NULL;
+}
+
+ash::LauncherID ChromeLauncherControllerPerBrowser::CreateTabbedLauncherItem(
+ LauncherItemController* controller,
+ IncognitoState is_incognito,
+ ash::LauncherItemStatus status) {
+ ash::LauncherID id = model_->next_id();
+ DCHECK(!HasItemController(id));
+ DCHECK(controller);
+ id_to_item_controller_map_[id] = controller;
+ controller->set_launcher_id(id);
+
+ ash::LauncherItem item;
+ item.type = ash::TYPE_TABBED;
+ item.is_incognito = (is_incognito == STATE_INCOGNITO);
+ item.status = status;
+ model_->Add(item);
+ return id;
+}
+
+ash::LauncherID ChromeLauncherControllerPerBrowser::CreateAppLauncherItem(
+ LauncherItemController* controller,
+ const std::string& app_id,
+ ash::LauncherItemStatus status) {
+ DCHECK(controller);
+ int index = 0;
+ // Panels are inserted on the left so as not to push all existing panels over.
+ if (controller->GetLauncherItemType() != ash::TYPE_APP_PANEL) {
+ index = model_->item_count();
+ // For the alternate shelf layout increment index (insert after app icon).
+ if (ash::switches::UseAlternateShelfLayout())
+ ++index;
+ }
+ return InsertAppLauncherItem(controller, app_id, status, index);
+}
+
+void ChromeLauncherControllerPerBrowser::SetItemStatus(
+ ash::LauncherID id,
+ ash::LauncherItemStatus status) {
+ int index = model_->ItemIndexByID(id);
+ DCHECK_GE(index, 0);
+ ash::LauncherItem item = model_->items()[index];
+ item.status = status;
+ model_->Set(index, item);
+}
+
+void ChromeLauncherControllerPerBrowser::SetItemController(
+ ash::LauncherID id,
+ LauncherItemController* controller) {
+ IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
+ DCHECK(iter != id_to_item_controller_map_.end());
+ iter->second->OnRemoved();
+ iter->second = controller;
+ controller->set_launcher_id(id);
+}
+
+void ChromeLauncherControllerPerBrowser::CloseLauncherItem(
+ ash::LauncherID id) {
+ if (IsPinned(id)) {
+ // Create a new shortcut controller.
+ IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
+ DCHECK(iter != id_to_item_controller_map_.end());
+ SetItemStatus(id, ash::STATUS_CLOSED);
+ std::string app_id = iter->second->app_id();
+ iter->second->OnRemoved();
+ iter->second = new AppShortcutLauncherItemController(app_id, this);
+ iter->second->set_launcher_id(id);
+ } else {
+ LauncherItemClosed(id);
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::Unpin(ash::LauncherID id) {
+ DCHECK(HasItemController(id));
+
+ LauncherItemController* controller = id_to_item_controller_map_[id];
+ if (controller->type() == LauncherItemController::TYPE_APP) {
+ int index = model_->ItemIndexByID(id);
+ ash::LauncherItem item = model_->items()[index];
+ item.type = ash::TYPE_PLATFORM_APP;
+ model_->Set(index, item);
+ } else {
+ LauncherItemClosed(id);
+ }
+ if (CanPin())
+ PersistPinnedState();
+}
+
+void ChromeLauncherControllerPerBrowser::Pin(ash::LauncherID id) {
+ DCHECK(HasItemController(id));
+
+ int index = model_->ItemIndexByID(id);
+ ash::LauncherItem item = model_->items()[index];
+
+ if (item.type != ash::TYPE_PLATFORM_APP)
+ return;
+
+ item.type = ash::TYPE_APP_SHORTCUT;
+ model_->Set(index, item);
+
+ if (CanPin())
+ PersistPinnedState();
+}
+
+bool ChromeLauncherControllerPerBrowser::IsPinned(ash::LauncherID id) {
+ int index = model_->ItemIndexByID(id);
+ ash::LauncherItemType type = model_->items()[index].type;
+ return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT);
+}
+
+void ChromeLauncherControllerPerBrowser::TogglePinned(ash::LauncherID id) {
+ if (!HasItemController(id))
+ return; // May happen if item closed with menu open.
+
+ if (IsPinned(id))
+ Unpin(id);
+ else
+ Pin(id);
+}
+
+bool ChromeLauncherControllerPerBrowser::IsPinnable(ash::LauncherID id) const {
+ int index = model_->ItemIndexByID(id);
+ if (index == -1)
+ return false;
+
+ ash::LauncherItemType type = model_->items()[index].type;
+ return ((type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_PLATFORM_APP) &&
+ CanPin());
+}
+
+void ChromeLauncherControllerPerBrowser::LockV1AppWithID(
+ const std::string& app_id) {
+}
+
+void ChromeLauncherControllerPerBrowser::UnlockV1AppWithID(
+ const std::string& app_id) {
+}
+
+void ChromeLauncherControllerPerBrowser::Launch(
+ ash::LauncherID id, int event_flags) {
+ if (!HasItemController(id))
+ return; // In case invoked from menu and item closed while menu up.
+ id_to_item_controller_map_[id]->Launch(event_flags);
+}
+
+void ChromeLauncherControllerPerBrowser::Close(ash::LauncherID id) {
+ if (!HasItemController(id))
+ return; // May happen if menu closed.
+ id_to_item_controller_map_[id]->Close();
+}
+
+bool ChromeLauncherControllerPerBrowser::IsOpen(ash::LauncherID id) {
+ if (!HasItemController(id))
+ return false;
+ return id_to_item_controller_map_[id]->IsOpen();
+}
+
+bool ChromeLauncherControllerPerBrowser::IsPlatformApp(ash::LauncherID id) {
+ if (!HasItemController(id))
+ return false;
+
+ std::string app_id = GetAppIDForLauncherID(id);
+ const Extension* extension = GetExtensionForAppID(app_id);
+ DCHECK(extension);
+ return extension->is_platform_app();
+}
+
+void ChromeLauncherControllerPerBrowser::LaunchApp(const std::string& app_id,
+ int event_flags) {
+ // |extension| could be NULL when it is being unloaded for updating.
+ const Extension* extension = GetExtensionForAppID(app_id);
+ if (!extension)
+ return;
+
+ const ExtensionService* service =
+ extensions::ExtensionSystem::Get(profile_)->extension_service();
+ if (!service->IsExtensionEnabledForLauncher(app_id)) {
+ // Do nothing if there is already a running enable flow.
+ if (extension_enable_flow_)
+ return;
+
+ extension_enable_flow_.reset(
+ new ExtensionEnableFlow(profile_, app_id, this));
+ extension_enable_flow_->StartForNativeWindow(NULL);
+ return;
+ }
+
+ chrome::OpenApplication(chrome::AppLaunchParams(GetProfileForNewWindows(),
+ extension,
+ event_flags));
+}
+
+void ChromeLauncherControllerPerBrowser::ActivateApp(const std::string& app_id,
+ int event_flags) {
+ if (app_id == extension_misc::kChromeAppId) {
+ BrowserShortcutClicked(event_flags);
+ return;
+ }
+
+ // If there is an existing non-shortcut controller for this app, open it.
+ ash::LauncherID id = GetLauncherIDForAppID(app_id);
+ URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
+ refocus_pattern.SetMatchAllURLs(true);
+
+ if (id > 0) {
+ LauncherItemController* controller = id_to_item_controller_map_[id];
+ if (controller->type() != LauncherItemController::TYPE_SHORTCUT) {
+ controller->Activate();
+ return;
+ }
+
+ AppShortcutLauncherItemController* app_controller =
+ static_cast<AppShortcutLauncherItemController*>(controller);
+ const GURL refocus_url = app_controller->refocus_url();
+
+ if (!refocus_url.is_empty())
+ refocus_pattern.Parse(refocus_url.spec());
+ }
+
+ // Check if there are any open tabs for this app.
+ AppIDToWebContentsListMap::iterator app_i =
+ app_id_to_web_contents_list_.find(app_id);
+ if (app_i != app_id_to_web_contents_list_.end()) {
+ for (WebContentsList::iterator tab_i = app_i->second.begin();
+ tab_i != app_i->second.end();
+ ++tab_i) {
+ WebContents* tab = *tab_i;
+ const GURL tab_url = tab->GetURL();
+ if (refocus_pattern.MatchesURL(tab_url)) {
+ Browser* browser = chrome::FindBrowserWithWebContents(tab);
+ TabStripModel* tab_strip = browser->tab_strip_model();
+ int index = tab_strip->GetIndexOfWebContents(tab);
+ DCHECK_NE(TabStripModel::kNoTab, index);
+ tab_strip->ActivateTabAt(index, false);
+ browser->window()->Show();
+ ash::wm::ActivateWindow(browser->window()->GetNativeWindow());
+ return;
+ }
+ }
+ }
+
+ LaunchApp(app_id, event_flags);
+}
+
+extensions::ExtensionPrefs::LaunchType
+ ChromeLauncherControllerPerBrowser::GetLaunchType(ash::LauncherID id) {
+ DCHECK(HasItemController(id));
+
+ const Extension* extension = GetExtensionForAppID(
+ id_to_item_controller_map_[id]->app_id());
+ return profile_->GetExtensionService()->extension_prefs()->GetLaunchType(
+ extension,
+ extensions::ExtensionPrefs::LAUNCH_DEFAULT);
+}
+
+std::string ChromeLauncherControllerPerBrowser::GetAppID(
+ content::WebContents* tab) {
+ return app_tab_helper_->GetAppID(tab);
+}
+
+ash::LauncherID ChromeLauncherControllerPerBrowser::GetLauncherIDForAppID(
+ const std::string& app_id) {
+ for (IDToItemControllerMap::const_iterator i =
+ id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ++i) {
+ if (i->second->type() == LauncherItemController::TYPE_APP_PANEL)
+ continue; // Don't include panels
+ if (i->second->app_id() == app_id)
+ return i->first;
+ }
+ return 0;
+}
+
+std::string ChromeLauncherControllerPerBrowser::GetAppIDForLauncherID(
+ ash::LauncherID id) {
+ DCHECK(HasItemController(id));
+ return id_to_item_controller_map_[id]->app_id();
+}
+
+void ChromeLauncherControllerPerBrowser::SetAppImage(
+ const std::string& id,
+ const gfx::ImageSkia& image) {
+ // TODO: need to get this working for shortcuts.
+
+ for (IDToItemControllerMap::const_iterator i =
+ id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ++i) {
+ if (i->second->app_id() != id)
+ continue;
+
+ int index = model_->ItemIndexByID(i->first);
+ ash::LauncherItem item = model_->items()[index];
+ item.image = image;
+ model_->Set(index, item);
+ // It's possible we're waiting on more than one item, so don't break.
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::OnAutoHideBehaviorChanged(
+ aura::RootWindow* root_window,
+ ash::ShelfAutoHideBehavior new_behavior) {
+ SetShelfAutoHideBehaviorPrefs(new_behavior, root_window);
+}
+
+void ChromeLauncherControllerPerBrowser::SetLauncherItemImage(
+ ash::LauncherID launcher_id,
+ const gfx::ImageSkia& image) {
+ int index = model_->ItemIndexByID(launcher_id);
+ if (index == -1)
+ return;
+ ash::LauncherItem item = model_->items()[index];
+ item.image = image;
+ model_->Set(index, item);
+}
+
+bool ChromeLauncherControllerPerBrowser::IsAppPinned(
+ const std::string& app_id) {
+ // Check the LauncherModel since there is no controller for the browser item.
+ if (app_id == extension_misc::kChromeAppId) {
+ for (size_t index = 0; index < model_->items().size(); index++) {
+ if (model_->items()[index].type == ash::TYPE_BROWSER_SHORTCUT)
+ return true;
+ }
+ return false;
+ }
+ for (IDToItemControllerMap::const_iterator i =
+ id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ++i) {
+ if (IsPinned(i->first) && i->second->app_id() == app_id)
+ return true;
+ }
+ return false;
+}
+
+void ChromeLauncherControllerPerBrowser::PinAppWithID(
+ const std::string& app_id) {
+ if (CanPin())
+ DoPinAppWithID(app_id);
+ else
+ NOTREACHED();
+}
+
+void ChromeLauncherControllerPerBrowser::SetLaunchType(
+ ash::LauncherID id,
+ extensions::ExtensionPrefs::LaunchType launch_type) {
+ if (!HasItemController(id))
+ return;
+
+ return profile_->GetExtensionService()->extension_prefs()->SetLaunchType(
+ id_to_item_controller_map_[id]->app_id(), launch_type);
+}
+
+void ChromeLauncherControllerPerBrowser::UnpinAppsWithID(
+ const std::string& app_id) {
+ if (CanPin())
+ DoUnpinAppsWithID(app_id);
+ else
+ NOTREACHED();
+}
+
+bool ChromeLauncherControllerPerBrowser::IsLoggedInAsGuest() {
+ return ProfileManager::GetDefaultProfileOrOffTheRecord()->IsOffTheRecord();
+}
+
+void ChromeLauncherControllerPerBrowser::CreateNewWindow() {
+ chrome::NewEmptyWindow(
+ GetProfileForNewWindows(), chrome::HOST_DESKTOP_TYPE_ASH);
+}
+
+void ChromeLauncherControllerPerBrowser::CreateNewIncognitoWindow() {
+ chrome::NewEmptyWindow(GetProfileForNewWindows()->GetOffTheRecordProfile(),
+ chrome::HOST_DESKTOP_TYPE_ASH);
+}
+
+bool ChromeLauncherControllerPerBrowser::CanPin() const {
+ const PrefService::Preference* pref =
+ profile_->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps);
+ return pref && pref->IsUserModifiable();
+}
+
+ash::ShelfAutoHideBehavior
+ ChromeLauncherControllerPerBrowser::GetShelfAutoHideBehavior(
+ aura::RootWindow* root_window) const {
+ // Don't show the shelf in the app mode.
+ if (chrome::IsRunningInAppMode())
+ return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN;
+
+ // See comment in |kShelfAlignment| as to why we consider two prefs.
+ const std::string behavior_value(
+ GetPrefForRootWindow(profile_->GetPrefs(),
+ root_window,
+ prefs::kShelfAutoHideBehaviorLocal,
+ prefs::kShelfAutoHideBehavior));
+
+ // Note: To maintain sync compatibility with old images of chrome/chromeos
+ // the set of values that may be encountered includes the now-extinct
+ // "Default" as well as "Never" and "Always", "Default" should now
+ // be treated as "Never" (http://crbug.com/146773).
+ if (behavior_value == ash::kShelfAutoHideBehaviorAlways)
+ return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
+ return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER;
+}
+
+bool ChromeLauncherControllerPerBrowser::CanUserModifyShelfAutoHideBehavior(
+ aura::RootWindow* root_window) const {
+ return profile_->GetPrefs()->
+ FindPreference(prefs::kShelfAutoHideBehaviorLocal)->IsUserModifiable();
+}
+
+void ChromeLauncherControllerPerBrowser::ToggleShelfAutoHideBehavior(
+ aura::RootWindow* root_window) {
+ ash::ShelfAutoHideBehavior behavior = GetShelfAutoHideBehavior(root_window) ==
+ ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS ?
+ ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER :
+ ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
+ SetShelfAutoHideBehaviorPrefs(behavior, root_window);
+ return;
+}
+
+void ChromeLauncherControllerPerBrowser::RemoveTabFromRunningApp(
+ WebContents* tab,
+ const std::string& app_id) {
+ web_contents_to_app_id_.erase(tab);
+ AppIDToWebContentsListMap::iterator i_app_id =
+ app_id_to_web_contents_list_.find(app_id);
+ if (i_app_id != app_id_to_web_contents_list_.end()) {
+ WebContentsList* tab_list = &i_app_id->second;
+ tab_list->remove(tab);
+ if (tab_list->empty()) {
+ app_id_to_web_contents_list_.erase(i_app_id);
+ i_app_id = app_id_to_web_contents_list_.end();
+ ash::LauncherID id = GetLauncherIDForAppID(app_id);
+ if (id > 0)
+ SetItemStatus(id, ash::STATUS_CLOSED);
+ }
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::UpdateAppState(
+ content::WebContents* contents,
+ AppState app_state) {
+ std::string app_id = GetAppID(contents);
+
+ // Check the old |app_id| for a tab. If the contents has changed we need to
+ // remove it from the previous app.
+ if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) {
+ std::string last_app_id = web_contents_to_app_id_[contents];
+ if (last_app_id != app_id)
+ RemoveTabFromRunningApp(contents, last_app_id);
+ }
+
+ if (app_id.empty())
+ return;
+
+ web_contents_to_app_id_[contents] = app_id;
+
+ if (app_state == APP_STATE_REMOVED) {
+ // The tab has gone away.
+ RemoveTabFromRunningApp(contents, app_id);
+ } else {
+ WebContentsList& tab_list(app_id_to_web_contents_list_[app_id]);
+
+ if (app_state == APP_STATE_INACTIVE) {
+ WebContentsList::const_iterator i_tab =
+ std::find(tab_list.begin(), tab_list.end(), contents);
+ if (i_tab == tab_list.end())
+ tab_list.push_back(contents);
+ if (i_tab != tab_list.begin()) {
+ // Going inactive, but wasn't the front tab, indicating that a new
+ // tab has already become active.
+ return;
+ }
+ } else {
+ tab_list.remove(contents);
+ tab_list.push_front(contents);
+ }
+ ash::LauncherID id = GetLauncherIDForAppID(app_id);
+ if (id > 0) {
+ // If the window is active, mark the app as active.
+ SetItemStatus(id, app_state == APP_STATE_WINDOW_ACTIVE ?
+ ash::STATUS_ACTIVE : ash::STATUS_RUNNING);
+ }
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::SetRefocusURLPatternForTest(
+ ash::LauncherID id,
+ const GURL& url) {
+ DCHECK(HasItemController(id));
+ LauncherItemController* controller = id_to_item_controller_map_[id];
+
+ int index = model_->ItemIndexByID(id);
+ if (index == -1) {
+ NOTREACHED() << "Invalid launcher id";
+ return;
+ }
+
+ ash::LauncherItemType type = model_->items()[index].type;
+ if (type == ash::TYPE_APP_SHORTCUT) {
+ AppShortcutLauncherItemController* app_controller =
+ static_cast<AppShortcutLauncherItemController*>(controller);
+ app_controller->set_refocus_url(url);
+ } else {
+ NOTREACHED() << "Invalid launcher type";
+ }
+}
+
+const Extension* ChromeLauncherControllerPerBrowser::GetExtensionForAppID(
+ const std::string& app_id) const {
+ return profile_->GetExtensionService()->GetInstalledExtension(app_id);
+}
+
+void ChromeLauncherControllerPerBrowser::ActivateWindowOrMinimizeIfActive(
+ ui::BaseWindow* window,
+ bool allow_minimize) {
+ window->Show();
+ window->Activate();
+}
+
+void ChromeLauncherControllerPerBrowser::BrowserShortcutClicked(
+ int event_flags) {
+#if defined(OS_CHROMEOS)
+ chromeos::default_pinned_apps_field_trial::RecordShelfClick(
+ chromeos::default_pinned_apps_field_trial::CHROME);
+#endif
+ if (event_flags & ui::EF_CONTROL_DOWN) {
+ CreateNewWindow();
+ return;
+ }
+
+ Browser* last_browser = chrome::FindTabbedBrowser(
+ GetProfileForNewWindows(), true, chrome::HOST_DESKTOP_TYPE_ASH);
+
+ if (!last_browser) {
+ CreateNewWindow();
+ return;
+ }
+
+ aura::Window* window = last_browser->window()->GetNativeWindow();
+ window->Show();
+ ash::wm::ActivateWindow(window);
+}
+
+void ChromeLauncherControllerPerBrowser::ItemSelected(
+ const ash::LauncherItem& item,
+ const ui::Event& event) {
+ if (item.type == ash::TYPE_BROWSER_SHORTCUT) {
+ BrowserShortcutClicked(event.flags());
+ return;
+ }
+
+ DCHECK(HasItemController(item.id));
+ LauncherItemController* item_controller = id_to_item_controller_map_[item.id];
+#if defined(OS_CHROMEOS)
+ if (!item_controller->app_id().empty()) {
+ chromeos::default_pinned_apps_field_trial::RecordShelfAppClick(
+ item_controller->app_id());
+ }
+#endif
+ item_controller->Clicked(event);
+}
+
+string16 ChromeLauncherControllerPerBrowser::GetTitle(
+ const ash::LauncherItem& item) {
+ if (item.type == ash::TYPE_BROWSER_SHORTCUT)
+ return l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
+
+ DCHECK(HasItemController(item.id));
+ return id_to_item_controller_map_[item.id]->GetTitle();
+}
+
+ui::MenuModel* ChromeLauncherControllerPerBrowser::CreateContextMenu(
+ const ash::LauncherItem& item,
+ aura::RootWindow* root_window) {
+ return new LauncherContextMenu(this, &item, root_window);
+}
+
+ash::LauncherMenuModel*
+ChromeLauncherControllerPerBrowser::CreateApplicationMenu(
+ const ash::LauncherItem& item,
+ int event_flags) {
+ // Not used by this launcher type.
+ return NULL;
+}
+
+ash::LauncherID ChromeLauncherControllerPerBrowser::GetIDByWindow(
+ aura::Window* window) {
+ for (IDToItemControllerMap::const_iterator i =
+ id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ++i) {
+ if (i->second->IsCurrentlyShownInWindow(window))
+ return i->first;
+ }
+ return 0;
+}
+
+bool ChromeLauncherControllerPerBrowser::IsDraggable(
+ const ash::LauncherItem& item) {
+ return item.type == ash::TYPE_APP_SHORTCUT ? CanPin() : true;
+}
+
+bool ChromeLauncherControllerPerBrowser::ShouldShowTooltip(
+ const ash::LauncherItem& item) {
+ if (item.type == ash::TYPE_APP_PANEL &&
+ id_to_item_controller_map_[item.id]->IsVisible())
+ return false;
+ return true;
+}
+
+void ChromeLauncherControllerPerBrowser::OnLauncherCreated(
+ ash::Launcher* launcher) {
+ launchers_.insert(launcher);
+ launcher->shelf_widget()->shelf_layout_manager()->AddObserver(this);
+}
+
+void ChromeLauncherControllerPerBrowser::OnLauncherDestroyed(
+ ash::Launcher* launcher) {
+ launchers_.erase(launcher);
+ // RemoveObserver is not called here, since by the time this method is called
+ // Launcher is already in its destructor.
+}
+
+void ChromeLauncherControllerPerBrowser::LauncherItemAdded(int index) {
+}
+
+void ChromeLauncherControllerPerBrowser::LauncherItemRemoved(
+ int index,
+ ash::LauncherID id) {
+}
+
+void ChromeLauncherControllerPerBrowser::LauncherItemMoved(
+ int start_index,
+ int target_index) {
+ ash::LauncherID id = model_->items()[target_index].id;
+ if (HasItemController(id) && IsPinned(id))
+ PersistPinnedState();
+ else if (!HasItemController(id) &&
+ model_->items()[target_index].type == ash::TYPE_BROWSER_SHORTCUT)
+ PersistPinnedState();
+}
+
+void ChromeLauncherControllerPerBrowser::LauncherItemChanged(
+ int index,
+ const ash::LauncherItem& old_item) {
+ ash::LauncherID id = model_->items()[index].id;
+ id_to_item_controller_map_[id]->LauncherItemChanged(index, old_item);
+}
+
+void ChromeLauncherControllerPerBrowser::LauncherStatusChanged() {
+}
+
+void ChromeLauncherControllerPerBrowser::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case chrome::NOTIFICATION_EXTENSION_LOADED: {
+ const Extension* extension =
+ content::Details<const Extension>(details).ptr();
+ if (IsAppPinned(extension->id())) {
+ // Clear and re-fetch to ensure icon is up-to-date.
+ app_icon_loader_->ClearImage(extension->id());
+ app_icon_loader_->FetchImage(extension->id());
+ }
+
+ UpdateAppLaunchersFromPref();
+ break;
+ }
+ case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
+ const content::Details<extensions::UnloadedExtensionInfo>& unload_info(
+ details);
+ const Extension* extension = unload_info->extension;
+ if (IsAppPinned(extension->id())) {
+ if (unload_info->reason == extension_misc::UNLOAD_REASON_UNINSTALL) {
+ DoUnpinAppsWithID(extension->id());
+ app_icon_loader_->ClearImage(extension->id());
+ } else {
+ app_icon_loader_->UpdateImage(extension->id());
+ }
+ }
+ break;
+ }
+ default:
+ NOTREACHED() << "Unexpected notification type=" << type;
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::OnShelfAlignmentChanged(
+ aura::RootWindow* root_window) {
+ const char* pref_value = NULL;
+ switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window)) {
+ case ash::SHELF_ALIGNMENT_BOTTOM:
+ pref_value = ash::kShelfAlignmentBottom;
+ break;
+ case ash::SHELF_ALIGNMENT_LEFT:
+ pref_value = ash::kShelfAlignmentLeft;
+ break;
+ case ash::SHELF_ALIGNMENT_RIGHT:
+ pref_value = ash::kShelfAlignmentRight;
+ break;
+ case ash::SHELF_ALIGNMENT_TOP:
+ pref_value = ash::kShelfAlignmentTop;
+ break;
+ }
+
+ UpdatePerDisplayPref(
+ profile_->GetPrefs(), root_window, prefs::kShelfAlignment, pref_value);
+
+ if (root_window == ash::Shell::GetPrimaryRootWindow()) {
+ // See comment in |kShelfAlignment| about why we have two prefs here.
+ profile_->GetPrefs()->SetString(prefs::kShelfAlignmentLocal, pref_value);
+ profile_->GetPrefs()->SetString(prefs::kShelfAlignment, pref_value);
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::OnDisplayConfigurationChanging() {
+}
+
+void ChromeLauncherControllerPerBrowser::OnDisplayConfigurationChanged() {
+ SetShelfBehaviorsFromPrefs();
+}
+
+void ChromeLauncherControllerPerBrowser::OnIsSyncingChanged() {
+ PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
+ MaybePropagatePrefToLocal(prefs,
+ prefs::kShelfAlignmentLocal,
+ prefs::kShelfAlignment);
+ MaybePropagatePrefToLocal(prefs,
+ prefs::kShelfAutoHideBehaviorLocal,
+ prefs::kShelfAutoHideBehavior);
+}
+
+void ChromeLauncherControllerPerBrowser::OnAppSyncUIStatusChanged() {
+ if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING)
+ model_->SetStatus(ash::LauncherModel::STATUS_LOADING);
+ else
+ model_->SetStatus(ash::LauncherModel::STATUS_NORMAL);
+}
+
+void ChromeLauncherControllerPerBrowser::ExtensionEnableFlowFinished() {
+ LaunchApp(extension_enable_flow_->extension_id(), ui::EF_NONE);
+ extension_enable_flow_.reset();
+}
+
+void ChromeLauncherControllerPerBrowser::ExtensionEnableFlowAborted(
+ bool user_initiated) {
+ extension_enable_flow_.reset();
+}
+
+void ChromeLauncherControllerPerBrowser::PersistPinnedState() {
+ if (ignore_persist_pinned_state_change_)
+ return;
+ // It is a coding error to call PersistPinnedState() if the pinned apps are
+ // not user-editable. The code should check earlier and not perform any
+ // modification actions that trigger persisting the state.
+ if (!CanPin()) {
+ NOTREACHED() << "Can't pin but pinned state being updated";
+ return;
+ }
+
+ // Mutating kPinnedLauncherApps is going to notify us and trigger us to
+ // process the change. We don't want that to happen so remove ourselves as a
+ // listener.
+ pref_change_registrar_.Remove(prefs::kPinnedLauncherApps);
+ {
+ ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
+ updater->Clear();
+ for (size_t i = 0; i < model_->items().size(); ++i) {
+ if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) {
+ ash::LauncherID id = model_->items()[i].id;
+ if (HasItemController(id) && IsPinned(id)) {
+ base::DictionaryValue* app_value = ash::CreateAppDict(
+ id_to_item_controller_map_[id]->app_id());
+ if (app_value)
+ updater->Append(app_value);
+ }
+ } else if (model_->items()[i].type == ash::TYPE_BROWSER_SHORTCUT) {
+ SetChromeIconIndexToPref(i);
+ }
+ }
+ }
+ pref_change_registrar_.Add(
+ prefs::kPinnedLauncherApps,
+ base::Bind(&ChromeLauncherControllerPerBrowser::
+ UpdateAppLaunchersFromPref,
+ base::Unretained(this)));
+}
+
+ash::LauncherModel* ChromeLauncherControllerPerBrowser::model() {
+ return model_;
+}
+
+Profile* ChromeLauncherControllerPerBrowser::profile() {
+ return profile_;
+}
+
+Profile* ChromeLauncherControllerPerBrowser::GetProfileForNewWindows() {
+ return ProfileManager::GetDefaultProfileOrOffTheRecord();
+}
+
+void ChromeLauncherControllerPerBrowser::LauncherItemClosed(
+ ash::LauncherID id) {
+ IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
+ DCHECK(iter != id_to_item_controller_map_.end());
+ app_icon_loader_->ClearImage(iter->second->app_id());
+ iter->second->OnRemoved();
+ id_to_item_controller_map_.erase(iter);
+ model_->RemoveItemAt(model_->ItemIndexByID(id));
+}
+
+void ChromeLauncherControllerPerBrowser::DoPinAppWithID(
+ const std::string& app_id) {
+ // If there is an item, do nothing and return.
+ if (IsAppPinned(app_id))
+ return;
+
+ ash::LauncherID launcher_id = GetLauncherIDForAppID(app_id);
+ if (launcher_id) {
+ // App item exists, pin it
+ Pin(launcher_id);
+ } else {
+ // Otherwise, create a shortcut item for it.
+ CreateAppShortcutLauncherItem(app_id, model_->item_count());
+ if (CanPin())
+ PersistPinnedState();
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::DoUnpinAppsWithID(
+ const std::string& app_id) {
+ for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
+ i != id_to_item_controller_map_.end(); ) {
+ IDToItemControllerMap::iterator current(i);
+ ++i;
+ if (current->second->app_id() == app_id && IsPinned(current->first))
+ Unpin(current->first);
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::UpdateAppLaunchersFromPref() {
+ // Construct a vector representation of to-be-pinned apps from the pref.
+ std::vector<std::string> pinned_apps;
+ int chrome_icon_index = GetChromeIconIndexFromPref();
+ const base::ListValue* pinned_apps_pref =
+ profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
+ for (base::ListValue::const_iterator it(pinned_apps_pref->begin());
+ it != pinned_apps_pref->end(); ++it) {
+ // To preserve the Chrome icon position, we insert a dummy slot for it - if
+ // the model has a Chrome item. While initializing we can come here with no
+ // item in which case the count would be 1 or below.
+ if (it - pinned_apps_pref->begin() == chrome_icon_index &&
+ model_->item_count() > 1) {
+ pinned_apps.push_back(extension_misc::kChromeAppId);
+ }
+ DictionaryValue* app = NULL;
+ std::string app_id;
+ if ((*it)->GetAsDictionary(&app) &&
+ app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id) &&
+ std::find(pinned_apps.begin(), pinned_apps.end(), app_id) ==
+ pinned_apps.end() &&
+ app_tab_helper_->IsValidID(app_id)) {
+ pinned_apps.push_back(app_id);
+ }
+ }
+
+ // Walk the model and |pinned_apps| from the pref lockstep, adding and
+ // removing items as necessary. NB: This code uses plain old indexing instead
+ // of iterators because of model mutations as part of the loop.
+ std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin());
+ int index = 0;
+ int max_index = model_->item_count();
+ if (ash::switches::UseAlternateShelfLayout()) {
+ ++index;
+ ++max_index;
+ }
+ for (; index < model_->item_count() && pref_app_id != pinned_apps.end();
+ ++index) {
+ // If the next app launcher according to the pref is present in the model,
+ // delete all app launcher entries in between.
+ if (*pref_app_id == extension_misc::kChromeAppId ||
+ IsAppPinned(*pref_app_id)) {
+ for (; index < model_->item_count(); ++index) {
+ const ash::LauncherItem& item(model_->items()[index]);
+ if (item.type != ash::TYPE_APP_SHORTCUT &&
+ item.type != ash::TYPE_BROWSER_SHORTCUT)
+ continue;
+
+ IDToItemControllerMap::const_iterator entry =
+ id_to_item_controller_map_.find(item.id);
+ if ((extension_misc::kChromeAppId == *pref_app_id &&
+ item.type == ash::TYPE_BROWSER_SHORTCUT) ||
+ (entry != id_to_item_controller_map_.end() &&
+ entry->second->app_id() == *pref_app_id)) {
+ ++pref_app_id;
+ break;
+ } else {
+ if (item.type == ash::TYPE_BROWSER_SHORTCUT) {
+ // We cannot delete the browser shortcut. As such we move it up by
+ // one. To avoid any side effects from our pinned state observer, we
+ // do not call the model directly.
+ MoveItemWithoutPinnedStateChangeNotification(index, index + 1);
+ } else {
+ LauncherItemClosed(item.id);
+ --max_index;
+ }
+ --index;
+ }
+ }
+ // If the item wasn't found, that means id_to_item_controller_map_
+ // is out of sync.
+ DCHECK(index < model_->item_count());
+ } else {
+ // This app wasn't pinned before, insert a new entry.
+ ash::LauncherID id = CreateAppShortcutLauncherItem(*pref_app_id, index);
+ index = model_->ItemIndexByID(id);
+ ++pref_app_id;
+ }
+ }
+
+ // Remove any trailing existing items.
+ while (index < model_->item_count()) {
+ const ash::LauncherItem& item(model_->items()[index]);
+ if (item.type == ash::TYPE_APP_SHORTCUT)
+ LauncherItemClosed(item.id);
+ else
+ ++index;
+ }
+
+ // Append unprocessed items from the pref to the end of the model.
+ for (; pref_app_id != pinned_apps.end(); ++pref_app_id) {
+ // Ignore the chrome icon.
+ if (*pref_app_id != extension_misc::kChromeAppId)
+ DoPinAppWithID(*pref_app_id);
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::SetShelfAutoHideBehaviorPrefs(
+ ash::ShelfAutoHideBehavior behavior,
+ aura::RootWindow* root_window) {
+ const char* value = NULL;
+ switch (behavior) {
+ case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
+ value = ash::kShelfAutoHideBehaviorAlways;
+ break;
+ case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
+ value = ash::kShelfAutoHideBehaviorNever;
+ break;
+ case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
+ // This one should not be a valid preference option for now. We only want
+ // to completely hide it when we run app mode.
+ NOTREACHED();
+ return;
+ }
+
+ UpdatePerDisplayPref(
+ profile_->GetPrefs(), root_window, prefs::kShelfAutoHideBehavior, value);
+
+ if (root_window == ash::Shell::GetPrimaryRootWindow()) {
+ // See comment in |kShelfAlignment| about why we have two prefs here.
+ profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal, value);
+ profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior, value);
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::SetShelfAutoHideBehaviorFromPrefs() {
+ ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
+
+ for (ash::Shell::RootWindowList::const_iterator iter = root_windows.begin();
+ iter != root_windows.end(); ++iter) {
+ ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
+ GetShelfAutoHideBehavior(*iter), *iter);
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::SetShelfAlignmentFromPrefs() {
+ if (!ash::ShelfWidget::ShelfAlignmentAllowed())
+ return;
+
+ ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
+
+ for (ash::Shell::RootWindowList::const_iterator iter = root_windows.begin();
+ iter != root_windows.end(); ++iter) {
+ // See comment in |kShelfAlignment| as to why we consider two prefs.
+ const std::string alignment_value(
+ GetPrefForRootWindow(profile_->GetPrefs(),
+ *iter,
+ prefs::kShelfAlignmentLocal,
+ prefs::kShelfAlignment));
+ ash::ShelfAlignment alignment = ash::SHELF_ALIGNMENT_BOTTOM;
+ if (alignment_value == ash::kShelfAlignmentLeft)
+ alignment = ash::SHELF_ALIGNMENT_LEFT;
+ else if (alignment_value == ash::kShelfAlignmentRight)
+ alignment = ash::SHELF_ALIGNMENT_RIGHT;
+ else if (alignment_value == ash::kShelfAlignmentTop)
+ alignment = ash::SHELF_ALIGNMENT_TOP;
+ ash::Shell::GetInstance()->SetShelfAlignment(alignment, *iter);
+ }
+}
+
+void ChromeLauncherControllerPerBrowser::SetShelfBehaviorsFromPrefs() {
+ SetShelfAutoHideBehaviorFromPrefs();
+ SetShelfAlignmentFromPrefs();
+}
+
+WebContents* ChromeLauncherControllerPerBrowser::GetLastActiveWebContents(
+ const std::string& app_id) {
+ AppIDToWebContentsListMap::const_iterator i =
+ app_id_to_web_contents_list_.find(app_id);
+ if (i == app_id_to_web_contents_list_.end())
+ return NULL;
+ DCHECK_GT(i->second.size(), 0u);
+ return *i->second.begin();
+}
+
+ash::LauncherID ChromeLauncherControllerPerBrowser::InsertAppLauncherItem(
+ LauncherItemController* controller,
+ const std::string& app_id,
+ ash::LauncherItemStatus status,
+ int index) {
+ ash::LauncherID id = model_->next_id();
+ DCHECK(!HasItemController(id));
+ DCHECK(controller);
+ id_to_item_controller_map_[id] = controller;
+ controller->set_launcher_id(id);
+
+ ash::LauncherItem item;
+ item.type = controller->GetLauncherItemType();
+ item.is_incognito = false;
+ item.image = extensions::IconsInfo::GetDefaultAppIcon();
+
+ WebContents* active_tab = GetLastActiveWebContents(app_id);
+ if (active_tab) {
+ Browser* browser = chrome::FindBrowserWithWebContents(active_tab);
+ DCHECK(browser);
+ if (browser->window()->IsActive())
+ status = ash::STATUS_ACTIVE;
+ else
+ status = ash::STATUS_RUNNING;
+ }
+ item.status = status;
+
+ model_->AddAt(index, item);
+
+ app_icon_loader_->FetchImage(app_id);
+
+ return id;
+}
+
+bool ChromeLauncherControllerPerBrowser::HasItemController(
+ ash::LauncherID id) const {
+ return id_to_item_controller_map_.find(id) !=
+ id_to_item_controller_map_.end();
+}
+
+ash::LauncherID
+ChromeLauncherControllerPerBrowser::CreateBrowserShortcutLauncherItem() {
+ ash::LauncherItem browser_shortcut;
+ browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT;
+ browser_shortcut.is_incognito = false;
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
+ ash::LauncherID id = model_->next_id();
+ size_t index = GetChromeIconIndexFromPref();
+ model_->AddAt(index, browser_shortcut);
+ return id;
+}
+
+void ChromeLauncherControllerPerBrowser::SetChromeIconIndexToPref(int index) {
+ profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index);
+}
+
+int ChromeLauncherControllerPerBrowser::GetChromeIconIndexFromPref() const {
+ size_t index = profile_->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex);
+ const base::ListValue* pinned_apps_pref =
+ profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
+ if (ash::switches::UseAlternateShelfLayout())
+ return std::max(static_cast<size_t>(1),
+ std::min(pinned_apps_pref->GetSize(), index));
+ return std::max(static_cast<size_t>(0),
+ std::min(pinned_apps_pref->GetSize(), index));
+}
+
+ash::LauncherID
+ChromeLauncherControllerPerBrowser::CreateAppShortcutLauncherItem(
+ const std::string& app_id,
+ int index) {
+ AppShortcutLauncherItemController* controller =
+ new AppShortcutLauncherItemController(app_id, this);
+ ash::LauncherID launcher_id = InsertAppLauncherItem(
+ controller, app_id, ash::STATUS_CLOSED, index);
+ return launcher_id;
+}
+
+void ChromeLauncherControllerPerBrowser::SetAppTabHelperForTest(
+ AppTabHelper* helper) {
+ app_tab_helper_.reset(helper);
+}
+
+void ChromeLauncherControllerPerBrowser::SetAppIconLoaderForTest(
+ extensions::AppIconLoader* loader) {
+ app_icon_loader_.reset(loader);
+}
+
+const std::string&
+ChromeLauncherControllerPerBrowser::GetAppIdFromLauncherIdForTest(
+ ash::LauncherID id) {
+ return id_to_item_controller_map_[id]->app_id();
+}
+
+void ChromeLauncherControllerPerBrowser::
+ MoveItemWithoutPinnedStateChangeNotification(int source_index,
+ int target_index) {
+ base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true);
+ model_->Move(source_index, target_index);
+}
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser.h
new file mode 100644
index 0000000..0204501
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser.h
@@ -0,0 +1,430 @@
+// Copyright (c) 2012 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_UI_ASH_LAUNCHER_CHROME_LAUNCHER_CONTROLLER_PER_BROWSER_H_
+#define CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_LAUNCHER_CONTROLLER_PER_BROWSER_H_
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "ash/display/display_controller.h"
+#include "ash/launcher/launcher_delegate.h"
+#include "ash/launcher/launcher_model_observer.h"
+#include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_layout_manager_observer.h"
+#include "ash/shelf/shelf_types.h"
+#include "ash/shell_observer.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/prefs/pref_change_registrar.h"
+#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/browser/prefs/pref_service_syncable_observer.h"
+#include "chrome/browser/ui/ash/app_sync_ui_state_observer.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "ui/aura/window_observer.h"
+
+class AppSyncUIState;
+class Browser;
+class BrowserLauncherItemControllerTest;
+class ExtensionEnableFlow;
+class LauncherItemController;
+class Profile;
+class ShellWindowLauncherController;
+
+namespace ash {
+class LauncherModel;
+}
+
+namespace aura {
+class Window;
+}
+
+namespace content {
+class WebContents;
+}
+
+namespace ui {
+class BaseWindow;
+}
+
+// ChromeLauncherControllerPerBrowser manages the launcher items needed for
+// content windows. Launcher items have a type, an optional app id, and a
+// controller. This incarnation manages the items on a per browser base using
+// browser proxies and application icons.
+// * Tabbed browsers and browser app windows have BrowserLauncherItemController,
+// owned by the BrowserView instance.
+// * App shell windows have ShellWindowLauncherItemController, owned by
+// ShellWindowLauncherController.
+// * Shortcuts have no LauncherItemController.
+class ChromeLauncherControllerPerBrowser
+ : public ash::LauncherModelObserver,
+ public ash::ShellObserver,
+ public ash::DisplayController::Observer,
+ public ChromeLauncherController,
+ public content::NotificationObserver,
+ public PrefServiceSyncableObserver,
+ public AppSyncUIStateObserver,
+ public ExtensionEnableFlowDelegate,
+ public ash::ShelfLayoutManagerObserver {
+ public:
+ ChromeLauncherControllerPerBrowser(Profile* profile,
+ ash::LauncherModel* model);
+ virtual ~ChromeLauncherControllerPerBrowser();
+
+ // ChromeLauncherController overrides:
+
+ // Initializes this ChromeLauncherControllerPerBrowser.
+ virtual void Init() OVERRIDE;
+
+ // Returns the new per application interface of the given launcher. If it is
+ // a per browser (old) controller, it will return NULL;
+ // TODO(skuhne): Remove when we rip out the old launcher.
+ virtual ChromeLauncherControllerPerApp* GetPerAppInterface() OVERRIDE;
+
+ // Creates a new tabbed item on the launcher for |controller|.
+ virtual ash::LauncherID CreateTabbedLauncherItem(
+ LauncherItemController* controller,
+ IncognitoState is_incognito,
+ ash::LauncherItemStatus status) OVERRIDE;
+
+ // Creates a new app item on the launcher for |controller|.
+ virtual ash::LauncherID CreateAppLauncherItem(
+ LauncherItemController* controller,
+ const std::string& app_id,
+ ash::LauncherItemStatus status) OVERRIDE;
+
+ // Updates the running status of an item.
+ virtual void SetItemStatus(ash::LauncherID id,
+ ash::LauncherItemStatus status) OVERRIDE;
+
+ // Updates the controller associated with id (which should be a shortcut).
+ // |controller| remains owned by caller.
+ virtual void SetItemController(ash::LauncherID id,
+ LauncherItemController* controller) OVERRIDE;
+
+ // Closes or unpins the launcher item.
+ virtual void CloseLauncherItem(ash::LauncherID id) OVERRIDE;
+
+ // Pins the specified id. Currently only supports platform apps.
+ virtual void Pin(ash::LauncherID id) OVERRIDE;
+
+ // Unpins the specified id, closing if not running.
+ virtual void Unpin(ash::LauncherID id) OVERRIDE;
+
+ // Returns true if the item identified by |id| is pinned.
+ virtual bool IsPinned(ash::LauncherID id) OVERRIDE;
+
+ // Pins/unpins the specified id.
+ virtual void TogglePinned(ash::LauncherID id) OVERRIDE;
+
+ // Returns true if the specified item can be pinned or unpinned. Only apps can
+ // be pinned.
+ virtual bool IsPinnable(ash::LauncherID id) const OVERRIDE;
+
+ // If there is no launcher item in the launcher for application |app_id|, one
+ // gets created. The (existing or created) launcher items get then locked
+ // against a users un-pinning removal.
+ virtual void LockV1AppWithID(const std::string& app_id) OVERRIDE;
+
+ // A previously locked launcher item of type |app_id| gets unlocked. If the
+ // lock count reaches 0 and the item is not pinned it will go away.
+ virtual void UnlockV1AppWithID(const std::string& app_id) OVERRIDE;
+
+ // Requests that the launcher item controller specified by |id| open a new
+ // instance of the app. |event_flags| holds the flags of the event which
+ // triggered this command.
+ virtual void Launch(ash::LauncherID id, int event_flags) OVERRIDE;
+
+ // Closes the specified item.
+ virtual void Close(ash::LauncherID id) OVERRIDE;
+
+ // Returns true if the specified item is open.
+ virtual bool IsOpen(ash::LauncherID id) OVERRIDE;
+
+ // Returns true if the specified item is for a platform app.
+ virtual bool IsPlatformApp(ash::LauncherID id) OVERRIDE;
+
+ // Opens a new instance of the application identified by |app_id|.
+ // Used by the app-list, and by pinned-app launcher items.
+ virtual void LaunchApp(const std::string& app_id, int event_flags) OVERRIDE;
+
+ // If |app_id| is running, reactivates the app's most recently active window,
+ // otherwise launches and activates the app.
+ // Used by the app-list, and by pinned-app launcher items.
+ virtual void ActivateApp(const std::string& app_id, int event_flags) OVERRIDE;
+
+ // Returns the launch type of app for the specified id.
+ virtual extensions::ExtensionPrefs::LaunchType GetLaunchType(
+ ash::LauncherID id) OVERRIDE;
+
+ // Returns the id of the app for the specified tab.
+ virtual std::string GetAppID(content::WebContents* tab) OVERRIDE;
+
+ virtual ash::LauncherID GetLauncherIDForAppID(
+ const std::string& app_id) OVERRIDE;
+ virtual std::string GetAppIDForLauncherID(ash::LauncherID id) OVERRIDE;
+
+ // Set the image for a specific launcher item (e.g. when set by the app).
+ virtual void SetLauncherItemImage(ash::LauncherID launcher_id,
+ const gfx::ImageSkia& image) OVERRIDE;
+
+ // Returns true if a pinned launcher item with given |app_id| could be found.
+ virtual bool IsAppPinned(const std::string& app_id) OVERRIDE;
+
+ // Pins an app with |app_id| to launcher. If there is a running instance in
+ // launcher, the running instance is pinned. If there is no running instance,
+ // a new launcher item is created and pinned.
+ virtual void PinAppWithID(const std::string& app_id) OVERRIDE;
+
+ // Updates the launche type of the app for the specified id to |launch_type|.
+ virtual void SetLaunchType(
+ ash::LauncherID id,
+ extensions::ExtensionPrefs::LaunchType launch_type) OVERRIDE;
+
+ // Unpins any app items whose id is |app_id|.
+ virtual void UnpinAppsWithID(const std::string& app_id) OVERRIDE;
+
+ // Returns true if the user is currently logged in as a guest.
+ virtual bool IsLoggedInAsGuest() OVERRIDE;
+
+ // Invoked when user clicks on button in the launcher and there is no last
+ // used window (or CTRL is held with the click).
+ virtual void CreateNewWindow() OVERRIDE;
+
+ // Invoked when the user clicks on button in the launcher to create a new
+ // incognito window.
+ virtual void CreateNewIncognitoWindow() OVERRIDE;
+
+ // Checks whether the user is allowed to pin apps. Pinning may be disallowed
+ // by policy in case there is a pre-defined set of pinned apps.
+ virtual bool CanPin() const OVERRIDE;
+
+ // Updates the pinned pref state. The pinned state consists of a list pref.
+ // Each item of the list is a dictionary. The key |kAppIDPath| gives the
+ // id of the app.
+ virtual void PersistPinnedState() OVERRIDE;
+
+ virtual ash::LauncherModel* model() OVERRIDE;
+
+ virtual Profile* profile() OVERRIDE;
+
+ // Gets the shelf auto-hide behavior on |root_window|.
+ virtual ash::ShelfAutoHideBehavior GetShelfAutoHideBehavior(
+ aura::RootWindow* root_window) const OVERRIDE;
+
+ // Returns |true| if the user is allowed to modify the shelf auto-hide
+ // behavior on |root_window|.
+ virtual bool CanUserModifyShelfAutoHideBehavior(
+ aura::RootWindow* root_window) const OVERRIDE;
+
+ // Toggles the shelf auto-hide behavior on |root_window|. Does nothing if the
+ // user is not allowed to modify the auto-hide behavior.
+ virtual void ToggleShelfAutoHideBehavior(
+ aura::RootWindow* root_window) OVERRIDE;
+
+ // The tab no longer represents its previously identified application.
+ virtual void RemoveTabFromRunningApp(content::WebContents* tab,
+ const std::string& app_id) OVERRIDE;
+
+ // Notify the controller that the state of an non platform app's tabs
+ // have changed,
+ virtual void UpdateAppState(content::WebContents* contents,
+ AppState app_state) OVERRIDE;
+
+ // Limits application refocusing to urls that match |url| for |id|.
+ virtual void SetRefocusURLPatternForTest(ash::LauncherID id,
+ const GURL& url) OVERRIDE;
+
+ // Returns the extension identified by |app_id|.
+ virtual const extensions::Extension* GetExtensionForAppID(
+ const std::string& app_id) const OVERRIDE;
+
+ // Activates a |window|. If |allow_minimize| is true and the system allows
+ // it, the the window will get minimized instead.
+ virtual void ActivateWindowOrMinimizeIfActive(ui::BaseWindow* window,
+ bool allow_minimize) OVERRIDE;
+
+ // ash::LauncherDelegate overrides:
+ virtual void ItemSelected(const ash::LauncherItem& item,
+ const ui::Event& event) OVERRIDE;
+ virtual string16 GetTitle(const ash::LauncherItem& item) OVERRIDE;
+ virtual ui::MenuModel* CreateContextMenu(
+ const ash::LauncherItem& item, aura::RootWindow* root) OVERRIDE;
+ virtual ash::LauncherMenuModel* CreateApplicationMenu(
+ const ash::LauncherItem& item,
+ int event_flags) OVERRIDE;
+ virtual ash::LauncherID GetIDByWindow(aura::Window* window) OVERRIDE;
+ virtual bool IsDraggable(const ash::LauncherItem& item) OVERRIDE;
+ virtual bool ShouldShowTooltip(const ash::LauncherItem& item) OVERRIDE;
+ virtual void OnLauncherCreated(ash::Launcher* launcher) OVERRIDE;
+ virtual void OnLauncherDestroyed(ash::Launcher* launcher) OVERRIDE;
+
+ // ash::LauncherModelObserver overrides:
+ virtual void LauncherItemAdded(int index) OVERRIDE;
+ virtual void LauncherItemRemoved(int index, ash::LauncherID id) OVERRIDE;
+ virtual void LauncherItemMoved(int start_index, int target_index) OVERRIDE;
+ virtual void LauncherItemChanged(int index,
+ const ash::LauncherItem& old_item) OVERRIDE;
+ virtual void LauncherStatusChanged() OVERRIDE;
+
+ // content::NotificationObserver overrides:
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // ash::ShellObserver overrides:
+ virtual void OnShelfAlignmentChanged(aura::RootWindow* root_window) OVERRIDE;
+
+ // ash::DisplayController::Observer overrides:
+ virtual void OnDisplayConfigurationChanging() OVERRIDE;
+ virtual void OnDisplayConfigurationChanged() OVERRIDE;
+
+ // PrefServiceSyncableObserver overrides:
+ virtual void OnIsSyncingChanged() OVERRIDE;
+
+ // AppSyncUIStateObserver overrides:
+ virtual void OnAppSyncUIStatusChanged() OVERRIDE;
+
+ // ExtensionEnableFlowDelegate overrides:
+ virtual void ExtensionEnableFlowFinished() OVERRIDE;
+ virtual void ExtensionEnableFlowAborted(bool user_initiated) OVERRIDE;
+
+ // extensions::AppIconLoader overrides:
+ virtual void SetAppImage(const std::string& app_id,
+ const gfx::ImageSkia& image) OVERRIDE;
+
+ // ash::ShelfLayoutManagerObserver overrides:
+ virtual void OnAutoHideBehaviorChanged(
+ aura::RootWindow* root_window,
+ ash::ShelfAutoHideBehavior new_behavior) OVERRIDE;
+
+ protected:
+ // ChromeLauncherController overrides:
+
+ // Creates a new app shortcut item and controller on the launcher at |index|.
+ // Use kInsertItemAtEnd to add a shortcut as the last item.
+ virtual ash::LauncherID CreateAppShortcutLauncherItem(
+ const std::string& app_id,
+ int index) OVERRIDE;
+
+ // Sets the AppTabHelper/AppIconLoader, taking ownership of the helper class.
+ // These are intended for testing.
+ virtual void SetAppTabHelperForTest(AppTabHelper* helper) OVERRIDE;
+ virtual void SetAppIconLoaderForTest(
+ extensions::AppIconLoader* loader) OVERRIDE;
+ virtual const std::string& GetAppIdFromLauncherIdForTest(
+ ash::LauncherID id) OVERRIDE;
+
+ private:
+ friend class ChromeLauncherControllerPerBrowserTest;
+
+ typedef std::map<ash::LauncherID, LauncherItemController*>
+ IDToItemControllerMap;
+ typedef std::list<content::WebContents*> WebContentsList;
+ typedef std::map<std::string, WebContentsList> AppIDToWebContentsListMap;
+ typedef std::map<content::WebContents*, std::string> WebContentsToAppIDMap;
+
+ // Returns the profile used for new windows.
+ Profile* GetProfileForNewWindows();
+
+ // Invoked when the associated browser or app is closed.
+ void LauncherItemClosed(ash::LauncherID id);
+
+ // Internal helpers for pinning and unpinning that handle both
+ // client-triggered and internal pinning operations.
+ void DoPinAppWithID(const std::string& app_id);
+ void DoUnpinAppsWithID(const std::string& app_id);
+
+ // Re-syncs launcher model with prefs::kPinnedLauncherApps.
+ void UpdateAppLaunchersFromPref();
+
+ // Persists the shelf auto-hide behavior to prefs.
+ void SetShelfAutoHideBehaviorPrefs(ash::ShelfAutoHideBehavior behavior,
+ aura::RootWindow* root_window);
+
+ // Sets the shelf auto-hide behavior from prefs.
+ void SetShelfAutoHideBehaviorFromPrefs();
+
+ // Sets the shelf alignment from prefs.
+ void SetShelfAlignmentFromPrefs();
+
+ // Sets both of auto-hide behavior and alignment from prefs.
+ void SetShelfBehaviorsFromPrefs();
+
+ // Returns the most recently active WebContents for an app.
+ content::WebContents* GetLastActiveWebContents(const std::string& app_id);
+
+ // Creates an app launcher to insert at |index|. Note that |index| may be
+ // adjusted by the model to meet ordering constraints.
+ ash::LauncherID InsertAppLauncherItem(
+ LauncherItemController* controller,
+ const std::string& app_id,
+ ash::LauncherItemStatus status,
+ int index);
+
+ bool HasItemController(ash::LauncherID id) const;
+
+ // Create LauncherItem for Browser Shortcut.
+ ash::LauncherID CreateBrowserShortcutLauncherItem();
+
+ // Update browser shortcut's index.
+ void SetChromeIconIndexToPref(int index);
+
+ // Get browser shortcut's index from pref.
+ int GetChromeIconIndexFromPref() const;
+
+ // Invoked when browser shortcut is clicked.
+ void BrowserShortcutClicked(int event_flags);
+
+ // Move an item internally (ignoring pinned state changes) from |index| to
+ // |target_index|.
+ void MoveItemWithoutPinnedStateChangeNotification(int source_index,
+ int target_index);
+
+ ash::LauncherModel* model_;
+
+ // Profile used for prefs and loading extensions. This is NOT necessarily the
+ // profile new windows are created with.
+ Profile* profile_;
+
+ IDToItemControllerMap id_to_item_controller_map_;
+
+ // Maintains activation order of web contents for each app.
+ AppIDToWebContentsListMap app_id_to_web_contents_list_;
+
+ // Direct access to app_id for a web contents.
+ WebContentsToAppIDMap web_contents_to_app_id_;
+
+ // Used to track shell windows.
+ scoped_ptr<ShellWindowLauncherController> shell_window_controller_;
+
+ // Used to get app info for tabs.
+ scoped_ptr<AppTabHelper> app_tab_helper_;
+
+ // Used to load the image for an app item.
+ scoped_ptr<extensions::AppIconLoader> app_icon_loader_;
+
+ content::NotificationRegistrar notification_registrar_;
+
+ PrefChangeRegistrar pref_change_registrar_;
+
+ AppSyncUIState* app_sync_ui_state_;
+
+ scoped_ptr<ExtensionEnableFlow> extension_enable_flow_;
+
+ // Launchers that are currently being observed.
+ std::set<ash::Launcher*> launchers_;
+
+ // If true, incoming pinned state changes should be ignored.
+ bool ignore_persist_pinned_state_change_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerPerBrowser);
+};
+
+#endif // CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_LAUNCHER_CONTROLLER_PER_BROWSER_H_
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser_unittest.cc
new file mode 100644
index 0000000..f91430b
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser_unittest.cc
@@ -0,0 +1,462 @@
+// Copyright (c) 2012 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/ui/ash/launcher/chrome_launcher_controller_per_browser.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "ash/launcher/launcher_model.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
+#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_pref_service_syncable.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/settings/device_settings_service.h"
+#endif
+
+using extensions::Extension;
+using extensions::Manifest;
+
+class ChromeLauncherControllerPerBrowserTest : public testing::Test {
+ protected:
+ ChromeLauncherControllerPerBrowserTest()
+ : profile_(new TestingProfile()),
+ extension_service_(NULL) {
+ DictionaryValue manifest;
+ manifest.SetString("name", "launcher controller test extension");
+ manifest.SetString("version", "1");
+ manifest.SetString("description", "for testing pinned apps");
+
+ extensions::TestExtensionSystem* extension_system(
+ static_cast<extensions::TestExtensionSystem*>(
+ extensions::ExtensionSystem::Get(profile_.get())));
+ extension_service_ = extension_system->CreateExtensionService(
+ CommandLine::ForCurrentProcess(), base::FilePath(), false);
+
+ std::string error;
+ extension1_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
+ manifest,
+ Extension::NO_FLAGS,
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ &error);
+ extension2_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
+ manifest,
+ Extension::NO_FLAGS,
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+ &error);
+ // Fake gmail extension.
+ extension3_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
+ manifest,
+ Extension::NO_FLAGS,
+ extension_misc::kGmailAppId,
+ &error);
+ // Fake search extension.
+ extension4_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
+ manifest,
+ Extension::NO_FLAGS,
+ extension_misc::kGoogleSearchAppId,
+ &error);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ profile_.reset();
+ // Execute any pending deletion tasks.
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void InsertPrefValue(base::ListValue* pref_value,
+ int index,
+ const std::string& extension_id) {
+ base::DictionaryValue* entry = new DictionaryValue();
+ entry->SetString(ash::kPinnedAppsPrefAppIDPath, extension_id);
+ pref_value->Insert(index, entry);
+ }
+
+ // Gets the currently configured app launchers from the controller.
+ void GetAppLaunchers(ChromeLauncherControllerPerBrowser* controller,
+ std::vector<std::string>* launchers) {
+ launchers->clear();
+ for (ash::LauncherItems::const_iterator iter(model_.items().begin());
+ iter != model_.items().end(); ++iter) {
+ ChromeLauncherControllerPerBrowser::IDToItemControllerMap::const_iterator
+ entry(controller->id_to_item_controller_map_.find(iter->id));
+ if (iter->type == ash::TYPE_APP_SHORTCUT &&
+ entry != controller->id_to_item_controller_map_.end()) {
+ launchers->push_back(entry->second->app_id());
+ }
+ }
+ }
+
+ std::string GetPinnedAppStatus(
+ ChromeLauncherController* launcher_controller) {
+ std::string result;
+ for (int i = 0; i < model_.item_count(); i++) {
+ switch (model_.items()[i].type) {
+ case ash::TYPE_APP_SHORTCUT: {
+ const std::string& app =
+ launcher_controller->GetAppIDForLauncherID(
+ model_.items()[i].id);
+ if (app == extension1_->id()) {
+ result += "App1, ";
+ EXPECT_TRUE(launcher_controller->IsAppPinned(extension1_->id()));
+ } else if (app == extension2_->id()) {
+ result += "App2, ";
+ EXPECT_TRUE(launcher_controller->IsAppPinned(extension2_->id()));
+ } else if (app == extension3_->id()) {
+ result += "App3, ";
+ EXPECT_TRUE(launcher_controller->IsAppPinned(extension3_->id()));
+ } else {
+ result += "unknown";
+ }
+ break;
+ }
+ case ash::TYPE_BROWSER_SHORTCUT:
+ result += "Chrome, ";
+ break;
+ case ash::TYPE_APP_LIST:
+ result += "AppList";
+ break;
+ default:
+ result += "Unknown";
+ break;
+ }
+ }
+ return result;
+ }
+
+ // Needed for extension service & friends to work.
+ content::TestBrowserThreadBundle thread_bundle_;
+
+#if defined OS_CHROMEOS
+ chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
+ chromeos::ScopedTestCrosSettings test_cros_settings_;
+ chromeos::ScopedTestUserManager test_user_manager_;
+#endif
+
+ scoped_refptr<Extension> extension1_;
+ scoped_refptr<Extension> extension2_;
+ scoped_refptr<Extension> extension3_;
+ scoped_refptr<Extension> extension4_;
+ scoped_ptr<TestingProfile> profile_;
+ ash::LauncherModel model_;
+
+ ExtensionService* extension_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerPerBrowserTest);
+};
+
+TEST_F(ChromeLauncherControllerPerBrowserTest, DefaultApps) {
+ ChromeLauncherControllerPerBrowser launcher_controller(profile_.get(),
+ &model_);
+ launcher_controller.Init();
+
+ // Model should only contain the browser shortcut and app list items.
+ EXPECT_EQ(2, model_.item_count());
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension2_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+
+ // Installing |extension3_| should add it to the launcher - behind the
+ // chrome icon.
+ extension_service_->AddExtension(extension3_.get());
+ EXPECT_EQ("Chrome, App3, AppList", GetPinnedAppStatus(&launcher_controller));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension2_->id()));
+}
+
+// Check that the restauration of launcher items is happening in the same order
+// as the user has pinned them (on another system) when they are synced reverse
+// order.
+TEST_F(ChromeLauncherControllerPerBrowserTest, RestoreDefaultAppsReverseOrder) {
+ ChromeLauncherControllerPerBrowser launcher_controller(profile_.get(),
+ &model_);
+ launcher_controller.Init();
+
+ base::ListValue policy_value;
+ InsertPrefValue(&policy_value, 0, extension1_->id());
+ InsertPrefValue(&policy_value, 1, extension2_->id());
+ InsertPrefValue(&policy_value, 2, extension3_->id());
+ profile_->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
+ policy_value.DeepCopy());
+ EXPECT_EQ(0, profile_->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex));
+ // Model should only contain the browser shortcut and app list items.
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension2_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+ EXPECT_EQ("Chrome, AppList", GetPinnedAppStatus(&launcher_controller));
+
+ // Installing |extension3_| should add it to the launcher - behind the
+ // chrome icon.
+ ash::LauncherItem item;
+ extension_service_->AddExtension(extension3_.get());
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension2_->id()));
+ EXPECT_EQ("Chrome, App3, AppList", GetPinnedAppStatus(&launcher_controller));
+
+ // Installing |extension2_| should add it to the launcher - behind the
+ // chrome icon, but in first location.
+ extension_service_->AddExtension(extension2_.get());
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_EQ("Chrome, App2, App3, AppList",
+ GetPinnedAppStatus(&launcher_controller));
+
+ // Installing |extension1_| should add it to the launcher - behind the
+ // chrome icon, but in first location.
+ extension_service_->AddExtension(extension1_.get());
+ EXPECT_EQ("Chrome, App1, App2, App3, AppList",
+ GetPinnedAppStatus(&launcher_controller));
+}
+
+// Check that the restauration of launcher items is happening in the same order
+// as the user has pinned them (on another system) when they are synced random
+// order.
+TEST_F(ChromeLauncherControllerPerBrowserTest, RestoreDefaultAppsRandomOrder) {
+ ChromeLauncherControllerPerBrowser launcher_controller(profile_.get(),
+ &model_);
+ launcher_controller.Init();
+
+ base::ListValue policy_value;
+ InsertPrefValue(&policy_value, 0, extension1_->id());
+ InsertPrefValue(&policy_value, 1, extension2_->id());
+ InsertPrefValue(&policy_value, 2, extension3_->id());
+ profile_->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
+ policy_value.DeepCopy());
+ EXPECT_EQ(0, profile_->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex));
+ // Model should only contain the browser shortcut and app list items.
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension2_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+ EXPECT_EQ("Chrome, AppList", GetPinnedAppStatus(&launcher_controller));
+
+ // Installing |extension2_| should add it to the launcher - behind the
+ // chrome icon.
+ extension_service_->AddExtension(extension2_.get());
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+ EXPECT_EQ("Chrome, App2, AppList", GetPinnedAppStatus(&launcher_controller));
+
+ // Installing |extension1_| should add it to the launcher - behind the
+ // chrome icon, but in first location.
+ extension_service_->AddExtension(extension1_.get());
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+ EXPECT_EQ("Chrome, App1, App2, AppList",
+ GetPinnedAppStatus(&launcher_controller));
+
+ // Installing |extension3_| should add it to the launcher - behind the
+ // chrome icon, but in first location.
+ extension_service_->AddExtension(extension3_.get());
+ EXPECT_EQ("Chrome, App1, App2, App3, AppList",
+ GetPinnedAppStatus(&launcher_controller));
+}
+
+// Check that the restauration of launcher items is happening in the same order
+// as the user has pinned / moved them (on another system) when they are synced
+// random order - including the chrome icon.
+TEST_F(ChromeLauncherControllerPerBrowserTest,
+ RestoreDefaultAppsRandomOrderChromeMoved) {
+ ChromeLauncherControllerPerBrowser launcher_controller(profile_.get(),
+ &model_);
+ launcher_controller.Init();
+ base::ListValue policy_value;
+ InsertPrefValue(&policy_value, 0, extension1_->id());
+ InsertPrefValue(&policy_value, 1, extension2_->id());
+ InsertPrefValue(&policy_value, 2, extension3_->id());
+ profile_->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
+ policy_value.DeepCopy());
+ profile_->GetTestingPrefService()->SetInteger(prefs::kShelfChromeIconIndex,
+ 1);
+ // Model should only contain the browser shortcut and app list items.
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension2_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+ EXPECT_EQ("Chrome, AppList", GetPinnedAppStatus(&launcher_controller));
+
+ // Installing |extension2_| should add it to the launcher - behind the
+ // chrome icon.
+ ash::LauncherItem item;
+ extension_service_->AddExtension(extension2_.get());
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+ EXPECT_EQ("Chrome, App2, AppList", GetPinnedAppStatus(&launcher_controller));
+
+ // Installing |extension1_| should add it to the launcher - behind the
+ // chrome icon, but in first location.
+ extension_service_->AddExtension(extension1_.get());
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+ EXPECT_EQ("App1, Chrome, App2, AppList",
+ GetPinnedAppStatus(&launcher_controller));
+
+ // Installing |extension3_| should add it to the launcher - behind the
+ // chrome icon, but in first location.
+ extension_service_->AddExtension(extension3_.get());
+ EXPECT_EQ("App1, Chrome, App2, App3, AppList",
+ GetPinnedAppStatus(&launcher_controller));
+}
+
+TEST_F(ChromeLauncherControllerPerBrowserTest, Policy) {
+ extension_service_->AddExtension(extension1_.get());
+ extension_service_->AddExtension(extension3_.get());
+
+ base::ListValue policy_value;
+ InsertPrefValue(&policy_value, 0, extension1_->id());
+ InsertPrefValue(&policy_value, 1, extension2_->id());
+ profile_->GetTestingPrefService()->SetManagedPref(prefs::kPinnedLauncherApps,
+ policy_value.DeepCopy());
+
+ // Only |extension1_| should get pinned. |extension2_| is specified but not
+ // installed, and |extension3_| is part of the default set, but that shouldn't
+ // take effect when the policy override is in place.
+ ChromeLauncherControllerPerBrowser launcher_controller(profile_.get(),
+ &model_);
+ launcher_controller.Init();
+ EXPECT_EQ(3, model_.item_count());
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_.items()[1].type);
+ EXPECT_TRUE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension2_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+
+ EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_.items()[0].type);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_.items()[1].type);
+ EXPECT_EQ(ash::TYPE_APP_LIST, model_.items()[2].type);
+
+ // Installing |extension2_| should add it to the launcher.
+ extension_service_->AddExtension(extension2_.get());
+ EXPECT_EQ(4, model_.item_count());
+ EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_.items()[0].type);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_.items()[1].type);
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_.items()[2].type);
+ EXPECT_EQ(ash::TYPE_APP_LIST, model_.items()[3].type);
+ EXPECT_TRUE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_TRUE(launcher_controller.IsAppPinned(extension2_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+
+ // Removing |extension1_| from the policy should be reflected in the launcher.
+ policy_value.Remove(0, NULL);
+ profile_->GetTestingPrefService()->SetManagedPref(prefs::kPinnedLauncherApps,
+ policy_value.DeepCopy());
+ EXPECT_EQ(3, model_.item_count());
+ EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_.items()[1].type);
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension1_->id()));
+ EXPECT_TRUE(launcher_controller.IsAppPinned(extension2_->id()));
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+}
+
+TEST_F(ChromeLauncherControllerPerBrowserTest, UnpinWithUninstall) {
+ extension_service_->AddExtension(extension3_.get());
+ extension_service_->AddExtension(extension4_.get());
+
+ ChromeLauncherControllerPerBrowser launcher_controller(profile_.get(),
+ &model_);
+ launcher_controller.Init();
+
+ EXPECT_TRUE(launcher_controller.IsAppPinned(extension3_->id()));
+ EXPECT_TRUE(launcher_controller.IsAppPinned(extension4_->id()));
+
+ extension_service_->UnloadExtension(extension3_->id(),
+ extension_misc::UNLOAD_REASON_UNINSTALL);
+
+ EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id()));
+ EXPECT_TRUE(launcher_controller.IsAppPinned(extension4_->id()));
+}
+
+TEST_F(ChromeLauncherControllerPerBrowserTest, PrefUpdates) {
+ extension_service_->AddExtension(extension2_.get());
+ extension_service_->AddExtension(extension3_.get());
+ extension_service_->AddExtension(extension4_.get());
+ ChromeLauncherControllerPerBrowser controller(profile_.get(), &model_);
+ controller.Init();
+
+ std::vector<std::string> expected_launchers;
+ std::vector<std::string> actual_launchers;
+ base::ListValue pref_value;
+ profile_->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
+ pref_value.DeepCopy());
+ GetAppLaunchers(&controller, &actual_launchers);
+ EXPECT_EQ(expected_launchers, actual_launchers);
+
+ // Unavailable extensions don't create launcher items.
+ InsertPrefValue(&pref_value, 0, extension1_->id());
+ InsertPrefValue(&pref_value, 1, extension2_->id());
+ InsertPrefValue(&pref_value, 2, extension4_->id());
+ profile_->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
+ pref_value.DeepCopy());
+ expected_launchers.push_back(extension2_->id());
+ expected_launchers.push_back(extension4_->id());
+ GetAppLaunchers(&controller, &actual_launchers);
+ EXPECT_EQ(expected_launchers, actual_launchers);
+
+ // Redundant pref entries show up only once.
+ InsertPrefValue(&pref_value, 2, extension3_->id());
+ InsertPrefValue(&pref_value, 2, extension3_->id());
+ InsertPrefValue(&pref_value, 5, extension3_->id());
+ profile_->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
+ pref_value.DeepCopy());
+ expected_launchers.insert(expected_launchers.begin() + 1, extension3_->id());
+ GetAppLaunchers(&controller, &actual_launchers);
+ EXPECT_EQ(expected_launchers, actual_launchers);
+
+ // Order changes are reflected correctly.
+ pref_value.Clear();
+ InsertPrefValue(&pref_value, 0, extension4_->id());
+ InsertPrefValue(&pref_value, 1, extension3_->id());
+ InsertPrefValue(&pref_value, 2, extension2_->id());
+ profile_->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
+ pref_value.DeepCopy());
+ std::reverse(expected_launchers.begin(), expected_launchers.end());
+ GetAppLaunchers(&controller, &actual_launchers);
+ EXPECT_EQ(expected_launchers, actual_launchers);
+
+ // Clearing works.
+ pref_value.Clear();
+ profile_->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
+ pref_value.DeepCopy());
+ expected_launchers.clear();
+ GetAppLaunchers(&controller, &actual_launchers);
+ EXPECT_EQ(expected_launchers, actual_launchers);
+}
+
+TEST_F(ChromeLauncherControllerPerBrowserTest, PendingInsertionOrder) {
+ extension_service_->AddExtension(extension1_.get());
+ extension_service_->AddExtension(extension3_.get());
+ ChromeLauncherControllerPerBrowser controller(profile_.get(), &model_);
+ controller.Init();
+
+ base::ListValue pref_value;
+ InsertPrefValue(&pref_value, 0, extension1_->id());
+ InsertPrefValue(&pref_value, 1, extension2_->id());
+ InsertPrefValue(&pref_value, 2, extension3_->id());
+ profile_->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
+ pref_value.DeepCopy());
+
+ std::vector<std::string> expected_launchers;
+ expected_launchers.push_back(extension1_->id());
+ expected_launchers.push_back(extension3_->id());
+ std::vector<std::string> actual_launchers;
+
+ GetAppLaunchers(&controller, &actual_launchers);
+ EXPECT_EQ(expected_launchers, actual_launchers);
+
+ // Install |extension2| and verify it shows up between the other two.
+ extension_service_->AddExtension(extension2_.get());
+ expected_launchers.insert(expected_launchers.begin() + 1, extension2_->id());
+ GetAppLaunchers(&controller, &actual_launchers);
+ EXPECT_EQ(expected_launchers, actual_launchers);
+}
diff --git a/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.cc b/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.cc
index b380e97..61c9881 100644
--- a/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.cc
@@ -4,7 +4,7 @@
#include "chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h"
-#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
LauncherApplicationMenuItemModel::LauncherApplicationMenuItemModel(
ChromeLauncherAppMenuItems item_list)
diff --git a/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h b/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h
index 46a483f..48816f4 100644
--- a/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h
+++ b/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h
@@ -7,12 +7,7 @@
#include "ash/launcher/launcher_delegate.h"
#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-
-class ChromeLauncherAppMenuItem;
-
-// A list of the elements which makes up a simple menu description.
-typedef ScopedVector<ChromeLauncherAppMenuItem> ChromeLauncherAppMenuItems;
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
// A menu model that builds the contents of a menu for a launcher item
// containing a list of running applications.
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
index 96bb851..dbcf723 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
@@ -13,18 +13,21 @@
#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_browser.h"
#include "chrome/test/base/testing_profile.h"
#include "ui/aura/root_window.h"
-class TestChromeLauncherController : public ChromeLauncherController {
+class TestChromeLauncherControllerPerBrowser :
+ public ChromeLauncherControllerPerBrowser {
public:
- TestChromeLauncherController(Profile* profile, ash::LauncherModel* model)
- : ChromeLauncherController(profile, model) {}
+ TestChromeLauncherControllerPerBrowser(
+ Profile* profile, ash::LauncherModel* model)
+ : ChromeLauncherControllerPerBrowser(profile, model) {}
virtual bool IsLoggedInAsGuest() OVERRIDE {
return false;
}
private:
- DISALLOW_COPY_AND_ASSIGN(TestChromeLauncherController);
+ DISALLOW_COPY_AND_ASSIGN(TestChromeLauncherControllerPerBrowser);
};
class LauncherContextMenuTest : public ash::test::AshTestBase {
@@ -40,7 +43,8 @@ class LauncherContextMenuTest : public ash::test::AshTestBase {
virtual void SetUp() OVERRIDE {
ash::test::AshTestBase::SetUp();
controller_.reset(
- new TestChromeLauncherController(profile(), &launcher_model_));
+ new TestChromeLauncherControllerPerBrowser(profile(),
+ &launcher_model_));
}
virtual void TearDown() OVERRIDE {
diff --git a/chrome/browser/ui/ash/launcher/shell_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/shell_window_launcher_item_controller.cc
index 0a52893..b4d562d85 100644
--- a/chrome/browser/ui/ash/launcher/shell_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/shell_window_launcher_item_controller.cc
@@ -11,6 +11,7 @@
#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_controller.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
#include "content/public/browser/web_contents.h"
#include "ui/aura/client/aura_constants.h"
@@ -154,7 +155,8 @@ void ShellWindowLauncherItemController::Clicked(const ui::Event& event) {
} else {
ShowAndActivateOrMinimize(panel);
}
- } else {
+ } else if (launcher_controller()->GetPerAppInterface() ||
+ shell_windows_.size() == 1) {
ShellWindow* window_to_show = last_active_shell_window_ ?
last_active_shell_window_ : shell_windows_.front();
// If the event was triggered by a keystroke, we try to advance to the next
@@ -166,6 +168,20 @@ void ShellWindowLauncherItemController::Clicked(const ui::Event& event) {
} else {
ShowAndActivateOrMinimize(window_to_show);
}
+ } else {
+ // TODO(stevenjb): Deprecate
+ if (!last_active_shell_window_ ||
+ last_active_shell_window_->GetBaseWindow()->IsActive()) {
+ // Restore all windows since there is no other way to restore them.
+ for (ShellWindowList::iterator iter = shell_windows_.begin();
+ iter != shell_windows_.end(); ++iter) {
+ ShellWindow* shell_window = *iter;
+ if (shell_window->GetBaseWindow()->IsMinimized())
+ shell_window->GetBaseWindow()->Restore();
+ }
+ }
+ if (last_active_shell_window_)
+ ShowAndActivateOrMinimize(last_active_shell_window_);
}
}
@@ -190,7 +206,7 @@ ShellWindowLauncherItemController::GetApplicationList(int event_flags) {
shell_window->GetTitle(),
image.get(), // Will be copied
app_id(),
- launcher_controller(),
+ launcher_controller()->GetPerAppInterface(),
index,
index == 0 /* has_leading_separator */));
++index;
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 0d1a1a8..d7dc080 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -236,6 +236,10 @@
'browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.h',
'browser/ui/ash/launcher/chrome_launcher_controller.cc',
'browser/ui/ash/launcher/chrome_launcher_controller.h',
+ 'browser/ui/ash/launcher/chrome_launcher_controller_per_app.cc',
+ 'browser/ui/ash/launcher/chrome_launcher_controller_per_app.h',
+ 'browser/ui/ash/launcher/chrome_launcher_controller_per_browser.cc',
+ 'browser/ui/ash/launcher/chrome_launcher_controller_per_browser.h',
'browser/ui/ash/launcher/launcher_app_tab_helper.cc',
'browser/ui/ash/launcher/launcher_app_tab_helper.h',
'browser/ui/ash/launcher/launcher_context_menu.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index fab07e4..57cddd0 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1549,6 +1549,7 @@
'browser/ui/ash/caps_lock_delegate_chromeos_browsertest.cc',
'browser/ui/ash/chrome_shell_delegate_browsertest.cc',
'browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc',
+ 'browser/ui/ash/launcher/chrome_launcher_controller_per_app_browsertest.cc',
'browser/ui/ash/launcher/launcher_favicon_loader_browsertest.cc',
'browser/ui/ash/shelf_browsertest.cc',
'browser/ui/ash/volume_controller_browsertest_chromeos.cc',
@@ -1959,6 +1960,7 @@
'browser/chromeos/system/tray_accessibility_browsertest.cc',
'browser/ui/ash/caps_lock_delegate_chromeos_browsertest.cc',
'browser/ui/ash/chrome_shell_delegate_browsertest.cc',
+ 'browser/ui/ash/launcher/chrome_launcher_controller_per_app_browsertest.cc',
'browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc',
'browser/ui/ash/launcher/launcher_favicon_loader_browsertest.cc',
'browser/ui/ash/shelf_browsertest.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 4a2abcd..075c1ea 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -1355,7 +1355,9 @@
'browser/ui/android/tab_model/tab_model_unittest.cc',
'browser/ui/ash/event_rewriter_unittest.cc',
'browser/ui/ash/ime_controller_chromeos_unittest.cc',
- 'browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc',
+ 'browser/ui/ash/launcher/browser_launcher_item_controller_unittest.cc',
+ 'browser/ui/ash/launcher/chrome_launcher_controller_per_app_unittest.cc',
+ 'browser/ui/ash/launcher/chrome_launcher_controller_per_browser_unittest.cc',
'browser/ui/ash/launcher/launcher_context_menu_unittest.cc',
'browser/ui/ash/screenshot_taker_unittest.cc',
'browser/ui/ash/window_positioner_unittest.cc',