Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / login / merge_session_throttle.cc
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/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"
34
35 using content::BrowserThread;
36 using content::RenderViewHost;
37 using content::WebContents;
38
39 namespace {
40
41 const int64 kMaxSessionRestoreTimeInSec = 60;
42
43 // The set of blocked profiles.
44 class ProfileSet : public base::NonThreadSafe,
45                    public std::set<Profile*> {
46  public:
47   ProfileSet() {
48   }
49
50   virtual ~ProfileSet() {
51   }
52
53   static ProfileSet* Get();
54
55  private:
56   friend struct ::base::DefaultLazyInstanceTraits<ProfileSet>;
57
58   DISALLOW_COPY_AND_ASSIGN(ProfileSet);
59 };
60
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;
65
66 ProfileSet* ProfileSet::Get() {
67   return g_blocked_profiles.Pointer();
68 }
69
70 }  // namespace
71
72 base::AtomicRefCount MergeSessionThrottle::all_profiles_restored_(0);
73
74 MergeSessionThrottle::MergeSessionThrottle(net::URLRequest* request,
75                                            ResourceType::Type resource_type)
76     : request_(request),
77       resource_type_(resource_type) {
78 }
79
80 MergeSessionThrottle::~MergeSessionThrottle() {
81   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
82 }
83
84 void MergeSessionThrottle::WillStartRequest(bool* defer) {
85   if (!ShouldDelayUrl(request_->url()))
86     return;
87
88   DVLOG(1) << "WillStartRequest: defer " << request_->url();
89   const content::ResourceRequestInfo* info =
90     content::ResourceRequestInfo::ForRequest(request_);
91   BrowserThread::PostTask(
92       BrowserThread::UI,
93       FROM_HERE,
94       base::Bind(
95           &MergeSessionThrottle::DeleayResourceLoadingOnUIThread,
96           resource_type_,
97           info->GetChildID(),
98           info->GetRouteID(),
99           request_->url(),
100           base::Bind(
101               &MergeSessionThrottle::OnBlockingPageComplete,
102               AsWeakPtr())));
103   *defer = true;
104 }
105
106 const char* MergeSessionThrottle::GetNameForLogging() const {
107   return "MergeSessionThrottle";
108 }
109
110 // static.
111 bool MergeSessionThrottle::AreAllSessionMergedAlready() {
112   return !base::AtomicRefCountIsZero(&all_profiles_restored_);
113 }
114
115 void MergeSessionThrottle::OnBlockingPageComplete() {
116   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
117   controller()->Resume();
118 }
119
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);
127 }
128
129 // static
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);
136
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!";
142     }
143   }
144 }
145
146 // static
147 void MergeSessionThrottle::UnblockProfile(Profile* profile) {
148   // Have we blocked loading of pages for this this profile
149   // before?
150   DVLOG(1) << "Unblocking profile " << profile;
151   ProfileSet::Get()->erase(profile);
152
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_;
157   }
158 }
159
160 // static
161 bool MergeSessionThrottle::ShouldDelayRequest(
162     int render_process_id,
163     int render_view_id) {
164   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
165
166   if (!chromeos::UserManager::Get()->IsUserLoggedIn()) {
167     return false;
168   } else if (!chromeos::UserManager::Get()->IsLoggedInAsRegularUser()) {
169     // This is not a regular user session, let's remove the throttle
170     // permanently.
171     if (!AreAllSessionMergedAlready())
172       base::AtomicRefCountInc(&all_profiles_restored_);
173
174     return false;
175   }
176
177   RenderViewHost* render_view_host =
178       RenderViewHost::FromID(render_process_id, render_view_id);
179   if (!render_view_host)
180     return false;
181
182   WebContents* web_contents =
183       WebContents::FromRenderViewHost(render_view_host);
184   if (!web_contents)
185     return false;
186
187   content::BrowserContext* browser_context =
188       web_contents->GetBrowserContext();
189   if (!browser_context)
190     return false;
191
192   Profile* profile = Profile::FromBrowserContext(browser_context);
193   if (!profile)
194     return false;
195
196   chromeos::OAuth2LoginManager* login_manager =
197       chromeos::OAuth2LoginManagerFactory::GetInstance()->GetForProfile(
198           profile);
199   if (!login_manager)
200     return false;
201
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
205       // block for now.
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?";
213       }
214       return false;
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);
223         return false;
224       }
225
226       // Add a new profile to the list of those that we are currently blocking
227       // blocking page loading for.
228       BlockProfile(profile);
229       return true;
230     }
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);
235       return false;
236     }
237   }
238
239   NOTREACHED();
240   return false;
241 }
242
243 // static.
244 void MergeSessionThrottle::DeleayResourceLoadingOnUIThread(
245     ResourceType::Type resource_type,
246     int render_process_id,
247     int render_view_id,
248     const GURL& url,
249     const CompletionCallback& callback) {
250   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
251
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();
263     } else {
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();
270     }
271   } else {
272     BrowserThread::PostTask(
273         BrowserThread::IO, FROM_HERE, callback);
274   }
275 }