| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h" | 5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "ash/common/ash_switches.h" | 11 #include "ash/common/ash_switches.h" |
| 12 #include "ash/common/multi_profile_uma.h" | 12 #include "ash/common/multi_profile_uma.h" |
| 13 #include "ash/common/shelf/shelf_item_delegate_manager.h" | |
| 14 #include "ash/common/shelf/shelf_model.h" | 13 #include "ash/common/shelf/shelf_model.h" |
| 15 #include "ash/common/system/tray/system_tray_delegate.h" | 14 #include "ash/common/system/tray/system_tray_delegate.h" |
| 16 #include "ash/common/wm_shell.h" | 15 #include "ash/common/wm_shell.h" |
| 17 #include "ash/desktop_background/desktop_background_controller.h" | 16 #include "ash/desktop_background/desktop_background_controller.h" |
| 18 #include "ash/root_window_controller.h" | 17 #include "ash/root_window_controller.h" |
| 19 #include "ash/shelf/shelf.h" | 18 #include "ash/shelf/shelf.h" |
| 20 #include "ash/shell.h" | 19 #include "ash/shell.h" |
| 21 #include "ash/wm/window_util.h" | 20 #include "ash/wm/window_util.h" |
| 22 #include "base/command_line.h" | 21 #include "base/command_line.h" |
| 23 #include "base/macros.h" | 22 #include "base/macros.h" |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 185 if (chrome::MultiUserWindowManager::GetMultiProfileMode() == | 184 if (chrome::MultiUserWindowManager::GetMultiProfileMode() == |
| 186 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) | 185 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) |
| 187 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile); | 186 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile); |
| 188 controller_->AdditionalUserAddedToSession(profile->GetOriginalProfile()); | 187 controller_->AdditionalUserAddedToSession(profile->GetOriginalProfile()); |
| 189 } | 188 } |
| 190 | 189 |
| 191 ChromeLauncherControllerImpl::ChromeLauncherControllerImpl( | 190 ChromeLauncherControllerImpl::ChromeLauncherControllerImpl( |
| 192 Profile* profile, | 191 Profile* profile, |
| 193 ash::ShelfModel* model) | 192 ash::ShelfModel* model) |
| 194 : model_(model), profile_(profile) { | 193 : model_(model), profile_(profile) { |
| 194 DCHECK(model_); |
| 195 if (!profile_) { | 195 if (!profile_) { |
| 196 // If no profile was passed, we take the currently active profile and use it | 196 // If no profile was passed, we take the currently active profile and use it |
| 197 // as the owner of the current desktop. | 197 // as the owner of the current desktop. |
| 198 // Use the original profile as on chromeos we may get a temporary off the | 198 // Use the original profile as on chromeos we may get a temporary off the |
| 199 // record profile, unless in guest session (where off the record profile is | 199 // record profile, unless in guest session (where off the record profile is |
| 200 // the right one). | 200 // the right one). |
| 201 profile_ = ProfileManager::GetActiveUserProfile(); | 201 profile_ = ProfileManager::GetActiveUserProfile(); |
| 202 if (!profile_->IsGuestSession() && !profile_->IsSystemProfile()) | 202 if (!profile_->IsGuestSession() && !profile_->IsSystemProfile()) |
| 203 profile_ = profile_->GetOriginalProfile(); | 203 profile_ = profile_->GetOriginalProfile(); |
| 204 | 204 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 } | 246 } |
| 247 app_window_controllers_.push_back(std::move(extension_app_window_controller)); | 247 app_window_controllers_.push_back(std::move(extension_app_window_controller)); |
| 248 | 248 |
| 249 std::unique_ptr<AppWindowLauncherController> arc_app_window_controller; | 249 std::unique_ptr<AppWindowLauncherController> arc_app_window_controller; |
| 250 arc_app_window_controller.reset( | 250 arc_app_window_controller.reset( |
| 251 new ArcAppWindowLauncherController(this, this)); | 251 new ArcAppWindowLauncherController(this, this)); |
| 252 app_window_controllers_.push_back(std::move(arc_app_window_controller)); | 252 app_window_controllers_.push_back(std::move(arc_app_window_controller)); |
| 253 | 253 |
| 254 // Right now ash::Shell isn't created for tests. | 254 // Right now ash::Shell isn't created for tests. |
| 255 // TODO(mukai): Allows it to observe display change and write tests. | 255 // TODO(mukai): Allows it to observe display change and write tests. |
| 256 if (ash::Shell::HasInstance()) { | 256 if (ash::Shell::HasInstance()) |
| 257 ash::Shell::GetInstance()->window_tree_host_manager()->AddObserver(this); | 257 ash::Shell::GetInstance()->window_tree_host_manager()->AddObserver(this); |
| 258 // If it got already set, we remove the observer first again and swap the | |
| 259 // ItemDelegateManager. | |
| 260 if (item_delegate_manager_) | |
| 261 item_delegate_manager_->RemoveObserver(this); | |
| 262 item_delegate_manager_ = | |
| 263 ash::Shell::GetInstance()->shelf_item_delegate_manager(); | |
| 264 item_delegate_manager_->AddObserver(this); | |
| 265 } | |
| 266 } | 258 } |
| 267 | 259 |
| 268 ChromeLauncherControllerImpl::~ChromeLauncherControllerImpl() { | 260 ChromeLauncherControllerImpl::~ChromeLauncherControllerImpl() { |
| 269 if (item_delegate_manager_) | |
| 270 item_delegate_manager_->RemoveObserver(this); | |
| 271 | |
| 272 // Reset the BrowserStatusMonitor as it has a weak pointer to this. | 261 // Reset the BrowserStatusMonitor as it has a weak pointer to this. |
| 273 browser_status_monitor_.reset(); | 262 browser_status_monitor_.reset(); |
| 274 | 263 |
| 275 // Reset the app window controllers here since it has a weak pointer to this. | 264 // Reset the app window controllers here since it has a weak pointer to this. |
| 276 app_window_controllers_.clear(); | 265 app_window_controllers_.clear(); |
| 277 | 266 |
| 278 model_->RemoveObserver(this); | 267 model_->RemoveObserver(this); |
| 279 if (ash::Shell::HasInstance()) | 268 if (ash::Shell::HasInstance()) |
| 280 ash::Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this); | 269 ash::Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this); |
| 281 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin(); | 270 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin(); |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 428 | 417 |
| 429 bool ChromeLauncherControllerImpl::IsPinnable(ash::ShelfID id) const { | 418 bool ChromeLauncherControllerImpl::IsPinnable(ash::ShelfID id) const { |
| 430 int index = model_->ItemIndexByID(id); | 419 int index = model_->ItemIndexByID(id); |
| 431 if (index == -1) | 420 if (index == -1) |
| 432 return false; | 421 return false; |
| 433 | 422 |
| 434 ash::ShelfItemType type = model_->items()[index].type; | 423 ash::ShelfItemType type = model_->items()[index].type; |
| 435 std::string app_id; | 424 std::string app_id; |
| 436 return ((type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_PLATFORM_APP || | 425 return ((type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_PLATFORM_APP || |
| 437 type == ash::TYPE_WINDOWED_APP) && | 426 type == ash::TYPE_WINDOWED_APP) && |
| 438 item_delegate_manager_->GetShelfItemDelegate(id)->CanPin()); | 427 model_->GetShelfItemDelegate(id)->CanPin()); |
| 439 } | 428 } |
| 440 | 429 |
| 441 void ChromeLauncherControllerImpl::LockV1AppWithID(const std::string& app_id) { | 430 void ChromeLauncherControllerImpl::LockV1AppWithID(const std::string& app_id) { |
| 442 ash::ShelfID id = GetShelfIDForAppID(app_id); | 431 ash::ShelfID id = GetShelfIDForAppID(app_id); |
| 443 if (!IsPinned(id) && !IsWindowedAppInLauncher(app_id)) { | 432 if (!IsPinned(id) && !IsWindowedAppInLauncher(app_id)) { |
| 444 CreateAppShortcutLauncherItemWithType(app_id, model_->item_count(), | 433 CreateAppShortcutLauncherItemWithType(app_id, model_->item_count(), |
| 445 ash::TYPE_WINDOWED_APP); | 434 ash::TYPE_WINDOWED_APP); |
| 446 id = GetShelfIDForAppID(app_id); | 435 id = GetShelfIDForAppID(app_id); |
| 447 } | 436 } |
| 448 CHECK(id); | 437 CHECK(id); |
| (...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1020 app_icon_loaders_.clear(); | 1009 app_icon_loaders_.clear(); |
| 1021 for (auto& loader : loaders) | 1010 for (auto& loader : loaders) |
| 1022 app_icon_loaders_.push_back(std::move(loader)); | 1011 app_icon_loaders_.push_back(std::move(loader)); |
| 1023 } | 1012 } |
| 1024 | 1013 |
| 1025 const std::string& ChromeLauncherControllerImpl::GetAppIdFromShelfIdForTest( | 1014 const std::string& ChromeLauncherControllerImpl::GetAppIdFromShelfIdForTest( |
| 1026 ash::ShelfID id) { | 1015 ash::ShelfID id) { |
| 1027 return id_to_item_controller_map_[id]->app_id(); | 1016 return id_to_item_controller_map_[id]->app_id(); |
| 1028 } | 1017 } |
| 1029 | 1018 |
| 1030 void ChromeLauncherControllerImpl::SetShelfItemDelegateManagerForTest( | |
| 1031 ash::ShelfItemDelegateManager* manager) { | |
| 1032 if (item_delegate_manager_) | |
| 1033 item_delegate_manager_->RemoveObserver(this); | |
| 1034 | |
| 1035 item_delegate_manager_ = manager; | |
| 1036 | |
| 1037 if (item_delegate_manager_) | |
| 1038 item_delegate_manager_->AddObserver(this); | |
| 1039 } | |
| 1040 | |
| 1041 /////////////////////////////////////////////////////////////////////////////// | 1019 /////////////////////////////////////////////////////////////////////////////// |
| 1042 // ChromeLauncherControllerImpl private: | 1020 // ChromeLauncherControllerImpl private: |
| 1043 | 1021 |
| 1044 void ChromeLauncherControllerImpl::RememberUnpinnedRunningApplicationOrder() { | 1022 void ChromeLauncherControllerImpl::RememberUnpinnedRunningApplicationOrder() { |
| 1045 RunningAppListIds list; | 1023 RunningAppListIds list; |
| 1046 for (int i = 0; i < model_->item_count(); i++) { | 1024 for (int i = 0; i < model_->item_count(); i++) { |
| 1047 ash::ShelfItemType type = model_->items()[i].type; | 1025 ash::ShelfItemType type = model_->items()[i].type; |
| 1048 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP) | 1026 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP) |
| 1049 list.push_back(GetAppIDForShelfID(model_->items()[i].id)); | 1027 list.push_back(GetAppIDForShelfID(model_->items()[i].id)); |
| 1050 } | 1028 } |
| (...skipping 399 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1450 ash::ShelfID ChromeLauncherControllerImpl::CreateBrowserShortcutLauncherItem() { | 1428 ash::ShelfID ChromeLauncherControllerImpl::CreateBrowserShortcutLauncherItem() { |
| 1451 ash::ShelfItem browser_shortcut; | 1429 ash::ShelfItem browser_shortcut; |
| 1452 browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT; | 1430 browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT; |
| 1453 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | 1431 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 1454 browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32); | 1432 browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32); |
| 1455 ash::ShelfID id = model_->next_id(); | 1433 ash::ShelfID id = model_->next_id(); |
| 1456 model_->AddAt(0, browser_shortcut); | 1434 model_->AddAt(0, browser_shortcut); |
| 1457 id_to_item_controller_map_[id] = | 1435 id_to_item_controller_map_[id] = |
| 1458 new BrowserShortcutLauncherItemController(this, model_); | 1436 new BrowserShortcutLauncherItemController(this, model_); |
| 1459 id_to_item_controller_map_[id]->set_shelf_id(id); | 1437 id_to_item_controller_map_[id]->set_shelf_id(id); |
| 1460 // ShelfItemDelegateManager owns BrowserShortcutLauncherItemController. | 1438 // ShelfModel owns BrowserShortcutLauncherItemController. |
| 1461 SetShelfItemDelegate(id, id_to_item_controller_map_[id]); | 1439 SetShelfItemDelegate(id, id_to_item_controller_map_[id]); |
| 1462 return id; | 1440 return id; |
| 1463 } | 1441 } |
| 1464 | 1442 |
| 1465 bool ChromeLauncherControllerImpl::IsIncognito( | 1443 bool ChromeLauncherControllerImpl::IsIncognito( |
| 1466 const content::WebContents* web_contents) const { | 1444 const content::WebContents* web_contents) const { |
| 1467 const Profile* profile = | 1445 const Profile* profile = |
| 1468 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 1446 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| 1469 return profile->IsOffTheRecord() && !profile->IsGuestSession() && | 1447 return profile->IsOffTheRecord() && !profile->IsGuestSession() && |
| 1470 !profile->IsSystemProfile(); | 1448 !profile->IsSystemProfile(); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1508 tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE); | 1486 tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE); |
| 1509 browser_to_close.pop_back(); | 1487 browser_to_close.pop_back(); |
| 1510 } | 1488 } |
| 1511 } | 1489 } |
| 1512 | 1490 |
| 1513 void ChromeLauncherControllerImpl::SetShelfItemDelegate( | 1491 void ChromeLauncherControllerImpl::SetShelfItemDelegate( |
| 1514 ash::ShelfID id, | 1492 ash::ShelfID id, |
| 1515 ash::ShelfItemDelegate* item_delegate) { | 1493 ash::ShelfItemDelegate* item_delegate) { |
| 1516 DCHECK_GT(id, 0); | 1494 DCHECK_GT(id, 0); |
| 1517 DCHECK(item_delegate); | 1495 DCHECK(item_delegate); |
| 1518 DCHECK(item_delegate_manager_); | 1496 model_->SetShelfItemDelegate( |
| 1519 item_delegate_manager_->SetShelfItemDelegate( | |
| 1520 id, std::unique_ptr<ash::ShelfItemDelegate>(item_delegate)); | 1497 id, std::unique_ptr<ash::ShelfItemDelegate>(item_delegate)); |
| 1521 } | 1498 } |
| 1522 | 1499 |
| 1523 void ChromeLauncherControllerImpl::AttachProfile(Profile* profile) { | 1500 void ChromeLauncherControllerImpl::AttachProfile(Profile* profile) { |
| 1524 profile_ = profile; | 1501 profile_ = profile; |
| 1525 // Either add the profile to the list of known profiles and make it the active | 1502 // Either add the profile to the list of known profiles and make it the active |
| 1526 // one for some functions of LauncherControllerHelper or create a new one. | 1503 // one for some functions of LauncherControllerHelper or create a new one. |
| 1527 if (!launcher_controller_helper_.get()) | 1504 if (!launcher_controller_helper_.get()) |
| 1528 launcher_controller_helper_.reset(new LauncherControllerHelper(profile_)); | 1505 launcher_controller_helper_.reset(new LauncherControllerHelper(profile_)); |
| 1529 else | 1506 else |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1612 const std::string& app_id) { | 1589 const std::string& app_id) { |
| 1613 for (const auto& app_icon_loader : app_icon_loaders_) { | 1590 for (const auto& app_icon_loader : app_icon_loaders_) { |
| 1614 if (app_icon_loader->CanLoadImageForApp(app_id)) | 1591 if (app_icon_loader->CanLoadImageForApp(app_id)) |
| 1615 return app_icon_loader.get(); | 1592 return app_icon_loader.get(); |
| 1616 } | 1593 } |
| 1617 | 1594 |
| 1618 return nullptr; | 1595 return nullptr; |
| 1619 } | 1596 } |
| 1620 | 1597 |
| 1621 /////////////////////////////////////////////////////////////////////////////// | 1598 /////////////////////////////////////////////////////////////////////////////// |
| 1622 // ash::ShelfItemDelegateManagerObserver: | |
| 1623 | |
| 1624 void ChromeLauncherControllerImpl::OnSetShelfItemDelegate( | |
| 1625 ash::ShelfID id, | |
| 1626 ash::ShelfItemDelegate* item_delegate) { | |
| 1627 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we | |
| 1628 // get into this state in the first place. | |
| 1629 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); | |
| 1630 if (iter == id_to_item_controller_map_.end() || item_delegate == iter->second) | |
| 1631 return; | |
| 1632 LOG(ERROR) << "Unexpected change of shelf item id: " << id; | |
| 1633 id_to_item_controller_map_.erase(iter); | |
| 1634 } | |
| 1635 | |
| 1636 /////////////////////////////////////////////////////////////////////////////// | |
| 1637 // ash::ShelfModelObserver: | 1599 // ash::ShelfModelObserver: |
| 1638 | 1600 |
| 1639 void ChromeLauncherControllerImpl::ShelfItemAdded(int index) { | 1601 void ChromeLauncherControllerImpl::ShelfItemAdded(int index) { |
| 1640 } | 1602 } |
| 1641 | 1603 |
| 1642 void ChromeLauncherControllerImpl::ShelfItemRemoved(int index, | 1604 void ChromeLauncherControllerImpl::ShelfItemRemoved(int index, |
| 1643 ash::ShelfID id) { | 1605 ash::ShelfID id) { |
| 1644 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we | 1606 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we |
| 1645 // get into this state in the first place. | 1607 // get into this state in the first place. |
| 1646 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); | 1608 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1659 // it is the app list with the alternate shelf layout. | 1621 // it is the app list with the alternate shelf layout. |
| 1660 DCHECK_NE(ash::TYPE_APP_LIST, item.type); | 1622 DCHECK_NE(ash::TYPE_APP_LIST, item.type); |
| 1661 if (HasShelfIDToAppIDMapping(item.id) && IsPinned(item.id)) | 1623 if (HasShelfIDToAppIDMapping(item.id) && IsPinned(item.id)) |
| 1662 SyncPinPosition(item.id); | 1624 SyncPinPosition(item.id); |
| 1663 } | 1625 } |
| 1664 | 1626 |
| 1665 void ChromeLauncherControllerImpl::ShelfItemChanged( | 1627 void ChromeLauncherControllerImpl::ShelfItemChanged( |
| 1666 int index, | 1628 int index, |
| 1667 const ash::ShelfItem& old_item) {} | 1629 const ash::ShelfItem& old_item) {} |
| 1668 | 1630 |
| 1631 void ChromeLauncherControllerImpl::OnSetShelfItemDelegate( |
| 1632 ash::ShelfID id, |
| 1633 ash::ShelfItemDelegate* item_delegate) { |
| 1634 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we |
| 1635 // get into this state in the first place. |
| 1636 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); |
| 1637 if (iter == id_to_item_controller_map_.end() || item_delegate == iter->second) |
| 1638 return; |
| 1639 LOG(ERROR) << "Unexpected change of shelf item id: " << id; |
| 1640 id_to_item_controller_map_.erase(iter); |
| 1641 } |
| 1642 |
| 1669 /////////////////////////////////////////////////////////////////////////////// | 1643 /////////////////////////////////////////////////////////////////////////////// |
| 1670 // ash::WindowTreeHostManager::Observer: | 1644 // ash::WindowTreeHostManager::Observer: |
| 1671 | 1645 |
| 1672 void ChromeLauncherControllerImpl::OnDisplayConfigurationChanged() { | 1646 void ChromeLauncherControllerImpl::OnDisplayConfigurationChanged() { |
| 1673 // In BOTTOM_LOCKED state, ignore the call of SetShelfBehaviorsFromPrefs. | 1647 // In BOTTOM_LOCKED state, ignore the call of SetShelfBehaviorsFromPrefs. |
| 1674 // Because it might be called by some operations, like crbug.com/627040 | 1648 // Because it might be called by some operations, like crbug.com/627040 |
| 1675 // rotating screen. | 1649 // rotating screen. |
| 1676 ash::Shelf* shelf = ash::Shelf::ForPrimaryDisplay(); | 1650 ash::Shelf* shelf = ash::Shelf::ForPrimaryDisplay(); |
| 1677 if (!shelf || shelf->alignment() != ash::SHELF_ALIGNMENT_BOTTOM_LOCKED) | 1651 if (!shelf || shelf->alignment() != ash::SHELF_ALIGNMENT_BOTTOM_LOCKED) |
| 1678 SetShelfBehaviorsFromPrefs(); | 1652 SetShelfBehaviorsFromPrefs(); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 1707 if (index == -1) | 1681 if (index == -1) |
| 1708 continue; | 1682 continue; |
| 1709 ash::ShelfItem item = model_->items()[index]; | 1683 ash::ShelfItem item = model_->items()[index]; |
| 1710 item.image = image; | 1684 item.image = image; |
| 1711 if (arc_deferred_launcher_) | 1685 if (arc_deferred_launcher_) |
| 1712 arc_deferred_launcher_->MaybeApplySpinningEffect(id, &item.image); | 1686 arc_deferred_launcher_->MaybeApplySpinningEffect(id, &item.image); |
| 1713 model_->Set(index, item); | 1687 model_->Set(index, item); |
| 1714 // It's possible we're waiting on more than one item, so don't break. | 1688 // It's possible we're waiting on more than one item, so don't break. |
| 1715 } | 1689 } |
| 1716 } | 1690 } |
| OLD | NEW |