Home | History | Annotate | Download | only in webui
      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