Fix infinite recursion on hiding panel when created during fullscreen mode.
[chromium-blink-merge.git] / chrome / browser / chromeos / login / merge_session_throttle.cc
blob3b36b9713947f35d9aedc240862b872c2bb73052
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"
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"
35 using content::BrowserThread;
36 using content::RenderViewHost;
37 using content::WebContents;
39 namespace {
41 const int64 kMaxSessionRestoreTimeInSec = 60;
43 // The set of blocked profiles.
44 class ProfileSet : public base::NonThreadSafe,
45 public std::set<Profile*> {
46 public:
47 ProfileSet() {
50 virtual ~ProfileSet() {
53 static ProfileSet* Get();
55 private:
56 friend struct ::base::DefaultLazyInstanceTraits<ProfileSet>;
58 DISALLOW_COPY_AND_ASSIGN(ProfileSet);
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;
66 ProfileSet* ProfileSet::Get() {
67 return g_blocked_profiles.Pointer();
70 } // namespace
72 base::AtomicRefCount MergeSessionThrottle::all_profiles_restored_(0);
74 MergeSessionThrottle::MergeSessionThrottle(net::URLRequest* request,
75 ResourceType::Type resource_type)
76 : request_(request),
77 resource_type_(resource_type) {
80 MergeSessionThrottle::~MergeSessionThrottle() {
81 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
84 void MergeSessionThrottle::WillStartRequest(bool* defer) {
85 if (!ShouldDelayUrl(request_->url()))
86 return;
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;
106 const char* MergeSessionThrottle::GetNameForLogging() const {
107 return "MergeSessionThrottle";
110 // static.
111 bool MergeSessionThrottle::AreAllSessionMergedAlready() {
112 return !base::AtomicRefCountIsZero(&all_profiles_restored_);
115 void MergeSessionThrottle::OnBlockingPageComplete() {
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
117 controller()->Resume();
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);
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);
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!";
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);
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_;
160 // static
161 bool MergeSessionThrottle::ShouldDelayRequest(
162 int render_process_id,
163 int render_view_id) {
164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
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_);
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 (chromeos::UserManager::Get()->IsLoggedInAsRegularUser() &&
210 !chromeos::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::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));
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();
271 } else {
272 BrowserThread::PostTask(
273 BrowserThread::IO, FROM_HERE, callback);