Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / login / signin / merge_session_throttle.cc
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.
4
5 #include "chrome/browser/chromeos/login/signin/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/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"
32
33 using content::BrowserThread;
34 using content::RenderViewHost;
35 using content::ResourceType;
36 using content::WebContents;
37
38 namespace {
39
40 const int64 kMaxSessionRestoreTimeInSec = 60;
41
42 // The set of blocked profiles.
43 class ProfileSet : public base::NonThreadSafe,
44                    public std::set<Profile*> {
45  public:
46   ProfileSet() {
47   }
48
49   virtual ~ProfileSet() {
50   }
51
52   static ProfileSet* Get();
53
54  private:
55   friend struct ::base::DefaultLazyInstanceTraits<ProfileSet>;
56
57   DISALLOW_COPY_AND_ASSIGN(ProfileSet);
58 };
59
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;
64
65 ProfileSet* ProfileSet::Get() {
66   return g_blocked_profiles.Pointer();
67 }
68
69 }  // namespace
70
71 base::AtomicRefCount MergeSessionThrottle::all_profiles_restored_(0);
72
73 MergeSessionThrottle::MergeSessionThrottle(net::URLRequest* request,
74                                            ResourceType resource_type)
75     : request_(request),
76       resource_type_(resource_type) {
77 }
78
79 MergeSessionThrottle::~MergeSessionThrottle() {
80   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
81 }
82
83 void MergeSessionThrottle::WillStartRequest(bool* defer) {
84   if (!ShouldDelayUrl(request_->url()))
85     return;
86
87   DVLOG(1) << "WillStartRequest: defer " << request_->url();
88   const content::ResourceRequestInfo* info =
89     content::ResourceRequestInfo::ForRequest(request_);
90   BrowserThread::PostTask(
91       BrowserThread::UI,
92       FROM_HERE,
93       base::Bind(
94           &MergeSessionThrottle::DeleayResourceLoadingOnUIThread,
95           resource_type_,
96           info->GetChildID(),
97           info->GetRouteID(),
98           request_->url(),
99           base::Bind(
100               &MergeSessionThrottle::OnBlockingPageComplete,
101               AsWeakPtr())));
102   *defer = true;
103 }
104
105 const char* MergeSessionThrottle::GetNameForLogging() const {
106   return "MergeSessionThrottle";
107 }
108
109 // static.
110 bool MergeSessionThrottle::AreAllSessionMergedAlready() {
111   return !base::AtomicRefCountIsZero(&all_profiles_restored_);
112 }
113
114 void MergeSessionThrottle::OnBlockingPageComplete() {
115   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
116   controller()->Resume();
117 }
118
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);
126 }
127
128 // static
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);
135
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!";
141     }
142   }
143 }
144
145 // static
146 void MergeSessionThrottle::UnblockProfile(Profile* profile) {
147   // Have we blocked loading of pages for this this profile
148   // before?
149   DVLOG(1) << "Unblocking profile " << profile;
150   ProfileSet::Get()->erase(profile);
151
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_;
156   }
157 }
158
159 // static
160 bool MergeSessionThrottle::ShouldDelayRequest(
161     int render_process_id,
162     int render_view_id) {
163   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
164
165   if (!user_manager::UserManager::Get()->IsUserLoggedIn()) {
166     return false;
167   } else if (!user_manager::UserManager::Get()->IsLoggedInAsRegularUser()) {
168     // This is not a regular user session, let's remove the throttle
169     // permanently.
170     if (!AreAllSessionMergedAlready())
171       base::AtomicRefCountInc(&all_profiles_restored_);
172
173     return false;
174   }
175
176   RenderViewHost* render_view_host =
177       RenderViewHost::FromID(render_process_id, render_view_id);
178   if (!render_view_host)
179     return false;
180
181   WebContents* web_contents =
182       WebContents::FromRenderViewHost(render_view_host);
183   if (!web_contents)
184     return false;
185
186   content::BrowserContext* browser_context =
187       web_contents->GetBrowserContext();
188   if (!browser_context)
189     return false;
190
191   Profile* profile = Profile::FromBrowserContext(browser_context);
192   if (!profile)
193     return false;
194
195   chromeos::OAuth2LoginManager* login_manager =
196       chromeos::OAuth2LoginManagerFactory::GetInstance()->GetForProfile(
197           profile);
198   if (!login_manager)
199     return false;
200
201   switch (login_manager->state()) {
202     case chromeos::OAuth2LoginManager::SESSION_RESTORE_NOT_STARTED:
203       // The session restore for this profile hasn't even started yet. Don't
204       // block for now.
205       // In theory this should not happen since we should
206       // kick off the session restore process for the newly added profile
207       // before we attempt loading any page.
208       if (user_manager::UserManager::Get()->IsLoggedInAsRegularUser() &&
209           !user_manager::UserManager::Get()->IsLoggedInAsStub()) {
210         LOG(WARNING) << "Loading content for a profile without "
211                      << "session restore?";
212       }
213       return false;
214     case chromeos::OAuth2LoginManager::SESSION_RESTORE_PREPARING:
215     case chromeos::OAuth2LoginManager::SESSION_RESTORE_IN_PROGRESS: {
216       // Check if the session restore has been going on for a while already.
217       // If so, don't attempt to block page loading.
218       if ((base::Time::Now() -
219            login_manager->session_restore_start()).InSeconds() >
220                kMaxSessionRestoreTimeInSec) {
221         UnblockProfile(profile);
222         return false;
223       }
224
225       // Add a new profile to the list of those that we are currently blocking
226       // blocking page loading for.
227       BlockProfile(profile);
228       return true;
229     }
230     case chromeos::OAuth2LoginManager::SESSION_RESTORE_DONE:
231     case chromeos::OAuth2LoginManager::SESSION_RESTORE_FAILED:
232     case chromeos::OAuth2LoginManager::SESSION_RESTORE_CONNECTION_FAILED: {
233       UnblockProfile(profile);
234       return false;
235     }
236   }
237
238   NOTREACHED();
239   return false;
240 }
241
242 // static.
243 void MergeSessionThrottle::DeleayResourceLoadingOnUIThread(
244     ResourceType resource_type,
245     int render_process_id,
246     int render_view_id,
247     const GURL& url,
248     const CompletionCallback& callback) {
249   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
250
251   if (ShouldDelayRequest(render_process_id, render_view_id)) {
252     // There is a chance that the tab closed after we decided to show
253     // the offline page on the IO thread and before we actually show the
254     // offline page here on the UI thread.
255     RenderViewHost* render_view_host =
256         RenderViewHost::FromID(render_process_id, render_view_id);
257     WebContents* web_contents = render_view_host ?
258         WebContents::FromRenderViewHost(render_view_host) : NULL;
259     if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME) {
260       DVLOG(1) << "Creating page waiter for " << url.spec();
261       (new chromeos::MergeSessionLoadPage(web_contents, url, callback))->Show();
262     } else {
263       DVLOG(1) << "Creating XHR waiter for " << url.spec();
264       DCHECK(resource_type == content::RESOURCE_TYPE_XHR);
265       Profile* profile = Profile::FromBrowserContext(
266           web_contents->GetBrowserContext());
267       (new chromeos::MergeSessionXHRRequestWaiter(profile,
268                                                   callback))->StartWaiting();
269     }
270   } else {
271     BrowserThread::PostTask(
272         BrowserThread::IO, FROM_HERE, callback);
273   }
274 }