• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/chromeos/login/merge_session_throttle.h"
6 
7 #include "base/bind.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"
32 
33 using content::BrowserThread;
34 using content::RenderViewHost;
35 using content::WebContents;
36 
37 namespace {
38 
39 const int64 kMaxSessionRestoreTimeInSec = 60;
40 
41 // The set of blocked profiles.
42 class ProfileSet : public base::NonThreadSafe,
43                    public std::set<Profile*> {
44  public:
45   ProfileSet();
46   virtual ~ProfileSet();
47   static ProfileSet* Get();
48 
49  private:
50   friend struct ::base::DefaultLazyInstanceTraits<ProfileSet>;
51 };
52 
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;
57 
ProfileSet()58 ProfileSet::ProfileSet() {
59 }
60 
~ProfileSet()61 ProfileSet::~ProfileSet() {
62 }
63 
Get()64 ProfileSet* ProfileSet::Get() {
65   return g_blocked_profiles.Pointer();
66 }
67 
68 }  // namespace
69 
70 base::AtomicRefCount MergeSessionThrottle::all_profiles_restored_(0);
71 
MergeSessionThrottle(net::URLRequest * request)72 MergeSessionThrottle::MergeSessionThrottle(net::URLRequest* request)
73     : request_(request) {
74 }
75 
~MergeSessionThrottle()76 MergeSessionThrottle::~MergeSessionThrottle() {
77   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
78 }
79 
WillStartRequest(bool * defer)80 void MergeSessionThrottle::WillStartRequest(bool* defer) {
81   DCHECK(request_->url().SchemeIsHTTPOrHTTPS());
82   if (!ShouldShowMergeSessionPage(request_->url()))
83     return;
84 
85   DVLOG(1) << "WillStartRequest: defer " << request_->url();
86   const content::ResourceRequestInfo* info =
87     content::ResourceRequestInfo::ForRequest(request_);
88   BrowserThread::PostTask(
89       BrowserThread::UI,
90       FROM_HERE,
91       base::Bind(
92           &MergeSessionThrottle::ShowDeleayedLoadingPageOnUIThread,
93           info->GetChildID(),
94           info->GetRouteID(),
95           request_->url(),
96           base::Bind(
97               &MergeSessionThrottle::OnBlockingPageComplete,
98               AsWeakPtr())));
99   *defer = true;
100 }
101 
GetNameForLogging() const102 const char* MergeSessionThrottle::GetNameForLogging() const {
103   return "MergeSessionThrottle";
104 }
105 
106 // static.
AreAllSessionMergedAlready()107 bool MergeSessionThrottle::AreAllSessionMergedAlready() {
108   return !base::AtomicRefCountIsZero(&all_profiles_restored_);
109 }
110 
OnBlockingPageComplete()111 void MergeSessionThrottle::OnBlockingPageComplete() {
112   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
113   controller()->Resume();
114 }
115 
ShouldShowMergeSessionPage(const GURL & url) const116 bool MergeSessionThrottle::ShouldShowMergeSessionPage(const GURL& url) const {
117   // If we are loading google properties while merge session is in progress,
118   // we will show delayed loading page instead.
119   return !net::NetworkChangeNotifier::IsOffline() &&
120          !AreAllSessionMergedAlready() &&
121          google_util::IsGoogleHostname(url.host(),
122                                        google_util::ALLOW_SUBDOMAIN);
123 }
124 
125 // static
BlockProfile(Profile * profile)126 void MergeSessionThrottle::BlockProfile(Profile* profile) {
127   // Add a new profile to the list of those that we are currently blocking
128   // blocking page loading for.
129   if (ProfileSet::Get()->find(profile) == ProfileSet::Get()->end()) {
130     DVLOG(1) << "Blocking profile " << profile;
131     ProfileSet::Get()->insert(profile);
132 
133     // Since a new profile just got blocked, we can not assume that
134     // all sessions are merged anymore.
135     if (AreAllSessionMergedAlready()) {
136       base::AtomicRefCountDec(&all_profiles_restored_);
137       DVLOG(1) << "Marking all sessions unmerged!";
138     }
139   }
140 }
141 
142 // static
UnblockProfile(Profile * profile)143 void MergeSessionThrottle::UnblockProfile(Profile* profile) {
144   // Have we blocked loading of pages for this this profile
145   // before?
146   DVLOG(1) << "Unblocking profile " << profile;
147   ProfileSet::Get()->erase(profile);
148 
149   // Check if there is any other profile to block on.
150   if (ProfileSet::Get()->size() == 0) {
151     base::AtomicRefCountInc(&all_profiles_restored_);
152     DVLOG(1) << "All profiles merged " << all_profiles_restored_;
153   }
154 }
155 
156 // static
ShouldShowInterstitialPage(int render_process_id,int render_view_id)157 bool MergeSessionThrottle::ShouldShowInterstitialPage(
158     int render_process_id,
159     int render_view_id) {
160   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
161 
162   if (!chromeos::UserManager::Get()->IsUserLoggedIn()) {
163     return false;
164   } else if (!chromeos::UserManager::Get()->IsLoggedInAsRegularUser()) {
165     // This is not a regular user session, let's remove the throttle
166     // permanently.
167     if (!AreAllSessionMergedAlready())
168       base::AtomicRefCountInc(&all_profiles_restored_);
169 
170     return false;
171   }
172 
173   RenderViewHost* render_view_host =
174       RenderViewHost::FromID(render_process_id, render_view_id);
175   if (!render_view_host)
176     return false;
177 
178   WebContents* web_contents =
179       WebContents::FromRenderViewHost(render_view_host);
180   if (!web_contents)
181     return false;
182 
183   content::BrowserContext* browser_context =
184       web_contents->GetBrowserContext();
185   if (!browser_context)
186     return false;
187 
188   Profile* profile = Profile::FromBrowserContext(browser_context);
189   if (!profile)
190     return false;
191 
192   chromeos::OAuth2LoginManager* login_manager =
193       chromeos::OAuth2LoginManagerFactory::GetInstance()->GetForProfile(
194           profile);
195   if (!login_manager)
196     return false;
197 
198   switch (login_manager->state()) {
199     case chromeos::OAuth2LoginManager::SESSION_RESTORE_NOT_STARTED:
200       // The session restore for this profile hasn't even started yet. Don't
201       // block for now.
202       // In theory this should not happen since we should
203       // kick off the session restore process for the newly added profile
204       // before we attempt loading any page.
205       if (chromeos::UserManager::Get()->IsLoggedInAsRegularUser() &&
206           !chromeos::UserManager::Get()->IsLoggedInAsStub()) {
207         LOG(WARNING) << "Loading content for a profile without "
208                      << "session restore?";
209       }
210       return false;
211     case chromeos::OAuth2LoginManager::SESSION_RESTORE_PREPARING:
212     case chromeos::OAuth2LoginManager::SESSION_RESTORE_IN_PROGRESS: {
213       // Check if the session restore has been going on for a while already.
214       // If so, don't attempt to block page loading.
215       if ((base::Time::Now() -
216            login_manager->session_restore_start()).InSeconds() >
217                kMaxSessionRestoreTimeInSec) {
218         UnblockProfile(profile);
219         return false;
220       }
221 
222       // Add a new profile to the list of those that we are currently blocking
223       // blocking page loading for.
224       BlockProfile(profile);
225       return true;
226     }
227     case chromeos::OAuth2LoginManager::SESSION_RESTORE_DONE:
228     case chromeos::OAuth2LoginManager::SESSION_RESTORE_FAILED:
229     case chromeos::OAuth2LoginManager::SESSION_RESTORE_CONNECTION_FAILED: {
230       UnblockProfile(profile);
231       return false;
232     }
233   }
234 
235   NOTREACHED();
236   return false;
237 }
238 
239 // static.
ShowDeleayedLoadingPageOnUIThread(int render_process_id,int render_view_id,const GURL & url,const chromeos::MergeSessionLoadPage::CompletionCallback & callback)240 void MergeSessionThrottle::ShowDeleayedLoadingPageOnUIThread(
241     int render_process_id,
242     int render_view_id,
243     const GURL& url,
244     const chromeos::MergeSessionLoadPage::CompletionCallback& callback) {
245   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
246 
247   if (ShouldShowInterstitialPage(render_process_id, render_view_id)) {
248     // There is a chance that the tab closed after we decided to show
249     // the offline page on the IO thread and before we actually show the
250     // offline page here on the UI thread.
251     RenderViewHost* render_view_host =
252         RenderViewHost::FromID(render_process_id, render_view_id);
253     WebContents* web_contents = render_view_host ?
254         WebContents::FromRenderViewHost(render_view_host) : NULL;
255     if (web_contents)
256       (new chromeos::MergeSessionLoadPage(web_contents, url, callback))->Show();
257   } else {
258     BrowserThread::PostTask(
259         BrowserThread::IO, FROM_HERE, callback);
260   }
261 }
262 
263