diff options
author | avi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-12 02:25:13 +0000 |
---|---|---|
committer | avi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-12 02:25:13 +0000 |
commit | 27ee16ff2b9370d70fb93233b73ec01856f08017 (patch) | |
tree | 0ee062b116b1b7ab29d9546b37a2ec3cc461e3b5 /chrome | |
parent | d2a0e1ab98ab2e01fdb2cb77387e917c1d57b4c8 (diff) | |
download | chromium_src-27ee16ff2b9370d70fb93233b73ec01856f08017.zip chromium_src-27ee16ff2b9370d70fb93233b73ec01856f08017.tar.gz chromium_src-27ee16ff2b9370d70fb93233b73ec01856f08017.tar.bz2 |
Unify and clean up system download notifications, add Mountain Lion support.
BUG=138962
TEST=as in bug
Review URL: https://chromiumcodereview.appspot.com/10827207
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151195 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/download/chrome_download_manager_delegate.cc | 9 | ||||
-rw-r--r-- | chrome/browser/download/download_completion_observer_win.cc | 111 | ||||
-rw-r--r-- | chrome/browser/download/download_completion_observer_win.h | 42 | ||||
-rw-r--r-- | chrome/browser/download/download_status_updater.cc | 33 | ||||
-rw-r--r-- | chrome/browser/download/download_status_updater.h | 12 | ||||
-rw-r--r-- | chrome/browser/download/download_status_updater_gtk.cc | 16 | ||||
-rw-r--r-- | chrome/browser/download/download_status_updater_mac.mm | 287 | ||||
-rw-r--r-- | chrome/browser/download/download_status_updater_unittest.cc | 85 | ||||
-rw-r--r-- | chrome/browser/download/download_status_updater_win.cc | 123 | ||||
-rw-r--r-- | chrome/browser/download/download_util.cc | 58 | ||||
-rw-r--r-- | chrome/browser/download/download_util.h | 9 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/download/download_item_mac.mm | 2 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/download/download_util_mac.h | 4 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/download/download_util_mac.mm | 24 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 5 |
15 files changed, 526 insertions, 294 deletions
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc index 27e5988..3a909c7 100644 --- a/chrome/browser/download/chrome_download_manager_delegate.cc +++ b/chrome/browser/download/chrome_download_manager_delegate.cc @@ -60,10 +60,6 @@ #include "chrome/browser/download/save_package_file_picker_chromeos.h" #endif -#if defined(OS_WIN) -#include "chrome/browser/download/download_completion_observer_win.h" -#endif // OS_WIN - using content::BrowserContext; using content::BrowserThread; using content::DownloadId; @@ -143,11 +139,6 @@ void ChromeDownloadManagerDelegate::SetDownloadManager(DownloadManager* dm) { extension_event_router_.reset(new ExtensionDownloadsEventRouter( profile_, download_manager_)); #endif - -#if defined(OS_WIN) - DownloadCompletionObserver* download_completion = - new DownloadCompletionObserver(dm); -#endif // OS_WIN } void ChromeDownloadManagerDelegate::Shutdown() { diff --git a/chrome/browser/download/download_completion_observer_win.cc b/chrome/browser/download/download_completion_observer_win.cc deleted file mode 100644 index 809e896..0000000 --- a/chrome/browser/download/download_completion_observer_win.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/download/download_completion_observer_win.h" - -#include <string> - -#include "base/logging.h" -#include "base/stl_util.h" -#include "base/string_number_conversions.h" -#include "base/win/metro.h" -#include "googleurl/src/gurl.h" -#include "grit/generated_resources.h" -#include "ui/base/l10n/l10n_util.h" - -using content::DownloadItem; -using content::DownloadManager; - -static const char kDownloadNotificationPrefix[] = "DownloadNotification"; -int g_next_notification_id = 0; - -DownloadCompletionObserver::DownloadCompletionObserver( - DownloadManager* manager) { - manager->AddObserver(this); -} - -DownloadCompletionObserver::~DownloadCompletionObserver() { - DCHECK(download_items_.empty()); -} - -void DownloadCompletionObserver::OnDownloadCreated(DownloadManager* manager, - DownloadItem* download) { - if (download->IsInProgress()) { - download_items_.insert(download); - download->AddObserver(this); - } -} - -void DownloadCompletionObserver::ManagerGoingDown(DownloadManager* manager) { - ClearDownloadItems(); - manager->RemoveObserver(this); - delete this; -} - -void DownloadCompletionObserver::OnDownloadUpdated(DownloadItem* download) { - switch (download->GetState()) { - case DownloadItem::COMPLETE: { - if (base::win::IsMetroProcess() && - !download->GetOpenWhenComplete() && - !download->ShouldOpenFileBasedOnExtension() && - !download->IsTemporary() && - !download->GetAutoOpened()) { - // In Windows 8 metro mode display a metro style notification which - // informs the user that the download is complete. - HMODULE metro = base::win::GetMetroModule(); - base::win::MetroNotification display_notification = - reinterpret_cast< base::win::MetroNotification>( - ::GetProcAddress(metro, "DisplayNotification")); - DCHECK(display_notification); - if (display_notification) { - string16 title = l10n_util::GetStringUTF16( - IDS_METRO_DOWNLOAD_COMPLETE_NOTIFICATION_TITLE); - string16 body = l10n_util::GetStringUTF16( - IDS_METRO_DOWNLOAD_COMPLETE_NOTIFICATION); - - // Dummy notification id. Every metro style notification needs a - // unique notification id. - std::string notification_id = kDownloadNotificationPrefix; - notification_id += base::IntToString(g_next_notification_id++); - - display_notification(download->GetURL().spec().c_str(), - "", - title.c_str(), - body.c_str(), - L"", - notification_id.c_str()); - } - } - DCHECK(ContainsKey(download_items_, download)); - download_items_.erase(download); - download->RemoveObserver(this); - break; - } - - case DownloadItem::INTERRUPTED: - case DownloadItem::CANCELLED: { - DCHECK(ContainsKey(download_items_, download)); - download_items_.erase(download); - download->RemoveObserver(this); - break; - } - - default: - break; - } -} - -void DownloadCompletionObserver::OnDownloadDestroyed(DownloadItem* download) { - DCHECK(ContainsKey(download_items_, download)); - download_items_.erase(download); - download->RemoveObserver(this); -} - -void DownloadCompletionObserver::ClearDownloadItems() { - for (std::set<DownloadItem*>::iterator it = download_items_.begin(); - it != download_items_.end(); ++it) { - (*it)->RemoveObserver(this); - } - download_items_.clear(); -} diff --git a/chrome/browser/download/download_completion_observer_win.h b/chrome/browser/download/download_completion_observer_win.h deleted file mode 100644 index ceca491..0000000 --- a/chrome/browser/download/download_completion_observer_win.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_COMPLETION_OBSERVER_WIN_H_ -#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_COMPLETION_OBSERVER_WIN_H_ - -#include <set> - -#include "base/basictypes.h" -#include "content/public/browser/download_item.h" -#include "content/public/browser/download_manager.h" - -// Tracks download item completions and notifies any interested parties -// (Windows 8 metro) about the same. On Windows 8 we display metro style -// notifications when a download completes. -class DownloadCompletionObserver - : public content::DownloadManager::Observer, - public content::DownloadItem::Observer { - public: - explicit DownloadCompletionObserver(content::DownloadManager* manager); - ~DownloadCompletionObserver(); - - // Methods inherited from content::DownloadManager::Observer. - virtual void OnDownloadCreated( - content::DownloadManager* manager, content::DownloadItem* download) - OVERRIDE; - virtual void ManagerGoingDown(content::DownloadManager* manager) OVERRIDE; - - // Methods inherited from content::DownloadItem::Observer. - virtual void OnDownloadUpdated(content::DownloadItem* download) OVERRIDE; - virtual void OnDownloadDestroyed(content::DownloadItem* download) OVERRIDE; - - private: - void ClearDownloadItems(); - - std::set<content::DownloadItem*> download_items_; - - DISALLOW_COPY_AND_ASSIGN(DownloadCompletionObserver); -}; - -#endif // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_COMPLETION_OBSERVER_WIN_H_ diff --git a/chrome/browser/download/download_status_updater.cc b/chrome/browser/download/download_status_updater.cc index a8dde9b..489d3be 100644 --- a/chrome/browser/download/download_status_updater.cc +++ b/chrome/browser/download/download_status_updater.cc @@ -55,12 +55,19 @@ void DownloadStatusUpdater::ModelChanged(content::DownloadManager* manager) { std::vector<content::DownloadItem*> downloads; manager->SearchDownloads(string16(), &downloads); + std::vector<content::DownloadItem*> added_downloads; for (std::vector<content::DownloadItem*>::iterator it = downloads.begin(); it != downloads.end(); ++it) { - UpdateItem(*it); + if (UpdateItem(*it)) + added_downloads.push_back(*it); } - UpdateAppIconDownloadProgress(); + for (std::vector<content::DownloadItem*>::iterator it = + added_downloads.begin(); + it != added_downloads.end(); + ++it) { + UpdateAppIconDownloadProgress(*it); + } } void DownloadStatusUpdater::ManagerGoingDown( @@ -74,7 +81,7 @@ void DownloadStatusUpdater::ManagerGoingDown( void DownloadStatusUpdater::OnDownloadUpdated( content::DownloadItem* download) { UpdateItem(download); - UpdateAppIconDownloadProgress(); + UpdateAppIconDownloadProgress(download); } void DownloadStatusUpdater::OnDownloadDestroyed( @@ -83,30 +90,30 @@ void DownloadStatusUpdater::OnDownloadDestroyed( items_.erase(download); download->RemoveObserver(this); } - UpdateAppIconDownloadProgress(); + UpdateAppIconDownloadProgress(download); } void DownloadStatusUpdater::OnDownloadOpened(content::DownloadItem* download) { } -void DownloadStatusUpdater::UpdateAppIconDownloadProgress() { - float progress = 0; - int download_count = 0; - bool progress_known = GetProgress(&progress, &download_count); - download_util::UpdateAppIconDownloadProgress(download_count, - progress_known, - progress); +#if defined(USE_AURA) || defined(OS_ANDROID) +void DownloadStatusUpdater::UpdateAppIconDownloadProgress( + content::DownloadItem* download) { + // TODO(davemoore): Implement once UX for aura download is decided <104742> + // TODO(avi): Implement for Android? } +#endif // React to a transition that a download associated with one of our // download managers has made. Our goal is to have only IN_PROGRESS // items on our set list, as they're the only ones that have relevance // to GetProgress() return values. -void DownloadStatusUpdater::UpdateItem(content::DownloadItem* download) { +bool DownloadStatusUpdater::UpdateItem(content::DownloadItem* download) { if (download->GetState() == content::DownloadItem::IN_PROGRESS) { if (!ContainsKey(items_, download)) { items_.insert(download); download->AddObserver(this); + return true; } } else { if (ContainsKey(items_, download)) { @@ -114,4 +121,6 @@ void DownloadStatusUpdater::UpdateItem(content::DownloadItem* download) { download->RemoveObserver(this); } } + + return false; } diff --git a/chrome/browser/download/download_status_updater.h b/chrome/browser/download/download_status_updater.h index 252be03..75218c5 100644 --- a/chrome/browser/download/download_status_updater.h +++ b/chrome/browser/download/download_status_updater.h @@ -42,12 +42,16 @@ class DownloadStatusUpdater virtual void OnDownloadDestroyed(content::DownloadItem* download) OVERRIDE; protected: - // Update the app icon. Virtual to be overridable for testing. - virtual void UpdateAppIconDownloadProgress(); + // Platform-specific function to update the platform UI for download progress. + // |download| is the download item that changed. Implementations should not + // hold the value of |download| as it is not guaranteed to remain valid. + // Virtual to be overridable for testing. + virtual void UpdateAppIconDownloadProgress(content::DownloadItem* download); private: - // Update the internal state tracking an item. - void UpdateItem(content::DownloadItem* download); + // Update the internal state tracking an item. Returns true if the item was + // added to the set of tracked items. + bool UpdateItem(content::DownloadItem* download); std::set<content::DownloadManager*> managers_; std::set<content::DownloadItem*> items_; diff --git a/chrome/browser/download/download_status_updater_gtk.cc b/chrome/browser/download/download_status_updater_gtk.cc new file mode 100644 index 0000000..6f045fff76 --- /dev/null +++ b/chrome/browser/download/download_status_updater_gtk.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/download/download_status_updater.h" + +#include "chrome/browser/ui/gtk/unity_service.h" + +void DownloadStatusUpdater::UpdateAppIconDownloadProgress( + content::DownloadItem* download) { + float progress = 0; + int download_count = 0; + GetProgress(&progress, &download_count); + unity::SetDownloadCount(download_count); + unity::SetProgressFraction(progress); +} diff --git a/chrome/browser/download/download_status_updater_mac.mm b/chrome/browser/download/download_status_updater_mac.mm new file mode 100644 index 0000000..240f7eb --- /dev/null +++ b/chrome/browser/download/download_status_updater_mac.mm @@ -0,0 +1,287 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/download/download_status_updater.h" + +#include "base/mac/foundation_util.h" +#include "base/memory/scoped_nsobject.h" +#include "base/supports_user_data.h" +#include "base/sys_string_conversions.h" +#include "content/public/browser/download_item.h" +#import "chrome/browser/ui/cocoa/dock_icon.h" +#include "googleurl/src/gurl.h" + +// --- Private 10.8 API for showing progress --- +// rdar://12058866 http://www.openradar.me/12058866 + +namespace { + +NSString* const kNSProgressAppBundleIdentifierKey = + @"NSProgressAppBundleIdentifierKey"; +NSString* const kNSProgressEstimatedTimeKey = + @"NSProgressEstimatedTimeKey"; +NSString* const kNSProgressFileCompletedCountKey = + @"NSProgressFileCompletedCountKey"; +NSString* const kNSProgressFileContainerURLKey = + @"NSProgressFileContainerURLKey"; +NSString* const kNSProgressFileDownloadingSourceURLKey = + @"NSProgressFileDownloadingSourceURLKey"; +NSString* const kNSProgressFileIconKey = + @"NSProgressFileIconKey"; +NSString* const kNSProgressFileIconOriginalRectKey = + @"NSProgressFileIconOriginalRectKey"; +NSString* const kNSProgressFileLocationCanChangeKey = + @"NSProgressFileLocationCanChangeKey"; +NSString* const kNSProgressFileOperationKindAirDropping = + @"NSProgressFileOperationKindAirDropping"; +NSString* const kNSProgressFileOperationKindCopying = + @"NSProgressFileOperationKindCopying"; +NSString* const kNSProgressFileOperationKindDecompressingAfterDownloading = + @"NSProgressFileOperationKindDecompressingAfterDownloading"; +NSString* const kNSProgressFileOperationKindDownloading = + @"NSProgressFileOperationKindDownloading"; +NSString* const kNSProgressFileOperationKindEncrypting = + @"NSProgressFileOperationKindEncrypting"; +NSString* const kNSProgressFileOperationKindKey = + @"NSProgressFileOperationKindKey"; +NSString* const kNSProgressFileTotalCountKey = + @"NSProgressFileTotalCountKey"; +NSString* const kNSProgressFileURLKey = + @"NSProgressFileURLKey"; +NSString* const kNSProgressIsWaitingKey = + @"NSProgressIsWaitingKey"; +NSString* const kNSProgressKindFile = + @"NSProgressKindFile"; +NSString* const kNSProgressThroughputKey = + @"NSProgressThroughputKey"; + +NSString* ProgressString(NSString* string) { + static NSMutableDictionary* cache; + static CFBundleRef foundation; + if (!cache) { + cache = [[NSMutableDictionary alloc] init]; + foundation = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Foundation")); + } + + NSString* result = [cache objectForKey:string]; + if (!result) { + NSString** ref = static_cast<NSString**>( + CFBundleGetDataPointerForName(foundation, + base::mac::NSToCFCast(string))); + if (ref) { + result = *ref; + [cache setObject:result forKey:string]; + } + } + + return result; +} + +} // namespace + +@interface NSProgress : NSObject + +- (id)initWithParent:(id)parent userInfo:(NSDictionary*)info; +@property(copy) NSString* kind; + +- (void)unpublish; +- (void)publish; + +- (void)setUserInfoObject:(id)object forKey:(NSString*)key; +- (NSDictionary*)userInfo; + +@property(readonly) double fractionCompleted; +// Set the totalUnitCount to -1 to indicate an indeterminate download. The dock +// shows a non-filling progress bar; the Finder is lame and draws its progress +// bar off the right side. +@property(readonly, getter=isIndeterminate) BOOL indeterminate; +@property long long completedUnitCount; +@property long long totalUnitCount; + +// Pausing appears to be unimplemented in 10.8.0. +- (void)pause; +@property(readonly, getter=isPaused) BOOL paused; +@property(getter=isPausable) BOOL pausable; +- (void)setPausingHandler:(id)blockOfUnknownSignature; + +- (void)cancel; +@property(readonly, getter=isCancelled) BOOL cancelled; +@property(getter=isCancellable) BOOL cancellable; +// Note that the cancellation handler block will be called on a random thread. +- (void)setCancellationHandler:(void (^)())block; + +// Allows other applications to provide feedback as to whether the progress is +// visible in that app. Note that the acknowledgement handler block will be +// called on a random thread. +// com.apple.dock => BOOL indicating whether the download target folder was +// successfully "flown to" at the beginning of the download. +// This primarily depends on whether the download target +// folder is in the dock. Note that if the download target +// folder is added or removed from the dock during the +// duration of the download, it will not trigger a callback. +// Note that if the "fly to the dock" keys were not set, the +// callback's parameter will always be NO. +// com.apple.Finder => always YES, no matter whether the download target +// folder's window is open. +- (void)handleAcknowledgementByAppWithBundleIdentifier:(NSString*)bundle + usingBlock:(void (^)(BOOL success))block; + +@end + +// --- Private 10.8 API for showing progress --- + +namespace { + +bool NSProgressSupported() { + static bool supported; + static bool valid; + if (!valid) { + supported = NSClassFromString(@"NSProgress"); + valid = true; + } + + return supported; +} + +const char kCrNSProgressUserDataKey[] = "CrNSProgressUserData"; + +class CrNSProgressUserData : public base::SupportsUserData::Data { + public: + CrNSProgressUserData(NSProgress* progress, const FilePath& target) + : target_(target) { + progress_.reset(progress); + } + virtual ~CrNSProgressUserData() {} + + NSProgress* progress() const { return progress_.get(); } + FilePath target() const { return target_; } + void setTarget(const FilePath& target) { target_ = target; } + + private: + scoped_nsobject<NSProgress> progress_; + FilePath target_; +}; + +void UpdateAppIcon(int download_count, + bool progress_known, + float progress) { + DockIcon* dock_icon = [DockIcon sharedDockIcon]; + [dock_icon setDownloads:download_count]; + [dock_icon setIndeterminate:!progress_known]; + [dock_icon setProgress:progress]; + [dock_icon updateIcon]; +} + +void CreateNSProgress(content::DownloadItem* download) { + NSURL* source_url = [NSURL URLWithString: + base::SysUTF8ToNSString(download->GetURL().spec())]; + FilePath destination_path = download->GetFullPath(); + NSURL* destination_url = [NSURL fileURLWithPath: + base::mac::FilePathToNSString(destination_path)]; + + // If there were an image to fly to the download folder in the dock, then + // the keys in the userInfo to set would be: + // - @"NSProgressFlyToImageKey" : NSImage + // - kNSProgressFileIconOriginalRectKey : NSValue of NSRect in global coords + + NSDictionary* user_info = @{ + ProgressString(kNSProgressFileDownloadingSourceURLKey) : source_url, + ProgressString(kNSProgressFileLocationCanChangeKey) : @true, + ProgressString(kNSProgressFileOperationKindKey) : + ProgressString(kNSProgressFileOperationKindDownloading), + ProgressString(kNSProgressFileURLKey) : destination_url + }; + Class progress_class = NSClassFromString(@"NSProgress"); + NSProgress* progress = [progress_class performSelector:@selector(alloc)]; + progress = [progress performSelector:@selector(initWithParent:userInfo:) + withObject:nil + withObject:user_info]; + progress.kind = ProgressString(kNSProgressKindFile); + + progress.pausable = NO; + progress.cancellable = YES; + [progress setCancellationHandler:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + download->Cancel(true); + }); + }]; + + progress.totalUnitCount = download->GetTotalBytes(); + progress.completedUnitCount = download->GetReceivedBytes(); + + [progress publish]; + + download->SetUserData(&kCrNSProgressUserDataKey, + new CrNSProgressUserData(progress, destination_path)); +} + +void UpdateNSProgress(content::DownloadItem* download, + CrNSProgressUserData* progress_data) { + NSProgress* progress = progress_data->progress(); + progress.totalUnitCount = download->GetTotalBytes(); + progress.completedUnitCount = download->GetReceivedBytes(); + + FilePath download_path = download->GetFullPath(); + if (progress_data->target() != download_path) { + progress_data->setTarget(download_path); + NSURL* download_url = [NSURL fileURLWithPath: + base::mac::FilePathToNSString(download_path)]; + [progress setUserInfoObject:download_url + forKey:ProgressString(kNSProgressFileURLKey)]; + } +} + +void DestroyNSProgress(content::DownloadItem* download, + CrNSProgressUserData* progress_data) { + NSProgress* progress = progress_data->progress(); + [progress unpublish]; + + download->RemoveUserData(&kCrNSProgressUserDataKey); +} + +} // namespace + +void DownloadStatusUpdater::UpdateAppIconDownloadProgress( + content::DownloadItem* download) { + + // Always update overall progress. + + float progress = 0; + int download_count = 0; + bool progress_known = GetProgress(&progress, &download_count); + UpdateAppIcon(download_count, progress_known, progress); + + // Update NSProgress-based indicators. + + if (NSProgressSupported()) { + CrNSProgressUserData* progress_data = static_cast<CrNSProgressUserData*>( + download->GetUserData(&kCrNSProgressUserDataKey)); + if (!progress_data) + CreateNSProgress(download); + else + UpdateNSProgress(download, progress_data); + + if (download->GetState() != content::DownloadItem::IN_PROGRESS) + DestroyNSProgress(download, progress_data); + } + + // Handle downloads that ended. + if (download->GetState() != content::DownloadItem::IN_PROGRESS) { + NSString* download_path = + base::mac::FilePathToNSString(download->GetFullPath()); + if (download->GetState() == content::DownloadItem::COMPLETE) { + // Bounce the dock icon. + [[NSDistributedNotificationCenter defaultCenter] + postNotificationName:@"com.apple.DownloadFileFinished" + object:download_path]; + } + + // Notify the Finder. + NSString* parent_path = [download_path stringByDeletingLastPathComponent]; + FNNotifyByPath( + reinterpret_cast<const UInt8*>([parent_path fileSystemRepresentation]), + kFNDirectoryModifiedMessage, + kNilOptions); + } +} diff --git a/chrome/browser/download/download_status_updater_unittest.cc b/chrome/browser/download/download_status_updater_unittest.cc index 6cc7ec03..7a36124 100644 --- a/chrome/browser/download/download_status_updater_unittest.cc +++ b/chrome/browser/download/download_status_updater_unittest.cc @@ -13,7 +13,6 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" - using ::testing::AtLeast; using ::testing::Mock; using ::testing::Return; @@ -22,10 +21,26 @@ using ::testing::StrictMock; using ::testing::_; class TestDownloadStatusUpdater : public DownloadStatusUpdater { + public: + TestDownloadStatusUpdater() : notification_count_(0), + acceptable_notification_item_(NULL) { + } + void SetAcceptableNotificationItem(content::DownloadItem* item) { + acceptable_notification_item_ = item; + } + size_t NotificationCount() { + return notification_count_; + } protected: - virtual void UpdateAppIconDownloadProgress() OVERRIDE { - return; + virtual void UpdateAppIconDownloadProgress( + content::DownloadItem* download) OVERRIDE { + ++notification_count_; + if (acceptable_notification_item_) + EXPECT_EQ(acceptable_notification_item_, download); } + private: + size_t notification_count_; + content::DownloadItem* acceptable_notification_item_; }; class DownloadStatusUpdaterTest : public testing::Test { @@ -120,13 +135,16 @@ class DownloadStatusUpdaterTest : public testing::Test { } // Set return values relevant to |DownloadStatusUpdater::GetProgress()| - // for the specified item + // for the specified item. void SetItemValues(int manager_index, int item_index, - int received_bytes, int total_bytes) { - EXPECT_CALL(*Item(manager_index, item_index), GetReceivedBytes()) + int received_bytes, int total_bytes, bool notify) { + content::MockDownloadItem* item(Item(manager_index, item_index)); + EXPECT_CALL(*item, GetReceivedBytes()) .WillRepeatedly(Return(received_bytes)); - EXPECT_CALL(*Item(manager_index, item_index), GetTotalBytes()) + EXPECT_CALL(*item, GetTotalBytes()) .WillRepeatedly(Return(total_bytes)); + if (notify) + updater_->OnDownloadUpdated(item); } // Transition specified item to completed. @@ -157,7 +175,7 @@ class DownloadStatusUpdaterTest : public testing::Test { // Pointer so we can verify that destruction triggers appropriate // changes. - DownloadStatusUpdater *updater_; + TestDownloadStatusUpdater *updater_; // Thread so that the DownloadManager (which is a DeleteOnUIThread // object) can be deleted. @@ -198,9 +216,9 @@ TEST_F(DownloadStatusUpdaterTest, OneManagerManyItems) { LinkManager(0); // Prime items - SetItemValues(0, 0, 10, 20); - SetItemValues(0, 1, 50, 60); - SetItemValues(0, 2, 90, 90); + SetItemValues(0, 0, 10, 20, false); + SetItemValues(0, 1, 50, 60, false); + SetItemValues(0, 2, 90, 90, false); float progress = -1; int download_count = -1; @@ -218,13 +236,46 @@ TEST_F(DownloadStatusUpdaterTest, OneManagerManyItems) { // Add a new item to manager and confirm progress is updated properly. AddItems(0, 1, 1); updater_->ModelChanged(Manager(0)); - SetItemValues(0, 3, 150, 200); + SetItemValues(0, 3, 150, 200, false); EXPECT_TRUE(updater_->GetProgress(&progress, &download_count)); EXPECT_FLOAT_EQ((50+150)/(60+200.0f), progress); EXPECT_EQ(2, download_count); } +// Test to ensure that the download progress notification is called correctly. +TEST_F(DownloadStatusUpdaterTest, ProgressNotification) { + size_t expected_notifications = updater_->NotificationCount(); + SetupManagers(1); + AddItems(0, 2, 2); + LinkManager(0); + + // Expect two notifications, one for each item; which item will come first + // isn't defined so it cannot be tested. + expected_notifications += 2; + ASSERT_EQ(expected_notifications, updater_->NotificationCount()); + + // Make progress on the first item. + updater_->SetAcceptableNotificationItem(Item(0, 0)); + SetItemValues(0, 0, 10, 20, true); + ++expected_notifications; + ASSERT_EQ(expected_notifications, updater_->NotificationCount()); + + // Second item completes! + updater_->SetAcceptableNotificationItem(Item(0, 1)); + CompleteItem(0, 1); + ++expected_notifications; + ASSERT_EQ(expected_notifications, updater_->NotificationCount()); + + // First item completes. + updater_->SetAcceptableNotificationItem(Item(0, 0)); + CompleteItem(0, 0); + ++expected_notifications; + ASSERT_EQ(expected_notifications, updater_->NotificationCount()); + + updater_->SetAcceptableNotificationItem(NULL); +} + // Confirm we recognize the situation where we have an unknown size. TEST_F(DownloadStatusUpdaterTest, UnknownSize) { SetupManagers(1); @@ -232,8 +283,8 @@ TEST_F(DownloadStatusUpdaterTest, UnknownSize) { LinkManager(0); // Prime items - SetItemValues(0, 0, 10, 20); - SetItemValues(0, 1, 50, -1); + SetItemValues(0, 0, 10, 20, false); + SetItemValues(0, 1, 50, -1, false); float progress = -1; int download_count = -1; @@ -276,9 +327,9 @@ TEST_F(DownloadStatusUpdaterTest, ManyManagersMixedItems) { AddItems(1, 3, 1); LinkManager(1); - SetItemValues(0, 0, 10, 20); - SetItemValues(0, 1, 50, 60); - SetItemValues(1, 0, 80, 90); + SetItemValues(0, 0, 10, 20, false); + SetItemValues(0, 1, 50, 60, false); + SetItemValues(1, 0, 80, 90, false); float progress = -1; int download_count = -1; diff --git a/chrome/browser/download/download_status_updater_win.cc b/chrome/browser/download/download_status_updater_win.cc new file mode 100644 index 0000000..3e28d6f --- /dev/null +++ b/chrome/browser/download/download_status_updater_win.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/download/download_status_updater.h" + +#include <string> +#include <shobjidl.h> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/string_number_conversions.h" +#include "base/win/metro.h" +#include "base/win/scoped_comptr.h" +#include "base/win/windows_version.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/browser_window.h" +#include "googleurl/src/gurl.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +// This code doesn't compile with Aura on. TODO(avi): hook it up so that +// win_aura can do platform integration. +#if !defined(USE_AURA) + +namespace { + +const char kDownloadNotificationPrefix[] = "DownloadNotification"; +int g_next_notification_id = 0; + +void UpdateTaskbarProgressBar(int download_count, + bool progress_known, + float progress) { + // Taskbar progress bar is only supported on Win7. + if (base::win::GetVersion() < base::win::VERSION_WIN7) + return; + + base::win::ScopedComPtr<ITaskbarList3> taskbar; + HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL, + CLSCTX_INPROC_SERVER); + if (FAILED(result)) { + VLOG(1) << "Failed creating a TaskbarList object: " << result; + return; + } + + result = taskbar->HrInit(); + if (FAILED(result)) { + LOG(ERROR) << "Failed initializing an ITaskbarList3 interface."; + return; + } + + // Iterate through all the browser windows, and draw the progress bar. + for (BrowserList::const_iterator browser_iterator = BrowserList::begin(); + browser_iterator != BrowserList::end(); browser_iterator++) { + Browser* browser = *browser_iterator; + BrowserWindow* window = browser->window(); + if (!window) + continue; + HWND frame = window->GetNativeWindow(); + if (download_count == 0 || progress == 1.0f) + taskbar->SetProgressState(frame, TBPF_NOPROGRESS); + else if (!progress_known) + taskbar->SetProgressState(frame, TBPF_INDETERMINATE); + else + taskbar->SetProgressValue(frame, static_cast<int>(progress * 100), 100); + } +} + +} // namespace + +void DownloadStatusUpdater::UpdateAppIconDownloadProgress( + content::DownloadItem* download) { + + // Always update overall progress. + + float progress = 0; + int download_count = 0; + bool progress_known = GetProgress(&progress, &download_count); + UpdateTaskbarProgressBar(download_count, progress_known, progress); + + // Fire notifications when downloads complete. + + if (!base::win::IsMetroProcess()) + return; + + if (download->GetState() != content::DownloadItem::COMPLETE) + return; + + if (download->GetOpenWhenComplete() || + download->ShouldOpenFileBasedOnExtension() || + download->IsTemporary() || + download->GetAutoOpened()) + return; + + // In Windows 8 metro mode display a metro style notification which + // informs the user that the download is complete. + HMODULE metro = base::win::GetMetroModule(); + base::win::MetroNotification display_notification = + reinterpret_cast<base::win::MetroNotification>( + ::GetProcAddress(metro, "DisplayNotification")); + DCHECK(display_notification); + if (display_notification) { + string16 title = l10n_util::GetStringUTF16( + IDS_METRO_DOWNLOAD_COMPLETE_NOTIFICATION_TITLE); + string16 body = l10n_util::GetStringUTF16( + IDS_METRO_DOWNLOAD_COMPLETE_NOTIFICATION); + + // Dummy notification id. Every metro style notification needs a + // unique notification id. + std::string notification_id = kDownloadNotificationPrefix; + notification_id += base::IntToString(g_next_notification_id++); + + display_notification(download->GetURL().spec().c_str(), + "", + title.c_str(), + body.c_str(), + L"", + notification_id.c_str()); + } +} + +#endif // !USE_AURA diff --git a/chrome/browser/download/download_util.cc b/chrome/browser/download/download_util.cc index b5a6a2e..7b940aa 100644 --- a/chrome/browser/download/download_util.cc +++ b/chrome/browser/download/download_util.cc @@ -27,8 +27,6 @@ #include "chrome/browser/download/download_extensions.h" #include "chrome/browser/download/download_item_model.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_window.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/time_format.h" @@ -52,12 +50,6 @@ #include "ui/gfx/image/image.h" #include "ui/gfx/rect.h" -#if defined(OS_WIN) -#include <shobjidl.h> - -#include "base/win/windows_version.h" -#endif - #if defined(TOOLKIT_VIEWS) #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drag_utils.h" @@ -68,12 +60,9 @@ #if defined(TOOLKIT_GTK) #include "chrome/browser/ui/gtk/custom_drag.h" -#include "chrome/browser/ui/gtk/unity_service.h" #endif // defined(TOOLKIT_GTK) #if defined(OS_WIN) && !defined(USE_AURA) -#include "base/win/scoped_comptr.h" -#include "chrome/browser/ui/browser_list.h" #include "ui/base/dragdrop/drag_source.h" #include "ui/base/dragdrop/os_exchange_data_provider_win.h" #endif @@ -554,53 +543,6 @@ string16 GetProgressStatusText(DownloadItem* download) { speed_text, amount, time_remaining); } -#if !defined(OS_MACOSX) -void UpdateAppIconDownloadProgress(int download_count, - bool progress_known, - float progress) { -#if defined(USE_AURA) - // TODO(davemoore) Implement once UX for download is decided <104742> -#elif defined(OS_WIN) - // Taskbar progress bar is only supported on Win7. - if (base::win::GetVersion() < base::win::VERSION_WIN7) - return; - - base::win::ScopedComPtr<ITaskbarList3> taskbar; - HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL, - CLSCTX_INPROC_SERVER); - if (FAILED(result)) { - VLOG(1) << "Failed creating a TaskbarList object: " << result; - return; - } - - result = taskbar->HrInit(); - if (FAILED(result)) { - LOG(ERROR) << "Failed initializing an ITaskbarList3 interface."; - return; - } - - // Iterate through all the browser windows, and draw the progress bar. - for (BrowserList::const_iterator browser_iterator = BrowserList::begin(); - browser_iterator != BrowserList::end(); browser_iterator++) { - Browser* browser = *browser_iterator; - BrowserWindow* window = browser->window(); - if (!window) - continue; - HWND frame = window->GetNativeWindow(); - if (download_count == 0 || progress == 1.0f) - taskbar->SetProgressState(frame, TBPF_NOPROGRESS); - else if (!progress_known) - taskbar->SetProgressState(frame, TBPF_INDETERMINATE); - else - taskbar->SetProgressValue(frame, static_cast<int>(progress * 100), 100); - } -#elif defined(TOOLKIT_GTK) - unity::SetDownloadCount(download_count); - unity::SetProgressFraction(progress); -#endif -} -#endif - FilePath GetCrDownloadPath(const FilePath& suggested_path) { return FilePath(suggested_path.value() + FILE_PATH_LITERAL(".crdownload")); } diff --git a/chrome/browser/download/download_util.h b/chrome/browser/download/download_util.h index cb1bdac..ddbfba8 100644 --- a/chrome/browser/download/download_util.h +++ b/chrome/browser/download/download_util.h @@ -138,15 +138,6 @@ base::DictionaryValue* CreateDownloadItemValue(content::DownloadItem* download, // Get the localized status text for an in-progress download. string16 GetProgressStatusText(content::DownloadItem* download); -// Update the application icon to indicate overall download progress. -// |download_count| is the number of downloads currently in progress. If -// |progress_known| is false, then at least one download is of indeterminate -// size and |progress| is invalid, otherwise |progress| indicates the overall -// download progress (float value from 0..1). -void UpdateAppIconDownloadProgress(int download_count, - bool progress_known, - float progress); - // Returns a .crdownload intermediate path for the |suggested_path|. FilePath GetCrDownloadPath(const FilePath& suggested_path); diff --git a/chrome/browser/ui/cocoa/download/download_item_mac.mm b/chrome/browser/ui/cocoa/download/download_item_mac.mm index 3a622b4..741575b 100644 --- a/chrome/browser/ui/cocoa/download/download_item_mac.mm +++ b/chrome/browser/ui/cocoa/download/download_item_mac.mm @@ -8,7 +8,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/download/download_item_model.h" #import "chrome/browser/ui/cocoa/download/download_item_controller.h" -#include "chrome/browser/ui/cocoa/download/download_util_mac.h" #include "content/public/browser/download_item.h" #include "ui/gfx/image/image.h" @@ -51,7 +50,6 @@ void DownloadItemMac::OnDownloadUpdated(content::DownloadItem* download) { [item_controller_ remove]; // We're deleted now! return; } - download_util::NotifySystemOfDownloadComplete(download->GetFullPath()); // fall through case DownloadItem::IN_PROGRESS: case DownloadItem::CANCELLED: diff --git a/chrome/browser/ui/cocoa/download/download_util_mac.h b/chrome/browser/ui/cocoa/download/download_util_mac.h index 5fe53d4..968f1a1c 100644 --- a/chrome/browser/ui/cocoa/download/download_util_mac.h +++ b/chrome/browser/ui/cocoa/download/download_util_mac.h @@ -15,10 +15,6 @@ namespace download_util { void AddFileToPasteboard(NSPasteboard* pasteboard, const FilePath& path); -// Notify the system that a download completed. This will cause the download -// folder in the dock to bounce. -void NotifySystemOfDownloadComplete(const FilePath& path); - } // namespace download_util #endif // CHROME_BROWSER_UI_COCOA_DOWNLOAD_DOWNLOAD_UTIL_MAC_H_ diff --git a/chrome/browser/ui/cocoa/download/download_util_mac.mm b/chrome/browser/ui/cocoa/download/download_util_mac.mm index 17e4826b..fe56ebd 100644 --- a/chrome/browser/ui/cocoa/download/download_util_mac.mm +++ b/chrome/browser/ui/cocoa/download/download_util_mac.mm @@ -7,7 +7,6 @@ #include "chrome/browser/ui/cocoa/download/download_util_mac.h" #include "base/sys_string_conversions.h" -#import "chrome/browser/ui/cocoa/dock_icon.h" #include "content/public/browser/download_item.h" #include "content/public/browser/download_manager.h" #include "ui/gfx/image/image.h" @@ -26,19 +25,6 @@ void AddFileToPasteboard(NSPasteboard* pasteboard, const FilePath& path) { [pasteboard setPropertyList:fileList forType:NSFilenamesPboardType]; } -void NotifySystemOfDownloadComplete(const FilePath& path) { - NSString* filePath = base::SysUTF8ToNSString(path.value()); - [[NSDistributedNotificationCenter defaultCenter] - postNotificationName:@"com.apple.DownloadFileFinished" - object:filePath]; - - NSString* parentPath = [filePath stringByDeletingLastPathComponent]; - FNNotifyByPath( - reinterpret_cast<const UInt8*>([parentPath fileSystemRepresentation]), - kFNDirectoryModifiedMessage, - kNilOptions); -} - void DragDownload(const DownloadItem* download, gfx::Image* icon, gfx::NativeView view) { @@ -69,14 +55,4 @@ void DragDownload(const DownloadItem* download, slideBack:YES]; } -void UpdateAppIconDownloadProgress(int download_count, - bool progress_known, - float progress) { - DockIcon* dock_icon = [DockIcon sharedDockIcon]; - [dock_icon setDownloads:download_count]; - [dock_icon setIndeterminate:!progress_known]; - [dock_icon setProgress:progress]; - [dock_icon updateIcon]; -} - } // namespace download_util diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index dbff421..630befd 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1027,8 +1027,6 @@ 'browser/download/chrome_download_manager_delegate.h', 'browser/download/download_completion_blocker.cc', 'browser/download/download_completion_blocker.h', - 'browser/download/download_completion_observer_win.cc', - 'browser/download/download_completion_observer_win.h', 'browser/download/download_crx_util.cc', 'browser/download/download_crx_util.h', 'browser/download/download_crx_util_android.cc', @@ -1067,6 +1065,9 @@ 'browser/download/download_resource_throttle.h', 'browser/download/download_status_updater.cc', 'browser/download/download_status_updater.h', + 'browser/download/download_status_updater_gtk.cc', + 'browser/download/download_status_updater_mac.mm', + 'browser/download/download_status_updater_win.cc', 'browser/download/download_util.cc', 'browser/download/download_util.h', 'browser/download/save_package_file_picker.cc', |