| 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/protector/settings_change_global_error.h" | |
| 6 | |
| 7 #include <bitset> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/compiler_specific.h" | |
| 11 #include "base/lazy_instance.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/stl_util.h" | |
| 14 #include "chrome/browser/platform_util.h" | |
| 15 #include "chrome/browser/profiles/profile.h" | |
| 16 #include "chrome/browser/protector/base_setting_change.h" | |
| 17 #include "chrome/browser/protector/settings_change_global_error_delegate.h" | |
| 18 #include "chrome/browser/ui/browser.h" | |
| 19 #include "chrome/browser/ui/browser_finder.h" | |
| 20 #include "chrome/browser/ui/browser_list.h" | |
| 21 #include "chrome/browser/ui/browser_window.h" | |
| 22 #include "chrome/browser/ui/global_error/global_error_service.h" | |
| 23 #include "chrome/browser/ui/global_error/global_error_service_factory.h" | |
| 24 #include "content/public/browser/browser_thread.h" | |
| 25 | |
| 26 using content::BrowserThread; | |
| 27 | |
| 28 namespace protector { | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 // Timeout before the global error is removed (wrench menu item disappears). | |
| 33 const int kMenuItemDisplayPeriodMs = 10*60*1000; // 10 min | |
| 34 | |
| 35 // Unset bits indicate available command IDs. | |
| 36 static base::LazyInstance< | |
| 37 std::bitset<IDC_SHOW_SETTINGS_CHANGE_LAST - | |
| 38 IDC_SHOW_SETTINGS_CHANGE_FIRST + 1> > menu_ids = | |
| 39 LAZY_INSTANCE_INITIALIZER; | |
| 40 | |
| 41 } // namespace | |
| 42 | |
| 43 SettingsChangeGlobalError::SettingsChangeGlobalError( | |
| 44 BaseSettingChange* change, | |
| 45 SettingsChangeGlobalErrorDelegate* delegate) | |
| 46 : change_(change), | |
| 47 delegate_(delegate), | |
| 48 profile_(NULL), | |
| 49 closed_by_button_(false), | |
| 50 show_on_browser_activation_(false), | |
| 51 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | |
| 52 menu_id_(-1) { | |
| 53 DCHECK(delegate_); | |
| 54 for (int i = IDC_SHOW_SETTINGS_CHANGE_FIRST; | |
| 55 i <= IDC_SHOW_SETTINGS_CHANGE_LAST; i++) { | |
| 56 if (!menu_ids.Get()[i - IDC_SHOW_SETTINGS_CHANGE_FIRST]) { | |
| 57 menu_id_ = i; | |
| 58 menu_ids.Get().set(i - IDC_SHOW_SETTINGS_CHANGE_FIRST); | |
| 59 break; | |
| 60 } | |
| 61 } | |
| 62 DCHECK(menu_id_ >= 0) << "Out of command IDs for SettingsChangeGlobalError"; | |
| 63 } | |
| 64 | |
| 65 SettingsChangeGlobalError::~SettingsChangeGlobalError() { | |
| 66 if (profile_) | |
| 67 RemoveFromProfile(); | |
| 68 if (menu_id_ >= 0) | |
| 69 menu_ids.Get().reset(menu_id_ - IDC_SHOW_SETTINGS_CHANGE_FIRST); | |
| 70 } | |
| 71 | |
| 72 void SettingsChangeGlobalError::AddToProfile( | |
| 73 Profile* profile, | |
| 74 bool show_bubble, | |
| 75 chrome::HostDesktopType desktop_type) { | |
| 76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 77 profile_ = profile; | |
| 78 GlobalErrorServiceFactory::GetForProfile(profile_)->AddGlobalError(this); | |
| 79 BrowserList::AddObserver(this); | |
| 80 if (show_bubble) { | |
| 81 ShowBubble(desktop_type); | |
| 82 } else { | |
| 83 // Start inactivity timer. | |
| 84 BrowserThread::PostDelayedTask( | |
| 85 BrowserThread::UI, FROM_HERE, | |
| 86 base::Bind(&SettingsChangeGlobalError::OnInactiveTimeout, | |
| 87 weak_factory_.GetWeakPtr()), | |
| 88 base::TimeDelta::FromMilliseconds(kMenuItemDisplayPeriodMs)); | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 void SettingsChangeGlobalError::RemoveFromProfile() { | |
| 93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 94 if (profile_) { | |
| 95 GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveGlobalError(this); | |
| 96 profile_ = NULL; | |
| 97 } | |
| 98 BrowserList::RemoveObserver(this); | |
| 99 // This will delete |this|. | |
| 100 delegate_->OnRemovedFromProfile(this); | |
| 101 } | |
| 102 | |
| 103 void SettingsChangeGlobalError::ShowBubble( | |
| 104 chrome::HostDesktopType desktop_type) { | |
| 105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 106 DCHECK(profile_); | |
| 107 Browser* browser = browser::FindTabbedBrowser(profile_, true, desktop_type); | |
| 108 if (browser) | |
| 109 ShowBubbleInBrowser(browser); | |
| 110 } | |
| 111 | |
| 112 bool SettingsChangeGlobalError::HasBadge() { | |
| 113 return true; | |
| 114 } | |
| 115 | |
| 116 int SettingsChangeGlobalError::GetBadgeResourceID() { | |
| 117 return change_->GetBadgeIconID(); | |
| 118 } | |
| 119 | |
| 120 bool SettingsChangeGlobalError::HasMenuItem() { | |
| 121 return true; | |
| 122 } | |
| 123 | |
| 124 int SettingsChangeGlobalError::MenuItemCommandID() { | |
| 125 return menu_id_; | |
| 126 } | |
| 127 | |
| 128 string16 SettingsChangeGlobalError::MenuItemLabel() { | |
| 129 return change_->GetBubbleTitle(); | |
| 130 } | |
| 131 | |
| 132 int SettingsChangeGlobalError::MenuItemIconResourceID() { | |
| 133 return change_->GetMenuItemIconID(); | |
| 134 } | |
| 135 | |
| 136 void SettingsChangeGlobalError::ExecuteMenuItem(Browser* browser) { | |
| 137 ShowBubbleInBrowser(browser); | |
| 138 } | |
| 139 | |
| 140 bool SettingsChangeGlobalError::HasBubbleView() { | |
| 141 return true; | |
| 142 } | |
| 143 | |
| 144 int SettingsChangeGlobalError::GetBubbleViewIconResourceID() { | |
| 145 return change_->GetBubbleIconID(); | |
| 146 } | |
| 147 | |
| 148 string16 SettingsChangeGlobalError::GetBubbleViewTitle() { | |
| 149 return change_->GetBubbleTitle(); | |
| 150 } | |
| 151 | |
| 152 string16 SettingsChangeGlobalError::GetBubbleViewMessage() { | |
| 153 return change_->GetBubbleMessage(); | |
| 154 } | |
| 155 | |
| 156 // The Accept and Revert buttons are swapped like the 'server' and 'client' | |
| 157 // concepts in X11. Accept button (the default one) discards changes | |
| 158 // (keeps using previous setting) while cancel button applies changes | |
| 159 // (switches to the new setting). This is sick and blows my mind. - ivankr | |
| 160 | |
| 161 string16 SettingsChangeGlobalError::GetBubbleViewAcceptButtonLabel() { | |
| 162 return change_->GetDiscardButtonText(); | |
| 163 } | |
| 164 | |
| 165 string16 SettingsChangeGlobalError::GetBubbleViewCancelButtonLabel() { | |
| 166 return change_->GetApplyButtonText(); | |
| 167 } | |
| 168 | |
| 169 void SettingsChangeGlobalError::OnBubbleViewDidClose(Browser* browser) { | |
| 170 // The bubble may be closed as the result of RemoveFromProfile call when | |
| 171 // merging this error with another one. | |
| 172 if (!profile_) | |
| 173 return; | |
| 174 if (!closed_by_button_) { | |
| 175 BrowserThread::PostDelayedTask( | |
| 176 BrowserThread::UI, FROM_HERE, | |
| 177 base::Bind(&SettingsChangeGlobalError::OnInactiveTimeout, | |
| 178 weak_factory_.GetWeakPtr()), | |
| 179 base::TimeDelta::FromMilliseconds(kMenuItemDisplayPeriodMs)); | |
| 180 #if !defined(TOOLKIT_GTK) | |
| 181 // TODO(ivankr): the logic for redisplaying bubble is disabled on Gtk, see | |
| 182 // http://crbug.com/115719. | |
| 183 if (browser->window() && | |
| 184 !platform_util::IsWindowActive(browser->window()->GetNativeWindow())) { | |
| 185 // Bubble closed because the entire window lost activation, display | |
| 186 // again when a window gets active. | |
| 187 show_on_browser_activation_ = true; | |
| 188 } | |
| 189 #endif | |
| 190 } else { | |
| 191 RemoveFromProfile(); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 void SettingsChangeGlobalError::BubbleViewAcceptButtonPressed( | |
| 196 Browser* browser) { | |
| 197 closed_by_button_ = true; | |
| 198 delegate_->OnDiscardChange(this, browser); | |
| 199 } | |
| 200 | |
| 201 void SettingsChangeGlobalError::BubbleViewCancelButtonPressed( | |
| 202 Browser* browser) { | |
| 203 closed_by_button_ = true; | |
| 204 delegate_->OnApplyChange(this, browser); | |
| 205 } | |
| 206 | |
| 207 void SettingsChangeGlobalError::OnBrowserSetLastActive( | |
| 208 Browser* browser) { | |
| 209 if (show_on_browser_activation_ && browser && browser->is_type_tabbed()) { | |
| 210 // A tabbed browser window got activated, show the error bubble again. | |
| 211 // Calling ShowBubble() immediately from here does not always work because | |
| 212 // the old browser window may still have focus. | |
| 213 // Multiple posted ShowBubble() calls are fine since the first successful | |
| 214 // one will invalidate all the weak pointers. | |
| 215 // Note that ShowBubble() will display the bubble in the last active browser | |
| 216 // (which may not be |browser| at the moment ShowBubble() is executed). | |
| 217 BrowserThread::PostTask( | |
| 218 BrowserThread::UI, FROM_HERE, | |
| 219 base::Bind(&SettingsChangeGlobalError::ShowBubble, | |
| 220 weak_factory_.GetWeakPtr(), | |
| 221 browser->host_desktop_type())); | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 void SettingsChangeGlobalError::ShowBubbleInBrowser(Browser* browser) { | |
| 226 show_on_browser_activation_ = false; | |
| 227 // Cancel any previously posted tasks so that the global error | |
| 228 // does not get removed on timeout while still showing the bubble. | |
| 229 weak_factory_.InvalidateWeakPtrs(); | |
| 230 ShowBubbleView(browser); | |
| 231 } | |
| 232 | |
| 233 void SettingsChangeGlobalError::OnInactiveTimeout() { | |
| 234 delegate_->OnDecisionTimeout(this); | |
| 235 RemoveFromProfile(); | |
| 236 } | |
| 237 | |
| 238 } // namespace protector | |
| OLD | NEW |