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 43 const char ScreenshotSource::kScreenshotUrlRoot[] = "chrome://screenshots/"; 44 // static 45 const char ScreenshotSource::kScreenshotCurrent[] = "current"; 46 // static 47 const char ScreenshotSource::kScreenshotSaved[] = "saved/"; 48 #if defined(OS_CHROMEOS) 49 // static 50 const char ScreenshotSource::kScreenshotPrefix[] = "Screenshot "; 51 // static 52 const char ScreenshotSource::kScreenshotSuffix[] = ".png"; 53 #endif 54 55 bool 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 65 ScreenshotSource::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 76 ScreenshotSource::~ScreenshotSource() {} 77 78 // static 79 std::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 111 bool ScreenshotSource::AreScreenshotsDisabled() { 112 return g_browser_process->local_state()->GetBoolean( 113 prefs::kDisableScreenshots); 114 } 115 116 // static 117 bool 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 142 std::string ScreenshotSource::GetSource() const { 143 return chrome::kChromeUIScreenshotPath; 144 } 145 146 void 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 154 std::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 160 ScreenshotDataPtr 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 172 void 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) 222 void 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 242 void 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 261 void 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