// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/content/web_state/content_web_state_builder.h"
#import "base/strings/sys_string_conversions.h"
#import "content/public/browser/navigation_controller.h"
#import "content/public/browser/navigation_entry.h"
#import "content/public/browser/restore_type.h"
#import "ios/web/content/content_browser_context.h"
#import "ios/web/content/web_state/content_web_state.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/session/crw_navigation_item_storage.h"
#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
#import "ios/web/public/session/crw_session_storage.h"
#import "ios/web/public/session/serializable_user_data_manager.h"
#import "net/http/http_util.h"
#import "services/network/public/cpp/shared_url_loader_factory.h"
namespace web {
void ExtractContentSessionStorage(ContentWebState* web_state,
content::NavigationController& controller,
web::BrowserState* browser_state,
CRWSessionStorage* session_storage) {
web_state->SetHasOpener(session_storage.hasOpener);
NSArray<CRWNavigationItemStorage*>* item_storages =
session_storage.itemStorages;
std::vector<std::unique_ptr<content::NavigationEntry>> items(
item_storages.count);
content::BrowserContext* browser_context =
ContentBrowserContext::FromBrowserState(browser_state);
for (size_t index = 0; index < item_storages.count; ++index) {
CRWNavigationItemStorage* navigation_item_storage = item_storages[index];
std::unique_ptr<content::NavigationEntry> new_entry =
content::NavigationController::CreateNavigationEntry(
navigation_item_storage.virtualURL, content::Referrer(),
/* initiator_origin= */ std::nullopt,
/* initiator_base_url= */ std::nullopt, ui::PAGE_TRANSITION_RELOAD,
/* is_renderer_initiated= */ false, std::string(), browser_context,
/* blob_url_loader_factory= */ nullptr);
new_entry->SetOriginalRequestURL(navigation_item_storage.URL);
if (navigation_item_storage.URL.SchemeIsHTTPOrHTTPS()) {
new_entry->SetURL(navigation_item_storage.URL);
new_entry->SetVirtualURL(navigation_item_storage.virtualURL);
} else {
new_entry->SetURL(navigation_item_storage.virtualURL);
}
new_entry->SetTimestamp(navigation_item_storage.timestamp);
new_entry->SetTitle(navigation_item_storage.title);
new_entry->SetTransitionType(ui::PAGE_TRANSITION_RELOAD);
std::ostringstream ss;
for (id key in navigation_item_storage.HTTPRequestHeaders) {
auto key_utf8 = base::SysNSStringToUTF8(key);
auto value_utf8 = base::SysNSStringToUTF8(
[navigation_item_storage.HTTPRequestHeaders objectForKey:key]);
if (!net::HttpUtil::IsValidHeaderName(key_utf8) ||
!net::HttpUtil::IsValidHeaderValue(value_utf8)) {
continue;
}
ss << key_utf8 << ": " << value_utf8 << "\n";
}
if (!ss.str().empty()) {
new_entry->AddExtraHeaders(ss.str());
}
GURL url = new_entry->GetURL();
if (web::BrowserURLRewriter::GetInstance()->RewriteURLIfNecessary(
&url, browser_state)) {
GURL virtual_url = new_entry->GetURL();
new_entry->SetURL(url);
new_entry->SetVirtualURL(virtual_url);
}
// Content doesn't allow about://newtab
if (url.possibly_invalid_spec() == "about://newtab/") {
new_entry->SetURL(GURL("chrome://newtab"));
}
items[index] = std::move(new_entry);
}
controller.Restore(session_storage.lastCommittedItemIndex,
content::RestoreType::kRestored, &items);
SerializableUserDataManager::FromWebState(web_state)->SetUserDataFromSession(
session_storage.userData);
}
CRWSessionStorage* BuildContentSessionStorage(
const ContentWebState* web_state,
ContentNavigationManager* navigation_manager) {
CRWSessionStorage* session_storage = [[CRWSessionStorage alloc] init];
session_storage.lastActiveTime = web_state->GetLastActiveTime();
session_storage.creationTime = web_state->GetCreationTime();
session_storage.stableIdentifier = web_state->GetStableIdentifier();
session_storage.hasOpener = web_state->HasOpener();
session_storage.lastCommittedItemIndex =
navigation_manager->GetLastCommittedItemIndex();
if (session_storage.lastCommittedItemIndex == -1) {
// This can happen when a session is saved during restoration. Instead,
// default to GetItemCount() - 1.
session_storage.lastCommittedItemIndex =
navigation_manager->GetItemCount() - 1;
}
NSMutableArray<CRWNavigationItemStorage*>* item_storages =
[[NSMutableArray alloc] init];
const size_t original_index = session_storage.lastCommittedItemIndex;
const size_t navigation_items =
static_cast<size_t>(navigation_manager->GetItemCount());
// Drop URLs larger than a certain threshold.
for (size_t index = 0; index < navigation_items; ++index) {
const NavigationItem* item = navigation_manager->GetItemAtIndex(index);
if (item->GetURL().spec().size() > url::kMaxURLChars) {
if (index <= original_index) {
session_storage.lastCommittedItemIndex--;
}
continue;
}
CRWNavigationItemStorage* storage = [[CRWNavigationItemStorage alloc] init];
storage.virtualURL = item->GetVirtualURL();
storage.URL = item->GetURL();
// Use default referrer if URL is longer than allowed. Navigation items with
// these long URLs will not be serialized, so there is no point in keeping
// referrer URL.
if (item->GetReferrer().url.spec().size() <= url::kMaxURLChars) {
storage.referrer = item->GetReferrer();
}
storage.timestamp = item->GetTimestamp();
storage.title = item->GetTitle();
storage.userAgentType = item->GetUserAgentType();
storage.HTTPRequestHeaders = item->GetHttpRequestHeaders();
[item_storages addObject:storage];
}
session_storage.itemStorages = item_storages;
session_storage.certPolicyCacheStorage =
[[CRWSessionCertificatePolicyCacheStorage alloc] init];
session_storage.certPolicyCacheStorage.certificateStorages =
[NSMutableSet set];
const SerializableUserDataManager* user_data_manager =
SerializableUserDataManager::FromWebState(web_state);
if (user_data_manager) {
session_storage.userData = user_data_manager->GetUserDataForSession();
}
session_storage.userAgentType = UserAgentType::AUTOMATIC;
return session_storage;
}
} // namespace web