1// Copyright (c) 2012 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/screenshot_source.h" 6 7#include "base/bind.h" 8#include "base/callback.h" 9#include "base/file_util.h" 10#include "base/files/file_path.h" 11#include "base/i18n/time_formatting.h" 12#include "base/memory/ref_counted_memory.h" 13#include "base/message_loop/message_loop.h" 14#include "base/path_service.h" 15#include "base/prefs/pref_service.h" 16#include "base/strings/string16.h" 17#include "base/strings/string_util.h" 18#include "base/strings/stringprintf.h" 19#include "chrome/browser/browser_process.h" 20#include "chrome/browser/download/download_prefs.h" 21#include "chrome/browser/profiles/profile.h" 22#include "chrome/browser/profiles/profile_manager.h" 23#include "chrome/common/chrome_paths.h" 24#include "chrome/common/pref_names.h" 25#include "chrome/common/url_constants.h" 26#include "url/url_canon.h" 27#include "url/url_util.h" 28 29#if defined(USE_ASH) 30#include "ash/shell.h" 31#include "ash/shell_delegate.h" 32#endif 33 34#if defined(OS_CHROMEOS) 35#include "chrome/browser/chromeos/drive/drive_integration_service.h" 36#include "chrome/browser/chromeos/drive/file_system_interface.h" 37#include "chrome/browser/chromeos/drive/file_system_util.h" 38#include "chromeos/login/login_state.h" 39#include "content/public/browser/browser_thread.h" 40#endif 41 42// static 43const char ScreenshotSource::kScreenshotUrlRoot[] = "chrome://screenshots/"; 44// static 45const char ScreenshotSource::kScreenshotCurrent[] = "current"; 46// static 47const char ScreenshotSource::kScreenshotSaved[] = "saved/"; 48#if defined(OS_CHROMEOS) 49// static 50const char ScreenshotSource::kScreenshotPrefix[] = "Screenshot "; 51// static 52const char ScreenshotSource::kScreenshotSuffix[] = ".png"; 53#endif 54 55bool ShouldUse24HourClock() { 56#if defined(OS_CHROMEOS) 57 Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); 58 if (profile) { 59 return profile->GetPrefs()->GetBoolean(prefs::kUse24HourClock); 60 } 61#endif 62 return base::GetHourClockType() == base::k24HourClock; 63} 64 65ScreenshotSource::ScreenshotSource( 66 std::vector<unsigned char>* current_screenshot, 67 Profile* profile) 68 : profile_(profile) { 69 // Setup the last screenshot taken. 70 if (current_screenshot) 71 current_screenshot_.reset(new ScreenshotData(*current_screenshot)); 72 else 73 current_screenshot_.reset(new ScreenshotData()); 74} 75 76ScreenshotSource::~ScreenshotSource() {} 77 78// static 79std::string ScreenshotSource::GetScreenshotBaseFilename() { 80 base::Time::Exploded now; 81 base::Time::Now().LocalExplode(&now); 82 83 // We don't use base/i18n/time_formatting.h here because it doesn't 84 // support our format. Don't use ICU either to avoid i18n file names 85 // for non-English locales. 86 // TODO(mukai): integrate this logic somewhere time_formatting.h 87 std::string file_name = base::StringPrintf( 88 "Screenshot %d-%02d-%02d at ", now.year, now.month, now.day_of_month); 89 90 if (ShouldUse24HourClock()) { 91 file_name.append(base::StringPrintf( 92 "%02d.%02d.%02d", now.hour, now.minute, now.second)); 93 } else { 94 int hour = now.hour; 95 if (hour > 12) { 96 hour -= 12; 97 } else if (hour == 0) { 98 hour = 12; 99 } 100 file_name.append(base::StringPrintf( 101 "%d.%02d.%02d ", hour, now.minute, now.second)); 102 file_name.append((now.hour >= 12) ? "PM" : "AM"); 103 } 104 105 return file_name; 106} 107 108#if defined(USE_ASH) 109 110// static 111bool ScreenshotSource::AreScreenshotsDisabled() { 112 return g_browser_process->local_state()->GetBoolean( 113 prefs::kDisableScreenshots); 114} 115 116// static 117bool ScreenshotSource::GetScreenshotDirectory(base::FilePath* directory) { 118 if (ScreenshotSource::AreScreenshotsDisabled()) 119 return false; 120 121 bool is_logged_in = true; 122 123#if defined(OS_CHROMEOS) 124 is_logged_in = chromeos::LoginState::Get()->IsUserLoggedIn(); 125#endif 126 127 if (is_logged_in) { 128 DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext( 129 ash::Shell::GetInstance()->delegate()->GetCurrentBrowserContext()); 130 *directory = download_prefs->DownloadPath(); 131 } else { 132 if (!file_util::GetTempDir(directory)) { 133 LOG(ERROR) << "Failed to find temporary directory."; 134 return false; 135 } 136 } 137 return true; 138} 139 140#endif 141 142std::string ScreenshotSource::GetSource() const { 143 return chrome::kChromeUIScreenshotPath; 144} 145 146void ScreenshotSource::StartDataRequest( 147 const std::string& path, 148 int render_process_id, 149 int render_view_id, 150 const content::URLDataSource::GotDataCallback& callback) { 151 SendScreenshot(path, callback); 152} 153 154std::string ScreenshotSource::GetMimeType(const std::string&) const { 155 // We need to explicitly return a mime type, otherwise if the user tries to 156 // drag the image they get no extension. 157 return "image/png"; 158} 159 160ScreenshotDataPtr ScreenshotSource::GetCachedScreenshot( 161 const std::string& screenshot_path) { 162 std::map<std::string, ScreenshotDataPtr>::iterator pos; 163 std::string path = screenshot_path.substr( 164 0, screenshot_path.find_first_of("?")); 165 if ((pos = cached_screenshots_.find(path)) != cached_screenshots_.end()) { 166 return pos->second; 167 } else { 168 return ScreenshotDataPtr(new ScreenshotData); 169 } 170} 171 172void ScreenshotSource::SendScreenshot( 173 const std::string& screenshot_path, 174 const content::URLDataSource::GotDataCallback& callback) { 175 // Strip the query param value - we only use it as a hack to ensure our 176 // image gets reloaded instead of being pulled from the browser cache 177 std::string path = screenshot_path.substr( 178 0, screenshot_path.find_first_of("?")); 179 if (path == ScreenshotSource::kScreenshotCurrent) { 180 CacheAndSendScreenshot(path, callback, current_screenshot_); 181#if defined(OS_CHROMEOS) 182 } else if (path.compare(0, strlen(ScreenshotSource::kScreenshotSaved), 183 ScreenshotSource::kScreenshotSaved) == 0) { 184 using content::BrowserThread; 185 186 std::string filename = 187 path.substr(strlen(ScreenshotSource::kScreenshotSaved)); 188 189 url_canon::RawCanonOutputT<char16> decoded; 190 url_util::DecodeURLEscapeSequences( 191 filename.data(), filename.size(), &decoded); 192 // Screenshot filenames don't use non-ascii characters. 193 std::string decoded_filename = UTF16ToASCII(string16( 194 decoded.data(), decoded.length())); 195 196 base::FilePath download_path; 197 GetScreenshotDirectory(&download_path); 198 if (drive::util::IsUnderDriveMountPoint(download_path)) { 199 drive::FileSystemInterface* file_system = 200 drive::DriveIntegrationServiceFactory::GetForProfile( 201 profile_)->file_system(); 202 file_system->GetFileByPath( 203 drive::util::ExtractDrivePath(download_path).Append(decoded_filename), 204 base::Bind(&ScreenshotSource::GetSavedScreenshotCallback, 205 base::Unretained(this), screenshot_path, callback)); 206 } else { 207 BrowserThread::PostTask( 208 BrowserThread::FILE, FROM_HERE, 209 base::Bind(&ScreenshotSource::SendSavedScreenshot, 210 base::Unretained(this), 211 screenshot_path, 212 callback, download_path.Append(decoded_filename))); 213 } 214#endif 215 } else { 216 CacheAndSendScreenshot( 217 path, callback, ScreenshotDataPtr(new ScreenshotData())); 218 } 219} 220 221#if defined(OS_CHROMEOS) 222void ScreenshotSource::SendSavedScreenshot( 223 const std::string& screenshot_path, 224 const content::URLDataSource::GotDataCallback& callback, 225 const base::FilePath& file) { 226 ScreenshotDataPtr read_bytes(new ScreenshotData); 227 int64 file_size = 0; 228 229 if (!file_util::GetFileSize(file, &file_size)) { 230 CacheAndSendScreenshot(screenshot_path, callback, read_bytes); 231 return; 232 } 233 234 read_bytes->resize(file_size); 235 if (!file_util::ReadFile(file, reinterpret_cast<char*>(&read_bytes->front()), 236 static_cast<int>(file_size))) 237 read_bytes->clear(); 238 239 CacheAndSendScreenshot(screenshot_path, callback, read_bytes); 240} 241 242void ScreenshotSource::GetSavedScreenshotCallback( 243 const std::string& screenshot_path, 244 const content::URLDataSource::GotDataCallback& callback, 245 drive::FileError error, 246 const base::FilePath& file, 247 scoped_ptr<drive::ResourceEntry> entry) { 248 if (error != drive::FILE_ERROR_OK) { 249 ScreenshotDataPtr read_bytes(new ScreenshotData); 250 CacheAndSendScreenshot(screenshot_path, callback, read_bytes); 251 return; 252 } 253 254 content::BrowserThread::PostTask( 255 content::BrowserThread::FILE, FROM_HERE, 256 base::Bind(&ScreenshotSource::SendSavedScreenshot, 257 base::Unretained(this), screenshot_path, callback, file)); 258} 259#endif 260 261void ScreenshotSource::CacheAndSendScreenshot( 262 const std::string& screenshot_path, 263 const content::URLDataSource::GotDataCallback& callback, 264 ScreenshotDataPtr bytes) { 265 // Strip the query from the screenshot path. 266 std::string path = screenshot_path.substr( 267 0, screenshot_path.find_first_of("?")); 268 cached_screenshots_[path] = bytes; 269 callback.Run(new base::RefCountedBytes(*bytes)); 270} 271