Commit f64c33d6 authored by erikchen's avatar erikchen Committed by Commit Bot

Delay sending TabDetachedAt and TabClosingAt observer notifications.

Previously, the notifications were sent during the tab-close-loop [if multiple
tabs were being closed]. This made reentrancy particularly dangerous.

The logic in Browser::TabDetachedAt and Browser::TabInsertedAt relied on the
|index| parameter to determine whether the index of the active tab has changed
[for which there is no notification]. This CL changes those methods to always
call SessionService::SetSelectedTabInWindow, and modifies
SessionService::SetSelectedTabInWindow to be idempotent.

Bug: 842194
Change-Id: I2fc54749e495ab9c925bdba89ef857c4ea3537d9
Reviewed-on: https://chromium-review.googlesource.com/1055732Reviewed-by: default avatarScott Violet <sky@chromium.org>
Commit-Queue: Erik Chen <erikchen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#558530}
parent e2cfcd78
...@@ -292,6 +292,7 @@ void SessionService::WindowClosed(const SessionID& window_id) { ...@@ -292,6 +292,7 @@ void SessionService::WindowClosed(const SessionID& window_id) {
} }
windows_tracking_.erase(window_id); windows_tracking_.erase(window_id);
last_selected_tab_in_window_.erase(window_id);
if (window_closing_ids_.find(window_id) != window_closing_ids_.end()) { if (window_closing_ids_.find(window_id) != window_closing_ids_.end()) {
window_closing_ids_.erase(window_id); window_closing_ids_.erase(window_id);
...@@ -469,6 +470,11 @@ void SessionService::SetSelectedTabInWindow(const SessionID& window_id, ...@@ -469,6 +470,11 @@ void SessionService::SetSelectedTabInWindow(const SessionID& window_id,
if (!ShouldTrackChangesToWindow(window_id)) if (!ShouldTrackChangesToWindow(window_id))
return; return;
auto it = last_selected_tab_in_window_.find(window_id);
if (it != last_selected_tab_in_window_.end() && it->second == index)
return;
last_selected_tab_in_window_[window_id] = index;
ScheduleCommand( ScheduleCommand(
sessions::CreateSetSelectedTabInWindowCommand(window_id, index)); sessions::CreateSetSelectedTabInWindowCommand(window_id, index));
} }
...@@ -757,6 +763,7 @@ void SessionService::ScheduleResetCommands() { ...@@ -757,6 +763,7 @@ void SessionService::ScheduleResetCommands() {
base_session_service_->ClearPendingCommands(); base_session_service_->ClearPendingCommands();
tab_to_available_range_.clear(); tab_to_available_range_.clear();
windows_tracking_.clear(); windows_tracking_.clear();
last_selected_tab_in_window_.clear();
rebuild_on_next_save_ = false; rebuild_on_next_save_ = false;
BuildCommandsFromBrowsers(&tab_to_available_range_, BuildCommandsFromBrowsers(&tab_to_available_range_,
&windows_tracking_); &windows_tracking_);
......
...@@ -384,6 +384,10 @@ class SessionService : public sessions::BaseSessionServiceDelegate, ...@@ -384,6 +384,10 @@ class SessionService : public sessions::BaseSessionServiceDelegate,
// Force session commands to be rebuild before next save event. // Force session commands to be rebuild before next save event.
bool rebuild_on_next_save_; bool rebuild_on_next_save_;
// Don't send duplicate SetSelectedTabInWindow commands when the selected
// tab's index hasn't changed.
std::map<SessionID, int> last_selected_tab_in_window_;
base::WeakPtrFactory<SessionService> weak_factory_; base::WeakPtrFactory<SessionService> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(SessionService); DISALLOW_COPY_AND_ASSIGN(SessionService);
......
...@@ -1027,15 +1027,12 @@ void Browser::TabClosingAt(TabStripModel* tab_strip_model, ...@@ -1027,15 +1027,12 @@ void Browser::TabClosingAt(TabStripModel* tab_strip_model,
} }
void Browser::TabDetachedAt(WebContents* contents, int index, bool was_active) { void Browser::TabDetachedAt(WebContents* contents, int index, bool was_active) {
// TabDetachedAt is called before TabStripModel has updated the if (!tab_strip_model_->closing_all()) {
// active index.
int old_active_index = tab_strip_model_->active_index();
if (index < old_active_index && !tab_strip_model_->closing_all()) {
SessionService* session_service = SessionService* session_service =
SessionServiceFactory::GetForProfileIfExisting(profile_); SessionServiceFactory::GetForProfileIfExisting(profile_);
if (session_service) { if (session_service) {
session_service->SetSelectedTabInWindow(session_id(), session_service->SetSelectedTabInWindow(session_id(),
old_active_index - 1); tab_strip_model_->active_index());
} }
} }
......
...@@ -199,7 +199,7 @@ struct TabStripModel::DetachNotifications { ...@@ -199,7 +199,7 @@ struct TabStripModel::DetachNotifications {
// The WebContents that were recently detached. Observers need to be notified // The WebContents that were recently detached. Observers need to be notified
// about these. These must be updated after construction. // about these. These must be updated after construction.
std::vector<DetachedWebContents> detached_web_contents; std::vector<std::unique_ptr<DetachedWebContents>> detached_web_contents;
// The selection model prior to any tabs being detached. // The selection model prior to any tabs being detached.
const ui::ListSelectionModel selection_model; const ui::ListSelectionModel selection_model;
...@@ -346,13 +346,14 @@ std::unique_ptr<content::WebContents> TabStripModel::DetachWebContentsAt( ...@@ -346,13 +346,14 @@ std::unique_ptr<content::WebContents> TabStripModel::DetachWebContentsAt(
DetachNotifications notifications(initially_active_web_contents, DetachNotifications notifications(initially_active_web_contents,
selection_model_, /*will_delete=*/false); selection_model_, /*will_delete=*/false);
DetachedWebContents dwc( std::unique_ptr<DetachedWebContents> dwc =
index, index, std::make_unique<DetachedWebContents>(
DetachWebContentsImpl(index, /*create_historical_tab=*/false, index, index,
/*will_delete=*/false)); DetachWebContentsImpl(index, /*create_historical_tab=*/false,
/*will_delete=*/false));
notifications.detached_web_contents.push_back(std::move(dwc)); notifications.detached_web_contents.push_back(std::move(dwc));
SendDetachWebContentsNotifications(&notifications); SendDetachWebContentsNotifications(&notifications);
return std::move(notifications.detached_web_contents[0].contents); return std::move(notifications.detached_web_contents[0]->contents);
} }
std::unique_ptr<content::WebContents> TabStripModel::DetachWebContentsImpl( std::unique_ptr<content::WebContents> TabStripModel::DetachWebContentsImpl(
...@@ -376,18 +377,8 @@ std::unique_ptr<content::WebContents> TabStripModel::DetachWebContentsImpl( ...@@ -376,18 +377,8 @@ std::unique_ptr<content::WebContents> TabStripModel::DetachWebContentsImpl(
std::unique_ptr<WebContentsData> old_data = std::move(contents_data_[index]); std::unique_ptr<WebContentsData> old_data = std::move(contents_data_[index]);
contents_data_.erase(contents_data_.begin() + index); contents_data_.erase(contents_data_.begin() + index);
if (will_delete) {
for (auto& observer : observers_)
observer.TabClosingAt(this, raw_web_contents, index);
}
if (empty())
closing_all_ = true;
for (auto& observer : observers_)
observer.TabDetachedAt(raw_web_contents, index, index == active_index());
if (empty()) { if (empty()) {
closing_all_ = true;
selection_model_.Clear(); selection_model_.Clear();
} else { } else {
int old_active = active_index(); int old_active = active_index();
...@@ -414,14 +405,45 @@ void TabStripModel::SendDetachWebContentsNotifications( ...@@ -414,14 +405,45 @@ void TabStripModel::SendDetachWebContentsNotifications(
DetachNotifications* notifications) { DetachNotifications* notifications) {
bool was_any_tab_selected = false; bool was_any_tab_selected = false;
// Sort the DetachedWebContents in decreasing order of
// |index_before_any_removals|. This is because |index_before_any_removals| is
// used by observers to update their own copy of TabStripModel state, and each
// removal affects subsequent removals of higher index.
std::sort(notifications->detached_web_contents.begin(),
notifications->detached_web_contents.end(),
[](const std::unique_ptr<DetachedWebContents>& dwc1,
const std::unique_ptr<DetachedWebContents>& dwc2) {
return dwc1->index_before_any_removals >
dwc2->index_before_any_removals;
});
for (auto& dwc : notifications->detached_web_contents) {
// TabClosingAt() must be sent before TabDetachedAt(), since some observers
// use the former to change the behavior of the latter.
// TODO(erikchen): Combine these notifications. https://crbug.com/842194.
if (notifications->will_delete) {
for (auto& observer : observers_) {
observer.TabClosingAt(this, dwc->contents.get(),
dwc->index_before_any_removals);
}
}
// TabDetachedAt() allows observers that keep their own model of
// |contents_data_| to keep that model in sync.
for (auto& observer : observers_) {
observer.TabDetachedAt(
dwc->contents.get(), dwc->index_before_any_removals,
notifications->initially_active_web_contents == dwc->contents.get());
}
}
for (auto& dwc : notifications->detached_web_contents) { for (auto& dwc : notifications->detached_web_contents) {
if (notifications->selection_model.IsSelected( if (notifications->selection_model.IsSelected(
dwc.index_before_any_removals)) { dwc->index_before_any_removals)) {
was_any_tab_selected = true; was_any_tab_selected = true;
} }
if (notifications->initially_active_web_contents && if (notifications->initially_active_web_contents &&
dwc.contents.get() == notifications->initially_active_web_contents) { dwc->contents.get() == notifications->initially_active_web_contents) {
for (auto& observer : observers_) for (auto& observer : observers_)
observer.TabDeactivated(notifications->initially_active_web_contents); observer.TabDeactivated(notifications->initially_active_web_contents);
...@@ -435,7 +457,7 @@ void TabStripModel::SendDetachWebContentsNotifications( ...@@ -435,7 +457,7 @@ void TabStripModel::SendDetachWebContentsNotifications(
if (notifications->will_delete) { if (notifications->will_delete) {
// This destroys the WebContents, which will also send // This destroys the WebContents, which will also send
// WebContentsDestroyed notifications. // WebContentsDestroyed notifications.
dwc.contents.reset(); dwc->contents.reset();
} }
} }
...@@ -1332,11 +1354,12 @@ bool TabStripModel::CloseWebContentses( ...@@ -1332,11 +1354,12 @@ bool TabStripModel::CloseWebContentses(
continue; continue;
} }
DetachedWebContents dwc( std::unique_ptr<DetachedWebContents> dwc =
original_indices[i], current_index, std::make_unique<DetachedWebContents>(
DetachWebContentsImpl(current_index, original_indices[i], current_index,
close_types & CLOSE_CREATE_HISTORICAL_TAB, DetachWebContentsImpl(current_index,
/*will_delete=*/true)); close_types & CLOSE_CREATE_HISTORICAL_TAB,
/*will_delete=*/true));
notifications.detached_web_contents.push_back(std::move(dwc)); notifications.detached_web_contents.push_back(std::move(dwc));
} }
......
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