Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(361)

Side by Side Diff: chrome/browser/download/download_status_updater_mac.mm

Issue 10827207: Mountain Lion: use the system download progress. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: v3.1 Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/download/download_status_updater.h"
6
7 #include "base/mac/foundation_util.h"
8 #include "base/memory/scoped_nsobject.h"
9 #include "base/supports_user_data.h"
10 #include "base/sys_string_conversions.h"
11 #include "content/public/browser/download_item.h"
12 #import "chrome/browser/ui/cocoa/dock_icon.h"
13 #include "googleurl/src/gurl.h"
14
15 // --- Private 10.8 API for showing progress ---
16 // rdar://12058866 http://www.openradar.me/12058866
17
18 namespace {
19
20 NSString* const kNSProgressAppBundleIdentifierKey =
21 @"NSProgressAppBundleIdentifierKey";
22 NSString* const kNSProgressEstimatedTimeKey =
23 @"NSProgressEstimatedTimeKey";
24 NSString* const kNSProgressFileCompletedCountKey =
25 @"NSProgressFileCompletedCountKey";
26 NSString* const kNSProgressFileContainerURLKey =
27 @"NSProgressFileContainerURLKey";
28 NSString* const kNSProgressFileDownloadingSourceURLKey =
29 @"NSProgressFileDownloadingSourceURLKey";
30 NSString* const kNSProgressFileIconKey =
31 @"NSProgressFileIconKey";
32 NSString* const kNSProgressFileIconOriginalRectKey =
33 @"NSProgressFileIconOriginalRectKey";
34 NSString* const kNSProgressFileLocationCanChangeKey =
35 @"NSProgressFileLocationCanChangeKey";
36 NSString* const kNSProgressFileOperationKindAirDropping =
37 @"NSProgressFileOperationKindAirDropping";
38 NSString* const kNSProgressFileOperationKindCopying =
39 @"NSProgressFileOperationKindCopying";
40 NSString* const kNSProgressFileOperationKindDecompressingAfterDownloading =
41 @"NSProgressFileOperationKindDecompressingAfterDownloading";
42 NSString* const kNSProgressFileOperationKindDownloading =
43 @"NSProgressFileOperationKindDownloading";
44 NSString* const kNSProgressFileOperationKindEncrypting =
45 @"NSProgressFileOperationKindEncrypting";
46 NSString* const kNSProgressFileOperationKindKey =
47 @"NSProgressFileOperationKindKey";
48 NSString* const kNSProgressFileTotalCountKey =
49 @"NSProgressFileTotalCountKey";
50 NSString* const kNSProgressFileURLKey =
51 @"NSProgressFileURLKey";
52 NSString* const kNSProgressIsWaitingKey =
53 @"NSProgressIsWaitingKey";
54 NSString* const kNSProgressKindFile =
55 @"NSProgressKindFile";
56 NSString* const kNSProgressThroughputKey =
57 @"NSProgressThroughputKey";
58
59 NSString* ProgressString(NSString* string) {
60 static NSMutableDictionary* cache;
61 static CFBundleRef foundation;
62 if (!cache) {
63 cache = [[NSMutableDictionary alloc] init];
64 foundation = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Foundation"));
65 }
66
67 NSString* result = [cache objectForKey:string];
68 if (!result) {
69 NSString** ref = static_cast<NSString**>(
70 CFBundleGetDataPointerForName(foundation,
71 base::mac::NSToCFCast(string)));
72 if (ref) {
73 result = *ref;
74 [cache setObject:result forKey:string];
75 }
76 }
77
78 return result;
79 }
80
81 } // namespace
82
83 @interface NSProgress : NSObject
84
85 - (id)initWithParent:(id)parent userInfo:(NSDictionary*)info;
86 @property(copy) NSString* kind;
87
88 - (void)unpublish;
89 - (void)publish;
90
91 - (void)setUserInfoObject:(id)object forKey:(NSString*)key;
92 - (NSDictionary*)userInfo;
93
94 @property(readonly) double fractionCompleted;
95 // Set the totalUnitCount to -1 to indicate an indeterminate download. The dock
96 // shows a non-filling progress bar; the Finder is lame and draws its progress
97 // bar off the right side.
98 @property(readonly, getter=isIndeterminate) BOOL indeterminate;
99 @property long long completedUnitCount;
100 @property long long totalUnitCount;
101
102 // Pausing appears to be unimplemented in 10.8.0.
103 - (void)pause;
104 @property(readonly, getter=isPaused) BOOL paused;
105 @property(getter=isPausable) BOOL pausable;
106 - (void)setPausingHandler:(id)blockOfUnknownSignature;
107
108 - (void)cancel;
109 @property(readonly, getter=isCancelled) BOOL cancelled;
110 @property(getter=isCancellable) BOOL cancellable;
111 // Note that the cancellation handler block will be called on a random thread.
112 - (void)setCancellationHandler:(void (^)())block;
113
114 // Allows other applications to provide feedback as to whether the progress is
115 // visible in that app. Note that the acknowledgement handler block will be
116 // called on a random thread.
117 // com.apple.dock => BOOL indicating whether the download target folder was
118 // successfully "flown to" at the beginning of the download.
119 // This primarily depends on whether the download target
120 // folder is in the dock. Note that if the download target
121 // folder is added or removed from the dock during the
122 // duration of the download, it will not trigger a callback.
123 // Note that if the "fly to the dock" keys were not set, the
124 // callback's parameter will always be NO.
125 // com.apple.Finder => always YES, no matter whether the download target
126 // folder's window is open.
127 - (void)handleAcknowledgementByAppWithBundleIdentifier:(NSString*)bundle
128 usingBlock:(void (^)(BOOL success))block;
129
130 @end
131
132 // --- Private 10.8 API for showing progress ---
133
134 namespace {
135
136 bool NSProgressSupported() {
137 static bool supported;
138 static bool valid;
139 if (!valid) {
140 supported = NSClassFromString(@"NSProgress");
141 valid = true;
142 }
143
144 return supported;
145 }
146
147 const char kCrNSProgressUserDataKey[] = "CrNSProgressUserData";
148
149 class CrNSProgressUserData : public base::SupportsUserData::Data {
150 public:
151 CrNSProgressUserData(NSProgress* progress, const FilePath& target)
152 : target_(target) {
153 progress_.reset(progress);
154 }
155 virtual ~CrNSProgressUserData() {}
156
157 NSProgress* progress() const { return progress_.get(); }
158 FilePath target() const { return target_; }
159 void setTarget(const FilePath& target) { target_ = target; }
160
161 private:
162 scoped_nsobject<NSProgress> progress_;
163 FilePath target_;
164 };
165
166 void UpdateAppIcon(int download_count,
167 bool progress_known,
168 float progress) {
169 DockIcon* dock_icon = [DockIcon sharedDockIcon];
170 [dock_icon setDownloads:download_count];
171 [dock_icon setIndeterminate:!progress_known];
172 [dock_icon setProgress:progress];
173 [dock_icon updateIcon];
174 }
175
176 void CreateNSProgress(content::DownloadItem* download) {
177 NSURL* source_url = [NSURL URLWithString:
178 base::SysUTF8ToNSString(download->GetURL().spec())];
179 FilePath destination_path = download->GetFullPath();
180 NSURL* destination_url = [NSURL fileURLWithPath:
181 base::mac::FilePathToNSString(destination_path)];
182
183 // If there were an image to fly to the download folder in the dock, then
184 // the keys in the userInfo to set would be:
185 // - @"NSProgressFlyToImageKey" : NSImage
186 // - kNSProgressFileIconOriginalRectKey : NSValue of NSRect in global coords
187
188 NSDictionary* user_info = @{
189 ProgressString(kNSProgressFileDownloadingSourceURLKey) : source_url,
190 ProgressString(kNSProgressFileLocationCanChangeKey) : @true,
191 ProgressString(kNSProgressFileOperationKindKey) :
192 ProgressString(kNSProgressFileOperationKindDownloading),
193 ProgressString(kNSProgressFileURLKey) : destination_url
194 };
195 Class progress_class = NSClassFromString(@"NSProgress");
196 NSProgress* progress = [progress_class performSelector:@selector(alloc)];
197 progress = [progress performSelector:@selector(initWithParent:userInfo:)
198 withObject:nil
199 withObject:user_info];
200 progress.kind = ProgressString(kNSProgressKindFile);
201
202 progress.pausable = NO;
203 progress.cancellable = YES;
204 [progress setCancellationHandler:^{
205 dispatch_async(dispatch_get_main_queue(), ^{
206 download->Cancel(true);
207 });
208 }];
209
210 progress.totalUnitCount = download->GetTotalBytes();
211 progress.completedUnitCount = download->GetReceivedBytes();
212
213 [progress publish];
214
215 download->SetUserData(&kCrNSProgressUserDataKey,
216 new CrNSProgressUserData(progress, destination_path));
217 }
218
219 void UpdateNSProgress(content::DownloadItem* download,
220 CrNSProgressUserData* progress_data) {
221 NSProgress* progress = progress_data->progress();
222 progress.totalUnitCount = download->GetTotalBytes();
223 progress.completedUnitCount = download->GetReceivedBytes();
224
225 FilePath download_path = download->GetFullPath();
226 if (progress_data->target() != download_path) {
227 progress_data->setTarget(download_path);
228 NSURL* download_url = [NSURL fileURLWithPath:
229 base::mac::FilePathToNSString(download_path)];
230 [progress setUserInfoObject:download_url
231 forKey:ProgressString(kNSProgressFileURLKey)];
232 }
233 }
234
235 void DestroyNSProgress(content::DownloadItem* download,
236 CrNSProgressUserData* progress_data) {
237 NSProgress* progress = progress_data->progress();
238 [progress unpublish];
239
240 download->RemoveUserData(&kCrNSProgressUserDataKey);
241 }
242
243 } // namespace
244
245 void DownloadStatusUpdater::UpdateAppIconDownloadProgress(
246 content::DownloadItem* download) {
247
248 // Always update overall progress.
249
250 float progress = 0;
251 int download_count = 0;
252 bool progress_known = GetProgress(&progress, &download_count);
253 UpdateAppIcon(download_count, progress_known, progress);
254
255 // Update NSProgress-based indicators.
256
257 if (NSProgressSupported()) {
258 CrNSProgressUserData* progress_data = static_cast<CrNSProgressUserData*>(
259 download->GetUserData(&kCrNSProgressUserDataKey));
260 if (!progress_data)
261 CreateNSProgress(download);
262 else
263 UpdateNSProgress(download, progress_data);
264
265 if (download->GetState() != content::DownloadItem::IN_PROGRESS)
266 DestroyNSProgress(download, progress_data);
267 }
268
269 // Handle downloads that ended.
270 if (download->GetState() != content::DownloadItem::IN_PROGRESS) {
271 NSString* download_path =
272 base::mac::FilePathToNSString(download->GetFullPath());
273 if (download->GetState() == content::DownloadItem::COMPLETE) {
274 // Bounce the dock icon.
275 [[NSDistributedNotificationCenter defaultCenter]
276 postNotificationName:@"com.apple.DownloadFileFinished"
277 object:download_path];
278 }
279
280 // Notify the Finder.
281 NSString* parent_path = [download_path stringByDeletingLastPathComponent];
282 FNNotifyByPath(
283 reinterpret_cast<const UInt8*>([parent_path fileSystemRepresentation]),
284 kFNDirectoryModifiedMessage,
285 kNilOptions);
286 }
287 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698