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/oauth2_login_manager.h"
16 #include "chrome/browser/chromeos/login/oauth2_login_manager_factory.h"
17 #include "chrome/browser/chromeos/login/user_manager.h"
18 #include "chrome/browser/chromeos/login/user_manager.h"
19 #include "chrome/browser/google/google_util.h"
20 #include "chrome/browser/net/chrome_url_request_context.h"
21 #include "chrome/common/url_constants.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::WebContents;
39 const int64 kMaxSessionRestoreTimeInSec = 60;
41 // The set of blocked profiles.
42 class ProfileSet : public base::NonThreadSafe,
43 public std::set<Profile*> {
46 virtual ~ProfileSet();
47 static ProfileSet* Get();
50 friend struct ::base::DefaultLazyInstanceTraits<ProfileSet>;
53 // Set of all of profiles for which restore session is in progress.
54 // This static member is accessible only form UI thread.
55 static base::LazyInstance<ProfileSet> g_blocked_profiles =
56 LAZY_INSTANCE_INITIALIZER;
58 ProfileSet::ProfileSet() {
61 ProfileSet::~ProfileSet() {
64 ProfileSet* ProfileSet::Get() {
65 return g_blocked_profiles.Pointer();
70 base::AtomicRefCount MergeSessionThrottle::all_profiles_restored_(0);
72 MergeSessionThrottle::MergeSessionThrottle(net::URLRequest* request)
76 MergeSessionThrottle::~MergeSessionThrottle() {
77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
80 void MergeSessionThrottle::WillStartRequest(bool* defer) {
81 DCHECK(request_->url().SchemeIsHTTPOrHTTPS());
82 if (!ShouldShowMergeSessionPage(request_->url()))
85 DVLOG(1) << "WillStartRequest: defer " << request_->url();
86 const content::ResourceRequestInfo* info =
87 content::ResourceRequestInfo::ForRequest(request_);
88 BrowserThread::PostTask(
92 &MergeSessionThrottle::ShowDeleayedLoadingPageOnUIThread,
97 &MergeSessionThrottle::OnBlockingPageComplete,
103 bool MergeSessionThrottle::AreAllSessionMergedAlready() {
104 return !base::AtomicRefCountIsZero(&all_profiles_restored_);
107 void MergeSessionThrottle::OnBlockingPageComplete() {
108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
109 controller()->Resume();
112 bool MergeSessionThrottle::ShouldShowMergeSessionPage(const GURL& url) const {
113 // If we are loading google properties while merge session is in progress,
114 // we will show delayed loading page instead.
115 return !net::NetworkChangeNotifier::IsOffline() &&
116 !AreAllSessionMergedAlready() &&
117 google_util::IsGoogleHostname(url.host(),
118 google_util::ALLOW_SUBDOMAIN);
122 void MergeSessionThrottle::BlockProfile(Profile* profile) {
123 // Add a new profile to the list of those that we are currently blocking
124 // blocking page loading for.
125 if (ProfileSet::Get()->find(profile) == ProfileSet::Get()->end()) {
126 DVLOG(1) << "Blocking profile " << profile;
127 ProfileSet::Get()->insert(profile);
129 // Since a new profile just got blocked, we can not assume that
130 // all sessions are merged anymore.
131 if (AreAllSessionMergedAlready()) {
132 base::AtomicRefCountDec(&all_profiles_restored_);
133 DVLOG(1) << "Marking all sessions unmerged!";
139 void MergeSessionThrottle::UnblockProfile(Profile* profile) {
140 // Have we blocked loading of pages for this this profile
142 DVLOG(1) << "Unblocking profile " << profile;
143 ProfileSet::Get()->erase(profile);
145 // Check if there is any other profile to block on.
146 if (ProfileSet::Get()->size() == 0) {
147 base::AtomicRefCountInc(&all_profiles_restored_);
148 DVLOG(1) << "All profiles merged " << all_profiles_restored_;
153 bool MergeSessionThrottle::ShouldShowInterstitialPage(
154 int render_process_id,
155 int render_view_id) {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
158 if (!chromeos::UserManager::Get()->IsUserLoggedIn()) {
160 } else if (!chromeos::UserManager::Get()->IsLoggedInAsRegularUser()) {
161 // This is not a regular user session, let's remove the throttle
163 if (!AreAllSessionMergedAlready())
164 base::AtomicRefCountInc(&all_profiles_restored_);
169 RenderViewHost* render_view_host =
170 RenderViewHost::FromID(render_process_id, render_view_id);
171 if (!render_view_host)
174 WebContents* web_contents =
175 WebContents::FromRenderViewHost(render_view_host);
179 content::BrowserContext* browser_context =
180 web_contents->GetBrowserContext();
181 if (!browser_context)
184 Profile* profile = Profile::FromBrowserContext(browser_context);
188 chromeos::OAuth2LoginManager* login_manager =
189 chromeos::OAuth2LoginManagerFactory::GetInstance()->GetForProfile(
194 switch (login_manager->state()) {
195 case chromeos::OAuth2LoginManager::SESSION_RESTORE_NOT_STARTED:
196 // The session restore for this profile hasn't even started yet. Don't
198 // In theory this should not happen since we should
199 // kick off the session restore process for the newly added profile
200 // before we attempt loading any page.
201 if (chromeos::UserManager::Get()->IsLoggedInAsRegularUser() &&
202 !chromeos::UserManager::Get()->IsLoggedInAsStub()) {
203 LOG(WARNING) << "Loading content for a profile without "
204 << "session restore?";
207 case chromeos::OAuth2LoginManager::SESSION_RESTORE_PREPARING:
208 case chromeos::OAuth2LoginManager::SESSION_RESTORE_IN_PROGRESS: {
209 // Check if the session restore has been going on for a while already.
210 // If so, don't attempt to block page loading.
211 if ((base::Time::Now() -
212 login_manager->session_restore_start()).InSeconds() >
213 kMaxSessionRestoreTimeInSec) {
214 UnblockProfile(profile);
218 // Add a new profile to the list of those that we are currently blocking
219 // blocking page loading for.
220 BlockProfile(profile);
223 case chromeos::OAuth2LoginManager::SESSION_RESTORE_DONE:
224 case chromeos::OAuth2LoginManager::SESSION_RESTORE_FAILED:
225 case chromeos::OAuth2LoginManager::SESSION_RESTORE_CONNECTION_FAILED: {
226 UnblockProfile(profile);
236 void MergeSessionThrottle::ShowDeleayedLoadingPageOnUIThread(
237 int render_process_id,
240 const chromeos::MergeSessionLoadPage::CompletionCallback& callback) {
241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
243 if (ShouldShowInterstitialPage(render_process_id, render_view_id)) {
244 // There is a chance that the tab closed after we decided to show
245 // the offline page on the IO thread and before we actually show the
246 // offline page here on the UI thread.
247 RenderViewHost* render_view_host =
248 RenderViewHost::FromID(render_process_id, render_view_id);
249 WebContents* web_contents = render_view_host ?
250 WebContents::FromRenderViewHost(render_view_host) : NULL;
252 (new chromeos::MergeSessionLoadPage(web_contents, url, callback))->Show();
254 BrowserThread::PostTask(
255 BrowserThread::IO, FROM_HERE, callback);