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 |