OLD | NEW |
| (Empty) |
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 } | |
OLD | NEW |