1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/browser_tabrestore.h"
#include <map>
#include <memory>
#include <utility>
#include "build/build_config.h"
#include "chrome/browser/apps/app_service/web_contents_app_id_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/session_restore.h"
#include "chrome/browser/sessions/session_service_base.h"
#include "chrome/browser/sessions/session_service_lookup.h"
#include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tab_ui_helper.h"
#include "chrome/browser/ui/tabs/public/tab_features.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_group_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "components/sessions/content/content_serialized_navigation_builder.h"
#include "components/tab_groups/tab_group_id.h"
#include "components/tabs/public/tab_group.h"
#include "components/tabs/public/tab_interface.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/restore_type.h"
#include "content/public/browser/session_storage_namespace.h"
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/range/range.h"
using content::NavigationEntry;
using content::RestoreType;
using content::WebContents;
using sessions::ContentSerializedNavigationBuilder;
using sessions::SerializedNavigationEntry;
namespace chrome {
namespace {
std::unique_ptr<WebContents> CreateRestoredTab(
Browser* browser,
const std::vector<SerializedNavigationEntry>& navigations,
int selected_navigation,
const std::string& extension_app_id,
base::TimeTicks last_active_time_ticks,
base::Time last_active_time,
content::SessionStorageNamespace* session_storage_namespace,
const sessions::SerializedUserAgentOverride& user_agent_override,
const std::map<std::string, std::string>& extra_data,
bool initially_hidden,
bool from_session_restore) {
GURL restore_url = navigations.at(selected_navigation).virtual_url();
// TODO(ajwong): Remove the temporary session_storage_namespace_map when
// we teach session restore to understand that one tab can have multiple
// SessionStorageNamespace objects. Also remove the
// session_storage_namespace.h include since we only need that to assign
// into the map.
content::SessionStorageNamespaceMap session_storage_namespace_map =
content::CreateMapWithDefaultSessionStorageNamespace(
browser->profile(), session_storage_namespace);
WebContents::CreateParams create_params(
browser->profile(),
tab_util::GetSiteInstanceForNewTab(browser->profile(), restore_url));
create_params.initially_hidden = initially_hidden;
create_params.desired_renderer_state =
WebContents::CreateParams::kNoRendererProcess;
create_params.last_active_time_ticks = last_active_time_ticks;
create_params.last_active_time = last_active_time;
std::unique_ptr<WebContents> web_contents =
WebContents::CreateWithSessionStorage(create_params,
session_storage_namespace_map);
apps::SetAppIdForWebContents(browser->profile(), web_contents.get(),
extension_app_id);
std::vector<std::unique_ptr<NavigationEntry>> entries =
ContentSerializedNavigationBuilder::ToNavigationEntries(
navigations, browser->profile());
blink::UserAgentOverride ua_override;
ua_override.ua_string_override = user_agent_override.ua_string_override;
ua_override.ua_metadata_override = blink::UserAgentMetadata::Demarshal(
user_agent_override.opaque_ua_metadata_override);
web_contents->SetUserAgentOverride(ua_override, false);
web_contents->GetController().Restore(selected_navigation,
RestoreType::kRestored, &entries);
DCHECK_EQ(0u, entries.size());
return web_contents;
}
// Start loading a restored tab after adding it to its browser, if visible.
//
// Without this, loading starts when
// WebContentsImpl::UpdateWebContentsVisibility(VISIBLE) is invoked, which
// happens at a different time on Mac vs. other desktop platform due to a
// different windowing system. Starting to load here ensures consistent behavior
// across desktop platforms and allows FirstWebContentsProfiler to have strict
// cross-platform expectations about events it observes.
void LoadRestoredTabIfVisible(Browser* browser,
content::WebContents* web_contents) {
if (web_contents->GetVisibility() != content::Visibility::VISIBLE) {
return;
}
DCHECK_EQ(browser->tab_strip_model()->GetActiveWebContents(), web_contents);
// A layout should already have been performed to determine the contents size.
// The contents size should not be empty, unless the browser size and restored
// size are also empty.
DCHECK(!browser->window()->GetContentsSize().IsEmpty() ||
(browser->window()->GetBounds().IsEmpty() &&
browser->window()->GetRestoredBounds().IsEmpty()));
DCHECK_EQ(web_contents->GetSize(), browser->window()->GetContentsSize());
web_contents->GetController().LoadIfNecessary();
}
WebContents* AddRestoredTabImpl(std::unique_ptr<WebContents> web_contents,
Browser* browser,
int tab_index,
std::optional<tab_groups::TabGroupId> group,
bool select,
bool pin,
bool from_session_restore,
std::optional<bool> is_active_browser) {
TabStripModel* const tab_strip_model = browser->tab_strip_model();
int add_types = select ? AddTabTypes::ADD_ACTIVE : AddTabTypes::ADD_NONE;
if (pin) {
tab_index =
std::min(tab_index, tab_strip_model->IndexOfFirstNonPinnedTab());
add_types |= AddTabTypes::ADD_PINNED;
}
if (tab_strip_model->group_model()) {
const std::optional<tab_groups::TabGroupId> surrounding_group =
tab_strip_model->GetSurroundingTabGroup(tab_index);
// If inserting at |tab_index| would put the tab within a different
// group, adjust the index to put it outside.
if (surrounding_group && surrounding_group != group) {
tab_index = tab_strip_model->group_model()
->GetTabGroup(*surrounding_group)
->ListTabs()
.end();
}
// `tab_index` should respect group contiguity.
if (group.has_value() &&
tab_strip_model->group_model()->ContainsTabGroup(group.value())) {
gfx::Range group_indices = tab_strip_model->group_model()
->GetTabGroup(group.value())
->ListTabs();
tab_index = std::clamp(tab_index, static_cast<int>(group_indices.start()),
static_cast<int>(group_indices.end()));
}
}
WebContents* raw_web_contents = web_contents.get();
// The two cases we could run into are -
// 1. Tab was a part of a group that is no longer present.
// 2. Tab is added to a group that is present in the tabstrip model or is an
// ungrouped tab.
if (group.has_value() && tab_strip_model->group_model() &&
!tab_strip_model->group_model()->ContainsTabGroup(group.value())) {
// Insert as a ungrouped tab and then add it to the new group.
const int actual_index = tab_strip_model->InsertWebContentsAt(
tab_index, std::move(web_contents), add_types);
tab_strip_model->AddToGroupForRestore({actual_index}, group.value());
} else {
tab_strip_model->InsertWebContentsAt(tab_index, std::move(web_contents),
add_types, group);
}
if (from_session_restore) {
// Indicate that the tab is created by session restore. This is used to hide
// the throbber when a background restored tab is loading.
tabs::TabInterface* const tab_interface =
tabs::TabInterface::GetFromContents(raw_web_contents);
tabs::TabFeatures* const tab_features = tab_interface->GetTabFeatures();
tab_features->tab_ui_helper()->set_created_by_session_restore(true);
}
// We set the size of the view here, before Blink does its initial layout.
// If we don't, the initial layout of background tabs will be performed
// with a view width of 0, which may cause script outputs and anchor link
// location calculations to be incorrect even after a new layout with
// proper view dimensions. TabStripModel::AddWebContents() contains similar
// logic.
//
// TODO(crbug.com/40113932): There should be a way to ask the browser
// to perform a layout so that size of the WebContents is right.
gfx::Size size = browser->window()->GetContentsSize();
// Fallback to the restore bounds if it's empty as the window is not shown
// yet and the bounds may not be available on all platforms.
if (size.IsEmpty()) {
size = browser->window()->GetRestoredBounds().size();
}
raw_web_contents->Resize(gfx::Rect(size));
const bool initially_hidden = !select || browser->window()->IsMinimized();
if (initially_hidden) {
raw_web_contents->WasHidden();
} else {
const bool should_activate =
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
// Activating a window on another space causes the system to switch to
// that space. Since the session restore process shows and activates
// windows itself, activating windows here should be safe to skip.
// Cautiously apply only to Windows and MacOS, for now
// (https://crbug.com/1019048).
!from_session_restore;
#else
true;
#endif
if (should_activate) {
browser->window()->Activate();
}
}
SessionServiceBase* session_service =
GetAppropriateSessionServiceIfExisting(browser);
if (session_service) {
session_service->TabRestored(raw_web_contents, pin);
}
// Immediate load if the browser activeness is true or unknown. That is, do
// not do immediate load for browsers that are known to be inactive.
bool should_load = is_active_browser.value_or(true);
// On OS_MAC, `LoadRestoredTabIfVisible` by default so that its tab loading
// behaves like other platforms to make FirstWebContentsProfiler wor
// properly. However, app restorations take longer than the normal browser
// window to be restored and that will cause `LoadRestoredTabIfVisible()` to
// fail. Skip LoadRestoredTabIfVisible if OS_MAC && the browser is an app
// browser.
#if BUILDFLAG(IS_MAC)
should_load = (browser->type() != Browser::Type::TYPE_APP);
#endif // BUILDFLAG(IS_MAC)
if (should_load) {
LoadRestoredTabIfVisible(browser, raw_web_contents);
}
return raw_web_contents;
}
} // namespace
WebContents* AddRestoredTab(
Browser* browser,
const std::vector<SerializedNavigationEntry>& navigations,
int tab_index,
int selected_navigation,
const std::string& extension_app_id,
std::optional<tab_groups::TabGroupId> group,
bool select,
bool pin,
base::TimeTicks last_active_time_ticks,
base::Time last_active_time,
content::SessionStorageNamespace* session_storage_namespace,
const sessions::SerializedUserAgentOverride& user_agent_override,
const std::map<std::string, std::string>& extra_data,
bool from_session_restore,
std::optional<bool> is_active_browser) {
const bool initially_hidden = !select || browser->window()->IsMinimized();
std::unique_ptr<WebContents> web_contents = CreateRestoredTab(
browser, navigations, selected_navigation, extension_app_id,
last_active_time_ticks, last_active_time, session_storage_namespace,
user_agent_override, extra_data, initially_hidden, from_session_restore);
return AddRestoredTabImpl(std::move(web_contents), browser, tab_index, group,
select, pin, from_session_restore,
is_active_browser);
}
WebContents* ReplaceRestoredTab(
Browser* browser,
const std::vector<SerializedNavigationEntry>& navigations,
int selected_navigation,
const std::string& extension_app_id,
content::SessionStorageNamespace* session_storage_namespace,
const sessions::SerializedUserAgentOverride& user_agent_override,
const std::map<std::string, std::string>& extra_data,
bool from_session_restore) {
std::unique_ptr<WebContents> web_contents = CreateRestoredTab(
browser, navigations, selected_navigation, extension_app_id,
base::TimeTicks(), base::Time(), session_storage_namespace,
user_agent_override, extra_data, false, from_session_restore);
WebContents* raw_web_contents = web_contents.get();
// ReplaceWebContentsAt won't animate in the restoration, so manually do the
// equivalent of ReplaceWebContentsAt.
TabStripModel* tab_strip = browser->tab_strip_model();
int insertion_index = tab_strip->active_index();
tab_strip->InsertWebContentsAt(
insertion_index + 1, std::move(web_contents),
AddTabTypes::ADD_ACTIVE | AddTabTypes::ADD_INHERIT_OPENER,
tab_strip->GetTabGroupForTab(insertion_index));
if (from_session_restore) {
// Indicate that the tab is created by session restore. This is used to hide
// the throbber when a background restored tab is loading.
tabs::TabInterface* const tab_interface =
tabs::TabInterface::GetFromContents(raw_web_contents);
tabs::TabFeatures* const tab_features = tab_interface->GetTabFeatures();
tab_features->tab_ui_helper()->set_created_by_session_restore(true);
}
tab_strip->CloseWebContentsAt(insertion_index, TabCloseTypes::CLOSE_NONE);
LoadRestoredTabIfVisible(browser, raw_web_contents);
return raw_web_contents;
}
} // namespace chrome
|