ExtensionSyncService: listen for relevant changes instead of being explicitly called...
[chromium-blink-merge.git] / chrome / browser / chromeos / login / signin / merge_session_throttle.cc
blob442de8325af45fb7e5b9843f7eed38129717871f
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.
5 #include "chrome/browser/chromeos/login/signin/merge_session_throttle.h"
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"
33 using content::BrowserThread;
34 using content::RenderViewHost;
35 using content::ResourceType;
36 using content::WebContents;
38 namespace {
40 const int64 kMaxSessionRestoreTimeInSec = 60;
42 // The set of blocked profiles.
43 class ProfileSet : public base::NonThreadSafe,
44 public std::set<Profile*> {
45 public:
46 ProfileSet() {
49 virtual ~ProfileSet() {
52 static ProfileSet* Get();
54 private:
55 friend struct ::base::DefaultLazyInstanceTraits<ProfileSet>;
57 DISALLOW_COPY_AND_ASSIGN(ProfileSet);
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;
65 ProfileSet* ProfileSet::Get() {
66 return g_blocked_profiles.Pointer();
69 } // namespace
71 base::AtomicRefCount MergeSessionThrottle::all_profiles_restored_(0);
73 MergeSessionThrottle::MergeSessionThrottle(net::URLRequest* request,
74 ResourceType resource_type)
75 : request_(request),
76 resource_type_(resource_type) {
79 MergeSessionThrottle::~MergeSessionThrottle() {
80 DCHECK_CURRENTLY_ON(BrowserThread::IO);
83 void MergeSessionThrottle::WillStartRequest(bool* defer) {
84 if (!ShouldDelayUrl(request_->url()))
85 return;
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;
105 const char* MergeSessionThrottle::GetNameForLogging() const {
106 return "MergeSessionThrottle";
109 // static.
110 bool MergeSessionThrottle::AreAllSessionMergedAlready() {
111 return !base::AtomicRefCountIsZero(&all_profiles_restored_);
114 void MergeSessionThrottle::OnBlockingPageComplete() {
115 DCHECK_CURRENTLY_ON(BrowserThread::IO);
116 controller()->Resume();
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);
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);
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!";
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);
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_;
159 // static
160 bool MergeSessionThrottle::ShouldDelayRequest(
161 int render_process_id,
162 int render_view_id) {
163 DCHECK_CURRENTLY_ON(BrowserThread::UI);
165 if (!user_manager::UserManager::Get()->IsUserLoggedIn()) {
166 return false;
167 } else if (!user_manager::UserManager::Get()->
168 IsLoggedInAsUserWithGaiaAccount()) {
169 // This is not a regular user session, let's remove the throttle
170 // permanently.
171 if (!AreAllSessionMergedAlready())
172 base::AtomicRefCountInc(&all_profiles_restored_);
174 return false;
177 RenderViewHost* render_view_host =
178 RenderViewHost::FromID(render_process_id, render_view_id);
179 if (!render_view_host)
180 return false;
182 WebContents* web_contents =
183 WebContents::FromRenderViewHost(render_view_host);
184 if (!web_contents)
185 return false;
187 content::BrowserContext* browser_context =
188 web_contents->GetBrowserContext();
189 if (!browser_context)
190 return false;
192 Profile* profile = Profile::FromBrowserContext(browser_context);
193 if (!profile)
194 return false;
196 chromeos::OAuth2LoginManager* login_manager =
197 chromeos::OAuth2LoginManagerFactory::GetInstance()->GetForProfile(
198 profile);
199 if (!login_manager)
200 return false;
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 (user_manager::UserManager::Get()->IsLoggedInAsUserWithGaiaAccount() &&
210 !user_manager::UserManager::Get()->IsLoggedInAsStub()) {
211 LOG(WARNING) << "Loading content for a profile without "
212 << "session restore?";
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;
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;
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;
239 NOTREACHED();
240 return false;
243 // static.
244 void MergeSessionThrottle::DeleayResourceLoadingOnUIThread(
245 ResourceType resource_type,
246 int render_process_id,
247 int render_view_id,
248 const GURL& url,
249 const CompletionCallback& callback) {
250 DCHECK_CURRENTLY_ON(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 == content::RESOURCE_TYPE_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 == content::RESOURCE_TYPE_XHR);
266 Profile* profile = Profile::FromBrowserContext(
267 web_contents->GetBrowserContext());
268 (new chromeos::MergeSessionXHRRequestWaiter(profile,
269 callback))->StartWaiting();
271 } else {
272 BrowserThread::PostTask(
273 BrowserThread::IO, FROM_HERE, callback);