Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / download / download_shelf.cc
blobd54141c2aa0b08ce9ca5f2eeba7e98e71bca20a2
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.
5 #define _USE_MATH_DEFINES // For VC++ to get M_PI. This has to be first.
7 #include "chrome/browser/download/download_shelf.h"
9 #include <cmath>
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "chrome/browser/download/download_item_model.h"
16 #include "chrome/browser/download/download_service.h"
17 #include "chrome/browser/download/download_service_factory.h"
18 #include "chrome/browser/download/download_started_animation.h"
19 #include "chrome/browser/platform_util.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/tabs/tab_strip_model.h"
23 #include "content/public/browser/browser_context.h"
24 #include "content/public/browser/download_item.h"
25 #include "content/public/browser/download_manager.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/web_contents_view.h"
28 #include "grit/locale_settings.h"
29 #include "grit/theme_resources.h"
30 #include "ui/base/l10n/l10n_util.h"
31 #include "ui/base/resource/resource_bundle.h"
32 #include "ui/gfx/animation/animation.h"
33 #include "ui/gfx/canvas.h"
34 #include "ui/gfx/image/image_skia.h"
36 #if defined(TOOLKIT_VIEWS)
37 #include "ui/views/view.h"
38 #endif
40 using content::DownloadItem;
42 namespace {
44 // Delay before we show a transient download.
45 const int64 kDownloadShowDelayInSeconds = 2;
47 // Get the opacity based on |animation_progress|, with values in [0.0, 1.0].
48 // Range of return value is [0, 255].
49 int GetOpacity(double animation_progress) {
50 DCHECK(animation_progress >= 0 && animation_progress <= 1);
52 // How many times to cycle the complete animation. This should be an odd
53 // number so that the animation ends faded out.
54 static const int kCompleteAnimationCycles = 5;
55 double temp = animation_progress * kCompleteAnimationCycles * M_PI + M_PI_2;
56 temp = sin(temp) / 2 + 0.5;
57 return static_cast<int>(255.0 * temp);
60 } // namespace
62 DownloadShelf::DownloadShelf()
63 : should_show_on_unhide_(false),
64 is_hidden_(false),
65 weak_ptr_factory_(this) {
68 DownloadShelf::~DownloadShelf() {}
70 // static
71 int DownloadShelf::GetBigProgressIconSize() {
72 static int big_progress_icon_size = 0;
73 if (big_progress_icon_size == 0) {
74 base::string16 locale_size_str =
75 l10n_util::GetStringUTF16(IDS_DOWNLOAD_BIG_PROGRESS_SIZE);
76 bool rc = base::StringToInt(locale_size_str, &big_progress_icon_size);
77 if (!rc || big_progress_icon_size < kBigProgressIconSize) {
78 NOTREACHED();
79 big_progress_icon_size = kBigProgressIconSize;
83 return big_progress_icon_size;
86 // static
87 int DownloadShelf::GetBigProgressIconOffset() {
88 return (GetBigProgressIconSize() - kBigIconSize) / 2;
91 // Download progress painting --------------------------------------------------
93 // Common images used for download progress animations. We load them once the
94 // first time we do a progress paint, then reuse them as they are always the
95 // same.
96 gfx::ImageSkia* g_foreground_16 = NULL;
97 gfx::ImageSkia* g_background_16 = NULL;
98 gfx::ImageSkia* g_foreground_32 = NULL;
99 gfx::ImageSkia* g_background_32 = NULL;
101 // static
102 void DownloadShelf::PaintCustomDownloadProgress(
103 gfx::Canvas* canvas,
104 const gfx::ImageSkia& background_image,
105 const gfx::ImageSkia& foreground_image,
106 int image_size,
107 const gfx::Rect& bounds,
108 int start_angle,
109 int percent_done) {
110 // Draw the background progress image.
111 canvas->DrawImageInt(background_image,
112 bounds.x(),
113 bounds.y());
115 // Layer the foreground progress image in an arc proportional to the download
116 // progress. The arc grows clockwise, starting in the midnight position, as
117 // the download progresses. However, if the download does not have known total
118 // size (the server didn't give us one), then we just spin an arc around until
119 // we're done.
120 float sweep_angle = 0.0;
121 float start_pos = static_cast<float>(kStartAngleDegrees);
122 if (percent_done < 0) {
123 sweep_angle = kUnknownAngleDegrees;
124 start_pos = static_cast<float>(start_angle);
125 } else if (percent_done > 0) {
126 sweep_angle = static_cast<float>(kMaxDegrees / 100.0 * percent_done);
129 // Set up an arc clipping region for the foreground image. Don't bother using
130 // a clipping region if it would round to 360 (really 0) degrees, since that
131 // would eliminate the foreground completely and be quite confusing (it would
132 // look like 0% complete when it should be almost 100%).
133 canvas->Save();
134 if (sweep_angle < static_cast<float>(kMaxDegrees - 1)) {
135 SkRect oval;
136 oval.set(SkIntToScalar(bounds.x()),
137 SkIntToScalar(bounds.y()),
138 SkIntToScalar(bounds.x() + image_size),
139 SkIntToScalar(bounds.y() + image_size));
140 SkPath path;
141 path.arcTo(oval,
142 SkFloatToScalar(start_pos),
143 SkFloatToScalar(sweep_angle), false);
144 path.lineTo(SkIntToScalar(bounds.x() + image_size / 2),
145 SkIntToScalar(bounds.y() + image_size / 2));
147 // gfx::Canvas::ClipPath does not provide for anti-aliasing.
148 canvas->sk_canvas()->clipPath(path, SkRegion::kIntersect_Op, true);
151 canvas->DrawImageInt(foreground_image,
152 bounds.x(),
153 bounds.y());
154 canvas->Restore();
157 // static
158 void DownloadShelf::PaintDownloadProgress(gfx::Canvas* canvas,
159 #if defined(TOOLKIT_VIEWS)
160 views::View* containing_view,
161 #endif
162 int origin_x,
163 int origin_y,
164 int start_angle,
165 int percent_done,
166 PaintDownloadProgressSize size) {
167 // Load up our common images.
168 if (!g_background_16) {
169 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
170 g_foreground_16 = rb.GetImageSkiaNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_16);
171 g_background_16 = rb.GetImageSkiaNamed(IDR_DOWNLOAD_PROGRESS_BACKGROUND_16);
172 g_foreground_32 = rb.GetImageSkiaNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_32);
173 g_background_32 = rb.GetImageSkiaNamed(IDR_DOWNLOAD_PROGRESS_BACKGROUND_32);
174 DCHECK_EQ(g_foreground_16->width(), g_background_16->width());
175 DCHECK_EQ(g_foreground_16->height(), g_background_16->height());
176 DCHECK_EQ(g_foreground_32->width(), g_background_32->width());
177 DCHECK_EQ(g_foreground_32->height(), g_background_32->height());
180 gfx::ImageSkia* background =
181 (size == BIG) ? g_background_32 : g_background_16;
182 gfx::ImageSkia* foreground =
183 (size == BIG) ? g_foreground_32 : g_foreground_16;
185 const int kProgressIconSize =
186 (size == BIG) ? kBigProgressIconSize : kSmallProgressIconSize;
188 // We start by storing the bounds of the images so that it is easy to mirror
189 // the bounds if the UI layout is RTL.
190 gfx::Rect bounds(origin_x, origin_y,
191 background->width(), background->height());
193 #if defined(TOOLKIT_VIEWS)
194 // Mirror the positions if necessary.
195 int mirrored_x = containing_view->GetMirroredXForRect(bounds);
196 bounds.set_x(mirrored_x);
197 #endif
199 // Draw the background progress image.
200 canvas->DrawImageInt(*background,
201 bounds.x(),
202 bounds.y());
204 PaintCustomDownloadProgress(canvas,
205 *background,
206 *foreground,
207 kProgressIconSize,
208 bounds,
209 start_angle,
210 percent_done);
213 // static
214 void DownloadShelf::PaintDownloadComplete(gfx::Canvas* canvas,
215 #if defined(TOOLKIT_VIEWS)
216 views::View* containing_view,
217 #endif
218 int origin_x,
219 int origin_y,
220 double animation_progress,
221 PaintDownloadProgressSize size) {
222 // Load up our common images.
223 if (!g_foreground_16) {
224 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
225 g_foreground_16 = rb.GetImageSkiaNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_16);
226 g_foreground_32 = rb.GetImageSkiaNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_32);
229 gfx::ImageSkia* complete = (size == BIG) ? g_foreground_32 : g_foreground_16;
231 gfx::Rect complete_bounds(origin_x, origin_y,
232 complete->width(), complete->height());
233 #if defined(TOOLKIT_VIEWS)
234 // Mirror the positions if necessary.
235 complete_bounds.set_x(containing_view->GetMirroredXForRect(complete_bounds));
236 #endif
238 // Start at full opacity, then loop back and forth five times before ending
239 // at zero opacity.
240 canvas->DrawImageInt(*complete, complete_bounds.x(), complete_bounds.y(),
241 GetOpacity(animation_progress));
244 // static
245 void DownloadShelf::PaintDownloadInterrupted(gfx::Canvas* canvas,
246 #if defined(TOOLKIT_VIEWS)
247 views::View* containing_view,
248 #endif
249 int origin_x,
250 int origin_y,
251 double animation_progress,
252 PaintDownloadProgressSize size) {
253 // Load up our common images.
254 if (!g_foreground_16) {
255 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
256 g_foreground_16 = rb.GetImageSkiaNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_16);
257 g_foreground_32 = rb.GetImageSkiaNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_32);
260 gfx::ImageSkia* complete = (size == BIG) ? g_foreground_32 : g_foreground_16;
262 gfx::Rect complete_bounds(origin_x, origin_y,
263 complete->width(), complete->height());
264 #if defined(TOOLKIT_VIEWS)
265 // Mirror the positions if necessary.
266 complete_bounds.set_x(containing_view->GetMirroredXForRect(complete_bounds));
267 #endif
269 // Start at zero opacity, then loop back and forth five times before ending
270 // at full opacity.
271 canvas->DrawImageInt(*complete, complete_bounds.x(), complete_bounds.y(),
272 GetOpacity(1.0 - animation_progress));
275 void DownloadShelf::AddDownload(DownloadItem* download) {
276 DCHECK(download);
277 if (DownloadItemModel(download).ShouldRemoveFromShelfWhenComplete()) {
278 // If we are going to remove the download from the shelf upon completion,
279 // wait a few seconds to see if it completes quickly. If it's a small
280 // download, then the user won't have time to interact with it.
281 base::MessageLoop::current()->PostDelayedTask(
282 FROM_HERE,
283 base::Bind(&DownloadShelf::ShowDownloadById,
284 weak_ptr_factory_.GetWeakPtr(),
285 download->GetId()),
286 GetTransientDownloadShowDelay());
287 } else {
288 ShowDownload(download);
292 void DownloadShelf::Show() {
293 if (is_hidden_) {
294 should_show_on_unhide_ = true;
295 return;
297 DoShow();
300 void DownloadShelf::Close(CloseReason reason) {
301 if (is_hidden_) {
302 should_show_on_unhide_ = false;
303 return;
305 DoClose(reason);
308 void DownloadShelf::Hide() {
309 if (is_hidden_)
310 return;
311 is_hidden_ = true;
312 if (IsShowing()) {
313 should_show_on_unhide_ = true;
314 DoClose(AUTOMATIC);
318 void DownloadShelf::Unhide() {
319 if (!is_hidden_)
320 return;
321 is_hidden_ = false;
322 if (should_show_on_unhide_) {
323 should_show_on_unhide_ = false;
324 DoShow();
328 base::TimeDelta DownloadShelf::GetTransientDownloadShowDelay() {
329 return base::TimeDelta::FromSeconds(kDownloadShowDelayInSeconds);
332 content::DownloadManager* DownloadShelf::GetDownloadManager() {
333 return content::BrowserContext::GetDownloadManager(browser()->profile());
336 void DownloadShelf::ShowDownload(DownloadItem* download) {
337 if (download->GetState() == DownloadItem::COMPLETE &&
338 DownloadItemModel(download).ShouldRemoveFromShelfWhenComplete())
339 return;
340 if (!DownloadServiceFactory::GetForBrowserContext(
341 download->GetBrowserContext())->IsShelfEnabled())
342 return;
344 if (is_hidden_)
345 Unhide();
346 Show();
347 DoAddDownload(download);
349 // browser() can be NULL for tests.
350 if (!browser())
351 return;
353 // Show the download started animation if:
354 // - Download started animation is enabled for this download. It is disabled
355 // for "Save As" downloads and extension installs, for example.
356 // - The browser has an active visible WebContents. (browser isn't minimized,
357 // or running under a test etc.)
358 // - Rich animations are enabled.
359 content::WebContents* shelf_tab =
360 browser()->tab_strip_model()->GetActiveWebContents();
361 if (DownloadItemModel(download).ShouldShowDownloadStartedAnimation() &&
362 shelf_tab &&
363 platform_util::IsVisible(shelf_tab->GetView()->GetNativeView()) &&
364 gfx::Animation::ShouldRenderRichAnimation()) {
365 DownloadStartedAnimation::Show(shelf_tab);
369 void DownloadShelf::ShowDownloadById(int32 download_id) {
370 content::DownloadManager* download_manager = GetDownloadManager();
371 if (!download_manager)
372 return;
374 DownloadItem* download = download_manager->GetDownload(download_id);
375 if (!download)
376 return;
378 ShowDownload(download);