File: browser_tabrestore.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (325 lines) | stat: -rw-r--r-- 14,126 bytes parent folder | download | duplicates (5)
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