Commit 8cfdf8fa authored by sdefresne's avatar sdefresne Committed by Commit bot

Implement 1:N mapping from ios::ChromeBrowserState to TabModel.

In preparation of the removal of BrowserListIOS, implements
free function to maintain explicitly the 1:N mapping from
ios::ChromeBrowserState to TabModel.

BUG=None

Review-Url: https://codereview.chromium.org/2621083003
Cr-Commit-Position: refs/heads/master@{#442958}
parent c164a254
...@@ -8,6 +8,7 @@ source_set("tabs") { ...@@ -8,6 +8,7 @@ source_set("tabs") {
"tab_delegate.h", "tab_delegate.h",
"tab_dialog_delegate.h", "tab_dialog_delegate.h",
"tab_model.h", "tab_model.h",
"tab_model_list.h",
"tab_model_observer.h", "tab_model_observer.h",
"tab_model_order_controller.h", "tab_model_order_controller.h",
"tab_model_synced_window_delegate.h", "tab_model_synced_window_delegate.h",
...@@ -36,6 +37,7 @@ source_set("tabs_internal") { ...@@ -36,6 +37,7 @@ source_set("tabs_internal") {
] ]
deps = [ deps = [
":tabs", ":tabs",
":tabs_internal_arc",
"//base", "//base",
"//components/content_settings/core/browser", "//components/content_settings/core/browser",
"//components/favicon/core", "//components/favicon/core",
...@@ -106,12 +108,26 @@ source_set("tabs_internal") { ...@@ -106,12 +108,26 @@ source_set("tabs_internal") {
"//ui/base", "//ui/base",
"//url", "//url",
] ]
allow_circular_includes_from = [ ":tabs_internal_arc" ]
libs = [ libs = [
"CoreLocation.framework", "CoreLocation.framework",
"UIKit.framework", "UIKit.framework",
] ]
} }
source_set("tabs_internal_arc") {
sources = [
"tab_model_list.mm",
]
deps = [
":tabs",
"//base",
"//ios/chrome/browser/browser_state",
]
libs = [ "Foundation.framework" ]
configs += [ "//build/config/compiler:enable_arc" ]
}
source_set("unit_tests") { source_set("unit_tests") {
testonly = true testonly = true
sources = [ sources = [
...@@ -122,6 +138,7 @@ source_set("unit_tests") { ...@@ -122,6 +138,7 @@ source_set("unit_tests") {
deps = [ deps = [
":tabs", ":tabs",
":tabs_internal", ":tabs_internal",
":unit_tests_arc",
"//base", "//base",
"//components/bookmarks/test", "//components/bookmarks/test",
"//components/history/core/browser", "//components/history/core/browser",
...@@ -147,3 +164,20 @@ source_set("unit_tests") { ...@@ -147,3 +164,20 @@ source_set("unit_tests") {
"//third_party/ocmock", "//third_party/ocmock",
] ]
} }
source_set("unit_tests_arc") {
testonly = true
sources = [
"tab_model_list_unittest.mm",
]
deps = [
":tabs",
":tabs_internal",
"//base",
"//ios/chrome/browser/browser_state:test_support",
"//ios/chrome/browser/sessions:test_support",
"//ios/web:test_support",
"//testing/gtest",
]
configs += [ "//build/config/compiler:enable_arc" ]
}
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#import "ios/chrome/browser/snapshots/snapshot_cache.h" #import "ios/chrome/browser/snapshots/snapshot_cache.h"
#include "ios/chrome/browser/tab_parenting_global_observer.h" #include "ios/chrome/browser/tab_parenting_global_observer.h"
#import "ios/chrome/browser/tabs/tab.h" #import "ios/chrome/browser/tabs/tab.h"
#import "ios/chrome/browser/tabs/tab_model_list.h"
#import "ios/chrome/browser/tabs/tab_model_observer.h" #import "ios/chrome/browser/tabs/tab_model_observer.h"
#import "ios/chrome/browser/tabs/tab_model_order_controller.h" #import "ios/chrome/browser/tabs/tab_model_order_controller.h"
#import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h" #import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h"
...@@ -299,6 +300,9 @@ void CleanCertificatePolicyCache( ...@@ -299,6 +300,9 @@ void CleanCertificatePolicyCache(
selector:@selector(applicationWillEnterForeground:) selector:@selector(applicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification name:UIApplicationWillEnterForegroundNotification
object:nil]; object:nil];
// Associate with ios::ChromeBrowserState.
RegisterTabModelWithChromeBrowserState(_browserState, self);
} }
return self; return self;
} }
...@@ -732,6 +736,9 @@ void CleanCertificatePolicyCache( ...@@ -732,6 +736,9 @@ void CleanCertificatePolicyCache(
// NOTE: This can be called multiple times, so must be robust against that. // NOTE: This can be called multiple times, so must be robust against that.
- (void)browserStateDestroyed { - (void)browserStateDestroyed {
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
if (_browserState) {
UnregisterTabModelFromChromeBrowserState(_browserState, self);
}
_browserState = nullptr; _browserState = nullptr;
} }
......
// 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.
#ifndef IOS_CHROME_BROWSER_TABS_TAB_MODEL_LIST_H_
#define IOS_CHROME_BROWSER_TABS_TAB_MODEL_LIST_H_
#import <Foundation/Foundation.h>
// This file contains free functions to help maintain a 1:N relationship
// between an ios::ChromeBrowserState and multiple TabModels.
@class TabModel;
namespace ios {
class ChromeBrowserState;
}
// Registers |tab_model| as associated to |browser_state|. The object will
// be retained until |UnregisterTabModelFromChromeBrowserState| is called.
// It is an error if |tab_model is already registered as associated to
// |browser_state|.
void RegisterTabModelWithChromeBrowserState(
ios::ChromeBrowserState* browser_state,
TabModel* tab_model);
// Unregisters the association between |tab_model| and |browser_state|.
// It is an error if no such association exists.
void UnregisterTabModelFromChromeBrowserState(
ios::ChromeBrowserState* browser_state,
TabModel* tab_model);
// Returns the list of all TabModels associated with |browser_state|.
NSArray<TabModel*>* GetTabModelsForChromeBrowserState(
ios::ChromeBrowserState* browser_state);
#endif // IOS_CHROME_BROWSER_TABS_TAB_MODEL_LIST_H_
// 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/chrome/browser/tabs/tab_model_list.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/supports_user_data.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/tabs/tab_model.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Wrapper around a NSMutableArray<TabModel> allowing to bind it to a
// base::SupportsUserData. Any base::SupportsUserData that owns this
// wrapper will owns the reference to the TabModels.
class TabModelList : public base::SupportsUserData::Data {
public:
TabModelList();
~TabModelList() override;
static TabModelList* GetForBrowserState(
ios::ChromeBrowserState* browser_state,
bool create);
NSMutableSet<TabModel*>* tab_models() const { return tab_models_; }
private:
NSMutableSet<TabModel*>* tab_models_;
DISALLOW_COPY_AND_ASSIGN(TabModelList);
};
const char kTabModelListKey = 0;
TabModelList::TabModelList() : tab_models_([[NSMutableSet alloc] init]) {}
TabModelList::~TabModelList() {
// TabModelList is destroyed during base::SupportsUserData destruction. At
// that point, all TabModels must have been unregistered already.
DCHECK_EQ([tab_models_ count], 0u);
}
// static
TabModelList* TabModelList::GetForBrowserState(
ios::ChromeBrowserState* browser_state,
bool create) {
TabModelList* tab_model_list =
static_cast<TabModelList*>(browser_state->GetUserData(&kTabModelListKey));
if (!tab_model_list && create) {
// The ownership of TabModelList is transfered to base::SupportsUserData
// via the SetUserData call (should take a std::unique_ptr<>).
tab_model_list = new TabModelList;
browser_state->SetUserData(&kTabModelListKey, tab_model_list);
}
return tab_model_list;
}
} // namespace
void RegisterTabModelWithChromeBrowserState(
ios::ChromeBrowserState* browser_state,
TabModel* tab_model) {
NSMutableSet<TabModel*>* tab_models =
TabModelList::GetForBrowserState(browser_state, true)->tab_models();
DCHECK(![tab_models containsObject:tab_model]);
[tab_models addObject:tab_model];
}
void UnregisterTabModelFromChromeBrowserState(
ios::ChromeBrowserState* browser_state,
TabModel* tab_model) {
NSMutableSet<TabModel*>* tab_models =
TabModelList::GetForBrowserState(browser_state, false)->tab_models();
DCHECK([tab_models containsObject:tab_model]);
[tab_models removeObject:tab_model];
}
NSArray<TabModel*>* GetTabModelsForChromeBrowserState(
ios::ChromeBrowserState* browser_state) {
TabModelList* tab_model_list =
TabModelList::GetForBrowserState(browser_state, false);
return tab_model_list ? [tab_model_list->tab_models() allObjects] : nil;
}
// 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/chrome/browser/tabs/tab_model_list.h"
#import "base/mac/scoped_nsobject.h"
#include "base/memory/ptr_util.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#import "ios/chrome/browser/sessions/test_session_service.h"
#import "ios/chrome/browser/tabs/tab_model.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
class TabModelListTest : public PlatformTest {
public:
TabModelListTest() {
TestChromeBrowserState::Builder test_cbs_builder;
chrome_browser_state_ = test_cbs_builder.Build();
}
TabModel* CreateTabModel() {
return [[TabModel alloc]
initWithSessionWindow:nil
sessionService:[[TestSessionService alloc] init]
browserState:chrome_browser_state_.get()];
}
NSArray<TabModel*>* RegisteredTabModels() {
return GetTabModelsForChromeBrowserState(chrome_browser_state_.get());
}
private:
web::TestWebThreadBundle thread_bundle_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
DISALLOW_COPY_AND_ASSIGN(TabModelListTest);
};
TEST_F(TabModelListTest, RegisterAndUnregister) {
EXPECT_EQ([RegisteredTabModels() count], 0u);
TabModel* tab_model = CreateTabModel();
EXPECT_EQ([RegisteredTabModels() count], 1u);
EXPECT_NE([RegisteredTabModels() indexOfObject:tab_model],
static_cast<NSUInteger>(NSNotFound));
[tab_model browserStateDestroyed];
EXPECT_EQ([RegisteredTabModels() count], 0u);
}
TEST_F(TabModelListTest, SupportsMultipleTabModel) {
EXPECT_EQ([RegisteredTabModels() count], 0u);
TabModel* tab_model1 = CreateTabModel();
EXPECT_EQ([RegisteredTabModels() count], 1u);
EXPECT_NE([RegisteredTabModels() indexOfObject:tab_model1],
static_cast<NSUInteger>(NSNotFound));
TabModel* tab_model2 = CreateTabModel();
EXPECT_EQ([RegisteredTabModels() count], 2u);
EXPECT_NE([RegisteredTabModels() indexOfObject:tab_model2],
static_cast<NSUInteger>(NSNotFound));
[tab_model1 browserStateDestroyed];
[tab_model2 browserStateDestroyed];
EXPECT_EQ([RegisteredTabModels() count], 0u);
}
...@@ -147,7 +147,7 @@ class TabModelTest : public PlatformTest { ...@@ -147,7 +147,7 @@ class TabModelTest : public PlatformTest {
TestChromeBrowserState::Builder test_cbs_builder; TestChromeBrowserState::Builder test_cbs_builder;
chrome_browser_state_ = test_cbs_builder.Build(); chrome_browser_state_ = test_cbs_builder.Build();
sessionWindow_.reset([[SessionWindowIOS new] retain]); sessionWindow_.reset([[SessionWindowIOS alloc] init]);
// Create tab model with just a dummy session service so the async state // Create tab model with just a dummy session service so the async state
// saving doesn't trigger unless actually wanted. // saving doesn't trigger unless actually wanted.
base::scoped_nsobject<TestSessionService> test_service( base::scoped_nsobject<TestSessionService> test_service(
......
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