| 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 |