Commit 43f8421c authored by David Black's avatar David Black Committed by Commit Bot

Fix crash in holding space view containers.

Previously the code which determined the index at which to insert a view
for a finalized item assumed that newer finalized items would have
already been added as views before older finalized items. This
assumption is not true when showing UI with a model already containing
finalized items.

Bug: 1143371
Change-Id: I836d8169b8ded78e418abd70bd95a146ababef53
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2505960
Commit-Queue: David Black <dmblack@google.com>
Reviewed-by: default avatarToni Baržić <tbarzic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#821906}
parent a613efeb
...@@ -35,7 +35,7 @@ void HoldingSpaceItemViewsContainer::OnHoldingSpaceModelAttached( ...@@ -35,7 +35,7 @@ void HoldingSpaceItemViewsContainer::OnHoldingSpaceModelAttached(
model_observer_.Add(model); model_observer_.Add(model);
for (const auto& item : model->items()) { for (const auto& item : model->items()) {
if (item->IsFinalized()) if (item->IsFinalized())
AddHoldingSpaceItemView(item.get()); AddHoldingSpaceItemView(item.get(), /*due_to_finalization=*/false);
} }
} }
...@@ -50,7 +50,7 @@ void HoldingSpaceItemViewsContainer::OnHoldingSpaceItemAdded( ...@@ -50,7 +50,7 @@ void HoldingSpaceItemViewsContainer::OnHoldingSpaceItemAdded(
if (!item->IsFinalized()) if (!item->IsFinalized())
return; return;
AddHoldingSpaceItemView(item); AddHoldingSpaceItemView(item, /*due_to_finalization=*/false);
} }
void HoldingSpaceItemViewsContainer::OnHoldingSpaceItemRemoved( void HoldingSpaceItemViewsContainer::OnHoldingSpaceItemRemoved(
...@@ -60,7 +60,7 @@ void HoldingSpaceItemViewsContainer::OnHoldingSpaceItemRemoved( ...@@ -60,7 +60,7 @@ void HoldingSpaceItemViewsContainer::OnHoldingSpaceItemRemoved(
void HoldingSpaceItemViewsContainer::OnHoldingSpaceItemFinalized( void HoldingSpaceItemViewsContainer::OnHoldingSpaceItemFinalized(
const HoldingSpaceItem* item) { const HoldingSpaceItem* item) {
AddHoldingSpaceItemView(item); AddHoldingSpaceItemView(item, /*due_to_finalization=*/true);
} }
} // namespace ash } // namespace ash
...@@ -36,7 +36,8 @@ class HoldingSpaceItemViewsContainer : public views::View, ...@@ -36,7 +36,8 @@ class HoldingSpaceItemViewsContainer : public views::View,
// widget is being closed (which happens asynchronously). // widget is being closed (which happens asynchronously).
void Reset(); void Reset();
virtual void AddHoldingSpaceItemView(const HoldingSpaceItem* item) = 0; virtual void AddHoldingSpaceItemView(const HoldingSpaceItem* item,
bool due_to_finalization) = 0;
virtual void RemoveAllHoldingSpaceItemViews() = 0; virtual void RemoveAllHoldingSpaceItemViews() = 0;
virtual void RemoveHoldingSpaceItemView(const HoldingSpaceItem* item) = 0; virtual void RemoveHoldingSpaceItemView(const HoldingSpaceItem* item) = 0;
......
...@@ -4,7 +4,11 @@ ...@@ -4,7 +4,11 @@
#include "ash/system/holding_space/holding_space_tray.h" #include "ash/system/holding_space/holding_space_tray.h"
#include <deque>
#include <vector>
#include "ash/public/cpp/ash_features.h" #include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/holding_space/holding_space_constants.h"
#include "ash/public/cpp/holding_space/holding_space_controller.h" #include "ash/public/cpp/holding_space/holding_space_controller.h"
#include "ash/public/cpp/holding_space/holding_space_image.h" #include "ash/public/cpp/holding_space/holding_space_image.h"
#include "ash/public/cpp/holding_space/holding_space_item.h" #include "ash/public/cpp/holding_space/holding_space_item.h"
...@@ -310,6 +314,38 @@ TEST_F(HoldingSpaceTrayTest, DownloadsContainer) { ...@@ -310,6 +314,38 @@ TEST_F(HoldingSpaceTrayTest, DownloadsContainer) {
test_api()->Close(); test_api()->Close();
} }
// Verifies the downloads container is shown and orders items as expected when
// the model contains a number of finalized items prior to showing UI.
TEST_F(HoldingSpaceTrayTest, DownloadsContainerWithFinalizedItemsOnly) {
MarkTimeOfFirstPin();
StartSession();
// Add a number of finalized download items.
std::deque<HoldingSpaceItem*> items;
for (size_t i = 0; i < kMaxDownloads; ++i) {
items.push_back(
AddItem(HoldingSpaceItem::Type::kDownload,
base::FilePath("/tmp/fake_" + base::NumberToString(i))));
}
test_api()->Show();
EXPECT_TRUE(test_api()->RecentFilesContainerShown());
std::vector<views::View*> download_files = test_api()->GetDownloadChips();
ASSERT_EQ(items.size(), download_files.size());
while (!items.empty()) {
// View order is expected to be reverse of item order.
auto* download_file = HoldingSpaceItemView::Cast(download_files.back());
EXPECT_EQ(download_file->item()->id(), items.front()->id());
items.pop_front();
download_files.pop_back();
}
test_api()->Close();
}
TEST_F(HoldingSpaceTrayTest, FinalizingDownloadItemThatShouldBeInvisible) { TEST_F(HoldingSpaceTrayTest, FinalizingDownloadItemThatShouldBeInvisible) {
StartSession(); StartSession();
test_api()->Show(); test_api()->Show();
...@@ -506,6 +542,38 @@ TEST_F(HoldingSpaceTrayTest, ScreenCaptureContainer) { ...@@ -506,6 +542,38 @@ TEST_F(HoldingSpaceTrayTest, ScreenCaptureContainer) {
test_api()->Close(); test_api()->Close();
} }
// Verifies the screen captures container is shown and orders items as expected
// when the model contains a number of finalized items prior to showing UI.
TEST_F(HoldingSpaceTrayTest, ScreenCapturesContainerWithFinalizedItemsOnly) {
MarkTimeOfFirstPin();
StartSession();
// Add a number of finalized screen capture items.
std::deque<HoldingSpaceItem*> items;
for (size_t i = 0; i < kMaxScreenCaptures; ++i) {
items.push_back(
AddItem(HoldingSpaceItem::Type::kScreenshot,
base::FilePath("/tmp/fake_" + base::NumberToString(i))));
}
test_api()->Show();
EXPECT_TRUE(test_api()->RecentFilesContainerShown());
std::vector<views::View*> screenshots = test_api()->GetScreenCaptureViews();
ASSERT_EQ(items.size(), screenshots.size());
while (!items.empty()) {
// View order is expected to be reverse of item order.
auto* screenshot = HoldingSpaceItemView::Cast(screenshots.back());
EXPECT_EQ(screenshot->item()->id(), items.front()->id());
items.pop_front();
screenshots.pop_back();
}
test_api()->Close();
}
TEST_F(HoldingSpaceTrayTest, FinalizingScreenCaptureItemThatShouldBeInvisible) { TEST_F(HoldingSpaceTrayTest, FinalizingScreenCaptureItemThatShouldBeInvisible) {
StartSession(); StartSession();
test_api()->Show(); test_api()->Show();
...@@ -772,4 +840,36 @@ TEST_F(HoldingSpaceTrayTest, ...@@ -772,4 +840,36 @@ TEST_F(HoldingSpaceTrayTest,
test_api()->Close(); test_api()->Close();
} }
// Verifies the pinned items container is shown and orders items as expected
// when the model contains a number of finalized items prior to showing UI.
TEST_F(HoldingSpaceTrayTest, PinnedFilesContainerWithFinalizedItemsOnly) {
MarkTimeOfFirstPin();
StartSession();
// Add a number of finalized pinned items.
std::deque<HoldingSpaceItem*> items;
for (int i = 0; i < 10; ++i) {
items.push_back(
AddItem(HoldingSpaceItem::Type::kPinnedFile,
base::FilePath("/tmp/fake_" + base::NumberToString(i))));
}
test_api()->Show();
EXPECT_TRUE(test_api()->PinnedFilesContainerShown());
std::vector<views::View*> pinned_files = test_api()->GetPinnedFileChips();
ASSERT_EQ(items.size(), pinned_files.size());
while (!items.empty()) {
// View order is expected to be reverse of item order.
auto* pinned_file = HoldingSpaceItemView::Cast(pinned_files.back());
EXPECT_EQ(pinned_file->item()->id(), items.front()->id());
items.pop_front();
pinned_files.pop_back();
}
test_api()->Close();
}
} // namespace ash } // namespace ash
...@@ -147,23 +147,26 @@ void PinnedFilesContainer::ViewHierarchyChanged( ...@@ -147,23 +147,26 @@ void PinnedFilesContainer::ViewHierarchyChanged(
SetVisible(details.is_add); SetVisible(details.is_add);
} }
void PinnedFilesContainer::AddHoldingSpaceItemView( void PinnedFilesContainer::AddHoldingSpaceItemView(const HoldingSpaceItem* item,
const HoldingSpaceItem* item) { bool due_to_finalization) {
DCHECK(!base::Contains(views_by_item_id_, item->id())); DCHECK(!base::Contains(views_by_item_id_, item->id()));
DCHECK(item->IsFinalized()); DCHECK(item->IsFinalized());
if (item->type() != HoldingSpaceItem::Type::kPinnedFile) if (item->type() != HoldingSpaceItem::Type::kPinnedFile)
return; return;
// Find the position to which the view should be added.
size_t index = 0; size_t index = 0;
for (const auto& candidate :
base::Reversed(HoldingSpaceController::Get()->model()->items())) { if (due_to_finalization) {
if (candidate->id() == item->id()) // Find the position at which the view should be added.
break; for (const auto& candidate :
if (candidate->IsFinalized() && base::Reversed(HoldingSpaceController::Get()->model()->items())) {
candidate->type() == HoldingSpaceItem::Type::kPinnedFile) { if (candidate->id() == item->id())
++index; break;
if (candidate->IsFinalized() &&
candidate->type() == HoldingSpaceItem::Type::kPinnedFile) {
++index;
}
} }
} }
......
...@@ -28,7 +28,8 @@ class PinnedFilesContainer : public HoldingSpaceItemViewsContainer { ...@@ -28,7 +28,8 @@ class PinnedFilesContainer : public HoldingSpaceItemViewsContainer {
// HoldingSpaceItemViewsContainer: // HoldingSpaceItemViewsContainer:
void ViewHierarchyChanged(const views::ViewHierarchyChangedDetails&) override; void ViewHierarchyChanged(const views::ViewHierarchyChangedDetails&) override;
void AddHoldingSpaceItemView(const HoldingSpaceItem* item) override; void AddHoldingSpaceItemView(const HoldingSpaceItem* item,
bool due_to_finalization) override;
void RemoveAllHoldingSpaceItemViews() override; void RemoveAllHoldingSpaceItemViews() override;
void RemoveHoldingSpaceItemView(const HoldingSpaceItem* item) override; void RemoveHoldingSpaceItemView(const HoldingSpaceItem* item) override;
......
...@@ -146,8 +146,8 @@ void RecentFilesContainer::ViewHierarchyChanged( ...@@ -146,8 +146,8 @@ void RecentFilesContainer::ViewHierarchyChanged(
OnDownloadsContainerViewHierarchyChanged(details); OnDownloadsContainerViewHierarchyChanged(details);
} }
void RecentFilesContainer::AddHoldingSpaceItemView( void RecentFilesContainer::AddHoldingSpaceItemView(const HoldingSpaceItem* item,
const HoldingSpaceItem* item) { bool due_to_finalization) {
DCHECK(item->IsFinalized()); DCHECK(item->IsFinalized());
if (item->type() != HoldingSpaceItem::Type::kScreenshot && if (item->type() != HoldingSpaceItem::Type::kScreenshot &&
...@@ -156,12 +156,16 @@ void RecentFilesContainer::AddHoldingSpaceItemView( ...@@ -156,12 +156,16 @@ void RecentFilesContainer::AddHoldingSpaceItemView(
} }
size_t index = 0; size_t index = 0;
for (const auto& candidate :
base::Reversed(HoldingSpaceController::Get()->model()->items())) { if (due_to_finalization) {
if (candidate->id() == item->id()) // Find the position at which the view should be added.
break; for (const auto& candidate :
if (candidate->IsFinalized() && candidate->type() == item->type()) base::Reversed(HoldingSpaceController::Get()->model()->items())) {
++index; if (candidate->id() == item->id())
break;
if (candidate->IsFinalized() && candidate->type() == item->type())
++index;
}
} }
if (item->type() == HoldingSpaceItem::Type::kScreenshot) if (item->type() == HoldingSpaceItem::Type::kScreenshot)
......
...@@ -29,7 +29,8 @@ class RecentFilesContainer : public HoldingSpaceItemViewsContainer { ...@@ -29,7 +29,8 @@ class RecentFilesContainer : public HoldingSpaceItemViewsContainer {
// HoldingSpaceItemViewsContainer: // HoldingSpaceItemViewsContainer:
void ChildVisibilityChanged(views::View* child) override; void ChildVisibilityChanged(views::View* child) override;
void ViewHierarchyChanged(const views::ViewHierarchyChangedDetails&) override; void ViewHierarchyChanged(const views::ViewHierarchyChangedDetails&) override;
void AddHoldingSpaceItemView(const HoldingSpaceItem* item) override; void AddHoldingSpaceItemView(const HoldingSpaceItem* item,
bool due_to_finalization) override;
void RemoveAllHoldingSpaceItemViews() override; void RemoveAllHoldingSpaceItemViews() override;
void RemoveHoldingSpaceItemView(const HoldingSpaceItem* item) override; void RemoveHoldingSpaceItemView(const HoldingSpaceItem* item) override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment