Commit 9955ef24 authored by Sylvain Defresne's avatar Sylvain Defresne Committed by Commit Bot

[ios] Add lock preventing nest batch operation on WebStateList

WebStateList is not re-entrant (already asserted by locked_)
so there is no point nesting batched operations. Add a bool
to ensure this is not the case.

Add a method to check whether a batched operation is in
progress (could be used internally to also omit sending
notification during a batched operation).

Bug: 1014526
Change-Id: I6b6dc3278312895a32898d898245f538f9a5264d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2089675
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#750916}
parent d5a6d52a
...@@ -85,6 +85,9 @@ class WebStateList { ...@@ -85,6 +85,9 @@ class WebStateList {
// Returns true if the list is currently mutating. // Returns true if the list is currently mutating.
bool IsMutating() const; bool IsMutating() const;
// Returns true if a batch operation is in progress.
bool IsBatchInProgress() const;
// Returns the currently active WebState or null if there is none. // Returns the currently active WebState or null if there is none.
web::WebState* GetActiveWebState() const; web::WebState* GetActiveWebState() const;
...@@ -276,6 +279,9 @@ class WebStateList { ...@@ -276,6 +279,9 @@ class WebStateList {
// by the returned base::AutoReset<bool>). // by the returned base::AutoReset<bool>).
bool locked_ = false; bool locked_ = false;
// Lock to prevent nesting batched operations.
bool batch_operation_in_progress_ = false;
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(WebStateList); DISALLOW_COPY_AND_ASSIGN(WebStateList);
......
...@@ -132,6 +132,11 @@ bool WebStateList::IsMutating() const { ...@@ -132,6 +132,11 @@ bool WebStateList::IsMutating() const {
return locked_; return locked_;
} }
bool WebStateList::IsBatchInProgress() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return batch_operation_in_progress_;
}
web::WebState* WebStateList::GetActiveWebState() const { web::WebState* WebStateList::GetActiveWebState() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (active_index_ != kInvalidIndex) if (active_index_ != kInvalidIndex)
...@@ -451,6 +456,10 @@ void WebStateList::RemoveObserver(WebStateListObserver* observer) { ...@@ -451,6 +456,10 @@ void WebStateList::RemoveObserver(WebStateListObserver* observer) {
void WebStateList::PerformBatchOperation( void WebStateList::PerformBatchOperation(
base::OnceCallback<void(WebStateList*)> operation) { base::OnceCallback<void(WebStateList*)> operation) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!batch_operation_in_progress_);
base::AutoReset<bool> lock(&batch_operation_in_progress_, /*locked=*/true);
for (auto& observer : observers_) for (auto& observer : observers_)
observer.WillBeginBatchOperation(this); observer.WillBeginBatchOperation(this);
if (!operation.is_null()) if (!operation.is_null())
......
...@@ -201,7 +201,7 @@ class WebStateListTest : public PlatformTest { ...@@ -201,7 +201,7 @@ class WebStateListTest : public PlatformTest {
DISALLOW_COPY_AND_ASSIGN(WebStateListTest); DISALLOW_COPY_AND_ASSIGN(WebStateListTest);
}; };
// Test that empty() matches count() != 0. // Tests that empty() matches count() != 0.
TEST_F(WebStateListTest, IsEmpty) { TEST_F(WebStateListTest, IsEmpty) {
EXPECT_EQ(0, web_state_list_.count()); EXPECT_EQ(0, web_state_list_.count());
EXPECT_TRUE(web_state_list_.empty()); EXPECT_TRUE(web_state_list_.empty());
...@@ -213,7 +213,7 @@ TEST_F(WebStateListTest, IsEmpty) { ...@@ -213,7 +213,7 @@ TEST_F(WebStateListTest, IsEmpty) {
EXPECT_FALSE(web_state_list_.empty()); EXPECT_FALSE(web_state_list_.empty());
} }
// Test that inserting a single webstate works. // Tests that inserting a single webstate works.
TEST_F(WebStateListTest, InsertUrlSingle) { TEST_F(WebStateListTest, InsertUrlSingle) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
...@@ -222,7 +222,7 @@ TEST_F(WebStateListTest, InsertUrlSingle) { ...@@ -222,7 +222,7 @@ TEST_F(WebStateListTest, InsertUrlSingle) {
EXPECT_EQ(kURL0, web_state_list_.GetWebStateAt(0)->GetVisibleURL().spec()); EXPECT_EQ(kURL0, web_state_list_.GetWebStateAt(0)->GetVisibleURL().spec());
} }
// Test that inserting multiple webstates puts them in the expected places. // Tests that inserting multiple webstates puts them in the expected places.
TEST_F(WebStateListTest, InsertUrlMultiple) { TEST_F(WebStateListTest, InsertUrlMultiple) {
web_state_list_.InsertWebState(0, CreateWebState(kURL0), web_state_list_.InsertWebState(0, CreateWebState(kURL0),
WebStateList::INSERT_FORCE_INDEX, WebStateList::INSERT_FORCE_INDEX,
...@@ -241,7 +241,7 @@ TEST_F(WebStateListTest, InsertUrlMultiple) { ...@@ -241,7 +241,7 @@ TEST_F(WebStateListTest, InsertUrlMultiple) {
EXPECT_EQ(kURL0, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec()); EXPECT_EQ(kURL0, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec());
} }
// Test webstate activation. // Tests webstate activation.
TEST_F(WebStateListTest, ActivateWebState) { TEST_F(WebStateListTest, ActivateWebState) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
EXPECT_EQ(nullptr, web_state_list_.GetActiveWebState()); EXPECT_EQ(nullptr, web_state_list_.GetActiveWebState());
...@@ -254,7 +254,7 @@ TEST_F(WebStateListTest, ActivateWebState) { ...@@ -254,7 +254,7 @@ TEST_F(WebStateListTest, ActivateWebState) {
web_state_list_.GetActiveWebState()); web_state_list_.GetActiveWebState());
} }
// Test activating a webstate as it is inserted. // Tests activating a webstate as it is inserted.
TEST_F(WebStateListTest, InsertActivate) { TEST_F(WebStateListTest, InsertActivate) {
web_state_list_.InsertWebState( web_state_list_.InsertWebState(
0, CreateWebState(kURL0), 0, CreateWebState(kURL0),
...@@ -267,7 +267,7 @@ TEST_F(WebStateListTest, InsertActivate) { ...@@ -267,7 +267,7 @@ TEST_F(WebStateListTest, InsertActivate) {
web_state_list_.GetActiveWebState()); web_state_list_.GetActiveWebState());
} }
// Test finding a known webstate. // Tests finding a known webstate.
TEST_F(WebStateListTest, GetIndexOfWebState) { TEST_F(WebStateListTest, GetIndexOfWebState) {
std::unique_ptr<web::TestWebState> web_state_0 = CreateWebState(kURL0); std::unique_ptr<web::TestWebState> web_state_0 = CreateWebState(kURL0);
web::WebState* target_web_state = web_state_0.get(); web::WebState* target_web_state = web_state_0.get();
...@@ -295,7 +295,7 @@ TEST_F(WebStateListTest, GetIndexOfWebState) { ...@@ -295,7 +295,7 @@ TEST_F(WebStateListTest, GetIndexOfWebState) {
EXPECT_EQ(2, web_state_list_.GetIndexOfWebState(target_web_state)); EXPECT_EQ(2, web_state_list_.GetIndexOfWebState(target_web_state));
} }
// Test finding a webstate by URL. // Tests finding a webstate by URL.
TEST_F(WebStateListTest, GetIndexOfWebStateWithURL) { TEST_F(WebStateListTest, GetIndexOfWebStateWithURL) {
// Empty list. // Empty list.
EXPECT_EQ(WebStateList::kInvalidIndex, EXPECT_EQ(WebStateList::kInvalidIndex,
...@@ -316,7 +316,7 @@ TEST_F(WebStateListTest, GetIndexOfWebStateWithURL) { ...@@ -316,7 +316,7 @@ TEST_F(WebStateListTest, GetIndexOfWebStateWithURL) {
EXPECT_EQ(1, web_state_list_.GetIndexOfWebStateWithURL(GURL(kURL0))); EXPECT_EQ(1, web_state_list_.GetIndexOfWebStateWithURL(GURL(kURL0)));
} }
// Test finding a non-active webstate by URL. // Tests finding a non-active webstate by URL.
TEST_F(WebStateListTest, GetIndexOfInactiveWebStateWithURL) { TEST_F(WebStateListTest, GetIndexOfInactiveWebStateWithURL) {
// Empty list. // Empty list.
EXPECT_EQ(WebStateList::kInvalidIndex, EXPECT_EQ(WebStateList::kInvalidIndex,
...@@ -356,7 +356,7 @@ TEST_F(WebStateListTest, GetIndexOfInactiveWebStateWithURL) { ...@@ -356,7 +356,7 @@ TEST_F(WebStateListTest, GetIndexOfInactiveWebStateWithURL) {
EXPECT_EQ(2, web_state_list_.GetIndexOfInactiveWebStateWithURL(GURL(kURL0))); EXPECT_EQ(2, web_state_list_.GetIndexOfInactiveWebStateWithURL(GURL(kURL0)));
} }
// Test that inserted webstates correctly inherit openers. // Tests that inserted webstates correctly inherit openers.
TEST_F(WebStateListTest, InsertInheritOpener) { TEST_F(WebStateListTest, InsertInheritOpener) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
web_state_list_.ActivateWebStateAt(0); web_state_list_.ActivateWebStateAt(0);
...@@ -374,7 +374,7 @@ TEST_F(WebStateListTest, InsertInheritOpener) { ...@@ -374,7 +374,7 @@ TEST_F(WebStateListTest, InsertInheritOpener) {
web_state_list_.GetOpenerOfWebStateAt(1).opener); web_state_list_.GetOpenerOfWebStateAt(1).opener);
} }
// Test moving webstates one place to the "right" (to a higher index). // Tests moving webstates one place to the "right" (to a higher index).
TEST_F(WebStateListTest, MoveWebStateAtRightByOne) { TEST_F(WebStateListTest, MoveWebStateAtRightByOne) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
AppendNewWebState(kURL1); AppendNewWebState(kURL1);
...@@ -396,7 +396,8 @@ TEST_F(WebStateListTest, MoveWebStateAtRightByOne) { ...@@ -396,7 +396,8 @@ TEST_F(WebStateListTest, MoveWebStateAtRightByOne) {
EXPECT_EQ(kURL2, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec()); EXPECT_EQ(kURL2, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec());
} }
// Test moving webstates more than one place to the "right" (to a higher index). // Tests moving webstates more than one place to the "right" (to a higher
// index).
TEST_F(WebStateListTest, MoveWebStateAtRightByMoreThanOne) { TEST_F(WebStateListTest, MoveWebStateAtRightByMoreThanOne) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
AppendNewWebState(kURL1); AppendNewWebState(kURL1);
...@@ -418,7 +419,7 @@ TEST_F(WebStateListTest, MoveWebStateAtRightByMoreThanOne) { ...@@ -418,7 +419,7 @@ TEST_F(WebStateListTest, MoveWebStateAtRightByMoreThanOne) {
EXPECT_EQ(kURL0, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec()); EXPECT_EQ(kURL0, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec());
} }
// Test moving webstates one place to the "left" (to a lower index). // Tests moving webstates one place to the "left" (to a lower index).
TEST_F(WebStateListTest, MoveWebStateAtLeftByOne) { TEST_F(WebStateListTest, MoveWebStateAtLeftByOne) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
AppendNewWebState(kURL1); AppendNewWebState(kURL1);
...@@ -440,7 +441,7 @@ TEST_F(WebStateListTest, MoveWebStateAtLeftByOne) { ...@@ -440,7 +441,7 @@ TEST_F(WebStateListTest, MoveWebStateAtLeftByOne) {
EXPECT_EQ(kURL1, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec()); EXPECT_EQ(kURL1, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec());
} }
// Test moving webstates more than one place to the "left" (to a lower index). // Tests moving webstates more than one place to the "left" (to a lower index).
TEST_F(WebStateListTest, MoveWebStateAtLeftByMoreThanOne) { TEST_F(WebStateListTest, MoveWebStateAtLeftByMoreThanOne) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
AppendNewWebState(kURL1); AppendNewWebState(kURL1);
...@@ -462,7 +463,7 @@ TEST_F(WebStateListTest, MoveWebStateAtLeftByMoreThanOne) { ...@@ -462,7 +463,7 @@ TEST_F(WebStateListTest, MoveWebStateAtLeftByMoreThanOne) {
EXPECT_EQ(kURL1, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec()); EXPECT_EQ(kURL1, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec());
} }
// Test "moving" webstates (calling MoveWebStateAt with the same source and // Tests "moving" webstates (calling MoveWebStateAt with the same source and
// destination indexes. // destination indexes.
TEST_F(WebStateListTest, MoveWebStateAtSameIndex) { TEST_F(WebStateListTest, MoveWebStateAtSameIndex) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
...@@ -485,7 +486,7 @@ TEST_F(WebStateListTest, MoveWebStateAtSameIndex) { ...@@ -485,7 +486,7 @@ TEST_F(WebStateListTest, MoveWebStateAtSameIndex) {
EXPECT_EQ(kURL2, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec()); EXPECT_EQ(kURL2, web_state_list_.GetWebStateAt(2)->GetVisibleURL().spec());
} }
// Test replacing webstates. // Tests replacing webstates.
TEST_F(WebStateListTest, ReplaceWebStateAt) { TEST_F(WebStateListTest, ReplaceWebStateAt) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
AppendNewWebState(kURL1); AppendNewWebState(kURL1);
...@@ -507,7 +508,7 @@ TEST_F(WebStateListTest, ReplaceWebStateAt) { ...@@ -507,7 +508,7 @@ TEST_F(WebStateListTest, ReplaceWebStateAt) {
EXPECT_EQ(kURL1, old_web_state->GetVisibleURL().spec()); EXPECT_EQ(kURL1, old_web_state->GetVisibleURL().spec());
} }
// Test detaching webstates at index 0. // Tests detaching webstates at index 0.
TEST_F(WebStateListTest, DetachWebStateAtIndexBegining) { TEST_F(WebStateListTest, DetachWebStateAtIndexBegining) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
AppendNewWebState(kURL1); AppendNewWebState(kURL1);
...@@ -528,7 +529,7 @@ TEST_F(WebStateListTest, DetachWebStateAtIndexBegining) { ...@@ -528,7 +529,7 @@ TEST_F(WebStateListTest, DetachWebStateAtIndexBegining) {
EXPECT_EQ(kURL2, web_state_list_.GetWebStateAt(1)->GetVisibleURL().spec()); EXPECT_EQ(kURL2, web_state_list_.GetWebStateAt(1)->GetVisibleURL().spec());
} }
// Test detaching webstates at an index that isn't 0 or the last index. // Tests detaching webstates at an index that isn't 0 or the last index.
TEST_F(WebStateListTest, DetachWebStateAtIndexMiddle) { TEST_F(WebStateListTest, DetachWebStateAtIndexMiddle) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
AppendNewWebState(kURL1); AppendNewWebState(kURL1);
...@@ -549,7 +550,7 @@ TEST_F(WebStateListTest, DetachWebStateAtIndexMiddle) { ...@@ -549,7 +550,7 @@ TEST_F(WebStateListTest, DetachWebStateAtIndexMiddle) {
EXPECT_EQ(kURL2, web_state_list_.GetWebStateAt(1)->GetVisibleURL().spec()); EXPECT_EQ(kURL2, web_state_list_.GetWebStateAt(1)->GetVisibleURL().spec());
} }
// Test detaching webstates at the last index. // Tests detaching webstates at the last index.
TEST_F(WebStateListTest, DetachWebStateAtIndexLast) { TEST_F(WebStateListTest, DetachWebStateAtIndexLast) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
AppendNewWebState(kURL1); AppendNewWebState(kURL1);
...@@ -570,7 +571,7 @@ TEST_F(WebStateListTest, DetachWebStateAtIndexLast) { ...@@ -570,7 +571,7 @@ TEST_F(WebStateListTest, DetachWebStateAtIndexLast) {
EXPECT_EQ(kURL1, web_state_list_.GetWebStateAt(1)->GetVisibleURL().spec()); EXPECT_EQ(kURL1, web_state_list_.GetWebStateAt(1)->GetVisibleURL().spec());
} }
// Test finding opended-by indexes on an empty list. // Tests finding opended-by indexes on an empty list.
TEST_F(WebStateListTest, OpenersEmptyList) { TEST_F(WebStateListTest, OpenersEmptyList) {
EXPECT_TRUE(web_state_list_.empty()); EXPECT_TRUE(web_state_list_.empty());
...@@ -589,7 +590,7 @@ TEST_F(WebStateListTest, OpenersEmptyList) { ...@@ -589,7 +590,7 @@ TEST_F(WebStateListTest, OpenersEmptyList) {
nullptr, WebStateList::kInvalidIndex, true)); nullptr, WebStateList::kInvalidIndex, true));
} }
// Test finding opended-by indexes when no webstates have been opened. // Tests finding opended-by indexes when no webstates have been opened.
TEST_F(WebStateListTest, OpenersNothingOpened) { TEST_F(WebStateListTest, OpenersNothingOpened) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
AppendNewWebState(kURL1); AppendNewWebState(kURL1);
...@@ -613,7 +614,7 @@ TEST_F(WebStateListTest, OpenersNothingOpened) { ...@@ -613,7 +614,7 @@ TEST_F(WebStateListTest, OpenersNothingOpened) {
} }
} }
// Test finding opended-by indexes when the opened child is at an index after // Tests finding opended-by indexes when the opened child is at an index after
// the parent. // the parent.
TEST_F(WebStateListTest, OpenersChildsAfterOpener) { TEST_F(WebStateListTest, OpenersChildsAfterOpener) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
...@@ -667,7 +668,7 @@ TEST_F(WebStateListTest, OpenersChildsAfterOpener) { ...@@ -667,7 +668,7 @@ TEST_F(WebStateListTest, OpenersChildsAfterOpener) {
opener, start_index, true)); opener, start_index, true));
} }
// Test finding opended-by indexes when the opened child is at an index before // Tests finding opended-by indexes when the opened child is at an index before
// the parent. // the parent.
TEST_F(WebStateListTest, OpenersChildsBeforeOpener) { TEST_F(WebStateListTest, OpenersChildsBeforeOpener) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
...@@ -684,7 +685,7 @@ TEST_F(WebStateListTest, OpenersChildsBeforeOpener) { ...@@ -684,7 +685,7 @@ TEST_F(WebStateListTest, OpenersChildsBeforeOpener) {
opener, start_index, false)); opener, start_index, false));
} }
// Test closing all webstates. // Tests closing all webstates.
TEST_F(WebStateListTest, CloseAllWebStates) { TEST_F(WebStateListTest, CloseAllWebStates) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
AppendNewWebState(kURL1); AppendNewWebState(kURL1);
...@@ -703,7 +704,7 @@ TEST_F(WebStateListTest, CloseAllWebStates) { ...@@ -703,7 +704,7 @@ TEST_F(WebStateListTest, CloseAllWebStates) {
EXPECT_TRUE(observer_.batch_operation_ended()); EXPECT_TRUE(observer_.batch_operation_ended());
} }
// Test closing one webstate. // Tests closing one webstate.
TEST_F(WebStateListTest, CloseWebState) { TEST_F(WebStateListTest, CloseWebState) {
AppendNewWebState(kURL0); AppendNewWebState(kURL0);
AppendNewWebState(kURL1); AppendNewWebState(kURL1);
...@@ -721,7 +722,7 @@ TEST_F(WebStateListTest, CloseWebState) { ...@@ -721,7 +722,7 @@ TEST_F(WebStateListTest, CloseWebState) {
EXPECT_FALSE(observer_.batch_operation_ended()); EXPECT_FALSE(observer_.batch_operation_ended());
} }
// Test that batch operation can be empty. // Tests that batch operation can be empty.
TEST_F(WebStateListTest, PerformBatchOperation_EmptyCallback) { TEST_F(WebStateListTest, PerformBatchOperation_EmptyCallback) {
observer_.ResetStatistics(); observer_.ResetStatistics();
...@@ -731,7 +732,7 @@ TEST_F(WebStateListTest, PerformBatchOperation_EmptyCallback) { ...@@ -731,7 +732,7 @@ TEST_F(WebStateListTest, PerformBatchOperation_EmptyCallback) {
EXPECT_TRUE(observer_.batch_operation_ended()); EXPECT_TRUE(observer_.batch_operation_ended());
} }
// Test that batch operation WebStateList is the correct one. // Tests that batch operation WebStateList is the correct one.
TEST_F(WebStateListTest, PerformBatchOperation_CorrectWebStateList) { TEST_F(WebStateListTest, PerformBatchOperation_CorrectWebStateList) {
WebStateList* captured_web_state_list = nullptr; WebStateList* captured_web_state_list = nullptr;
web_state_list_.PerformBatchOperation(base::BindOnce( web_state_list_.PerformBatchOperation(base::BindOnce(
...@@ -742,3 +743,18 @@ TEST_F(WebStateListTest, PerformBatchOperation_CorrectWebStateList) { ...@@ -742,3 +743,18 @@ TEST_F(WebStateListTest, PerformBatchOperation_CorrectWebStateList) {
EXPECT_EQ(captured_web_state_list, &web_state_list_); EXPECT_EQ(captured_web_state_list, &web_state_list_);
} }
// Tests that IsBatchInProgress() returns the correct value.
TEST_F(WebStateListTest, PerformBatchOperation_IsBatchInProgress) {
EXPECT_FALSE(web_state_list_.IsBatchInProgress());
bool captured_batch_in_progress = false;
web_state_list_.PerformBatchOperation(base::BindOnce(
[](bool* captured_batch_in_progress, WebStateList* web_state_list) {
*captured_batch_in_progress = web_state_list->IsBatchInProgress();
},
&captured_batch_in_progress));
EXPECT_FALSE(web_state_list_.IsBatchInProgress());
EXPECT_TRUE(captured_batch_in_progress);
}
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