1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h"
6
7#include <algorithm>
8#include <string>
9#include <vector>
10
11#include "ash/launcher/launcher_model.h"
12#include "ash/launcher/launcher_model_observer.h"
13#include "base/command_line.h"
14#include "base/compiler_specific.h"
15#include "base/files/file_path.h"
16#include "base/memory/scoped_ptr.h"
17#include "base/message_loop/message_loop.h"
18#include "base/strings/utf_string_conversions.h"
19#include "base/values.h"
20#include "chrome/browser/extensions/extension_service.h"
21#include "chrome/browser/extensions/test_extension_system.h"
22#include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
23#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
24#include "chrome/browser/ui/ash/launcher/shell_window_launcher_item_controller.h"
25#include "chrome/browser/ui/browser.h"
26#include "chrome/browser/ui/browser_commands.h"
27#include "chrome/browser/ui/browser_finder.h"
28#include "chrome/browser/ui/browser_list.h"
29#include "chrome/browser/ui/host_desktop.h"
30#include "chrome/browser/ui/tabs/tab_strip_model.h"
31#include "chrome/common/extensions/extension.h"
32#include "chrome/common/extensions/extension_constants.h"
33#include "chrome/common/extensions/extension_manifest_constants.h"
34#include "chrome/common/pref_names.h"
35#include "chrome/test/base/browser_with_test_window_test.h"
36#include "chrome/test/base/testing_pref_service_syncable.h"
37#include "chrome/test/base/testing_profile.h"
38#include "content/public/test/test_browser_thread.h"
39#include "testing/gtest/include/gtest/gtest.h"
40#include "ui/base/models/menu_model.h"
41
42using extensions::Extension;
43using extensions::Manifest;
44
45namespace {
46const char* offline_gmail_url = "https://mail.google.com/mail/mu/u";
47const char* gmail_url = "https://mail.google.com/mail/u";
48}
49
50namespace {
51
52// LauncherModelObserver implementation that tracks what messages are invoked.
53class TestLauncherModelObserver : public ash::LauncherModelObserver {
54 public:
55  TestLauncherModelObserver()
56    : added_(0),
57      removed_(0),
58      changed_(0) {
59  }
60
61  virtual ~TestLauncherModelObserver() {
62  }
63
64  // LauncherModelObserver
65  virtual void LauncherItemAdded(int index) OVERRIDE {
66    ++added_;
67    last_index_ = index;
68  }
69
70  virtual void LauncherItemRemoved(int index, ash::LauncherID id) OVERRIDE {
71    ++removed_;
72    last_index_ = index;
73  }
74
75  virtual void LauncherItemChanged(int index,
76                                   const ash::LauncherItem& old_item) OVERRIDE {
77    ++changed_;
78    last_index_ = index;
79  }
80
81  virtual void LauncherItemMoved(int start_index, int target_index) OVERRIDE {
82    last_index_ = target_index;
83  }
84
85  virtual void LauncherStatusChanged() OVERRIDE {
86  }
87
88  void clear_counts() {
89    added_ = 0;
90    removed_ = 0;
91    changed_ = 0;
92    last_index_ = 0;
93  }
94
95  int added() const { return added_; }
96  int removed() const { return removed_; }
97  int changed() const { return changed_; }
98  int last_index() const { return last_index_; }
99
100 private:
101  int added_;
102  int removed_;
103  int changed_;
104  int last_index_;
105
106  DISALLOW_COPY_AND_ASSIGN(TestLauncherModelObserver);
107};
108
109// Test implementation of AppIconLoader.
110class TestAppIconLoaderImpl : public extensions::AppIconLoader {
111 public:
112  TestAppIconLoaderImpl() : fetch_count_(0) {
113  }
114
115  virtual ~TestAppIconLoaderImpl() {
116  }
117
118  // AppIconLoader implementation:
119  virtual void FetchImage(const std::string& id) OVERRIDE {
120    ++fetch_count_;
121  }
122
123  virtual void ClearImage(const std::string& id) OVERRIDE {
124  }
125
126  virtual void UpdateImage(const std::string& id) OVERRIDE {
127  }
128
129  int fetch_count() const { return fetch_count_; }
130
131 private:
132  int fetch_count_;
133
134  DISALLOW_COPY_AND_ASSIGN(TestAppIconLoaderImpl);
135};
136
137}  // namespace
138
139class ChromeLauncherControllerPerAppTest : public BrowserWithTestWindowTest {
140 protected:
141  ChromeLauncherControllerPerAppTest()
142      : extension_service_(NULL) {
143    SetHostDesktopType(chrome::HOST_DESKTOP_TYPE_ASH);
144  }
145
146  virtual ~ChromeLauncherControllerPerAppTest() {
147  }
148
149  virtual void SetUp() OVERRIDE {
150    BrowserWithTestWindowTest::SetUp();
151
152    model_.reset(new ash::LauncherModel);
153    model_observer_.reset(new TestLauncherModelObserver);
154    model_->AddObserver(model_observer_.get());
155
156    DictionaryValue manifest;
157    manifest.SetString(extension_manifest_keys::kName,
158                       "launcher controller test extension");
159    manifest.SetString(extension_manifest_keys::kVersion, "1");
160    manifest.SetString(extension_manifest_keys::kDescription,
161                       "for testing pinned apps");
162
163    extensions::TestExtensionSystem* extension_system(
164        static_cast<extensions::TestExtensionSystem*>(
165            extensions::ExtensionSystem::Get(profile())));
166    extension_service_ = extension_system->CreateExtensionService(
167        CommandLine::ForCurrentProcess(), base::FilePath(), false);
168
169    std::string error;
170    extension1_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
171                                    manifest,
172                                    Extension::NO_FLAGS,
173                                    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
174                                    &error);
175    extension2_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
176                                    manifest,
177                                    Extension::NO_FLAGS,
178                                    "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
179                                    &error);
180    // Fake gmail extension.
181    DictionaryValue manifest_gmail;
182    manifest_gmail.SetString(extension_manifest_keys::kName,
183                             "Gmail launcher controller test extension");
184    manifest_gmail.SetString(extension_manifest_keys::kVersion, "1");
185    manifest_gmail.SetString(extension_manifest_keys::kDescription,
186                             "for testing pinned Gmail");
187    manifest_gmail.SetString(extension_manifest_keys::kLaunchWebURL,
188                             "https://mail.google.com/mail/ca");
189    ListValue* list = new ListValue();
190    list->Append(Value::CreateStringValue("*://mail.google.com/mail/ca"));
191    manifest_gmail.Set(extension_manifest_keys::kWebURLs, list);
192
193    extension3_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
194                                    manifest_gmail,
195                                    Extension::NO_FLAGS,
196                                    extension_misc::kGmailAppId,
197                                    &error);
198
199    // Fake search extension.
200    extension4_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
201                                    manifest,
202                                    Extension::NO_FLAGS,
203                                    extension_misc::kGoogleSearchAppId,
204                                    &error);
205  }
206
207  virtual void TearDown() OVERRIDE {
208    model_->RemoveObserver(model_observer_.get());
209    model_observer_.reset();
210    launcher_controller_.reset();
211    model_.reset();
212
213    BrowserWithTestWindowTest::TearDown();
214  }
215
216  void InitLauncherController() {
217    launcher_controller_.reset(
218        new ChromeLauncherControllerPerApp(profile(), model_.get()));
219    launcher_controller_->Init();
220  }
221
222  void InitLauncherControllerWithBrowser() {
223    chrome::NewTab(browser());
224    BrowserList::SetLastActive(browser());
225    InitLauncherController();
226  }
227
228  void SetAppIconLoader(extensions::AppIconLoader* loader) {
229    launcher_controller_->SetAppIconLoaderForTest(loader);
230  }
231
232  void InsertPrefValue(base::ListValue* pref_value,
233                       int index,
234                       const std::string& extension_id) {
235    base::DictionaryValue* entry = new DictionaryValue();
236    entry->SetString(ash::kPinnedAppsPrefAppIDPath, extension_id);
237    pref_value->Insert(index, entry);
238  }
239
240  // Gets the currently configured app launchers from the controller.
241  void GetAppLaunchers(ChromeLauncherControllerPerApp* controller,
242                       std::vector<std::string>* launchers) {
243    launchers->clear();
244    for (ash::LauncherItems::const_iterator iter(model_->items().begin());
245         iter != model_->items().end(); ++iter) {
246      ChromeLauncherControllerPerApp::IDToItemControllerMap::const_iterator
247          entry(controller->id_to_item_controller_map_.find(iter->id));
248      if (iter->type == ash::TYPE_APP_SHORTCUT &&
249          entry != controller->id_to_item_controller_map_.end()) {
250        launchers->push_back(entry->second->app_id());
251      }
252    }
253  }
254
255  std::string GetPinnedAppStatus() {
256    std::string result;
257    for (int i = 0; i < model_->item_count(); i++) {
258      switch (model_->items()[i].type) {
259        case ash::TYPE_APP_SHORTCUT: {
260          const std::string& app =
261              launcher_controller_->GetAppIDForLauncherID(
262                  model_->items()[i].id);
263          if (app == extension1_->id()) {
264            result += "App1, ";
265            EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
266          } else if (app == extension2_->id()) {
267            result += "App2, ";
268            EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
269          } else if (app == extension3_->id()) {
270            result += "App3, ";
271            EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id()));
272          } else {
273            result += "unknown";
274          }
275          break;
276          }
277        case ash::TYPE_BROWSER_SHORTCUT:
278          result += "Chrome, ";
279          break;
280        case ash::TYPE_APP_LIST:
281          result += "AppList";
282          break;
283        default:
284          result += "Unknown";
285          break;
286      }
287    }
288    return result;
289  }
290
291  // Needed for extension service & friends to work.
292  scoped_refptr<Extension> extension1_;
293  scoped_refptr<Extension> extension2_;
294  scoped_refptr<Extension> extension3_;
295  scoped_refptr<Extension> extension4_;
296  scoped_ptr<ChromeLauncherControllerPerApp> launcher_controller_;
297  scoped_ptr<TestLauncherModelObserver> model_observer_;
298  scoped_ptr<ash::LauncherModel> model_;
299
300  ExtensionService* extension_service_;
301
302  DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerPerAppTest);
303};
304
305TEST_F(ChromeLauncherControllerPerAppTest, DefaultApps) {
306  InitLauncherController();
307  // Model should only contain the browser shortcut and app list items.
308  EXPECT_EQ(2, model_->item_count());
309  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
310  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
311  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
312
313  // Installing |extension3_| should add it to the launcher - behind the
314  // chrome icon.
315  extension_service_->AddExtension(extension3_.get());
316  EXPECT_EQ("Chrome, App3, AppList", GetPinnedAppStatus());
317  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
318  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
319}
320
321// Check that the restauration of launcher items is happening in the same order
322// as the user has pinned them (on another system) when they are synced reverse
323// order.
324TEST_F(ChromeLauncherControllerPerAppTest, RestoreDefaultAppsReverseOrder) {
325  InitLauncherController();
326
327  base::ListValue policy_value;
328  InsertPrefValue(&policy_value, 0, extension1_->id());
329  InsertPrefValue(&policy_value, 1, extension2_->id());
330  InsertPrefValue(&policy_value, 2, extension3_->id());
331  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
332                                                  policy_value.DeepCopy());
333  EXPECT_EQ(0, profile()->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex));
334  // Model should only contain the browser shortcut and app list items.
335  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
336  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
337  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
338  EXPECT_EQ("Chrome, AppList", GetPinnedAppStatus());
339
340  // Installing |extension3_| should add it to the launcher - behind the
341  // chrome icon.
342  ash::LauncherItem item;
343  extension_service_->AddExtension(extension3_.get());
344  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
345  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
346  EXPECT_EQ("Chrome, App3, AppList", GetPinnedAppStatus());
347
348  // Installing |extension2_| should add it to the launcher - behind the
349  // chrome icon, but in first location.
350  extension_service_->AddExtension(extension2_.get());
351  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
352  EXPECT_EQ("Chrome, App2, App3, AppList", GetPinnedAppStatus());
353
354  // Installing |extension1_| should add it to the launcher - behind the
355  // chrome icon, but in first location.
356  extension_service_->AddExtension(extension1_.get());
357  EXPECT_EQ("Chrome, App1, App2, App3, AppList", GetPinnedAppStatus());
358}
359
360// Check that the restauration of launcher items is happening in the same order
361// as the user has pinned them (on another system) when they are synced random
362// order.
363TEST_F(ChromeLauncherControllerPerAppTest, RestoreDefaultAppsRandomOrder) {
364  InitLauncherController();
365
366  base::ListValue policy_value;
367  InsertPrefValue(&policy_value, 0, extension1_->id());
368  InsertPrefValue(&policy_value, 1, extension2_->id());
369  InsertPrefValue(&policy_value, 2, extension3_->id());
370  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
371                                                  policy_value.DeepCopy());
372  EXPECT_EQ(0, profile()->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex));
373  // Model should only contain the browser shortcut and app list items.
374  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
375  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
376  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
377  EXPECT_EQ("Chrome, AppList", GetPinnedAppStatus());
378
379  // Installing |extension2_| should add it to the launcher - behind the
380  // chrome icon.
381  extension_service_->AddExtension(extension2_.get());
382  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
383  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
384  EXPECT_EQ("Chrome, App2, AppList", GetPinnedAppStatus());
385
386  // Installing |extension1_| should add it to the launcher - behind the
387  // chrome icon, but in first location.
388  extension_service_->AddExtension(extension1_.get());
389  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
390  EXPECT_EQ("Chrome, App1, App2, AppList", GetPinnedAppStatus());
391
392  // Installing |extension3_| should add it to the launcher - behind the
393  // chrome icon, but in first location.
394  extension_service_->AddExtension(extension3_.get());
395  EXPECT_EQ("Chrome, App1, App2, App3, AppList", GetPinnedAppStatus());
396}
397
398// Check that the restauration of launcher items is happening in the same order
399// as the user has pinned / moved them (on another system) when they are synced
400// random order - including the chrome icon.
401TEST_F(ChromeLauncherControllerPerAppTest,
402    RestoreDefaultAppsRandomOrderChromeMoved) {
403  InitLauncherController();
404
405  base::ListValue policy_value;
406  InsertPrefValue(&policy_value, 0, extension1_->id());
407  InsertPrefValue(&policy_value, 1, extension2_->id());
408  InsertPrefValue(&policy_value, 2, extension3_->id());
409  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
410                                                  policy_value.DeepCopy());
411  profile()->GetTestingPrefService()->SetInteger(prefs::kShelfChromeIconIndex,
412                                                 1);
413  // Model should only contain the browser shortcut and app list items.
414  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
415  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
416  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
417  EXPECT_EQ("Chrome, AppList", GetPinnedAppStatus());
418
419  // Installing |extension2_| should add it to the launcher - behind the
420  // chrome icon.
421  ash::LauncherItem item;
422  extension_service_->AddExtension(extension2_.get());
423  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
424  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
425  EXPECT_EQ("Chrome, App2, AppList", GetPinnedAppStatus());
426
427  // Installing |extension1_| should add it to the launcher - behind the
428  // chrome icon, but in first location.
429  extension_service_->AddExtension(extension1_.get());
430  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
431  EXPECT_EQ("App1, Chrome, App2, AppList", GetPinnedAppStatus());
432
433  // Installing |extension3_| should add it to the launcher - behind the
434  // chrome icon, but in first location.
435  extension_service_->AddExtension(extension3_.get());
436  EXPECT_EQ("App1, Chrome, App2, App3, AppList", GetPinnedAppStatus());
437}
438
439// Check that syncing to a different state does the correct thing.
440TEST_F(ChromeLauncherControllerPerAppTest, RestoreDefaultAppsResyncOrder) {
441  InitLauncherController();
442  base::ListValue policy_value;
443  InsertPrefValue(&policy_value, 0, extension1_->id());
444  InsertPrefValue(&policy_value, 1, extension2_->id());
445  InsertPrefValue(&policy_value, 2, extension3_->id());
446  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
447                                                  policy_value.DeepCopy());
448  EXPECT_EQ(0, profile()->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex));
449  extension_service_->AddExtension(extension2_.get());
450  extension_service_->AddExtension(extension1_.get());
451  extension_service_->AddExtension(extension3_.get());
452  EXPECT_EQ("Chrome, App1, App2, App3, AppList", GetPinnedAppStatus());
453
454  // Change the order with increasing chrome position and decreasing position.
455  base::ListValue policy_value1;
456  InsertPrefValue(&policy_value1, 0, extension3_->id());
457  InsertPrefValue(&policy_value1, 1, extension1_->id());
458  InsertPrefValue(&policy_value1, 2, extension2_->id());
459  profile()->GetTestingPrefService()->SetInteger(prefs::kShelfChromeIconIndex,
460                                                 2);
461  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
462                                                  policy_value1.DeepCopy());
463  EXPECT_EQ("App3, App1, Chrome, App2, AppList", GetPinnedAppStatus());
464  base::ListValue policy_value2;
465  InsertPrefValue(&policy_value2, 0, extension2_->id());
466  InsertPrefValue(&policy_value2, 1, extension3_->id());
467  InsertPrefValue(&policy_value2, 2, extension1_->id());
468  profile()->GetTestingPrefService()->SetInteger(prefs::kShelfChromeIconIndex,
469                                                 1);
470  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
471                                                  policy_value2.DeepCopy());
472  EXPECT_EQ("App2, Chrome, App3, App1, AppList", GetPinnedAppStatus());
473}
474
475// Check that simple locking of an application will 'create' a launcher item.
476TEST_F(ChromeLauncherControllerPerAppTest, CheckLockApps) {
477  InitLauncherController();
478  // Model should only contain the browser shortcut and app list items.
479  EXPECT_EQ(2, model_->item_count());
480  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
481  EXPECT_FALSE(
482      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
483  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
484  EXPECT_FALSE(
485      launcher_controller_->IsWindowedAppInLauncher(extension2_->id()));
486
487  launcher_controller_->LockV1AppWithID(extension1_->id());
488
489  EXPECT_EQ(3, model_->item_count());
490  EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[1].type);
491  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
492  EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
493  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
494  EXPECT_FALSE(
495      launcher_controller_->IsWindowedAppInLauncher(extension2_->id()));
496
497  launcher_controller_->UnlockV1AppWithID(extension1_->id());
498
499  EXPECT_EQ(2, model_->item_count());
500  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
501  EXPECT_FALSE(
502      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
503  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
504  EXPECT_FALSE(
505      launcher_controller_->IsWindowedAppInLauncher(extension2_->id()));
506}
507
508// Check that multiple locks of an application will be properly handled.
509TEST_F(ChromeLauncherControllerPerAppTest, CheckMukltiLockApps) {
510  InitLauncherController();
511  // Model should only contain the browser shortcut and app list items.
512  EXPECT_EQ(2, model_->item_count());
513  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
514  EXPECT_FALSE(
515      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
516
517  for (int i = 0; i < 2; i++) {
518    launcher_controller_->LockV1AppWithID(extension1_->id());
519
520    EXPECT_EQ(3, model_->item_count());
521    EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[1].type);
522    EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
523    EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(
524        extension1_->id()));
525  }
526
527  launcher_controller_->UnlockV1AppWithID(extension1_->id());
528
529  EXPECT_EQ(3, model_->item_count());
530  EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[1].type);
531  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
532  EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
533
534  launcher_controller_->UnlockV1AppWithID(extension1_->id());
535
536  EXPECT_EQ(2, model_->item_count());
537  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
538  EXPECT_FALSE(
539      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
540  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
541  EXPECT_FALSE(
542      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
543}
544
545// Check that already pinned items are not effected by locks.
546TEST_F(ChromeLauncherControllerPerAppTest, CheckAlreadyPinnedLockApps) {
547  InitLauncherController();
548  // Model should only contain the browser shortcut and app list items.
549  EXPECT_EQ(2, model_->item_count());
550  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
551  EXPECT_FALSE(
552      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
553
554  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
555  launcher_controller_->PinAppWithID(extension1_->id());
556  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
557
558  EXPECT_EQ(3, model_->item_count());
559  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
560  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
561  EXPECT_FALSE(
562      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
563
564  launcher_controller_->LockV1AppWithID(extension1_->id());
565
566  EXPECT_EQ(3, model_->item_count());
567  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
568  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
569  EXPECT_FALSE(
570      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
571
572  launcher_controller_->UnlockV1AppWithID(extension1_->id());
573
574  EXPECT_EQ(3, model_->item_count());
575  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
576  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
577  EXPECT_FALSE(
578      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
579
580  launcher_controller_->UnpinAppsWithID(extension1_->id());
581
582  EXPECT_EQ(2, model_->item_count());
583}
584
585// Check that already pinned items which get locked stay after unpinning.
586TEST_F(ChromeLauncherControllerPerAppTest, CheckPinnedAppsStayAfterUnlock) {
587  InitLauncherController();
588  // Model should only contain the browser shortcut and app list items.
589  EXPECT_EQ(2, model_->item_count());
590  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
591  EXPECT_FALSE(
592      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
593
594  launcher_controller_->PinAppWithID(extension1_->id());
595
596  EXPECT_EQ(3, model_->item_count());
597  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
598  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
599  EXPECT_FALSE(
600      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
601
602  launcher_controller_->LockV1AppWithID(extension1_->id());
603
604  EXPECT_EQ(3, model_->item_count());
605  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
606  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
607  EXPECT_FALSE(
608      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
609
610  launcher_controller_->UnpinAppsWithID(extension1_->id());
611
612  EXPECT_EQ(3, model_->item_count());
613  EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[1].type);
614  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
615  EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
616
617  launcher_controller_->UnlockV1AppWithID(extension1_->id());
618
619  EXPECT_EQ(2, model_->item_count());
620}
621
622// Check that lock -> pin -> unlock -> unpin does properly transition.
623TEST_F(ChromeLauncherControllerPerAppTest, CheckLockPinUnlockUnpin) {
624  InitLauncherController();
625  // Model should only contain the browser shortcut and app list items.
626  EXPECT_EQ(2, model_->item_count());
627  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
628  EXPECT_FALSE(
629      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
630
631  launcher_controller_->LockV1AppWithID(extension1_->id());
632
633  EXPECT_EQ(3, model_->item_count());
634  EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[1].type);
635  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
636  EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
637
638  launcher_controller_->PinAppWithID(extension1_->id());
639
640  EXPECT_EQ(3, model_->item_count());
641  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
642  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
643  EXPECT_FALSE(
644      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
645
646  launcher_controller_->UnlockV1AppWithID(extension1_->id());
647
648  EXPECT_EQ(3, model_->item_count());
649  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
650  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
651  EXPECT_FALSE(
652      launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
653
654  launcher_controller_->UnpinAppsWithID(extension1_->id());
655
656  EXPECT_EQ(2, model_->item_count());
657}
658
659TEST_F(ChromeLauncherControllerPerAppTest, Policy) {
660  extension_service_->AddExtension(extension1_.get());
661  extension_service_->AddExtension(extension3_.get());
662
663  base::ListValue policy_value;
664  InsertPrefValue(&policy_value, 0, extension1_->id());
665  InsertPrefValue(&policy_value, 1, extension2_->id());
666  profile()->GetTestingPrefService()->SetManagedPref(prefs::kPinnedLauncherApps,
667                                                     policy_value.DeepCopy());
668
669  // Only |extension1_| should get pinned. |extension2_| is specified but not
670  // installed, and |extension3_| is part of the default set, but that shouldn't
671  // take effect when the policy override is in place.
672  InitLauncherController();
673  EXPECT_EQ(3, model_->item_count());
674  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
675  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
676  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
677  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
678
679  // Installing |extension2_| should add it to the launcher.
680  extension_service_->AddExtension(extension2_.get());
681  EXPECT_EQ(4, model_->item_count());
682  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
683  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type);
684  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
685  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
686  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
687
688  // Removing |extension1_| from the policy should be reflected in the launcher.
689  policy_value.Remove(0, NULL);
690  profile()->GetTestingPrefService()->SetManagedPref(prefs::kPinnedLauncherApps,
691                                                     policy_value.DeepCopy());
692  EXPECT_EQ(3, model_->item_count());
693  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type);
694  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
695  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
696  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
697}
698
699TEST_F(ChromeLauncherControllerPerAppTest, UnpinWithUninstall) {
700  extension_service_->AddExtension(extension3_.get());
701  extension_service_->AddExtension(extension4_.get());
702
703  InitLauncherController();
704
705  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id()));
706  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension4_->id()));
707
708  extension_service_->UnloadExtension(extension3_->id(),
709                                      extension_misc::UNLOAD_REASON_UNINSTALL);
710
711  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
712  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension4_->id()));
713}
714
715TEST_F(ChromeLauncherControllerPerAppTest, PrefUpdates) {
716  extension_service_->AddExtension(extension2_.get());
717  extension_service_->AddExtension(extension3_.get());
718  extension_service_->AddExtension(extension4_.get());
719
720  InitLauncherController();
721
722  std::vector<std::string> expected_launchers;
723  std::vector<std::string> actual_launchers;
724  base::ListValue pref_value;
725  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
726                                                 pref_value.DeepCopy());
727  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
728  EXPECT_EQ(expected_launchers, actual_launchers);
729
730  // Unavailable extensions don't create launcher items.
731  InsertPrefValue(&pref_value, 0, extension1_->id());
732  InsertPrefValue(&pref_value, 1, extension2_->id());
733  InsertPrefValue(&pref_value, 2, extension4_->id());
734  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
735                                                 pref_value.DeepCopy());
736  expected_launchers.push_back(extension2_->id());
737  expected_launchers.push_back(extension4_->id());
738  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
739  EXPECT_EQ(expected_launchers, actual_launchers);
740
741  // Redundant pref entries show up only once.
742  InsertPrefValue(&pref_value, 2, extension3_->id());
743  InsertPrefValue(&pref_value, 2, extension3_->id());
744  InsertPrefValue(&pref_value, 5, extension3_->id());
745  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
746                                                 pref_value.DeepCopy());
747  expected_launchers.insert(expected_launchers.begin() + 1, extension3_->id());
748  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
749  EXPECT_EQ(expected_launchers, actual_launchers);
750
751  // Order changes are reflected correctly.
752  pref_value.Clear();
753  InsertPrefValue(&pref_value, 0, extension4_->id());
754  InsertPrefValue(&pref_value, 1, extension3_->id());
755  InsertPrefValue(&pref_value, 2, extension2_->id());
756  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
757                                                 pref_value.DeepCopy());
758  std::reverse(expected_launchers.begin(), expected_launchers.end());
759  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
760  EXPECT_EQ(expected_launchers, actual_launchers);
761
762  // Clearing works.
763  pref_value.Clear();
764  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
765                                                 pref_value.DeepCopy());
766  expected_launchers.clear();
767  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
768  EXPECT_EQ(expected_launchers, actual_launchers);
769}
770
771TEST_F(ChromeLauncherControllerPerAppTest, PendingInsertionOrder) {
772  extension_service_->AddExtension(extension1_.get());
773  extension_service_->AddExtension(extension3_.get());
774
775  InitLauncherController();
776
777  base::ListValue pref_value;
778  InsertPrefValue(&pref_value, 0, extension1_->id());
779  InsertPrefValue(&pref_value, 1, extension2_->id());
780  InsertPrefValue(&pref_value, 2, extension3_->id());
781  profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps,
782                                                 pref_value.DeepCopy());
783
784  std::vector<std::string> expected_launchers;
785  expected_launchers.push_back(extension1_->id());
786  expected_launchers.push_back(extension3_->id());
787  std::vector<std::string> actual_launchers;
788
789  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
790  EXPECT_EQ(expected_launchers, actual_launchers);
791
792  // Install |extension2| and verify it shows up between the other two.
793  extension_service_->AddExtension(extension2_.get());
794  expected_launchers.insert(expected_launchers.begin() + 1, extension2_->id());
795  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
796  EXPECT_EQ(expected_launchers, actual_launchers);
797}
798
799// Checks the created menus and menu lists for correctness. It uses the given
800// |controller| to create the objects for the given |item| and checks the
801// found item count against the |expected_items|. The |title| list contains the
802// menu titles in the order of their appearance in the menu (not including the
803// application name).
804bool CheckMenuCreation(ChromeLauncherControllerPerApp* controller,
805                       const ash::LauncherItem& item,
806                       size_t expected_items,
807                       string16 title[],
808                       bool is_browser) {
809  ChromeLauncherAppMenuItems items = controller->GetApplicationList(item, 0);
810  // A new behavior has been added: Only show menus if there is at least one
811  // item available.
812  if (expected_items < 1 && is_browser) {
813    EXPECT_EQ(0u, items.size());
814    return items.size() == 0;
815  }
816  // There should be one item in there: The title.
817  EXPECT_EQ(expected_items + 1, items.size());
818  EXPECT_FALSE(items[0]->IsEnabled());
819  for (size_t i = 0; i < expected_items; i++) {
820    EXPECT_EQ(title[i], items[1 + i]->title());
821    // Check that the first real item has a leading separator.
822    if (i == 1)
823      EXPECT_TRUE(items[i]->HasLeadingSeparator());
824    else
825      EXPECT_FALSE(items[i]->HasLeadingSeparator());
826  }
827
828  scoped_ptr<ash::LauncherMenuModel> menu(
829      controller->CreateApplicationMenu(item, 0));
830  // The first element in the menu is a spacing separator. On some systems
831  // (e.g. Windows) such things do not exist. As such we check the existence
832  // and adjust dynamically.
833  int first_item = menu->GetTypeAt(0) == ui::MenuModel::TYPE_SEPARATOR ? 1 : 0;
834  int expected_menu_items = first_item +
835                            (expected_items ? (expected_items + 3) : 2);
836  EXPECT_EQ(expected_menu_items, menu->GetItemCount());
837  EXPECT_FALSE(menu->IsEnabledAt(first_item));
838  if (expected_items) {
839    EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR,
840              menu->GetTypeAt(first_item + 1));
841  }
842  return items.size() == expected_items + 1;
843}
844
845// Check that browsers get reflected correctly in the launcher menu.
846TEST_F(ChromeLauncherControllerPerAppTest, BrowserMenuGeneration) {
847  EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
848  chrome::NewTab(browser());
849
850  InitLauncherController();
851
852  // Check that the browser list is empty at this time.
853  ash::LauncherItem item_browser;
854  item_browser.type = ash::TYPE_BROWSER_SHORTCUT;
855  item_browser.id =
856      launcher_controller_->GetLauncherIDForAppID(extension_misc::kChromeAppId);
857  EXPECT_TRUE(CheckMenuCreation(
858      launcher_controller_.get(), item_browser, 0, NULL, true));
859
860  // Now make the created browser() visible by adding it to the active browser
861  // list.
862  BrowserList::SetLastActive(browser());
863  string16 title1 = ASCIIToUTF16("Test1");
864  NavigateAndCommitActiveTabWithTitle(browser(), GURL("http://test1"), title1);
865  string16 one_menu_item[] = {title1};
866
867  EXPECT_TRUE(CheckMenuCreation(
868      launcher_controller_.get(), item_browser, 1, one_menu_item, true));
869
870  // Create one more browser/window and check that one more was added.
871  Browser::CreateParams ash_params(profile(), chrome::HOST_DESKTOP_TYPE_ASH);
872  scoped_ptr<Browser> browser2(
873      chrome::CreateBrowserWithTestWindowForParams(&ash_params));
874  chrome::NewTab(browser2.get());
875  BrowserList::SetLastActive(browser2.get());
876  string16 title2 = ASCIIToUTF16("Test2");
877  NavigateAndCommitActiveTabWithTitle(browser2.get(), GURL("http://test2"),
878                                      title2);
879
880  // Check that the list contains now two entries - make furthermore sure that
881  // the active item is the first entry.
882  string16 two_menu_items[] = {title1, title2};
883  EXPECT_TRUE(CheckMenuCreation(
884      launcher_controller_.get(), item_browser, 2, two_menu_items, true));
885
886  // Apparently we have to close all tabs we have.
887  chrome::CloseTab(browser2.get());
888}
889
890// Check that V1 apps are correctly reflected in the launcher menu using the
891// refocus logic.
892// Note that the extension matching logic is tested by the extension system
893// and does not need a separate test here.
894TEST_F(ChromeLauncherControllerPerAppTest, V1AppMenuGeneration) {
895  EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
896  EXPECT_EQ(0, browser()->tab_strip_model()->count());
897
898  InitLauncherControllerWithBrowser();
899
900  // Model should only contain the browser shortcut and app list items.
901  EXPECT_EQ(2, model_->item_count());
902  EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id()));
903
904  // Installing |extension3_| adds it to the launcher.
905  ash::LauncherID gmail_id = model_->next_id();
906  extension_service_->AddExtension(extension3_.get());
907  EXPECT_EQ(3, model_->item_count());
908  int gmail_index = model_->ItemIndexByID(gmail_id);
909  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[gmail_index].type);
910  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id()));
911  launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url));
912
913  // Check the menu content.
914  ash::LauncherItem item_browser;
915  item_browser.type = ash::TYPE_BROWSER_SHORTCUT;
916  item_browser.id =
917      launcher_controller_->GetLauncherIDForAppID(extension_misc::kChromeAppId);
918
919  ash::LauncherItem item_gmail;
920  item_gmail.type = ash::TYPE_APP_SHORTCUT;
921  item_gmail.id = gmail_id;
922  EXPECT_TRUE(CheckMenuCreation(
923      launcher_controller_.get(), item_gmail, 0, NULL, false));
924
925  // Set the gmail URL to a new tab.
926  string16 title1 = ASCIIToUTF16("Test1");
927  NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1);
928
929  string16 one_menu_item[] = {title1};
930  EXPECT_TRUE(CheckMenuCreation(
931      launcher_controller_.get(), item_gmail, 1, one_menu_item, false));
932
933  // Create one empty tab.
934  chrome::NewTab(browser());
935  string16 title2 = ASCIIToUTF16("Test2");
936  NavigateAndCommitActiveTabWithTitle(
937      browser(),
938      GURL("https://bla"),
939      title2);
940
941  // and another one with another gmail instance.
942  chrome::NewTab(browser());
943  string16 title3 = ASCIIToUTF16("Test3");
944  NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title3);
945  string16 two_menu_items[] = {title1, title3};
946  EXPECT_TRUE(CheckMenuCreation(
947      launcher_controller_.get(), item_gmail, 2, two_menu_items, false));
948
949  // Even though the item is in the V1 app list, it should also be in the
950  // browser list.
951  string16 browser_menu_item[] = {title3};
952  EXPECT_TRUE(CheckMenuCreation(
953      launcher_controller_.get(), item_browser, 1, browser_menu_item, false));
954
955  // Test that closing of (all) the item(s) does work (and all menus get
956  // updated properly).
957  launcher_controller_->Close(item_gmail.id);
958
959  EXPECT_TRUE(CheckMenuCreation(
960      launcher_controller_.get(), item_gmail, 0, NULL, false));
961  string16 browser_menu_item2[] = {title2};
962  EXPECT_TRUE(CheckMenuCreation(
963      launcher_controller_.get(), item_browser, 1, browser_menu_item2, false));
964}
965
966// Checks that the generated menu list properly activates items.
967TEST_F(ChromeLauncherControllerPerAppTest, V1AppMenuExecution) {
968  InitLauncherControllerWithBrowser();
969
970  // Add |extension3_| to the launcher and add two items.
971  GURL gmail = GURL("https://mail.google.com/mail/u");
972  ash::LauncherID gmail_id = model_->next_id();
973  extension_service_->AddExtension(extension3_.get());
974  launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url));
975  string16 title1 = ASCIIToUTF16("Test1");
976  NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1);
977  chrome::NewTab(browser());
978  string16 title2 = ASCIIToUTF16("Test2");
979  NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title2);
980
981  // Check that the menu is properly set.
982  ash::LauncherItem item_gmail;
983  item_gmail.type = ash::TYPE_APP_SHORTCUT;
984  item_gmail.id = gmail_id;
985  string16 two_menu_items[] = {title1, title2};
986  EXPECT_TRUE(CheckMenuCreation(
987      launcher_controller_.get(), item_gmail, 2, two_menu_items, false));
988  EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
989  // Execute the second item in the list (which shouldn't do anything since that
990  // item is per definition already the active tab).
991  {
992    scoped_ptr<ash::LauncherMenuModel> menu(
993        launcher_controller_->CreateApplicationMenu(item_gmail, 0));
994    // The first element in the menu is a spacing separator. On some systems
995    // (e.g. Windows) such things do not exist. As such we check the existence
996    // and adjust dynamically.
997    int first_item =
998        (menu->GetTypeAt(0) == ui::MenuModel::TYPE_SEPARATOR) ? 1 : 0;
999    menu->ActivatedAt(first_item + 3);
1000  }
1001  EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
1002
1003  // Execute the first item.
1004  {
1005    scoped_ptr<ash::LauncherMenuModel> menu(
1006        launcher_controller_->CreateApplicationMenu(item_gmail, 0));
1007    int first_item =
1008        (menu->GetTypeAt(0) == ui::MenuModel::TYPE_SEPARATOR) ? 1 : 0;
1009    menu->ActivatedAt(first_item + 2);
1010  }
1011  // Now the active tab should be the second item.
1012  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
1013}
1014
1015// Checks that the generated menu list properly deletes items.
1016TEST_F(ChromeLauncherControllerPerAppTest, V1AppMenuDeletionExecution) {
1017  InitLauncherControllerWithBrowser();
1018
1019  // Add |extension3_| to the launcher and add two items.
1020  GURL gmail = GURL("https://mail.google.com/mail/u");
1021  ash::LauncherID gmail_id = model_->next_id();
1022  extension_service_->AddExtension(extension3_.get());
1023  launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url));
1024  string16 title1 = ASCIIToUTF16("Test1");
1025  NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1);
1026  chrome::NewTab(browser());
1027  string16 title2 = ASCIIToUTF16("Test2");
1028  NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title2);
1029
1030  // Check that the menu is properly set.
1031  ash::LauncherItem item_gmail;
1032  item_gmail.type = ash::TYPE_APP_SHORTCUT;
1033  item_gmail.id = gmail_id;
1034  string16 two_menu_items[] = {title1, title2};
1035  EXPECT_TRUE(CheckMenuCreation(
1036      launcher_controller_.get(), item_gmail, 2, two_menu_items, false));
1037
1038  int tabs = browser()->tab_strip_model()->count();
1039  // Activate the proper tab through the menu item.
1040  {
1041    ChromeLauncherAppMenuItems items =
1042        launcher_controller_->GetApplicationList(item_gmail, 0);
1043    items[1]->Execute(0);
1044    EXPECT_EQ(tabs, browser()->tab_strip_model()->count());
1045  }
1046
1047  // Delete one tab through the menu item.
1048  {
1049    ChromeLauncherAppMenuItems items =
1050        launcher_controller_->GetApplicationList(item_gmail, 0);
1051    items[1]->Execute(ui::EF_SHIFT_DOWN);
1052    EXPECT_EQ(--tabs, browser()->tab_strip_model()->count());
1053  }
1054}
1055
1056// Tests that panels create launcher items correctly
1057TEST_F(ChromeLauncherControllerPerAppTest, AppPanels) {
1058  InitLauncherControllerWithBrowser();
1059  EXPECT_EQ(1, model_observer_->added());
1060
1061  TestAppIconLoaderImpl* app_icon_loader = new TestAppIconLoaderImpl();
1062  SetAppIconLoader(app_icon_loader);
1063
1064  // Test adding an app panel
1065  std::string app_id = extension1_->id();
1066  ShellWindowLauncherItemController app_panel_controller(
1067      LauncherItemController::TYPE_APP_PANEL, "id", app_id,
1068      launcher_controller_.get());
1069  ash::LauncherID launcher_id1 = launcher_controller_->CreateAppLauncherItem(
1070      &app_panel_controller, app_id, ash::STATUS_RUNNING);
1071  int panel_index = model_observer_->last_index();
1072  EXPECT_EQ(2, model_observer_->added());
1073  EXPECT_EQ(0, model_observer_->changed());
1074  EXPECT_EQ(1, app_icon_loader->fetch_count());
1075  model_observer_->clear_counts();
1076
1077  // App panels should have a separate identifier than the app id
1078  EXPECT_EQ(0, launcher_controller_->GetLauncherIDForAppID(app_id));
1079
1080  // Setting the app image image should not change the panel if it set its icon
1081  app_panel_controller.set_image_set_by_controller(true);
1082  gfx::ImageSkia image;
1083  launcher_controller_->SetAppImage(app_id, image);
1084  EXPECT_EQ(0, model_observer_->changed());
1085  model_observer_->clear_counts();
1086
1087  // Add a second app panel and verify that it get the same index as the first
1088  // one had, being added to the left of the existing panel.
1089  ash::LauncherID launcher_id2 = launcher_controller_->CreateAppLauncherItem(
1090      &app_panel_controller, app_id, ash::STATUS_RUNNING);
1091  EXPECT_EQ(panel_index, model_observer_->last_index());
1092  EXPECT_EQ(1, model_observer_->added());
1093  model_observer_->clear_counts();
1094
1095  launcher_controller_->CloseLauncherItem(launcher_id2);
1096  launcher_controller_->CloseLauncherItem(launcher_id1);
1097  EXPECT_EQ(2, model_observer_->removed());
1098}
1099
1100// Tests that the Gmail extension matches more then the app itself claims with
1101// the manifest file.
1102TEST_F(ChromeLauncherControllerPerAppTest, GmailMatching) {
1103  InitLauncherControllerWithBrowser();
1104
1105  // Create a Gmail browser tab.
1106  chrome::NewTab(browser());
1107  string16 title = ASCIIToUTF16("Test");
1108  NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title);
1109  content::WebContents* content =
1110      browser()->tab_strip_model()->GetActiveWebContents();
1111
1112  // Check that the launcher controller does not recognize the running app.
1113  EXPECT_FALSE(launcher_controller_->ContentCanBeHandledByGmailApp(content));
1114
1115  // Installing |extension3_| adds it to the launcher.
1116  ash::LauncherID gmail_id = model_->next_id();
1117  extension_service_->AddExtension(extension3_.get());
1118  EXPECT_EQ(3, model_->item_count());
1119  int gmail_index = model_->ItemIndexByID(gmail_id);
1120  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[gmail_index].type);
1121  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id()));
1122
1123  // Check that it is now handled.
1124  EXPECT_TRUE(launcher_controller_->ContentCanBeHandledByGmailApp(content));
1125
1126  // Check also that the app has detected that properly.
1127  ash::LauncherItem item_gmail;
1128  item_gmail.type = ash::TYPE_APP_SHORTCUT;
1129  item_gmail.id = gmail_id;
1130  EXPECT_EQ(2U, launcher_controller_->GetApplicationList(item_gmail, 0).size());
1131}
1132
1133// Tests that the Gmail extension does not match the offline verison.
1134TEST_F(ChromeLauncherControllerPerAppTest, GmailOfflineMatching) {
1135  InitLauncherControllerWithBrowser();
1136
1137  // Create a Gmail browser tab.
1138  chrome::NewTab(browser());
1139  string16 title = ASCIIToUTF16("Test");
1140  NavigateAndCommitActiveTabWithTitle(browser(),
1141                                      GURL(offline_gmail_url),
1142                                      title);
1143  content::WebContents* content =
1144      browser()->tab_strip_model()->GetActiveWebContents();
1145
1146  // Installing |extension3_| adds it to the launcher.
1147  ash::LauncherID gmail_id = model_->next_id();
1148  extension_service_->AddExtension(extension3_.get());
1149  EXPECT_EQ(3, model_->item_count());
1150  int gmail_index = model_->ItemIndexByID(gmail_id);
1151  EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[gmail_index].type);
1152  EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id()));
1153
1154  // The content should not be able to be handled by the app.
1155  EXPECT_FALSE(launcher_controller_->ContentCanBeHandledByGmailApp(content));
1156}
1157