| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/managed_mode.h" | |
| 6 | |
| 7 #include "base/command_line.h" | |
| 8 #include "chrome/browser/browser_process.h" | |
| 9 #include "chrome/browser/extensions/extension_service.h" | |
| 10 #include "chrome/browser/extensions/extension_system.h" | |
| 11 #include "chrome/browser/managed_mode_url_filter.h" | |
| 12 #include "chrome/browser/prefs/pref_service.h" | |
| 13 #include "chrome/browser/profiles/profile.h" | |
| 14 #include "chrome/browser/ui/browser.h" | |
| 15 #include "chrome/browser/ui/browser_list.h" | |
| 16 #include "chrome/browser/ui/browser_window.h" | |
| 17 #include "chrome/common/chrome_notification_types.h" | |
| 18 #include "chrome/common/chrome_switches.h" | |
| 19 #include "chrome/common/pref_names.h" | |
| 20 #include "content/public/browser/browser_thread.h" | |
| 21 #include "content/public/browser/notification_service.h" | |
| 22 #include "grit/generated_resources.h" | |
| 23 #include "ui/base/l10n/l10n_util.h" | |
| 24 | |
| 25 using content::BrowserThread; | |
| 26 | |
| 27 // A bridge from ManagedMode (which lives on the UI thread) to | |
| 28 // ManagedModeURLFilter (which lives on the IO thread). | |
| 29 class ManagedMode::URLFilterContext { | |
| 30 public: | |
| 31 URLFilterContext() {} | |
| 32 ~URLFilterContext() {} | |
| 33 | |
| 34 const ManagedModeURLFilter* url_filter() const { | |
| 35 return &url_filter_; | |
| 36 } | |
| 37 | |
| 38 void SetActive(bool in_managed_mode) { | |
| 39 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 40 // Because ManagedMode is a singleton, we can pass the pointer to | |
| 41 // |url_filter_| unretained. | |
| 42 BrowserThread::PostTask(BrowserThread::IO, | |
| 43 FROM_HERE, | |
| 44 base::Bind( | |
| 45 &ManagedModeURLFilter::SetActive, | |
| 46 base::Unretained(&url_filter_), | |
| 47 in_managed_mode)); | |
| 48 } | |
| 49 | |
| 50 void ShutdownOnUIThread() { | |
| 51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 52 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, this); | |
| 53 } | |
| 54 | |
| 55 private: | |
| 56 ManagedModeURLFilter url_filter_; | |
| 57 | |
| 58 DISALLOW_COPY_AND_ASSIGN(URLFilterContext); | |
| 59 }; | |
| 60 | |
| 61 // static | |
| 62 ManagedMode* ManagedMode::GetInstance() { | |
| 63 return Singleton<ManagedMode, LeakySingletonTraits<ManagedMode> >::get(); | |
| 64 } | |
| 65 | |
| 66 // static | |
| 67 void ManagedMode::RegisterPrefs(PrefService* prefs) { | |
| 68 prefs->RegisterBooleanPref(prefs::kInManagedMode, false); | |
| 69 } | |
| 70 | |
| 71 // static | |
| 72 void ManagedMode::Init(Profile* profile) { | |
| 73 GetInstance()->InitImpl(profile); | |
| 74 } | |
| 75 | |
| 76 void ManagedMode::InitImpl(Profile* profile) { | |
| 77 DCHECK(g_browser_process); | |
| 78 DCHECK(g_browser_process->local_state()); | |
| 79 | |
| 80 Profile* original_profile = profile->GetOriginalProfile(); | |
| 81 // Set the value directly in the PrefService instead of using | |
| 82 // CommandLinePrefStore so we can change it at runtime. | |
| 83 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoManaged)) { | |
| 84 SetInManagedMode(NULL); | |
| 85 } else if (IsInManagedModeImpl() || | |
| 86 CommandLine::ForCurrentProcess()->HasSwitch(switches::kManaged)) { | |
| 87 SetInManagedMode(original_profile); | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 // static | |
| 92 bool ManagedMode::IsInManagedMode() { | |
| 93 return GetInstance()->IsInManagedModeImpl(); | |
| 94 } | |
| 95 | |
| 96 bool ManagedMode::IsInManagedModeImpl() const { | |
| 97 // |g_browser_process| can be NULL during startup. | |
| 98 if (!g_browser_process) | |
| 99 return false; | |
| 100 // Local State can be NULL during unit tests. | |
| 101 if (!g_browser_process->local_state()) | |
| 102 return false; | |
| 103 return g_browser_process->local_state()->GetBoolean(prefs::kInManagedMode); | |
| 104 } | |
| 105 | |
| 106 // static | |
| 107 void ManagedMode::EnterManagedMode(Profile* profile, | |
| 108 const EnterCallback& callback) { | |
| 109 GetInstance()->EnterManagedModeImpl(profile, callback); | |
| 110 } | |
| 111 | |
| 112 void ManagedMode::EnterManagedModeImpl(Profile* profile, | |
| 113 const EnterCallback& callback) { | |
| 114 Profile* original_profile = profile->GetOriginalProfile(); | |
| 115 if (IsInManagedModeImpl()) { | |
| 116 callback.Run(original_profile == managed_profile_); | |
| 117 return; | |
| 118 } | |
| 119 if (!callbacks_.empty()) { | |
| 120 // We are already in the process of entering managed mode, waiting for | |
| 121 // browsers to close. Don't allow entering managed mode again for a | |
| 122 // different profile, and queue the callback for the same profile. | |
| 123 if (original_profile != managed_profile_) | |
| 124 callback.Run(false); | |
| 125 else | |
| 126 callbacks_.push_back(callback); | |
| 127 return; | |
| 128 } | |
| 129 | |
| 130 if (!PlatformConfirmEnter()) { | |
| 131 callback.Run(false); | |
| 132 return; | |
| 133 } | |
| 134 // Close all other profiles. | |
| 135 // At this point, we shouldn't be waiting for other browsers to close (yet). | |
| 136 DCHECK_EQ(0u, browsers_to_close_.size()); | |
| 137 for (BrowserList::const_iterator i = BrowserList::begin(); | |
| 138 i != BrowserList::end(); ++i) { | |
| 139 if ((*i)->profile()->GetOriginalProfile() != original_profile) | |
| 140 browsers_to_close_.insert(*i); | |
| 141 } | |
| 142 | |
| 143 if (browsers_to_close_.empty()) { | |
| 144 SetInManagedMode(original_profile); | |
| 145 callback.Run(true); | |
| 146 return; | |
| 147 } | |
| 148 // Remember the profile we're trying to manage while we wait for other | |
| 149 // browsers to close. | |
| 150 managed_profile_ = original_profile; | |
| 151 callbacks_.push_back(callback); | |
| 152 registrar_.Add(this, chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST, | |
| 153 content::NotificationService::AllSources()); | |
| 154 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED, | |
| 155 content::NotificationService::AllSources()); | |
| 156 for (std::set<Browser*>::const_iterator i = browsers_to_close_.begin(); | |
| 157 i != browsers_to_close_.end(); ++i) { | |
| 158 (*i)->window()->Close(); | |
| 159 } | |
| 160 } | |
| 161 | |
| 162 // static | |
| 163 void ManagedMode::LeaveManagedMode() { | |
| 164 GetInstance()->LeaveManagedModeImpl(); | |
| 165 } | |
| 166 | |
| 167 void ManagedMode::LeaveManagedModeImpl() { | |
| 168 bool confirmed = PlatformConfirmLeave(); | |
| 169 if (confirmed) | |
| 170 SetInManagedMode(NULL); | |
| 171 } | |
| 172 | |
| 173 // static | |
| 174 const ManagedModeURLFilter* ManagedMode::GetURLFilter() { | |
| 175 return GetInstance()->GetURLFilterImpl(); | |
| 176 } | |
| 177 | |
| 178 const ManagedModeURLFilter* ManagedMode::GetURLFilterImpl() { | |
| 179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 180 return url_filter_context_->url_filter(); | |
| 181 } | |
| 182 | |
| 183 std::string ManagedMode::GetDebugPolicyProviderName() const { | |
| 184 // Save the string space in official builds. | |
| 185 #ifdef NDEBUG | |
| 186 NOTREACHED(); | |
| 187 return std::string(); | |
| 188 #else | |
| 189 return "Managed Mode"; | |
| 190 #endif | |
| 191 } | |
| 192 | |
| 193 bool ManagedMode::UserMayLoad(const extensions::Extension* extension, | |
| 194 string16* error) const { | |
| 195 return ExtensionManagementPolicyImpl(error); | |
| 196 } | |
| 197 | |
| 198 bool ManagedMode::UserMayModifySettings(const extensions::Extension* extension, | |
| 199 string16* error) const { | |
| 200 return ExtensionManagementPolicyImpl(error); | |
| 201 } | |
| 202 | |
| 203 bool ManagedMode::ExtensionManagementPolicyImpl(string16* error) const { | |
| 204 if (!IsInManagedModeImpl()) | |
| 205 return true; | |
| 206 | |
| 207 if (error) | |
| 208 *error = l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOCKED_MANAGED_MODE); | |
| 209 return false; | |
| 210 } | |
| 211 | |
| 212 void ManagedMode::OnBrowserAdded(Browser* browser) { | |
| 213 // Return early if we don't have any queued callbacks. | |
| 214 if (callbacks_.empty()) | |
| 215 return; | |
| 216 | |
| 217 DCHECK(managed_profile_); | |
| 218 if (browser->profile()->GetOriginalProfile() != managed_profile_) | |
| 219 FinalizeEnter(false); | |
| 220 } | |
| 221 | |
| 222 void ManagedMode::OnBrowserRemoved(Browser* browser) { | |
| 223 // Return early if we don't have any queued callbacks. | |
| 224 if (callbacks_.empty()) | |
| 225 return; | |
| 226 | |
| 227 DCHECK(managed_profile_); | |
| 228 if (browser->profile()->GetOriginalProfile() == managed_profile_) { | |
| 229 // Ignore closing browser windows that are in managed mode. | |
| 230 return; | |
| 231 } | |
| 232 size_t count = browsers_to_close_.erase(browser); | |
| 233 DCHECK_EQ(1u, count); | |
| 234 if (browsers_to_close_.empty()) | |
| 235 FinalizeEnter(true); | |
| 236 } | |
| 237 | |
| 238 ManagedMode::ManagedMode() : managed_profile_(NULL), | |
| 239 url_filter_context_(new URLFilterContext) { | |
| 240 BrowserList::AddObserver(this); | |
| 241 } | |
| 242 | |
| 243 ManagedMode::~ManagedMode() { | |
| 244 // This class usually is a leaky singleton, so this destructor shouldn't be | |
| 245 // called. We still do some cleanup, in case we're owned by a unit test. | |
| 246 BrowserList::RemoveObserver(this); | |
| 247 DCHECK_EQ(0u, callbacks_.size()); | |
| 248 DCHECK_EQ(0u, browsers_to_close_.size()); | |
| 249 url_filter_context_.release()->ShutdownOnUIThread(); | |
| 250 } | |
| 251 | |
| 252 void ManagedMode::Observe(int type, | |
| 253 const content::NotificationSource& source, | |
| 254 const content::NotificationDetails& details) { | |
| 255 // Return early if we don't have any queued callbacks. | |
| 256 if (callbacks_.empty()) | |
| 257 return; | |
| 258 | |
| 259 switch (type) { | |
| 260 case chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST: { | |
| 261 FinalizeEnter(false); | |
| 262 return; | |
| 263 } | |
| 264 case chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED: { | |
| 265 Browser* browser = content::Source<Browser>(source).ptr(); | |
| 266 if (browsers_to_close_.find(browser) != browsers_to_close_.end()) | |
| 267 FinalizeEnter(false); | |
| 268 return; | |
| 269 } | |
| 270 case chrome::NOTIFICATION_EXTENSION_LOADED: | |
| 271 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { | |
| 272 if (managed_profile_) | |
| 273 UpdateWhitelist(); | |
| 274 break; | |
| 275 } | |
| 276 default: | |
| 277 NOTREACHED(); | |
| 278 } | |
| 279 } | |
| 280 | |
| 281 void ManagedMode::FinalizeEnter(bool result) { | |
| 282 if (result) | |
| 283 SetInManagedMode(managed_profile_); | |
| 284 for (std::vector<EnterCallback>::iterator it = callbacks_.begin(); | |
| 285 it != callbacks_.end(); ++it) { | |
| 286 it->Run(result); | |
| 287 } | |
| 288 callbacks_.clear(); | |
| 289 browsers_to_close_.clear(); | |
| 290 registrar_.RemoveAll(); | |
| 291 } | |
| 292 | |
| 293 bool ManagedMode::PlatformConfirmEnter() { | |
| 294 // TODO(bauerb): Show platform-specific confirmation dialog. | |
| 295 return true; | |
| 296 } | |
| 297 | |
| 298 bool ManagedMode::PlatformConfirmLeave() { | |
| 299 // TODO(bauerb): Show platform-specific confirmation dialog. | |
| 300 return true; | |
| 301 } | |
| 302 | |
| 303 void ManagedMode::SetInManagedMode(Profile* newly_managed_profile) { | |
| 304 // Register the ManagementPolicy::Provider before changing the pref when | |
| 305 // setting it, and unregister it after changing the pref when clearing it, | |
| 306 // so pref observers see the correct ManagedMode state. | |
| 307 bool in_managed_mode = !!newly_managed_profile; | |
| 308 if (in_managed_mode) { | |
| 309 DCHECK(!managed_profile_ || managed_profile_ == newly_managed_profile); | |
| 310 extensions::ExtensionSystem::Get( | |
| 311 newly_managed_profile)->management_policy()->RegisterProvider(this); | |
| 312 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, | |
| 313 content::Source<Profile>(newly_managed_profile)); | |
| 314 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, | |
| 315 content::Source<Profile>(newly_managed_profile)); | |
| 316 } else { | |
| 317 extensions::ExtensionSystem::Get( | |
| 318 managed_profile_)->management_policy()->UnregisterProvider(this); | |
| 319 registrar_.Remove(this, chrome::NOTIFICATION_EXTENSION_LOADED, | |
| 320 content::Source<Profile>(managed_profile_)); | |
| 321 registrar_.Remove(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, | |
| 322 content::Source<Profile>(managed_profile_)); | |
| 323 } | |
| 324 | |
| 325 managed_profile_ = newly_managed_profile; | |
| 326 url_filter_context_->SetActive(in_managed_mode); | |
| 327 g_browser_process->local_state()->SetBoolean(prefs::kInManagedMode, | |
| 328 in_managed_mode); | |
| 329 if (in_managed_mode) | |
| 330 UpdateWhitelist(); | |
| 331 | |
| 332 // This causes the avatar and the profile menu to get updated. | |
| 333 content::NotificationService::current()->Notify( | |
| 334 chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, | |
| 335 content::NotificationService::AllBrowserContextsAndSources(), | |
| 336 content::NotificationService::NoDetails()); | |
| 337 } | |
| 338 | |
| 339 void ManagedMode::UpdateWhitelist() { | |
| 340 DCHECK(managed_profile_); | |
| 341 // TODO(bauerb): Update URL filter with whitelist. | |
| 342 } | |
| OLD | NEW |