1 // Copyright (c) 2013 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/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/merge_session_load_page.h"
16 #include "chrome/browser/chromeos/login/merge_session_xhr_request_waiter.h"
17 #include "chrome/browser/chromeos/login/oauth2_login_manager.h"
18 #include "chrome/browser/chromeos/login/oauth2_login_manager_factory.h"
19 #include "chrome/browser/chromeos/login/user_manager.h"
20 #include "chrome/browser/chromeos/login/user_manager.h"
21 #include "chrome/browser/google/google_util.h"
22 #include "chrome/browser/net/chrome_url_request_context.h"
23 #include "chrome/common/url_constants.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/resource_controller.h"
27 #include "content/public/browser/resource_request_info.h"
28 #include "content/public/browser/web_contents.h"
29 #include "net/base/net_errors.h"
30 #include "net/base/net_util.h"
31 #include "net/base/network_change_notifier.h"
32 #include "net/url_request/url_request.h"
33 #include "net/url_request/url_request_context.h"
35 using content::BrowserThread
;
36 using content::RenderViewHost
;
37 using content::WebContents
;
41 const int64 kMaxSessionRestoreTimeInSec
= 60;
43 // The set of blocked profiles.
44 class ProfileSet
: public base::NonThreadSafe
,
45 public std::set
<Profile
*> {
50 virtual ~ProfileSet() {
53 static ProfileSet
* Get();
56 friend struct ::base::DefaultLazyInstanceTraits
<ProfileSet
>;
58 DISALLOW_COPY_AND_ASSIGN(ProfileSet
);
61 // Set of all of profiles for which restore session is in progress.
62 // This static member is accessible only form UI thread.
63 static base::LazyInstance
<ProfileSet
> g_blocked_profiles
=
64 LAZY_INSTANCE_INITIALIZER
;
66 ProfileSet
* ProfileSet::Get() {
67 return g_blocked_profiles
.Pointer();
72 base::AtomicRefCount
MergeSessionThrottle::all_profiles_restored_(0);
74 MergeSessionThrottle::MergeSessionThrottle(net::URLRequest
* request
,
75 ResourceType::Type resource_type
)
77 resource_type_(resource_type
) {
80 MergeSessionThrottle::~MergeSessionThrottle() {
81 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
84 void MergeSessionThrottle::WillStartRequest(bool* defer
) {
85 if (!ShouldDelayUrl(request_
->url()))
88 DVLOG(1) << "WillStartRequest: defer " << request_
->url();
89 const content::ResourceRequestInfo
* info
=
90 content::ResourceRequestInfo::ForRequest(request_
);
91 BrowserThread::PostTask(
95 &MergeSessionThrottle::DeleayResourceLoadingOnUIThread
,
101 &MergeSessionThrottle::OnBlockingPageComplete
,
106 const char* MergeSessionThrottle::GetNameForLogging() const {
107 return "MergeSessionThrottle";
111 bool MergeSessionThrottle::AreAllSessionMergedAlready() {
112 return !base::AtomicRefCountIsZero(&all_profiles_restored_
);
115 void MergeSessionThrottle::OnBlockingPageComplete() {
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
117 controller()->Resume();
120 bool MergeSessionThrottle::ShouldDelayUrl(const GURL
& url
) const {
121 // If we are loading google properties while merge session is in progress,
122 // we will show delayed loading page instead.
123 return !net::NetworkChangeNotifier::IsOffline() &&
124 !AreAllSessionMergedAlready() &&
125 google_util::IsGoogleHostname(url
.host(),
126 google_util::ALLOW_SUBDOMAIN
);
130 void MergeSessionThrottle::BlockProfile(Profile
* profile
) {
131 // Add a new profile to the list of those that we are currently blocking
132 // blocking page loading for.
133 if (ProfileSet::Get()->find(profile
) == ProfileSet::Get()->end()) {
134 DVLOG(1) << "Blocking profile " << profile
;
135 ProfileSet::Get()->insert(profile
);
137 // Since a new profile just got blocked, we can not assume that
138 // all sessions are merged anymore.
139 if (AreAllSessionMergedAlready()) {
140 base::AtomicRefCountDec(&all_profiles_restored_
);
141 DVLOG(1) << "Marking all sessions unmerged!";
147 void MergeSessionThrottle::UnblockProfile(Profile
* profile
) {
148 // Have we blocked loading of pages for this this profile
150 DVLOG(1) << "Unblocking profile " << profile
;
151 ProfileSet::Get()->erase(profile
);
153 // Check if there is any other profile to block on.
154 if (ProfileSet::Get()->size() == 0) {
155 base::AtomicRefCountInc(&all_profiles_restored_
);
156 DVLOG(1) << "All profiles merged " << all_profiles_restored_
;
161 bool MergeSessionThrottle::ShouldDelayRequest(
162 int render_process_id
,
163 int render_view_id
) {
164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
166 if (!chromeos::UserManager::Get()->IsUserLoggedIn()) {
168 } else if (!chromeos::UserManager::Get()->IsLoggedInAsRegularUser()) {
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 (chromeos::UserManager::Get()->IsLoggedInAsRegularUser() &&
210 !chromeos::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::Type resource_type
,
246 int render_process_id
,
249 const CompletionCallback
& callback
) {
250 DCHECK(BrowserThread::CurrentlyOn(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
== ResourceType::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
== ResourceType::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
);