blob: 19e8003bfe43f5e52552ceba11edb2d81962cdfb [file] [log] [blame]
// Copyright (c) 2012 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/ui/webui/screenshot_source.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/i18n/time_formatting.h"
#include "base/memory/ref_counted_memory.h"
#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/prefs/pref_service.h"
#include "base/string16.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "googleurl/src/url_canon.h"
#include "googleurl/src/url_util.h"
#if defined(USE_ASH)
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#endif
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/drive/drive_file_system_interface.h"
#include "chrome/browser/chromeos/drive/drive_file_system_util.h"
#include "chrome/browser/chromeos/drive/drive_system_service.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "content/public/browser/browser_thread.h"
#endif
// static
const char ScreenshotSource::kScreenshotUrlRoot[] = "chrome://screenshots/";
// static
const char ScreenshotSource::kScreenshotCurrent[] = "current";
// static
const char ScreenshotSource::kScreenshotSaved[] = "saved/";
#if defined(OS_CHROMEOS)
// static
const char ScreenshotSource::kScreenshotPrefix[] = "Screenshot ";
// static
const char ScreenshotSource::kScreenshotSuffix[] = ".png";
#endif
bool ShouldUse24HourClock() {
#if defined(OS_CHROMEOS)
Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
if (profile) {
return profile->GetPrefs()->GetBoolean(prefs::kUse24HourClock);
}
#endif
return base::GetHourClockType() == base::k24HourClock;
}
ScreenshotSource::ScreenshotSource(
std::vector<unsigned char>* current_screenshot,
Profile* profile)
: profile_(profile) {
// Setup the last screenshot taken.
if (current_screenshot)
current_screenshot_.reset(new ScreenshotData(*current_screenshot));
else
current_screenshot_.reset(new ScreenshotData());
}
ScreenshotSource::~ScreenshotSource() {}
// static
std::string ScreenshotSource::GetScreenshotBaseFilename() {
base::Time::Exploded now;
base::Time::Now().LocalExplode(&now);
// We don't use base/i18n/time_formatting.h here because it doesn't
// support our format. Don't use ICU either to avoid i18n file names
// for non-English locales.
// TODO(mukai): integrate this logic somewhere time_formatting.h
std::string file_name = base::StringPrintf(
"Screenshot %d-%02d-%02d at ", now.year, now.month, now.day_of_month);
if (ShouldUse24HourClock()) {
file_name.append(base::StringPrintf(
"%02d.%02d.%02d", now.hour, now.minute, now.second));
} else {
int hour = now.hour;
if (hour > 12) {
hour -= 12;
} else if (hour == 0) {
hour = 12;
}
file_name.append(base::StringPrintf(
"%d.%02d.%02d ", hour, now.minute, now.second));
file_name.append((now.hour >= 12) ? "PM" : "AM");
}
return file_name;
}
#if defined(USE_ASH)
// static
bool ScreenshotSource::AreScreenshotsDisabled() {
return g_browser_process->local_state()->GetBoolean(
prefs::kDisableScreenshots);
}
// static
bool ScreenshotSource::GetScreenshotDirectory(base::FilePath* directory) {
if (ScreenshotSource::AreScreenshotsDisabled())
return false;
bool is_logged_in = true;
#if defined(OS_CHROMEOS)
is_logged_in = chromeos::UserManager::Get()->IsUserLoggedIn();
#endif
if (is_logged_in) {
DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext(
ash::Shell::GetInstance()->delegate()->GetCurrentBrowserContext());
*directory = download_prefs->DownloadPath();
} else {
if (!file_util::GetTempDir(directory)) {
LOG(ERROR) << "Failed to find temporary directory.";
return false;
}
}
return true;
}
#endif
std::string ScreenshotSource::GetSource() {
return chrome::kChromeUIScreenshotPath;
}
void ScreenshotSource::StartDataRequest(
const std::string& path,
bool is_incognito,
const content::URLDataSource::GotDataCallback& callback) {
SendScreenshot(path, callback);
}
std::string ScreenshotSource::GetMimeType(const std::string&) const {
// We need to explicitly return a mime type, otherwise if the user tries to
// drag the image they get no extension.
return "image/png";
}
ScreenshotDataPtr ScreenshotSource::GetCachedScreenshot(
const std::string& screenshot_path) {
std::map<std::string, ScreenshotDataPtr>::iterator pos;
std::string path = screenshot_path.substr(
0, screenshot_path.find_first_of("?"));
if ((pos = cached_screenshots_.find(path)) != cached_screenshots_.end()) {
return pos->second;
} else {
return ScreenshotDataPtr(new ScreenshotData);
}
}
void ScreenshotSource::SendScreenshot(
const std::string& screenshot_path,
const content::URLDataSource::GotDataCallback& callback) {
// Strip the query param value - we only use it as a hack to ensure our
// image gets reloaded instead of being pulled from the browser cache
std::string path = screenshot_path.substr(
0, screenshot_path.find_first_of("?"));
if (path == ScreenshotSource::kScreenshotCurrent) {
CacheAndSendScreenshot(path, callback, current_screenshot_);
#if defined(OS_CHROMEOS)
} else if (path.compare(0, strlen(ScreenshotSource::kScreenshotSaved),
ScreenshotSource::kScreenshotSaved) == 0) {
using content::BrowserThread;
std::string filename =
path.substr(strlen(ScreenshotSource::kScreenshotSaved));
url_canon::RawCanonOutputT<char16> decoded;
url_util::DecodeURLEscapeSequences(
filename.data(), filename.size(), &decoded);
// Screenshot filenames don't use non-ascii characters.
std::string decoded_filename = UTF16ToASCII(string16(
decoded.data(), decoded.length()));
base::FilePath download_path;
GetScreenshotDirectory(&download_path);
if (drive::util::IsUnderDriveMountPoint(download_path)) {
drive::DriveFileSystemInterface* file_system =
drive::DriveSystemServiceFactory::GetForProfile(
profile_)->file_system();
file_system->GetFileByResourceId(
decoded_filename,
drive::DriveClientContext(drive::USER_INITIATED),
base::Bind(&ScreenshotSource::GetSavedScreenshotCallback,
base::Unretained(this), screenshot_path, callback),
google_apis::GetContentCallback());
} else {
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&ScreenshotSource::SendSavedScreenshot,
base::Unretained(this),
screenshot_path,
callback, download_path.Append(decoded_filename)));
}
#endif
} else {
CacheAndSendScreenshot(
path, callback, ScreenshotDataPtr(new ScreenshotData()));
}
}
#if defined(OS_CHROMEOS)
void ScreenshotSource::SendSavedScreenshot(
const std::string& screenshot_path,
const content::URLDataSource::GotDataCallback& callback,
const base::FilePath& file) {
ScreenshotDataPtr read_bytes(new ScreenshotData);
int64 file_size = 0;
if (!file_util::GetFileSize(file, &file_size)) {
CacheAndSendScreenshot(screenshot_path, callback, read_bytes);
return;
}
read_bytes->resize(file_size);
if (!file_util::ReadFile(file, reinterpret_cast<char*>(&read_bytes->front()),
static_cast<int>(file_size)))
read_bytes->clear();
CacheAndSendScreenshot(screenshot_path, callback, read_bytes);
}
void ScreenshotSource::GetSavedScreenshotCallback(
const std::string& screenshot_path,
const content::URLDataSource::GotDataCallback& callback,
drive::DriveFileError error,
const base::FilePath& file,
const std::string& unused_mime_type,
drive::DriveFileType file_type) {
if (error != drive::DRIVE_FILE_OK || file_type != drive::REGULAR_FILE) {
ScreenshotDataPtr read_bytes(new ScreenshotData);
CacheAndSendScreenshot(screenshot_path, callback, read_bytes);
return;
}
content::BrowserThread::PostTask(
content::BrowserThread::FILE, FROM_HERE,
base::Bind(&ScreenshotSource::SendSavedScreenshot,
base::Unretained(this), screenshot_path, callback, file));
}
#endif
void ScreenshotSource::CacheAndSendScreenshot(
const std::string& screenshot_path,
const content::URLDataSource::GotDataCallback& callback,
ScreenshotDataPtr bytes) {
// Strip the query from the screenshot path.
std::string path = screenshot_path.substr(
0, screenshot_path.find_first_of("?"));
cached_screenshots_[path] = bytes;
callback.Run(new base::RefCountedBytes(*bytes));
}