1// Copyright 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/chromeos/extensions/wallpaper_api.h"
6
7#include "ash/desktop_background/desktop_background_controller.h"
8#include "base/files/file_util.h"
9#include "base/lazy_instance.h"
10#include "base/path_service.h"
11#include "base/prefs/pref_service.h"
12#include "base/strings/stringprintf.h"
13#include "base/threading/worker_pool.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/common/chrome_paths.h"
18#include "chrome/common/extensions/extension_constants.h"
19#include "chrome/common/pref_names.h"
20#include "components/user_manager/user.h"
21#include "components/user_manager/user_manager.h"
22#include "net/base/load_flags.h"
23#include "net/http/http_status_code.h"
24#include "net/url_request/url_fetcher.h"
25#include "net/url_request/url_fetcher_delegate.h"
26#include "url/gurl.h"
27
28using base::BinaryValue;
29using content::BrowserThread;
30
31typedef base::Callback<void(bool success, const std::string&)> FetchCallback;
32
33namespace set_wallpaper = extensions::api::wallpaper::SetWallpaper;
34
35namespace {
36
37class WallpaperFetcher : public net::URLFetcherDelegate {
38 public:
39  WallpaperFetcher() {}
40
41  virtual ~WallpaperFetcher() {}
42
43  void FetchWallpaper(const GURL& url, FetchCallback callback) {
44    CancelPreviousFetch();
45    callback_ = callback;
46    url_fetcher_.reset(net::URLFetcher::Create(url,
47                                               net::URLFetcher::GET,
48                                               this));
49    url_fetcher_->SetRequestContext(
50        g_browser_process->system_request_context());
51    url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
52    url_fetcher_->Start();
53  }
54
55 private:
56  // URLFetcherDelegate overrides:
57  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
58    DCHECK(url_fetcher_.get() == source);
59
60    bool success = source->GetStatus().is_success() &&
61                   source->GetResponseCode() == net::HTTP_OK;
62    std::string response;
63    if (success) {
64      source->GetResponseAsString(&response);
65    } else {
66      response = base::StringPrintf(
67          "Downloading wallpaper %s failed. The response code is %d.",
68          source->GetOriginalURL().ExtractFileName().c_str(),
69          source->GetResponseCode());
70    }
71    url_fetcher_.reset();
72    callback_.Run(success, response);
73  }
74
75  void CancelPreviousFetch() {
76    if (url_fetcher_.get()) {
77      callback_.Run(false, wallpaper_api_util::kCancelWallpaperMessage);
78      url_fetcher_.reset();
79    }
80  }
81
82  scoped_ptr<net::URLFetcher> url_fetcher_;
83  FetchCallback callback_;
84};
85
86base::LazyInstance<WallpaperFetcher> g_wallpaper_fetcher =
87    LAZY_INSTANCE_INITIALIZER;
88
89}  // namespace
90
91WallpaperSetWallpaperFunction::WallpaperSetWallpaperFunction() {
92}
93
94WallpaperSetWallpaperFunction::~WallpaperSetWallpaperFunction() {
95}
96
97bool WallpaperSetWallpaperFunction::RunAsync() {
98  DCHECK_CURRENTLY_ON(BrowserThread::UI);
99  params_ = set_wallpaper::Params::Create(*args_);
100  EXTENSION_FUNCTION_VALIDATE(params_);
101
102  // Gets email address and username hash while at UI thread.
103  user_id_ = user_manager::UserManager::Get()->GetLoggedInUser()->email();
104  user_id_hash_ =
105      user_manager::UserManager::Get()->GetLoggedInUser()->username_hash();
106
107  if (params_->details.data) {
108    StartDecode(*params_->details.data);
109  } else {
110    GURL wallpaper_url(*params_->details.url);
111    if (wallpaper_url.is_valid()) {
112      g_wallpaper_fetcher.Get().FetchWallpaper(
113          wallpaper_url,
114          base::Bind(&WallpaperSetWallpaperFunction::OnWallpaperFetched, this));
115    } else {
116      SetError("URL is invalid.");
117      SendResponse(false);
118    }
119  }
120  return true;
121}
122
123void WallpaperSetWallpaperFunction::OnWallpaperDecoded(
124    const gfx::ImageSkia& image) {
125  chromeos::WallpaperManager* wallpaper_manager =
126      chromeos::WallpaperManager::Get();
127  base::FilePath thumbnail_path = wallpaper_manager->GetCustomWallpaperPath(
128      chromeos::kThumbnailWallpaperSubDir,
129      user_id_hash_,
130      params_->details.filename);
131
132  sequence_token_ = BrowserThread::GetBlockingPool()->
133      GetNamedSequenceToken(chromeos::kWallpaperSequenceTokenName);
134  scoped_refptr<base::SequencedTaskRunner> task_runner =
135      BrowserThread::GetBlockingPool()->
136          GetSequencedTaskRunnerWithShutdownBehavior(sequence_token_,
137              base::SequencedWorkerPool::BLOCK_SHUTDOWN);
138  ash::WallpaperLayout layout = wallpaper_api_util::GetLayoutEnum(
139      set_wallpaper::Params::Details::ToString(params_->details.layout));
140  bool update_wallpaper =
141      user_id_ == user_manager::UserManager::Get()->GetActiveUser()->email();
142  wallpaper_manager->SetCustomWallpaper(user_id_,
143                                        user_id_hash_,
144                                        params_->details.filename,
145                                        layout,
146                                        user_manager::User::CUSTOMIZED,
147                                        image,
148                                        update_wallpaper);
149  unsafe_wallpaper_decoder_ = NULL;
150
151  if (params_->details.thumbnail) {
152    image.EnsureRepsForSupportedScales();
153    scoped_ptr<gfx::ImageSkia> deep_copy(image.DeepCopy());
154    // Generates thumbnail before call api function callback. We can then
155    // request thumbnail in the javascript callback.
156    task_runner->PostTask(
157        FROM_HERE,
158        base::Bind(&WallpaperSetWallpaperFunction::GenerateThumbnail,
159                   this,
160                   thumbnail_path,
161                   base::Passed(deep_copy.Pass())));
162  } else {
163    // Save current extenion name. It will be displayed in the component
164    // wallpaper picker app. If current extension is the component wallpaper
165    // picker, set an empty string.
166    Profile* profile = Profile::FromBrowserContext(browser_context());
167    if (extension()->id() == extension_misc::kWallpaperManagerId) {
168      profile->GetPrefs()->SetString(prefs::kCurrentWallpaperAppName,
169                                     std::string());
170    } else {
171      profile->GetPrefs()->SetString(prefs::kCurrentWallpaperAppName,
172                                     extension()->name());
173    }
174    SendResponse(true);
175  }
176}
177
178void WallpaperSetWallpaperFunction::GenerateThumbnail(
179    const base::FilePath& thumbnail_path, scoped_ptr<gfx::ImageSkia> image) {
180  DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread(
181      sequence_token_));
182  if (!base::PathExists(thumbnail_path.DirName()))
183    base::CreateDirectory(thumbnail_path.DirName());
184
185  scoped_refptr<base::RefCountedBytes> data;
186  chromeos::WallpaperManager::Get()->ResizeImage(
187      *image,
188      ash::WALLPAPER_LAYOUT_STRETCH,
189      chromeos::kWallpaperThumbnailWidth,
190      chromeos::kWallpaperThumbnailHeight,
191      &data,
192      NULL);
193  BrowserThread::PostTask(
194      BrowserThread::UI, FROM_HERE,
195      base::Bind(
196          &WallpaperSetWallpaperFunction::ThumbnailGenerated,
197          this, data));
198}
199
200void WallpaperSetWallpaperFunction::ThumbnailGenerated(
201    base::RefCountedBytes* data) {
202  BinaryValue* result = BinaryValue::CreateWithCopiedBuffer(
203      reinterpret_cast<const char*>(data->front()), data->size());
204  SetResult(result);
205  SendResponse(true);
206}
207
208void WallpaperSetWallpaperFunction::OnWallpaperFetched(
209    bool success,
210    const std::string& response) {
211  if (success) {
212    params_->details.data.reset(new std::string(response));
213    StartDecode(*params_->details.data);
214  } else {
215    SetError(response);
216    SendResponse(false);
217  }
218}
219