| Index: ios/shared/chrome/browser/tabs/web_state_list_serialization.mm
|
| diff --git a/ios/shared/chrome/browser/tabs/web_state_list_serialization.mm b/ios/shared/chrome/browser/tabs/web_state_list_serialization.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0662f3ab5f0a4f0e5eb59ef928baef35b3d35ac5
|
| --- /dev/null
|
| +++ b/ios/shared/chrome/browser/tabs/web_state_list_serialization.mm
|
| @@ -0,0 +1,175 @@
|
| +// Copyright 2017 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#import "ios/shared/chrome/browser/tabs/web_state_list_serialization.h"
|
| +
|
| +#include <memory>
|
| +#include <unordered_map>
|
| +
|
| +#include "base/callback.h"
|
| +#include "base/logging.h"
|
| +#import "base/mac/foundation_util.h"
|
| +#import "ios/shared/chrome/browser/tabs/web_state_list.h"
|
| +#import "ios/shared/chrome/browser/tabs/web_state_opener.h"
|
| +#import "ios/web/public/serializable_user_data_manager.h"
|
| +#import "ios/web/public/web_state/web_state.h"
|
| +
|
| +#if !defined(__has_feature) || !__has_feature(objc_arc)
|
| +#error "This file requires ARC support."
|
| +#endif
|
| +
|
| +namespace {
|
| +// Keys used to store information about the opener-opened relationship between
|
| +// the WebStates stored in the WebStateList.
|
| +NSString* const kOpenerIndexKey = @"OpenerIndex";
|
| +NSString* const kOpenerNavigationIndexKey = @"OpenerNavigationIndex";
|
| +
|
| +// Legacy keys used to store information about the opener-opener relationship
|
| +// before the M-60 release. Remove once M-70 has shipped.
|
| +NSString* const kObjectIDKey = @"TabID";
|
| +NSString* const kOpenerIDKey = @"OpenerID";
|
| +
|
| +// Returns whether the opener-opener relationship is encoded with legacy format.
|
| +// The legacy format (pre M-59) references the opener by id while the new format
|
| +// references it by index.
|
| +// TODO(crbug.com/704941): remove once no sessions uses the old format.
|
| +bool IsSessionUsingLegacyFormat(WebStateList* web_state_list, int old_count) {
|
| + for (int index = old_count; index < web_state_list->count(); ++index) {
|
| + web::WebState* web_state = web_state_list->GetWebStateAt(index);
|
| + web::SerializableUserDataManager* user_data_manager =
|
| + web::SerializableUserDataManager::FromWebState(web_state);
|
| +
|
| + if (!user_data_manager->GetValueForSerializationKey(kOpenerIndexKey))
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +// Restores the WebStates opener-opened relationship. The relationship is
|
| +// encoded using legacy format.
|
| +// TODO(crbug.com/704941): remove once no sessions uses the old format.
|
| +void RestoreRelationshipLegacy(WebStateList* web_state_list, int old_count) {
|
| + NSMutableDictionary<NSString*, NSValue*>* id_to_web_state =
|
| + [NSMutableDictionary dictionary];
|
| +
|
| + for (int index = old_count; index < web_state_list->count(); ++index) {
|
| + web::WebState* web_state = web_state_list->GetWebStateAt(index);
|
| + web::SerializableUserDataManager* user_data_manager =
|
| + web::SerializableUserDataManager::FromWebState(web_state);
|
| +
|
| + NSString* object_id = base::mac::ObjCCast<NSString>(
|
| + user_data_manager->GetValueForSerializationKey(kObjectIDKey));
|
| +
|
| + if (!object_id || ![object_id length])
|
| + continue;
|
| +
|
| + if (id_to_web_state[object_id] != nil)
|
| + continue;
|
| +
|
| + id_to_web_state[object_id] = [NSValue valueWithPointer:web_state];
|
| + }
|
| +
|
| + for (int index = old_count; index < web_state_list->count(); ++index) {
|
| + web::WebState* web_state = web_state_list->GetWebStateAt(index);
|
| + web::SerializableUserDataManager* user_data_manager =
|
| + web::SerializableUserDataManager::FromWebState(web_state);
|
| +
|
| + NSString* opener_id = base::mac::ObjCCast<NSString>(
|
| + user_data_manager->GetValueForSerializationKey(kOpenerIDKey));
|
| +
|
| + NSNumber* boxed_opener_navigation_index = base::mac::ObjCCast<NSNumber>(
|
| + user_data_manager->GetValueForSerializationKey(
|
| + kOpenerNavigationIndexKey));
|
| +
|
| + if (!opener_id || !boxed_opener_navigation_index || ![opener_id length])
|
| + continue;
|
| +
|
| + if (id_to_web_state[opener_id] == nil)
|
| + continue;
|
| +
|
| + web::WebState* opener_web_state =
|
| + static_cast<web::WebState*>(id_to_web_state[opener_id].pointerValue);
|
| +
|
| + web_state_list->SetOpenerOfWebStateAt(
|
| + index, WebStateOpener(opener_web_state,
|
| + [boxed_opener_navigation_index intValue]));
|
| + }
|
| +}
|
| +
|
| +// Restores the WebStates opener-opened relationship.
|
| +void RestoreRelationship(WebStateList* web_state_list, int old_count) {
|
| + if (IsSessionUsingLegacyFormat(web_state_list, old_count))
|
| + return RestoreRelationshipLegacy(web_state_list, old_count);
|
| +
|
| + for (int index = old_count; index < web_state_list->count(); ++index) {
|
| + web::WebState* web_state = web_state_list->GetWebStateAt(index);
|
| + web::SerializableUserDataManager* user_data_manager =
|
| + web::SerializableUserDataManager::FromWebState(web_state);
|
| +
|
| + NSNumber* boxed_opener_index = base::mac::ObjCCast<NSNumber>(
|
| + user_data_manager->GetValueForSerializationKey(kOpenerIndexKey));
|
| +
|
| + NSNumber* boxed_opener_navigation_index = base::mac::ObjCCast<NSNumber>(
|
| + user_data_manager->GetValueForSerializationKey(
|
| + kOpenerNavigationIndexKey));
|
| +
|
| + if (!boxed_opener_index || !boxed_opener_navigation_index)
|
| + continue;
|
| +
|
| + // If opener index is out of bound then assume there is no opener.
|
| + int opener_index = [boxed_opener_index intValue] + old_count;
|
| + if (opener_index < old_count || opener_index >= web_state_list->count())
|
| + continue;
|
| +
|
| + web::WebState* opener = web_state_list->GetWebStateAt(opener_index);
|
| + web_state_list->SetOpenerOfWebStateAt(
|
| + index,
|
| + WebStateOpener(opener, [boxed_opener_navigation_index intValue]));
|
| + }
|
| +}
|
| +} // namespace
|
| +
|
| +NSArray<CRWSessionStorage*>* SerializeWebStateList(
|
| + WebStateList* web_state_list) {
|
| + NSMutableArray<CRWSessionStorage*>* serialized_session =
|
| + [NSMutableArray arrayWithCapacity:web_state_list->count()];
|
| +
|
| + for (int index = 0; index < web_state_list->count(); ++index) {
|
| + web::WebState* web_state = web_state_list->GetWebStateAt(index);
|
| + WebStateOpener opener = web_state_list->GetOpenerOfWebStateAt(index);
|
| +
|
| + web::SerializableUserDataManager* user_data_manager =
|
| + web::SerializableUserDataManager::FromWebState(web_state);
|
| +
|
| + int opener_index = WebStateList::kInvalidIndex;
|
| + if (opener.opener) {
|
| + opener_index = web_state_list->GetIndexOfWebState(opener.opener);
|
| + DCHECK_NE(opener_index, WebStateList::kInvalidIndex);
|
| + user_data_manager->AddSerializableData(@(opener_index), kOpenerIndexKey);
|
| + user_data_manager->AddSerializableData(@(opener.navigation_index),
|
| + kOpenerNavigationIndexKey);
|
| + } else {
|
| + user_data_manager->AddSerializableData([NSNull null], kOpenerIndexKey);
|
| + user_data_manager->AddSerializableData([NSNull null],
|
| + kOpenerNavigationIndexKey);
|
| + }
|
| +
|
| + [serialized_session addObject:web_state->BuildSessionStorage()];
|
| + }
|
| +
|
| + return [serialized_session copy];
|
| +}
|
| +
|
| +void DeserializeWebStateList(WebStateList* web_state_list,
|
| + NSArray<CRWSessionStorage*>* sessions,
|
| + const WebStateFactory& web_state_factory) {
|
| + int old_count = web_state_list->count();
|
| + for (CRWSessionStorage* session in sessions) {
|
| + std::unique_ptr<web::WebState> web_state = web_state_factory.Run(session);
|
| + web_state_list->InsertWebState(web_state_list->count(),
|
| + std::move(web_state));
|
| + }
|
| +
|
| + RestoreRelationship(web_state_list, old_count);
|
| +}
|
|
|