- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / options / chromeos / change_picture_options_handler.cc
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/ui/webui/options/chromeos/change_picture_options_handler.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/metrics/histogram.h"
11 #include "base/path_service.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/chromeos/camera_detector.h"
17 #include "chrome/browser/chromeos/login/default_user_images.h"
18 #include "chrome/browser/chromeos/login/user_image.h"
19 #include "chrome/browser/chromeos/login/user_image_manager.h"
20 #include "chrome/browser/chromeos/login/user_manager.h"
21 #include "chrome/browser/ui/browser_finder.h"
22 #include "chrome/browser/ui/browser_window.h"
23 #include "chrome/browser/ui/chrome_select_file_policy.h"
24 #include "chrome/common/chrome_paths.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/common/url_constants.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/notification_service.h"
29 #include "content/public/browser/web_ui.h"
30 #include "content/public/common/url_constants.h"
31 #include "grit/generated_resources.h"
32 #include "grit/theme_resources.h"
33 #include "net/base/data_url.h"
34 #include "ui/base/l10n/l10n_util.h"
35 #include "ui/base/resource/resource_bundle.h"
36 #include "ui/base/webui/web_ui_util.h"
37 #include "ui/views/widget/widget.h"
38 #include "url/gurl.h"
39
40 using content::BrowserThread;
41
42 namespace chromeos {
43 namespace options {
44
45 namespace {
46
47 // Returns info about extensions for files we support as user images.
48 ui::SelectFileDialog::FileTypeInfo GetUserImageFileTypeInfo() {
49   ui::SelectFileDialog::FileTypeInfo file_type_info;
50   file_type_info.extensions.resize(1);
51
52   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("bmp"));
53
54   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("jpg"));
55   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("jpeg"));
56
57   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("png"));
58
59   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("tif"));
60   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("tiff"));
61
62   file_type_info.extension_description_overrides.resize(1);
63   file_type_info.extension_description_overrides[0] =
64       l10n_util::GetStringUTF16(IDS_IMAGE_FILES);
65
66   return file_type_info;
67 }
68
69 // Time histogram suffix for profile image download.
70 const char kProfileDownloadReason[] = "Preferences";
71
72 }  // namespace
73
74 ChangePictureOptionsHandler::ChangePictureOptionsHandler()
75     : previous_image_url_(content::kAboutBlankURL),
76       previous_image_index_(User::kInvalidImageIndex),
77       weak_factory_(this) {
78   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
79       content::NotificationService::AllSources());
80   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
81       content::NotificationService::AllSources());
82   registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
83       content::NotificationService::AllSources());
84 }
85
86 ChangePictureOptionsHandler::~ChangePictureOptionsHandler() {
87   if (select_file_dialog_.get())
88     select_file_dialog_->ListenerDestroyed();
89   if (image_decoder_.get())
90     image_decoder_->set_delegate(NULL);
91 }
92
93 void ChangePictureOptionsHandler::GetLocalizedValues(
94     DictionaryValue* localized_strings) {
95   DCHECK(localized_strings);
96   localized_strings->SetString("changePicturePage",
97       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_DIALOG_TITLE));
98   localized_strings->SetString("changePicturePageDescription",
99       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_DIALOG_TEXT));
100   localized_strings->SetString("takePhoto",
101       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_TAKE_PHOTO));
102   localized_strings->SetString("chooseFile",
103       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_CHOOSE_FILE));
104   localized_strings->SetString("profilePhoto",
105       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PROFILE_PHOTO));
106   localized_strings->SetString("profilePhotoLoading",
107       l10n_util::GetStringUTF16(
108           IDS_OPTIONS_CHANGE_PICTURE_PROFILE_LOADING_PHOTO));
109   localized_strings->SetString("previewAltText",
110       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PREVIEW_ALT));
111   localized_strings->SetString("authorCredit",
112       l10n_util::GetStringUTF16(IDS_OPTIONS_SET_WALLPAPER_AUTHOR_TEXT));
113   localized_strings->SetString("photoFromCamera",
114       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PHOTO_FROM_CAMERA));
115   localized_strings->SetString("photoCaptureAccessibleText",
116       l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_CAPTURE_ACCESSIBLE_TEXT));
117   localized_strings->SetString("photoDiscardAccessibleText",
118       l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_DISCARD_ACCESSIBLE_TEXT));
119 }
120
121 void ChangePictureOptionsHandler::RegisterMessages() {
122   web_ui()->RegisterMessageCallback("chooseFile",
123       base::Bind(&ChangePictureOptionsHandler::HandleChooseFile,
124                  base::Unretained(this)));
125   web_ui()->RegisterMessageCallback("photoTaken",
126       base::Bind(&ChangePictureOptionsHandler::HandlePhotoTaken,
127                  base::Unretained(this)));
128   web_ui()->RegisterMessageCallback("checkCameraPresence",
129       base::Bind(&ChangePictureOptionsHandler::HandleCheckCameraPresence,
130                  base::Unretained(this)));
131   web_ui()->RegisterMessageCallback("onChangePicturePageShown",
132       base::Bind(&ChangePictureOptionsHandler::HandlePageShown,
133                  base::Unretained(this)));
134   web_ui()->RegisterMessageCallback("onChangePicturePageInitialized",
135       base::Bind(&ChangePictureOptionsHandler::HandlePageInitialized,
136                  base::Unretained(this)));
137   web_ui()->RegisterMessageCallback("selectImage",
138       base::Bind(&ChangePictureOptionsHandler::HandleSelectImage,
139                  base::Unretained(this)));
140 }
141
142 void ChangePictureOptionsHandler::SendDefaultImages() {
143   base::ListValue image_urls;
144   for (int i = kFirstDefaultImageIndex; i < kDefaultImagesCount; ++i) {
145     scoped_ptr<base::DictionaryValue> image_data(new base::DictionaryValue);
146     image_data->SetString("url", GetDefaultImageUrl(i));
147     image_data->SetString(
148         "author", l10n_util::GetStringUTF16(kDefaultImageAuthorIDs[i]));
149     image_data->SetString(
150         "website", l10n_util::GetStringUTF16(kDefaultImageWebsiteIDs[i]));
151     image_urls.Append(image_data.release());
152   }
153   web_ui()->CallJavascriptFunction("ChangePictureOptions.setDefaultImages",
154                                    image_urls);
155 }
156
157 void ChangePictureOptionsHandler::HandleChooseFile(const ListValue* args) {
158   DCHECK(args && args->empty());
159   select_file_dialog_ = ui::SelectFileDialog::Create(
160       this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
161
162   base::FilePath downloads_path;
163   if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_path)) {
164     NOTREACHED();
165     return;
166   }
167
168   // Static so we initialize it only once.
169   CR_DEFINE_STATIC_LOCAL(ui::SelectFileDialog::FileTypeInfo, file_type_info,
170       (GetUserImageFileTypeInfo()));
171
172   select_file_dialog_->SelectFile(
173       ui::SelectFileDialog::SELECT_OPEN_FILE,
174       l10n_util::GetStringUTF16(IDS_DOWNLOAD_TITLE),
175       downloads_path,
176       &file_type_info,
177       0,
178       FILE_PATH_LITERAL(""),
179       GetBrowserWindow(),
180       NULL);
181 }
182
183 void ChangePictureOptionsHandler::HandlePhotoTaken(
184     const base::ListValue* args) {
185   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
186   std::string image_url;
187   if (!args || args->GetSize() != 1 || !args->GetString(0, &image_url))
188     NOTREACHED();
189   DCHECK(!image_url.empty());
190
191   std::string mime_type, charset, raw_data;
192   if (!net::DataURL::Parse(GURL(image_url), &mime_type, &charset, &raw_data))
193     NOTREACHED();
194   DCHECK_EQ("image/png", mime_type);
195
196   user_photo_ = gfx::ImageSkia();
197   user_photo_data_url_ = image_url;
198
199   if (image_decoder_.get())
200     image_decoder_->set_delegate(NULL);
201   image_decoder_ = new ImageDecoder(this, raw_data,
202                                     ImageDecoder::DEFAULT_CODEC);
203   scoped_refptr<base::MessageLoopProxy> task_runner =
204       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
205   image_decoder_->Start(task_runner);
206 }
207
208 void ChangePictureOptionsHandler::HandleCheckCameraPresence(
209     const base::ListValue* args) {
210   DCHECK(args->empty());
211   CheckCameraPresence();
212 }
213
214 void ChangePictureOptionsHandler::HandlePageInitialized(
215     const base::ListValue* args) {
216   DCHECK(args && args->empty());
217   SendDefaultImages();
218 }
219
220 void ChangePictureOptionsHandler::HandlePageShown(const base::ListValue* args) {
221   DCHECK(args && args->empty());
222   CheckCameraPresence();
223   SendSelectedImage();
224   UpdateProfileImage();
225 }
226
227 void ChangePictureOptionsHandler::SendSelectedImage() {
228   const User* user = UserManager::Get()->GetLoggedInUser();
229   DCHECK(!user->email().empty());
230
231   previous_image_index_ = user->image_index();
232   switch (previous_image_index_) {
233     case User::kExternalImageIndex: {
234       // User has image from camera/file, record it and add to the image list.
235       previous_image_ = user->image();
236       SendOldImage(webui::GetBitmapDataUrl(*previous_image_.bitmap()));
237       break;
238     }
239     case User::kProfileImageIndex: {
240       // User has his/her Profile image as the current image.
241       SendProfileImage(user->image(), true);
242       break;
243     }
244     default: {
245       DCHECK(previous_image_index_ >= 0 &&
246              previous_image_index_ < kDefaultImagesCount);
247       if (previous_image_index_ >= kFirstDefaultImageIndex) {
248         // User has image from the current set of default images.
249         base::StringValue image_url(GetDefaultImageUrl(previous_image_index_));
250         web_ui()->CallJavascriptFunction(
251             "ChangePictureOptions.setSelectedImage", image_url);
252       } else {
253         // User has an old default image, so present it in the same manner as a
254         // previous image from file.
255         SendOldImage(GetDefaultImageUrl(previous_image_index_));
256       }
257     }
258   }
259 }
260
261 void ChangePictureOptionsHandler::SendProfileImage(const gfx::ImageSkia& image,
262                                                    bool should_select) {
263   base::StringValue data_url(webui::GetBitmapDataUrl(*image.bitmap()));
264   base::FundamentalValue select(should_select);
265   web_ui()->CallJavascriptFunction("ChangePictureOptions.setProfileImage",
266                                    data_url, select);
267 }
268
269 void ChangePictureOptionsHandler::UpdateProfileImage() {
270   UserImageManager* user_image_manager =
271       UserManager::Get()->GetUserImageManager();
272
273   // If we have a downloaded profile image and haven't sent it in
274   // |SendSelectedImage|, send it now (without selecting).
275   if (previous_image_index_ != User::kProfileImageIndex &&
276       !user_image_manager->DownloadedProfileImage().isNull())
277     SendProfileImage(user_image_manager->DownloadedProfileImage(), false);
278
279   user_image_manager->DownloadProfileImage(kProfileDownloadReason);
280 }
281
282 void ChangePictureOptionsHandler::SendOldImage(const std::string& image_url) {
283   previous_image_url_ = image_url;
284   base::StringValue url(image_url);
285   web_ui()->CallJavascriptFunction("ChangePictureOptions.setOldImage", url);
286 }
287
288 void ChangePictureOptionsHandler::HandleSelectImage(const ListValue* args) {
289   std::string image_url;
290   std::string image_type;
291   if (!args ||
292       args->GetSize() != 2 ||
293       !args->GetString(0, &image_url) ||
294       !args->GetString(1, &image_type)) {
295     NOTREACHED();
296     return;
297   }
298   DCHECK(!image_url.empty());
299   DCHECK(!image_type.empty());
300
301   const User* user = UserManager::Get()->GetLoggedInUser();
302   UserImageManager* user_image_manager =
303       UserManager::Get()->GetUserImageManager();
304   int image_index = User::kInvalidImageIndex;
305   bool waiting_for_camera_photo = false;
306
307   if (image_type == "old") {
308     // Previous image re-selected.
309     if (previous_image_index_ == User::kExternalImageIndex) {
310       DCHECK(!previous_image_.isNull());
311       user_image_manager->SaveUserImage(
312           user->email(), UserImage::CreateAndEncode(previous_image_));
313     } else {
314       DCHECK(previous_image_index_ >= 0 &&
315              previous_image_index_ < kFirstDefaultImageIndex);
316       user_image_manager->SaveUserDefaultImageIndex(
317           user->email(), previous_image_index_);
318     }
319
320     UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
321                               kHistogramImageOld,
322                               kHistogramImagesCount);
323     VLOG(1) << "Selected old user image";
324   } else if (image_type == "default" &&
325              IsDefaultImageUrl(image_url, &image_index)) {
326     // One of the default user images.
327     user_image_manager->SaveUserDefaultImageIndex(user->email(), image_index);
328
329     UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
330                               GetDefaultImageHistogramValue(image_index),
331                               kHistogramImagesCount);
332     VLOG(1) << "Selected default user image: " << image_index;
333   } else if (image_type == "camera") {
334     // Camera image is selected.
335     if (user_photo_.isNull()) {
336       DCHECK(image_decoder_.get());
337       waiting_for_camera_photo = true;
338       VLOG(1) << "Still waiting for camera image to decode";
339     } else {
340       SetImageFromCamera(user_photo_);
341     }
342   } else if (image_type == "profile") {
343     // Profile image selected. Could be previous (old) user image.
344     user_image_manager->SaveUserImageFromProfileImage(user->email());
345
346     if (previous_image_index_ == User::kProfileImageIndex) {
347       UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
348                                 kHistogramImageOld,
349                                 kHistogramImagesCount);
350       VLOG(1) << "Selected old (profile) user image";
351     } else {
352       UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
353                                 kHistogramImageFromProfile,
354                                 kHistogramImagesCount);
355       VLOG(1) << "Selected profile image";
356     }
357   } else {
358     NOTREACHED() << "Unexpected image type: " << image_type;
359   }
360
361   // Ignore the result of the previous decoding if it's no longer needed.
362   if (!waiting_for_camera_photo && image_decoder_.get())
363     image_decoder_->set_delegate(NULL);
364 }
365
366 void ChangePictureOptionsHandler::FileSelected(const base::FilePath& path,
367                                                int index,
368                                                void* params) {
369   UserManager* user_manager = UserManager::Get();
370   user_manager->GetUserImageManager()->SaveUserImageFromFile(
371       user_manager->GetLoggedInUser()->email(), path);
372   UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
373                             kHistogramImageFromFile,
374                             kHistogramImagesCount);
375   VLOG(1) << "Selected image from file";
376 }
377
378 void ChangePictureOptionsHandler::SetImageFromCamera(
379     const gfx::ImageSkia& photo) {
380   UserManager* user_manager = UserManager::Get();
381   user_manager->GetUserImageManager()->SaveUserImage(
382       user_manager->GetLoggedInUser()->email(),
383       UserImage::CreateAndEncode(photo));
384   UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
385                             kHistogramImageFromCamera,
386                             kHistogramImagesCount);
387   VLOG(1) << "Selected camera photo";
388 }
389
390 void ChangePictureOptionsHandler::CheckCameraPresence() {
391   CameraDetector::StartPresenceCheck(
392       base::Bind(&ChangePictureOptionsHandler::OnCameraPresenceCheckDone,
393                  weak_factory_.GetWeakPtr()));
394 }
395
396 void ChangePictureOptionsHandler::SetCameraPresent(bool present) {
397   base::FundamentalValue present_value(present);
398   web_ui()->CallJavascriptFunction("ChangePictureOptions.setCameraPresent",
399                                    present_value);
400 }
401
402 void ChangePictureOptionsHandler::OnCameraPresenceCheckDone() {
403   SetCameraPresent(CameraDetector::camera_presence() ==
404                    CameraDetector::kCameraPresent);
405 }
406
407 void ChangePictureOptionsHandler::Observe(
408     int type,
409     const content::NotificationSource& source,
410     const content::NotificationDetails& details) {
411   OptionsPageUIHandler::Observe(type, source, details);
412   if (type == chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED) {
413     // User profile image has been updated.
414     SendProfileImage(*content::Details<const gfx::ImageSkia>(details).ptr(),
415                      false);
416   } else if (type == chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED) {
417     // Not initialized yet.
418     if (previous_image_index_ == User::kInvalidImageIndex)
419       return;
420     SendSelectedImage();
421   }
422 }
423
424 gfx::NativeWindow ChangePictureOptionsHandler::GetBrowserWindow() const {
425   Browser* browser =
426       chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
427   return browser->window()->GetNativeWindow();
428 }
429
430 void ChangePictureOptionsHandler::OnImageDecoded(
431     const ImageDecoder* decoder,
432     const SkBitmap& decoded_image) {
433   DCHECK_EQ(image_decoder_.get(), decoder);
434   image_decoder_ = NULL;
435   user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
436   SetImageFromCamera(user_photo_);
437 }
438
439 void ChangePictureOptionsHandler::OnDecodeImageFailed(
440     const ImageDecoder* decoder) {
441   NOTREACHED() << "Failed to decode PNG image from WebUI";
442 }
443
444 }  // namespace options
445 }  // namespace chromeos