OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/ash/screenshot_taker.h" | 5 #include "chrome/browser/ui/ash/screenshot_taker.h" |
6 | 6 |
7 #include <climits> | 7 #include <climits> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "ash/shell.h" | 10 #include "ash/shell.h" |
(...skipping 17 matching lines...) Expand all Loading... | |
28 #include "content/public/browser/browser_thread.h" | 28 #include "content/public/browser/browser_thread.h" |
29 #include "ui/aura/root_window.h" | 29 #include "ui/aura/root_window.h" |
30 #include "ui/aura/window.h" | 30 #include "ui/aura/window.h" |
31 | 31 |
32 #if defined(OS_CHROMEOS) | 32 #if defined(OS_CHROMEOS) |
33 #include "chrome/browser/chromeos/gdata/gdata_util.h" | 33 #include "chrome/browser/chromeos/gdata/gdata_util.h" |
34 #include "chrome/browser/chromeos/login/user_manager.h" | 34 #include "chrome/browser/chromeos/login/user_manager.h" |
35 #endif | 35 #endif |
36 | 36 |
37 namespace { | 37 namespace { |
38 const int kScreenshotMinimumIntervalInMS = 500; | 38 // How opaque should the layer that we flash onscreen to provide visual |
39 // feedback after the screenshot is taken be? | |
40 const float kVisualFeedbackLayerOpacity = 0.25f; | |
41 | |
42 // How long should the visual feedback layer be displayed? | |
43 const int64 kVisualFeedbackLayerDisplayTimeMs = 100; | |
44 | |
45 // The minimum interval between two screenshot commands. It has to be | |
46 // more than 1000 to prevent the conflict of filenames. | |
47 const int kScreenshotMinimumIntervalInMS = 1000; | |
39 | 48 |
40 bool ShouldUse24HourClock() { | 49 bool ShouldUse24HourClock() { |
41 #if defined(OS_CHROMEOS) | 50 #if defined(OS_CHROMEOS) |
42 Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); | 51 Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); |
43 if (profile) { | 52 if (profile) { |
44 PrefService* pref_service = profile->GetPrefs(); | 53 PrefService* pref_service = profile->GetPrefs(); |
45 if (pref_service) { | 54 if (pref_service) { |
46 return pref_service->GetBoolean(prefs::kUse24HourClock); | 55 return pref_service->GetBoolean(prefs::kUse24HourClock); |
47 } | 56 } |
48 } | 57 } |
49 #endif | 58 #endif |
50 return base::GetHourClockType() == base::k24HourClock; | 59 return base::GetHourClockType() == base::k24HourClock; |
51 } | 60 } |
52 | 61 |
53 std::string GetScreenShotBaseFilename(bool use_24hour_clock) { | 62 bool AreScreenshotsDisabled() { |
63 return g_browser_process->local_state()->GetBoolean( | |
64 prefs::kDisableScreenshots); | |
65 } | |
66 | |
67 std::string GetScreenshotBaseFilename() { | |
54 base::Time::Exploded now; | 68 base::Time::Exploded now; |
55 base::Time::Now().LocalExplode(&now); | 69 base::Time::Now().LocalExplode(&now); |
56 | 70 |
57 // We don't use base/i18n/time_formatting.h here because it doesn't | 71 // We don't use base/i18n/time_formatting.h here because it doesn't |
58 // support our format. Don't use ICU either to avoid i18n file names | 72 // support our format. Don't use ICU either to avoid i18n file names |
59 // for non-English locales. | 73 // for non-English locales. |
60 // TODO(mukai): integrate this logic somewhere time_formatting.h | 74 // TODO(mukai): integrate this logic somewhere time_formatting.h |
61 std::string file_name = base::StringPrintf( | 75 std::string file_name = base::StringPrintf( |
62 "Screenshot %d-%02d-%02d at ", now.year, now.month, now.day_of_month); | 76 "Screenshot %d-%02d-%02d at ", now.year, now.month, now.day_of_month); |
63 | 77 |
64 if (use_24hour_clock) { | 78 if (ShouldUse24HourClock()) { |
65 file_name.append(base::StringPrintf( | 79 file_name.append(base::StringPrintf( |
66 "%02d.%02d.%02d", now.hour, now.minute, now.second)); | 80 "%02d.%02d.%02d", now.hour, now.minute, now.second)); |
67 } else { | 81 } else { |
68 int hour = now.hour; | 82 int hour = now.hour; |
69 if (hour > 12) { | 83 if (hour > 12) { |
70 hour -= 12; | 84 hour -= 12; |
71 } else if (hour == 0) { | 85 } else if (hour == 0) { |
72 hour = 12; | 86 hour = 12; |
73 } | 87 } |
74 file_name.append(base::StringPrintf( | 88 file_name.append(base::StringPrintf( |
75 "%d.%02d.%02d ", hour, now.minute, now.second)); | 89 "%d.%02d.%02d ", hour, now.minute, now.second)); |
76 file_name.append((now.hour >= 12) ? "PM" : "AM"); | 90 file_name.append((now.hour >= 12) ? "PM" : "AM"); |
77 } | 91 } |
78 | 92 |
79 return file_name; | 93 return file_name; |
80 } | 94 } |
81 | 95 |
82 FilePath GetScreenshotPath(const FilePath& base_directory, | 96 bool GetScreenshotDirectory(FilePath* directory) { |
83 const std::string& base_name) { | 97 if (AreScreenshotsDisabled()) |
84 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | 98 return false; |
85 for (int retry = 0; retry < INT_MAX; retry++) { | |
86 std::string retry_suffix; | |
87 if (retry > 0) | |
88 retry_suffix = base::StringPrintf(" (%d)", retry + 1); | |
89 | 99 |
90 FilePath file_path = base_directory.AppendASCII( | 100 bool is_logged_in = true; |
91 base_name + retry_suffix + ".png"); | 101 #if defined(OS_CHROMEOS) |
92 if (!file_util::PathExists(file_path)) | 102 is_logged_in = chromeos::UserManager::Get()->IsUserLoggedIn(); |
93 return file_path; | 103 #endif |
104 | |
105 if (is_logged_in) { | |
106 DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext( | |
107 ash::Shell::GetInstance()->delegate()->GetCurrentBrowserContext()); | |
108 *directory = download_prefs->DownloadPath(); | |
109 } else { | |
110 if (!file_util::GetTempDir(directory)) { | |
111 LOG(ERROR) << "Failed to find temporary directory."; | |
112 return false; | |
113 } | |
94 } | 114 } |
95 return FilePath(); | 115 return true; |
96 } | 116 } |
97 | 117 |
98 void SaveScreenshotToLocalFile(scoped_refptr<base::RefCountedBytes> png_data, | 118 void SaveScreenshot(const FilePath& screenshot_path, |
99 const FilePath& screenshot_path) { | 119 scoped_refptr<base::RefCountedBytes> png_data) { |
100 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | 120 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
121 DCHECK(!screenshot_path.empty()); | |
101 if (static_cast<size_t>(file_util::WriteFile( | 122 if (static_cast<size_t>(file_util::WriteFile( |
102 screenshot_path, | 123 screenshot_path, |
103 reinterpret_cast<char*>(&(png_data->data()[0])), | 124 reinterpret_cast<char*>(&(png_data->data()[0])), |
104 png_data->size())) != png_data->size()) { | 125 png_data->size())) != png_data->size()) { |
105 LOG(ERROR) << "Failed to save to " << screenshot_path.value(); | 126 LOG(ERROR) << "Failed to save to " << screenshot_path.value(); |
106 } | 127 } |
107 } | 128 } |
108 | 129 |
109 void SaveScreenshot(const FilePath& screenshot_directory, | |
110 const std::string& base_name, | |
111 scoped_refptr<base::RefCountedBytes> png_data) { | |
112 FilePath screenshot_path = GetScreenshotPath(screenshot_directory, base_name); | |
113 if (screenshot_path.empty()) { | |
114 LOG(ERROR) << "Failed to find a screenshot file name."; | |
115 return; | |
116 } | |
117 SaveScreenshotToLocalFile(png_data, screenshot_path); | |
118 } | |
119 | |
120 // TODO(kinaba): crbug.com/140425, remove this ungly #ifdef dispatch. | 130 // TODO(kinaba): crbug.com/140425, remove this ungly #ifdef dispatch. |
121 #ifdef OS_CHROMEOS | 131 #ifdef OS_CHROMEOS |
122 void SaveScreenshotToGData(scoped_refptr<base::RefCountedBytes> png_data, | 132 void SaveScreenshotToGData(scoped_refptr<base::RefCountedBytes> png_data, |
123 gdata::GDataFileError error, | 133 gdata::GDataFileError error, |
124 const FilePath& local_path) { | 134 const FilePath& local_path) { |
125 if (error != gdata::GDATA_FILE_OK) { | 135 if (error != gdata::GDATA_FILE_OK) { |
126 LOG(ERROR) << "Failed to write screenshot image to Google Drive: " << error; | 136 LOG(ERROR) << "Failed to write screenshot image to Google Drive: " << error; |
127 return; | 137 return; |
128 } | 138 } |
129 SaveScreenshotToLocalFile(png_data, local_path); | 139 SaveScreenshot(local_path, png_data); |
130 } | 140 } |
131 | 141 |
132 void PostSaveScreenshotTask(const FilePath& screenshot_directory, | 142 void PostSaveScreenshotTask(const FilePath& screenshot_path, |
133 const std::string& base_name, | |
134 scoped_refptr<base::RefCountedBytes> png_data) { | 143 scoped_refptr<base::RefCountedBytes> png_data) { |
135 if (gdata::util::IsUnderGDataMountPoint(screenshot_directory)) { | 144 if (gdata::util::IsUnderGDataMountPoint(screenshot_path)) { |
136 Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); | 145 Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); |
137 if (profile) { | 146 if (profile) { |
138 // TODO(kinaba,mukai): crbug.com/140749. Take care of the case | 147 // TODO(kinaba,mukai): crbug.com/140749. Take care of the case |
139 // "base_name.png" already exists. | 148 // "base_name.png" already exists. |
kinaba
2012/08/07 05:01:30
We can remove this comment.
Jun Mukai
2012/08/07 05:03:27
Done.
| |
140 gdata::util::PrepareWritableFileAndRun( | 149 gdata::util::PrepareWritableFileAndRun( |
141 profile, | 150 profile, |
142 screenshot_directory.Append(base_name + ".png"), | 151 screenshot_path, |
143 base::Bind(&SaveScreenshotToGData, png_data)); | 152 base::Bind(&SaveScreenshotToGData, png_data)); |
144 } | 153 } |
145 } else { | 154 } else { |
146 content::BrowserThread::PostTask( | 155 content::BrowserThread::PostTask( |
147 content::BrowserThread::FILE, FROM_HERE, | 156 content::BrowserThread::FILE, FROM_HERE, |
148 base::Bind(&SaveScreenshot, screenshot_directory, base_name, png_data)); | 157 base::Bind(&SaveScreenshot, screenshot_path, png_data)); |
149 } | 158 } |
150 } | 159 } |
151 #else | 160 #else |
152 void PostSaveScreenshotTask(const FilePath& screenshot_directory, | 161 void PostSaveScreenshotTask(const FilePath& screenshot_path, |
153 const std::string& base_name, | |
154 scoped_refptr<base::RefCountedBytes> png_data) { | 162 scoped_refptr<base::RefCountedBytes> png_data) { |
155 content::BrowserThread::PostTask( | 163 content::BrowserThread::PostTask( |
156 content::BrowserThread::FILE, FROM_HERE, | 164 content::BrowserThread::FILE, FROM_HERE, |
157 base::Bind(&SaveScreenshot, screenshot_directory, base_name, png_data)); | 165 base::Bind(&SaveScreenshot, screenshot_path, png_data)); |
158 } | 166 } |
159 #endif | 167 #endif |
160 | 168 |
161 bool AreScreenshotsDisabled() { | |
162 return g_browser_process->local_state()->GetBoolean( | |
163 prefs::kDisableScreenshots); | |
164 } | |
165 | |
166 bool GrabWindowSnapshot(aura::Window* window, | 169 bool GrabWindowSnapshot(aura::Window* window, |
167 const gfx::Rect& snapshot_bounds, | 170 const gfx::Rect& snapshot_bounds, |
168 std::vector<unsigned char>* png_data) { | 171 std::vector<unsigned char>* png_data) { |
169 #if defined(OS_LINUX) | 172 #if defined(OS_LINUX) |
170 // chrome::GrabWindowSnapshotForUser checks this too, but | 173 // chrome::GrabWindowSnapshotForUser checks this too, but |
171 // RootWindow::GrabSnapshot does not. | 174 // RootWindow::GrabSnapshot does not. |
172 if (AreScreenshotsDisabled()) | 175 if (AreScreenshotsDisabled()) |
173 return false; | 176 return false; |
174 | 177 |
175 // We use XGetImage() for Linux/ChromeOS for performance reasons. | 178 // We use XGetImage() for Linux/ChromeOS for performance reasons. |
176 // See crbug.com/119492 | 179 // See crbug.com/119492 |
177 // TODO(mukai): remove this when the performance issue has been fixed. | 180 // TODO(mukai): remove this when the performance issue has been fixed. |
178 if (window->GetRootWindow()->GrabSnapshot(snapshot_bounds, png_data)) | 181 if (window->GetRootWindow()->GrabSnapshot(snapshot_bounds, png_data)) |
179 return true; | 182 return true; |
180 #endif // OS_LINUX | 183 #endif // OS_LINUX |
181 | 184 |
182 return chrome::GrabWindowSnapshotForUser(window, png_data, snapshot_bounds); | 185 return chrome::GrabWindowSnapshotForUser(window, png_data, snapshot_bounds); |
183 } | 186 } |
184 | 187 |
185 // How opaque should the layer that we flash onscreen to provide visual | |
186 // feedback after the screenshot is taken be? | |
187 const float kVisualFeedbackLayerOpacity = 0.25f; | |
188 | |
189 // How long should the visual feedback layer be displayed? | |
190 const int64 kVisualFeedbackLayerDisplayTimeMs = 100; | |
191 | |
192 } // namespace | 188 } // namespace |
193 | 189 |
194 ScreenshotTaker::ScreenshotTaker() { | 190 ScreenshotTaker::ScreenshotTaker() { |
195 } | 191 } |
196 | 192 |
197 ScreenshotTaker::~ScreenshotTaker() { | 193 ScreenshotTaker::~ScreenshotTaker() { |
198 } | 194 } |
199 | 195 |
200 void ScreenshotTaker::HandleTakeScreenshot(aura::Window* window) { | 196 void ScreenshotTaker::HandleTakeScreenshotForAllRootWindows() { |
201 HandleTakePartialScreenshot(window, window->bounds()); | 197 FilePath screenshot_directory; |
198 if (!GetScreenshotDirectory(&screenshot_directory)) | |
199 return; | |
200 | |
201 std::string screenshot_basename = GetScreenshotBaseFilename(); | |
202 ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows(); | |
203 for (size_t i = 0; i < root_windows.size(); ++i) { | |
204 aura::RootWindow* root_window = root_windows[i]; | |
205 scoped_refptr<base::RefCountedBytes> png_data(new base::RefCountedBytes); | |
206 std::string basename = screenshot_basename; | |
207 gfx::Rect rect = root_window->bounds(); | |
208 if (root_windows.size() > 1) | |
209 basename += base::StringPrintf(" - %d", static_cast<int>(i + 1)); | |
210 if (GrabWindowSnapshot(root_window, rect, &png_data->data())) { | |
211 DisplayVisualFeedback(rect); | |
212 PostSaveScreenshotTask( | |
213 screenshot_directory.AppendASCII(basename + ".png"), png_data); | |
214 } else { | |
215 LOG(ERROR) << "Failed to grab the window screenshot for " << i; | |
216 } | |
217 } | |
218 last_screenshot_timestamp_ = base::Time::Now(); | |
202 } | 219 } |
203 | 220 |
204 void ScreenshotTaker::HandleTakePartialScreenshot( | 221 void ScreenshotTaker::HandleTakePartialScreenshot( |
205 aura::Window* window, const gfx::Rect& rect) { | 222 aura::Window* window, const gfx::Rect& rect) { |
206 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 223 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
207 | 224 |
208 if (AreScreenshotsDisabled()) | 225 FilePath screenshot_directory; |
226 if (!GetScreenshotDirectory(&screenshot_directory)) | |
209 return; | 227 return; |
210 | 228 |
211 scoped_refptr<base::RefCountedBytes> png_data(new base::RefCountedBytes); | 229 scoped_refptr<base::RefCountedBytes> png_data(new base::RefCountedBytes); |
212 | 230 |
213 bool is_logged_in = true; | |
214 #if defined(OS_CHROMEOS) | |
215 is_logged_in = chromeos::UserManager::Get()->IsUserLoggedIn(); | |
216 #endif | |
217 | |
218 FilePath screenshot_directory; | |
219 if (is_logged_in) { | |
220 DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext( | |
221 ash::Shell::GetInstance()->delegate()->GetCurrentBrowserContext()); | |
222 screenshot_directory = download_prefs->DownloadPath(); | |
223 } else { | |
224 if (!file_util::GetTempDir(&screenshot_directory)) { | |
225 LOG(ERROR) << "Failed to find temporary directory."; | |
226 return; | |
227 } | |
228 } | |
229 | |
230 if (GrabWindowSnapshot(window, rect, &png_data->data())) { | 231 if (GrabWindowSnapshot(window, rect, &png_data->data())) { |
231 last_screenshot_timestamp_ = base::Time::Now(); | 232 last_screenshot_timestamp_ = base::Time::Now(); |
232 DisplayVisualFeedback(rect); | 233 DisplayVisualFeedback(rect); |
233 PostSaveScreenshotTask(screenshot_directory, | 234 PostSaveScreenshotTask( |
234 GetScreenShotBaseFilename(ShouldUse24HourClock()), | 235 screenshot_directory.AppendASCII(GetScreenshotBaseFilename() + ".png"), |
235 png_data); | 236 png_data); |
236 } else { | 237 } else { |
237 LOG(ERROR) << "Failed to grab the window screenshot"; | 238 LOG(ERROR) << "Failed to grab the window screenshot"; |
238 } | 239 } |
239 } | 240 } |
240 | 241 |
241 bool ScreenshotTaker::CanTakeScreenshot() { | 242 bool ScreenshotTaker::CanTakeScreenshot() { |
242 return last_screenshot_timestamp_.is_null() || | 243 return last_screenshot_timestamp_.is_null() || |
243 base::Time::Now() - last_screenshot_timestamp_ > | 244 base::Time::Now() - last_screenshot_timestamp_ > |
244 base::TimeDelta::FromMilliseconds( | 245 base::TimeDelta::FromMilliseconds( |
245 kScreenshotMinimumIntervalInMS); | 246 kScreenshotMinimumIntervalInMS); |
(...skipping 14 matching lines...) Expand all Loading... | |
260 ash::internal::kShellWindowId_OverlayContainer)->layer(); | 261 ash::internal::kShellWindowId_OverlayContainer)->layer(); |
261 parent->Add(visual_feedback_layer_.get()); | 262 parent->Add(visual_feedback_layer_.get()); |
262 visual_feedback_layer_->SetVisible(true); | 263 visual_feedback_layer_->SetVisible(true); |
263 | 264 |
264 MessageLoopForUI::current()->PostDelayedTask( | 265 MessageLoopForUI::current()->PostDelayedTask( |
265 FROM_HERE, | 266 FROM_HERE, |
266 base::Bind(&ScreenshotTaker::CloseVisualFeedbackLayer, | 267 base::Bind(&ScreenshotTaker::CloseVisualFeedbackLayer, |
267 base::Unretained(this)), | 268 base::Unretained(this)), |
268 base::TimeDelta::FromMilliseconds(kVisualFeedbackLayerDisplayTimeMs)); | 269 base::TimeDelta::FromMilliseconds(kVisualFeedbackLayerDisplayTimeMs)); |
269 } | 270 } |
OLD | NEW |