OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 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 #ifdef CHROME_PERSONALIZATION | |
6 | |
7 #include "chrome/browser/sync/personalization.h" | |
8 | |
9 #include "app/resource_bundle.h" | |
10 #include "base/command_line.h" | |
11 #include "base/file_path.h" | |
12 #include "base/file_util.h" | |
13 #include "base/path_service.h" | |
14 #include "base/string_util.h" | |
15 #include "chrome/app/chrome_dll_resource.h" | |
16 #include "chrome/browser/browser.h" | |
17 #include "chrome/browser/browser_process.h" | |
18 #include "chrome/browser/browser_url_handler.h" | |
19 #include "chrome/browser/command_updater.h" | |
20 #include "chrome/browser/options_window.h" | |
21 #include "chrome/browser/profile.h" | |
22 #include "chrome/browser/profile_manager.h" | |
23 #include "chrome/common/chrome_paths.h" | |
24 #include "chrome/common/chrome_switches.h" | |
25 #include "chrome/common/jstemplate_builder.h" | |
26 #include "chrome/common/notification_service.h" | |
27 #include "chrome/common/pref_service.h" | |
28 #include "chrome/browser/dom_ui/new_tab_page_sync_handler.h" | |
29 #include "chrome/browser/sync/personalization_strings.h" | |
30 #include "chrome/browser/sync/auth_error_state.h" | |
31 #include "chrome/browser/sync/profile_sync_service.h" | |
32 #include "googleurl/src/gurl.h" | |
33 #include "grit/app_resources.h" | |
34 #include "grit/browser_resources.h" | |
35 #include "net/url_request/url_request.h" | |
36 | |
37 using sync_api::SyncManager; | |
38 | |
39 // TODO(ncarter): Move these switches into chrome_switches. They are here | |
40 // now because we want to keep them secret during early development. | |
41 namespace switches { | |
42 const wchar_t kSyncServiceURL[] = L"sync-url"; | |
43 const wchar_t kSyncServicePort[] = L"sync-port"; | |
44 const wchar_t kSyncUserForTest[] = L"sync-user-for-test"; | |
45 const wchar_t kSyncPasswordForTest[] = L"sync-password-for-test"; | |
46 } | |
47 | |
48 // TODO(munjal): Move these preferences to common/pref_names.h. | |
49 // Names of various preferences. | |
50 namespace prefs { | |
51 const wchar_t kSyncPath[] = L"sync"; | |
52 const wchar_t kSyncLastSyncedTime[] = L"sync.last_synced_time"; | |
53 const wchar_t kSyncUserName[] = L"sync.username"; | |
54 const wchar_t kSyncHasSetupCompleted[] = L"sync.has_setup_completed"; | |
55 } | |
56 | |
57 // Top-level path for our network layer DataSource. | |
58 static const char kCloudyResourcesPath[] = "resources"; | |
59 // Path for cloudy:stats page. | |
60 static const char kCloudyStatsPath[] = "stats"; | |
61 // Path for the gaia sync login dialog. | |
62 static const char kCloudyGaiaLoginPath[] = "gaialogin"; | |
63 static const char kCloudyMergeAndSyncPath[] = "mergeandsync"; | |
64 static const char kCloudyThrobberPath[] = "throbber.png"; | |
65 static const char kCloudySetupFlowPath[] = "setup"; | |
66 | |
67 namespace Personalization { | |
68 | |
69 static std::wstring MakeAuthErrorText(AuthErrorState state) { | |
70 switch (state) { | |
71 case AUTH_ERROR_INVALID_GAIA_CREDENTIALS: | |
72 return L"INVALID_GAIA_CREDENTIALS"; | |
73 case AUTH_ERROR_USER_NOT_SIGNED_UP: | |
74 return L"USER_NOT_SIGNED_UP"; | |
75 case AUTH_ERROR_CONNECTION_FAILED: | |
76 return L"CONNECTION_FAILED"; | |
77 default: | |
78 return std::wstring(); | |
79 } | |
80 } | |
81 | |
82 bool IsP13NDisabled(Profile* profile) { | |
83 const CommandLine* command_line = CommandLine::ForCurrentProcess(); | |
84 if (command_line->HasSwitch(switches::kDisableP13n)) | |
85 return true; | |
86 return !profile || profile->GetProfilePersonalization() == NULL; | |
87 } | |
88 | |
89 bool NeedsDOMUI(const GURL& url) { | |
90 return url.SchemeIs(kPersonalizationScheme) && | |
91 (url.path().find(kCloudyGaiaLoginPath) != std::string::npos) || | |
92 (url.path().find(kCloudySetupFlowPath) != std::string::npos) || | |
93 (url.path().find(kCloudyMergeAndSyncPath) != std::string::npos); | |
94 } | |
95 | |
96 class CloudyResourceSource : public ChromeURLDataManager::DataSource { | |
97 public: | |
98 CloudyResourceSource() | |
99 : DataSource(kCloudyResourcesPath, MessageLoop::current()) { | |
100 } | |
101 virtual ~CloudyResourceSource() { } | |
102 | |
103 virtual void StartDataRequest(const std::string& path, int request_id); | |
104 | |
105 virtual std::string GetMimeType(const std::string& path) const { | |
106 if (path == kCloudyThrobberPath) | |
107 return "image/png"; | |
108 else | |
109 return "text/html"; | |
110 } | |
111 private: | |
112 DISALLOW_COPY_AND_ASSIGN(CloudyResourceSource); | |
113 }; | |
114 | |
115 class CloudyStatsSource : public ChromeURLDataManager::DataSource { | |
116 public: | |
117 CloudyStatsSource() : DataSource(kCloudyStatsPath, MessageLoop::current()) { | |
118 } | |
119 virtual ~CloudyStatsSource() { } | |
120 virtual void StartDataRequest(const std::string& path, int request_id) { | |
121 std::string response(MakeCloudyStats()); | |
122 scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); | |
123 html_bytes->data.resize(response.size()); | |
124 std::copy(response.begin(), response.end(), html_bytes->data.begin()); | |
125 SendResponse(request_id, html_bytes); | |
126 } | |
127 virtual std::string GetMimeType(const std::string& path) const { | |
128 return "text/html"; | |
129 } | |
130 private: | |
131 DISALLOW_COPY_AND_ASSIGN(CloudyStatsSource); | |
132 }; | |
133 | |
134 DOMMessageHandler* CreateNewTabPageHandler(DOMUI* dom_ui) { | |
135 return (new NewTabPageSyncHandler())->Attach(dom_ui); | |
136 } | |
137 | |
138 std::string GetNewTabSource() { | |
139 static const StringPiece new_tab_html( | |
140 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
141 IDR_NEW_TAB_P13N_HTML)); | |
142 | |
143 std::string data_uri("data:text/html,"); | |
144 data_uri.append(std::string(new_tab_html.data(), new_tab_html.size())); | |
145 return GURL(data_uri).spec(); | |
146 } | |
147 | |
148 std::wstring GetMenuItemInfoText(Browser* browser) { | |
149 browser->command_updater()->UpdateCommandEnabled(IDC_P13N_INFO, true); | |
150 return kMenuLabelStartSync; | |
151 } | |
152 | |
153 void HandleMenuItemClick(Profile* p) { | |
154 // The menu item is enabled either when the sync is not enabled by the user | |
155 // or when it's enabled but the user name is empty. In the former case enable | |
156 // sync. In the latter case, show the login dialog. | |
157 ProfileSyncService* service = p->GetProfilePersonalization()->sync_service(); | |
158 DCHECK(service); | |
159 if (service->IsSyncEnabledByUser()) { | |
160 ShowOptionsWindow(OPTIONS_PAGE_USER_DATA, OPTIONS_GROUP_NONE, p); | |
161 } else { | |
162 service->EnableForUser(); | |
163 } | |
164 } | |
165 | |
166 } // namespace Personalization | |
167 | |
168 class ProfilePersonalizationImpl : public ProfilePersonalization, | |
169 public NotificationObserver { | |
170 public: | |
171 explicit ProfilePersonalizationImpl(Profile *p) | |
172 : profile_(p) { | |
173 // g_browser_process and/or io_thread may not exist during testing. | |
174 if (g_browser_process && g_browser_process->io_thread()) { | |
175 // Add our network layer data source for 'cloudy' URLs. | |
176 // TODO(timsteele): This one belongs in BrowserAboutHandler. | |
177 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, | |
178 NewRunnableMethod(&chrome_url_data_manager, | |
179 &ChromeURLDataManager::AddDataSource, | |
180 new Personalization::CloudyStatsSource())); | |
181 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, | |
182 NewRunnableMethod(&chrome_url_data_manager, | |
183 &ChromeURLDataManager::AddDataSource, | |
184 new Personalization::CloudyResourceSource())); | |
185 } | |
186 | |
187 registrar_.Add(this, NotificationType::BOOKMARK_MODEL_LOADED, | |
188 Source<Profile>(profile_)); | |
189 } | |
190 virtual ~ProfilePersonalizationImpl() {} | |
191 | |
192 // ProfilePersonalization implementation | |
193 virtual ProfileSyncService* sync_service() { | |
194 if (!sync_service_.get()) | |
195 InitSyncService(); | |
196 return sync_service_.get(); | |
197 } | |
198 | |
199 // NotificationObserver implementation. | |
200 virtual void Observe(NotificationType type, | |
201 const NotificationSource& source, | |
202 const NotificationDetails& details) { | |
203 DCHECK_EQ(type.value, NotificationType::BOOKMARK_MODEL_LOADED); | |
204 if (!sync_service_.get()) | |
205 InitSyncService(); | |
206 registrar_.RemoveAll(); | |
207 } | |
208 | |
209 void InitSyncService() { | |
210 sync_service_.reset(new ProfileSyncService(profile_)); | |
211 sync_service_->Initialize(); | |
212 } | |
213 | |
214 private: | |
215 Profile* profile_; | |
216 NotificationRegistrar registrar_; | |
217 scoped_ptr<ProfileSyncService> sync_service_; | |
218 DISALLOW_COPY_AND_ASSIGN(ProfilePersonalizationImpl); | |
219 }; | |
220 | |
221 namespace Personalization { | |
222 | |
223 void CloudyResourceSource::StartDataRequest(const std::string& path_raw, | |
224 int request_id) { | |
225 scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); | |
226 if (path_raw == kCloudyThrobberPath) { | |
227 ResourceBundle::GetSharedInstance().LoadImageResourceBytes(IDR_THROBBER, | |
228 &html_bytes->data); | |
229 SendResponse(request_id, html_bytes); | |
230 return; | |
231 } | |
232 | |
233 std::string response; | |
234 if (path_raw == kCloudyGaiaLoginPath) { | |
235 static const StringPiece html(ResourceBundle::GetSharedInstance() | |
236 .GetRawDataResource(IDR_GAIA_LOGIN_HTML)); | |
237 response = html.as_string(); | |
238 } else if (path_raw == kCloudyMergeAndSyncPath) { | |
239 static const StringPiece html(ResourceBundle::GetSharedInstance() | |
240 .GetRawDataResource(IDR_MERGE_AND_SYNC_HTML)); | |
241 response = html.as_string(); | |
242 } else if (path_raw == kCloudySetupFlowPath) { | |
243 static const StringPiece html(ResourceBundle::GetSharedInstance() | |
244 .GetRawDataResource(IDR_SYNC_SETUP_FLOW_HTML)); | |
245 response = html.as_string(); | |
246 } | |
247 // Send the response. | |
248 html_bytes->data.resize(response.size()); | |
249 std::copy(response.begin(), response.end(), html_bytes->data.begin()); | |
250 SendResponse(request_id, html_bytes); | |
251 } | |
252 | |
253 ProfilePersonalization* CreateProfilePersonalization(Profile* p) { | |
254 return new ProfilePersonalizationImpl(p); | |
255 } | |
256 | |
257 void CleanupProfilePersonalization(ProfilePersonalization* p) { | |
258 if (p) delete p; | |
259 } | |
260 | |
261 static void AddBoolDetail(ListValue* details, const std::wstring& stat_name, | |
262 bool stat_value) { | |
263 DictionaryValue* val = new DictionaryValue; | |
264 val->SetString(L"stat_name", stat_name); | |
265 val->SetBoolean(L"stat_value", stat_value); | |
266 details->Append(val); | |
267 } | |
268 | |
269 static void AddIntDetail(ListValue* details, const std::wstring& stat_name, | |
270 int64 stat_value) { | |
271 DictionaryValue* val = new DictionaryValue; | |
272 val->SetString(L"stat_name", stat_name); | |
273 val->SetString(L"stat_value", FormatNumber(stat_value)); | |
274 details->Append(val); | |
275 } | |
276 | |
277 std::string MakeCloudyStats() { | |
278 FilePath user_data_dir; | |
279 if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) | |
280 return std::string(); | |
281 ProfileManager* profile_manager = g_browser_process->profile_manager(); | |
282 Profile* profile = profile_manager->GetDefaultProfile(user_data_dir); | |
283 ProfilePersonalization* p13n_profile = profile->GetProfilePersonalization(); | |
284 ProfileSyncService* service = p13n_profile->sync_service(); | |
285 | |
286 DictionaryValue strings; | |
287 if (!service->IsSyncEnabledByUser()) { | |
288 strings.SetString(L"summary", L"SYNC DISABLED"); | |
289 } else { | |
290 SyncManager::Status full_status(service->QueryDetailedSyncStatus()); | |
291 | |
292 strings.SetString(L"summary", | |
293 ProfileSyncService::BuildSyncStatusSummaryText( | |
294 full_status.summary)); | |
295 | |
296 strings.Set(L"authenticated", | |
297 new FundamentalValue(full_status.authenticated)); | |
298 strings.SetString(L"auth_problem", | |
299 MakeAuthErrorText(service->GetAuthErrorState())); | |
300 | |
301 strings.SetString(L"time_since_sync", service->GetLastSyncedTimeString()); | |
302 | |
303 ListValue* details = new ListValue(); | |
304 strings.Set(L"details", details); | |
305 AddBoolDetail(details, L"Server Up", full_status.server_up); | |
306 AddBoolDetail(details, L"Server Reachable", full_status.server_reachable); | |
307 AddBoolDetail(details, L"Server Broken", full_status.server_broken); | |
308 AddBoolDetail(details, L"Notifications Enabled", | |
309 full_status.notifications_enabled); | |
310 AddIntDetail(details, L"Notifications Received", | |
311 full_status.notifications_received); | |
312 AddIntDetail(details, L"Notifications Sent", | |
313 full_status.notifications_sent); | |
314 AddIntDetail(details, L"Unsynced Count", full_status.unsynced_count); | |
315 AddIntDetail(details, L"Conflicting Count", full_status.conflicting_count); | |
316 AddBoolDetail(details, L"Syncing", full_status.syncing); | |
317 AddBoolDetail(details, L"Syncer Paused", full_status.syncer_paused); | |
318 AddBoolDetail(details, L"Initial Sync Ended", | |
319 full_status.initial_sync_ended); | |
320 AddBoolDetail(details, L"Syncer Stuck", full_status.syncer_stuck); | |
321 AddIntDetail(details, L"Updates Available", full_status.updates_available); | |
322 AddIntDetail(details, L"Updates Received", full_status.updates_received); | |
323 AddBoolDetail(details, L"Disk Full", full_status.disk_full); | |
324 AddBoolDetail(details, L"Invalid Store", full_status.invalid_store); | |
325 AddIntDetail(details, L"Max Consecutive Errors", | |
326 full_status.max_consecutive_errors); | |
327 } | |
328 | |
329 static const StringPiece sync_html( | |
330 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
331 IDR_ABOUT_SYNC_HTML)); | |
332 | |
333 return jstemplate_builder::GetTemplateHtml( | |
334 sync_html, &strings , "t" /* template root node id */); | |
335 } | |
336 | |
337 } // namespace Personalization | |
338 | |
339 #endif | |
OLD | NEW |