blob: 274863ccde3b344d0c27b958629d6ec593d8b036 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/extensions/wallpaper_api.h"
#include <string>
#include <utility>
#include <vector>
#include "ash/public/cpp/wallpaper_types.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/ref_counted_memory.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/extensions/wallpaper_private_api.h"
#include "chrome/browser/chromeos/file_manager/app_id.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/wallpaper_controller_client.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension_constants.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/event_router.h"
#include "net/base/load_flags.h"
#include "net/http/http_response_headers.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "url/gurl.h"
using base::Value;
using content::BrowserThread;
typedef base::Callback<void(bool success, const std::string&)> FetchCallback;
namespace set_wallpaper = extensions::api::wallpaper::SetWallpaper;
namespace {
class WallpaperFetcher {
public:
WallpaperFetcher() {}
void FetchWallpaper(const GURL& url, FetchCallback callback) {
CancelPreviousFetch();
original_url_ = url;
callback_ = callback;
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("wallpaper_fetcher", R"(
semantics {
sender: "Wallpaper Fetcher"
description:
"Chrome OS downloads wallpaper upon user request."
trigger:
"When an app or extension requests to download "
"a wallpaper from a remote URL."
data:
"User-selected image."
destination: WEBSITE
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting:
"This feature cannot be disabled by settings, but it is only "
"triggered by user request."
policy_exception_justification: "Not implemented."
})");
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = original_url_;
resource_request->load_flags = net::LOAD_DISABLE_CACHE;
simple_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation);
network::mojom::URLLoaderFactory* loader_factory =
g_browser_process->system_network_context_manager()
->GetURLLoaderFactory();
simple_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory,
base::BindOnce(&WallpaperFetcher::OnSimpleLoaderComplete,
base::Unretained(this)));
}
private:
void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body) {
std::string response;
bool success = false;
if (response_body) {
response = std::move(*response_body);
success = true;
} else if (simple_loader_->ResponseInfo() &&
simple_loader_->ResponseInfo()->headers) {
int response_code =
simple_loader_->ResponseInfo()->headers->response_code();
response = base::StringPrintf(
"Downloading wallpaper %s failed. The response code is %d.",
original_url_.ExtractFileName().c_str(), response_code);
}
simple_loader_.reset();
callback_.Run(success, response);
callback_.Reset();
}
void CancelPreviousFetch() {
if (simple_loader_.get()) {
callback_.Run(false, wallpaper_api_util::kCancelWallpaperMessage);
callback_.Reset();
simple_loader_.reset();
}
}
GURL original_url_;
std::unique_ptr<network::SimpleURLLoader> simple_loader_;
FetchCallback callback_;
};
base::LazyInstance<WallpaperFetcher>::DestructorAtExit g_wallpaper_fetcher =
LAZY_INSTANCE_INITIALIZER;
// Gets the |User| for a given |BrowserContext|. The function will only return
// valid objects.
const user_manager::User* GetUserFromBrowserContext(
content::BrowserContext* context) {
Profile* profile = Profile::FromBrowserContext(context);
DCHECK(profile);
const user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
DCHECK(user);
return user;
}
} // namespace
WallpaperSetWallpaperFunction::WallpaperSetWallpaperFunction() {
}
WallpaperSetWallpaperFunction::~WallpaperSetWallpaperFunction() {
}
ExtensionFunction::ResponseAction WallpaperSetWallpaperFunction::Run() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
params_ = set_wallpaper::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_);
// Gets account id from the caller, ensuring multiprofile compatibility.
const user_manager::User* user = GetUserFromBrowserContext(browser_context());
account_id_ = user->GetAccountId();
wallpaper_files_id_ =
WallpaperControllerClient::Get()->GetFilesId(account_id_);
if (params_->details.data) {
StartDecode(*params_->details.data);
// StartDecode() responds asynchronously.
return RespondLater();
}
if (!params_->details.url)
return RespondNow(Error("Either url or data field is required."));
GURL wallpaper_url(*params_->details.url);
if (!wallpaper_url.is_valid())
return RespondNow(Error("URL is invalid."));
g_wallpaper_fetcher.Get().FetchWallpaper(
wallpaper_url,
base::Bind(&WallpaperSetWallpaperFunction::OnWallpaperFetched, this));
// FetchWallpaper() repsonds asynchronously.
return RespondLater();
}
void WallpaperSetWallpaperFunction::OnWallpaperDecoded(
const gfx::ImageSkia& image) {
ash::WallpaperLayout layout = wallpaper_api_util::GetLayoutEnum(
extensions::api::wallpaper::ToString(params_->details.layout));
wallpaper_api_util::RecordCustomWallpaperLayout(layout);
const std::string file_name =
base::FilePath(params_->details.filename).BaseName().value();
WallpaperControllerClient::Get()->SetCustomWallpaper(
account_id_, wallpaper_files_id_, file_name, layout, image,
/*preview_mode=*/false);
unsafe_wallpaper_decoder_ = nullptr;
// We need to generate thumbnail image anyway to make the current third party
// wallpaper syncable through different devices.
image.EnsureRepsForSupportedScales();
scoped_refptr<base::RefCountedBytes> thumbnail_data;
GenerateThumbnail(
image, gfx::Size(kWallpaperThumbnailWidth, kWallpaperThumbnailHeight),
&thumbnail_data);
scoped_refptr<base::RefCountedBytes> original_data;
GenerateThumbnail(image, image.size(), &original_data);
std::unique_ptr<Value> original_result = Value::CreateWithCopiedBuffer(
reinterpret_cast<const char*>(original_data->front()),
original_data->size());
std::unique_ptr<Value> thumbnail_result = Value::CreateWithCopiedBuffer(
reinterpret_cast<const char*>(thumbnail_data->front()),
thumbnail_data->size());
// Inform the native Wallpaper Picker Application that the current wallpaper
// has been modified by a third party application.
if (extension()->id() != extension_misc::kWallpaperManagerId) {
Profile* profile = Profile::FromBrowserContext(browser_context());
extensions::EventRouter* event_router =
extensions::EventRouter::Get(profile);
std::unique_ptr<base::ListValue> event_args(new base::ListValue());
event_args->Append(original_result->CreateDeepCopy());
event_args->Append(thumbnail_result->CreateDeepCopy());
event_args->AppendString(
extensions::api::wallpaper::ToString(params_->details.layout));
// Setting wallpaper from right click menu in 'Files' app is a feature that
// was implemented in crbug.com/578935. Since 'Files' app is a built-in v1
// app in ChromeOS, we should treat it slightly differently with other third
// party apps: the wallpaper set by the 'Files' app should still be syncable
// and it should not appear in the wallpaper grid in the Wallpaper Picker.
// But we should not display the 'wallpaper-set-by-mesage' since it might
// introduce confusion as shown in crbug.com/599407.
event_args->AppendString(
(extension()->id() == file_manager::kFileManagerAppId)
? std::string()
: extension()->name());
std::unique_ptr<extensions::Event> event(new extensions::Event(
extensions::events::WALLPAPER_PRIVATE_ON_WALLPAPER_CHANGED_BY_3RD_PARTY,
extensions::api::wallpaper_private::OnWallpaperChangedBy3rdParty::
kEventName,
std::move(event_args)));
event_router->DispatchEventToExtension(extension_misc::kWallpaperManagerId,
std::move(event));
}
Respond(params_->details.thumbnail
? OneArgument(thumbnail_result->CreateDeepCopy())
: NoArguments());
}
void WallpaperSetWallpaperFunction::OnWallpaperFetched(
bool success,
const std::string& response) {
if (success) {
params_->details.data.reset(
new std::vector<uint8_t>(response.begin(), response.end()));
StartDecode(*params_->details.data);
// StartDecode() will Respond later through OnWallpaperDecoded()
} else {
Respond(Error(response));
}
}