1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/login/signin/merge_session_throttle.h"
8 #include "base/lazy_instance.h"
9 #include "base/logging.h"
10 #include "base/memory/singleton.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_util.h"
13 #include "base/threading/non_thread_safe.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/chromeos/login/signin/merge_session_load_page.h"
16 #include "chrome/browser/chromeos/login/signin/merge_session_xhr_request_waiter.h"
17 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h"
18 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.h"
19 #include "chrome/common/url_constants.h"
20 #include "components/google/core/browser/google_util.h"
21 #include "components/user_manager/user_manager.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/render_view_host.h"
24 #include "content/public/browser/resource_controller.h"
25 #include "content/public/browser/resource_request_info.h"
26 #include "content/public/browser/web_contents.h"
27 #include "net/base/net_errors.h"
28 #include "net/base/net_util.h"
29 #include "net/base/network_change_notifier.h"
30 #include "net/url_request/url_request.h"
31 #include "net/url_request/url_request_context.h"
33 using content::BrowserThread
;
34 using content::RenderViewHost
;
35 using content::ResourceType
;
36 using content::WebContents
;
40 const int64 kMaxSessionRestoreTimeInSec
= 60;
42 // The set of blocked profiles.
43 class ProfileSet
: public base::NonThreadSafe
,
44 public std::set
<Profile
*> {
49 virtual ~ProfileSet() {
52 static ProfileSet
* Get();
55 friend struct ::base::DefaultLazyInstanceTraits
<ProfileSet
>;
57 DISALLOW_COPY_AND_ASSIGN(ProfileSet
);
60 // Set of all of profiles for which restore session is in progress.
61 // This static member is accessible only form UI thread.
62 static base::LazyInstance
<ProfileSet
> g_blocked_profiles
=
63 LAZY_INSTANCE_INITIALIZER
;
65 ProfileSet
* ProfileSet::Get() {
66 return g_blocked_profiles
.Pointer();
71 base::AtomicRefCount
MergeSessionThrottle::all_profiles_restored_(0);
73 MergeSessionThrottle::MergeSessionThrottle(net::URLRequest
* request
,
74 ResourceType resource_type
)
76 resource_type_(resource_type
) {
79 MergeSessionThrottle::~MergeSessionThrottle() {
80 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
83 void MergeSessionThrottle::WillStartRequest(bool* defer
) {
84 if (!ShouldDelayUrl(request_
->url()))
87 DVLOG(1) << "WillStartRequest: defer " << request_
->url();
88 const content::ResourceRequestInfo
* info
=
89 content::ResourceRequestInfo::ForRequest(request_
);
90 BrowserThread::PostTask(
94 &MergeSessionThrottle::DeleayResourceLoadingOnUIThread
,
100 &MergeSessionThrottle::OnBlockingPageComplete
,
105 const char* MergeSessionThrottle::GetNameForLogging() const {
106 return "MergeSessionThrottle";
110 bool MergeSessionThrottle::AreAllSessionMergedAlready() {
111 return !base::AtomicRefCountIsZero(&all_profiles_restored_
);
114 void MergeSessionThrottle::OnBlockingPageComplete() {
115 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
116 controller()->Resume();
119 bool MergeSessionThrottle::ShouldDelayUrl(const GURL
& url
) const {
120 // If we are loading google properties while merge session is in progress,
121 // we will show delayed loading page instead.
122 return !net::NetworkChangeNotifier::IsOffline() &&
123 !AreAllSessionMergedAlready() &&
124 google_util::IsGoogleHostname(url
.host(),
125 google_util::ALLOW_SUBDOMAIN
);
129 void MergeSessionThrottle::BlockProfile(Profile
* profile
) {
130 // Add a new profile to the list of those that we are currently blocking
131 // blocking page loading for.
132 if (ProfileSet::Get()->find(profile
) == ProfileSet::Get()->end()) {
133 DVLOG(1) << "Blocking profile " << profile
;
134 ProfileSet::Get()->insert(profile
);
136 // Since a new profile just got blocked, we can not assume that
137 // all sessions are merged anymore.
138 if (AreAllSessionMergedAlready()) {
139 base::AtomicRefCountDec(&all_profiles_restored_
);
140 DVLOG(1) << "Marking all sessions unmerged!";
146 void MergeSessionThrottle::UnblockProfile(Profile
* profile
) {
147 // Have we blocked loading of pages for this this profile
149 DVLOG(1) << "Unblocking profile " << profile
;
150 ProfileSet::Get()->erase(profile
);
152 // Check if there is any other profile to block on.
153 if (ProfileSet::Get()->size() == 0) {
154 base::AtomicRefCountInc(&all_profiles_restored_
);
155 DVLOG(1) << "All profiles merged " << all_profiles_restored_
;
160 bool MergeSessionThrottle::ShouldDelayRequest(
161 int render_process_id
,
162 int render_view_id
) {
163 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
165 if (!user_manager::UserManager::Get()->IsUserLoggedIn()) {
167 } else if (!user_manager::UserManager::Get()->
168 IsLoggedInAsUserWithGaiaAccount()) {
169 // This is not a regular user session, let's remove the throttle
171 if (!AreAllSessionMergedAlready())
172 base::AtomicRefCountInc(&all_profiles_restored_
);
177 RenderViewHost
* render_view_host
=
178 RenderViewHost::FromID(render_process_id
, render_view_id
);
179 if (!render_view_host
)
182 WebContents
* web_contents
=
183 WebContents::FromRenderViewHost(render_view_host
);
187 content::BrowserContext
* browser_context
=
188 web_contents
->GetBrowserContext();
189 if (!browser_context
)
192 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
196 chromeos::OAuth2LoginManager
* login_manager
=
197 chromeos::OAuth2LoginManagerFactory::GetInstance()->GetForProfile(
202 switch (login_manager
->state()) {
203 case chromeos::OAuth2LoginManager::SESSION_RESTORE_NOT_STARTED
:
204 // The session restore for this profile hasn't even started yet. Don't
206 // In theory this should not happen since we should
207 // kick off the session restore process for the newly added profile
208 // before we attempt loading any page.
209 if (user_manager::UserManager::Get()->IsLoggedInAsUserWithGaiaAccount() &&
210 !user_manager::UserManager::Get()->IsLoggedInAsStub()) {
211 LOG(WARNING
) << "Loading content for a profile without "
212 << "session restore?";
215 case chromeos::OAuth2LoginManager::SESSION_RESTORE_PREPARING
:
216 case chromeos::OAuth2LoginManager::SESSION_RESTORE_IN_PROGRESS
: {
217 // Check if the session restore has been going on for a while already.
218 // If so, don't attempt to block page loading.
219 if ((base::Time::Now() -
220 login_manager
->session_restore_start()).InSeconds() >
221 kMaxSessionRestoreTimeInSec
) {
222 UnblockProfile(profile
);
226 // Add a new profile to the list of those that we are currently blocking
227 // blocking page loading for.
228 BlockProfile(profile
);
231 case chromeos::OAuth2LoginManager::SESSION_RESTORE_DONE
:
232 case chromeos::OAuth2LoginManager::SESSION_RESTORE_FAILED
:
233 case chromeos::OAuth2LoginManager::SESSION_RESTORE_CONNECTION_FAILED
: {
234 UnblockProfile(profile
);
244 void MergeSessionThrottle::DeleayResourceLoadingOnUIThread(
245 ResourceType resource_type
,
246 int render_process_id
,
249 const CompletionCallback
& callback
) {
250 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
252 if (ShouldDelayRequest(render_process_id
, render_view_id
)) {
253 // There is a chance that the tab closed after we decided to show
254 // the offline page on the IO thread and before we actually show the
255 // offline page here on the UI thread.
256 RenderViewHost
* render_view_host
=
257 RenderViewHost::FromID(render_process_id
, render_view_id
);
258 WebContents
* web_contents
= render_view_host
?
259 WebContents::FromRenderViewHost(render_view_host
) : NULL
;
260 if (resource_type
== content::RESOURCE_TYPE_MAIN_FRAME
) {
261 DVLOG(1) << "Creating page waiter for " << url
.spec();
262 (new chromeos::MergeSessionLoadPage(web_contents
, url
, callback
))->Show();
264 DVLOG(1) << "Creating XHR waiter for " << url
.spec();
265 DCHECK(resource_type
== content::RESOURCE_TYPE_XHR
);
266 Profile
* profile
= Profile::FromBrowserContext(
267 web_contents
->GetBrowserContext());
268 (new chromeos::MergeSessionXHRRequestWaiter(profile
,
269 callback
))->StartWaiting();
272 BrowserThread::PostTask(
273 BrowserThread::IO
, FROM_HERE
, callback
);