Skip to content

Instantly share code, notes, and snippets.

@titanous
Last active December 17, 2015 11:49
Show Gist options
  • Save titanous/5604757 to your computer and use it in GitHub Desktop.
Save titanous/5604757 to your computer and use it in GitHub Desktop.
NSProgress classdump from Foundation.framework and usage in Chrome
/*
* Generated by class-dump 3.4 (64 bit).
*
* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2012 by Steve Nygard.
*/
@protocol NSProgressPublisher <NSObject>
- (oneway void)appWithBundleID:(id)arg1 didAcknowledgeWithSuccess:(BOOL)arg2;
- (oneway void)pause;
- (oneway void)cancel;
- (oneway void)stopProvidingValues;
- (oneway void)startProvidingValuesWithInitialAcceptor:(id)arg1;
@end
@protocol NSProgressRegistrar
- (oneway void)removeSubscriberForID:(id)arg1;
- (oneway void)addSubscriber:(id)arg1 forID:(id)arg2 appBundleID:(id)arg3 fileURL:(id)arg4;
- (oneway void)removePublisherForID:(id)arg1;
- (oneway void)observePublisherForID:(id)arg1 value:(id)arg2 forKey:(id)arg3 inUserInfo:(BOOL)arg4;
- (oneway void)addPublisher:(id)arg1 forID:(id)arg2 acknowledgementAppBundleIDs:(id)arg3 fileURL:(id)arg4;
@end
@protocol NSProgressSubscriber <NSObject>
- (oneway void)removePublisherForID:(id)arg1;
- (oneway void)observePublisherForID:(id)arg1 value:(id)arg2 forKey:(id)arg3 inUserInfo:(BOOL)arg4;
- (oneway void)addPublisher:(id)arg1 forID:(id)arg2 withValues:(id)arg3 isOld:(BOOL)arg4;
@end
@interface NSProgress : NSObject <NSProgressPublisher>
{
NSProgress *_parent;
long long _portionOfParentUnitCount;
id _values;
id _valuesSeenFromMainThread;
id _cancellationHandler;
id _pausingHandler;
long long _pendingUnitCount;
id _userInfoProxy;
NSString *_publisherID;
NSXPCConnection *_connection;
long long _unpublishingBlockageCount;
long long _remoteObserverCount;
NSMutableDictionary *_acknowledgementHandlersByBundleID;
NSMutableDictionary *_lastNotificationTimesByKey;
NSMutableDictionary *_userInfoLastNotificationTimesByKey;
id _reserved1;
id _reserved2;
}
+ (void)removeSubscriber:(id)arg1;
+ (id)addSubscriberForFileURL:(id)arg1 usingBlock:(id)arg2;
+ (id)keyPathsForValuesAffectingFractionCompleted;
+ (id)keyPathsForValuesAffectingLocalizedDescription;
+ (BOOL)automaticallyNotifiesObserversForKey:(id)arg1;
+ (id)progressWithTotalUnitCount:(long long)arg1;
+ (id)currentProgress;
+ (id)_registrarInterface;
+ (id)_subscriberInterface;
+ (id)_publisherInterface;
@property(readonly, getter=isOld) BOOL old;
- (void)acknowledgeWithSuccess:(BOOL)arg1;
- (oneway void)appWithBundleID:(id)arg1 didAcknowledgeWithSuccess:(BOOL)arg2;
- (oneway void)stopProvidingValues;
- (oneway void)startProvidingValuesWithInitialAcceptor:(id)arg1;
- (void)handleAcknowledgementByAppWithBundleIdentifier:(id)arg1 usingBlock:(id)arg2;
- (void)unpublish;
- (void)_unblockUnpublishing;
- (void)publish;
@property(copy) NSString *kind;
- (id)ownedDictionaryObjectForKey:(id)arg1;
- (id)ownedDictionaryKeyEnumerator;
- (unsigned long long)ownedDictionaryCount;
- (id)userInfo;
- (void)pause;
- (void)_pause;
- (void)cancel;
- (void)_cancel;
@property(readonly) double fractionCompleted;
- (double)_fractionCompletedUsingValuesFinder:(id)arg1;
@property(readonly, getter=isIndeterminate) BOOL indeterminate;
- (void)setUserInfoObject:(id)arg1 forKey:(id)arg2;
- (void)_setUserInfoValue:(id)arg1 forKey:(id)arg2;
- (void)setPausingHandler:(id)arg1;
- (void)setCancellationHandler:(id)arg1;
@property(readonly, getter=isPaused) BOOL paused;
@property(readonly, getter=isCancelled) BOOL cancelled;
@property(getter=isPausable) BOOL pausable;
@property(getter=isCancellable) BOOL cancellable;
@property(copy) NSString *localizedDescription;
@property long long completedUnitCount;
@property long long totalUnitCount;
- (void)_getValueUsingBlock:(id)arg1;
- (void)_setValueForKey:(id)arg1 usingBlock:(id)arg2;
- (void)__setValueForKey:(id)arg1 usingBlock:(id)arg2;
- (void)_notifyRemoteObserversOfValueForKey:(id)arg1 inUserInfo:(BOOL)arg2;
- (void)__notifyRemoteObserversOfValueForKey:(id)arg1 inUserInfo:(BOOL)arg2;
- (void)resignCurrent;
- (void)becomeCurrentWithPendingUnitCount:(long long)arg1;
- (void)dealloc;
- (id)initWithParent:(id)arg1 userInfo:(id)arg2;
- (id)init;
- (void)_setValue:(id)arg1 forKey:(id)arg2 inUserInfo:(BOOL)arg3;
- (id)_initWithValues:(id)arg1;
@property(readonly) NSString *localizedItemCountDescription;
- (void)acknowledge;
- (void)handleAcknowledgementByAppWithBundleIdentifer:(id)arg1 usingBlock:(id)arg2;
@end
__attribute__((visibility("hidden")))
@interface NSProgressPublisherProxy : NSObject <NSProgressPublisher>
{
id <NSProgressPublisher> _forwarder;
id _publisherID;
NSFileAccessNode *_itemLocation;
NSMutableSet *_bundleIDsOfUnacknowledgedApps;
}
- (id)description;
- (id)descriptionWithIndenting:(id)arg1;
- (oneway void)appWithBundleID:(id)arg1 didAcknowledgeWithSuccess:(BOOL)arg2;
- (oneway void)pause;
- (oneway void)cancel;
- (oneway void)stopProvidingValues;
- (oneway void)startProvidingValuesWithInitialAcceptor:(id)arg1;
- (void)broadcastValue:(id)arg1 forKey:(id)arg2 inUserInfo:(BOOL)arg3;
- (void)setItemLocation:(id)arg1;
- (id)publisherID;
- (void)dealloc;
- (id)initWithForwarder:(id)arg1 publisherID:(id)arg2 acknowledgementAppBundleIDs:(id)arg3;
@end
__attribute__((visibility("hidden")))
@interface NSProgressSubscriberProxy : NSObject <NSProgressSubscriber>
{
id <NSProgressSubscriber> _forwarder;
id _subscriberID;
NSFileAccessNode *_itemLocation;
NSString *_appBundleIDOrNil;
}
- (id)description;
- (id)descriptionWithIndenting:(id)arg1;
- (oneway void)removePublisherForID:(id)arg1;
- (oneway void)observePublisherForID:(id)arg1 value:(id)arg2 forKey:(id)arg3 inUserInfo:(BOOL)arg4;
- (oneway void)addPublisher:(id)arg1 forID:(id)arg2 withValues:(id)arg3 isOld:(BOOL)arg4;
- (void)setItemLocation:(id)arg1;
- (id)appBundleID;
- (void)dealloc;
- (id)initWithForwarder:(id)arg1 subscriberID:(id)arg2 appBundleID:(id)arg3;
@end
@interface NSProgressRegistrar : NSObject <NSXPCListenerDelegate, NSProgressRegistrar>
{
struct dispatch_queue_s *_queue;
NSFileAccessNode *_rootFileAccessNode;
NSMutableDictionary *_publishersAndSubscribersByID;
}
- (oneway void)removeSubscriberForID:(id)arg1;
- (oneway void)addSubscriber:(id)arg1 forID:(id)arg2 appBundleID:(id)arg3 fileURL:(id)arg4;
- (oneway void)removePublisherForID:(id)arg1;
- (oneway void)observePublisherForID:(id)arg1 value:(id)arg2 forKey:(id)arg3 inUserInfo:(BOOL)arg4;
- (oneway void)addPublisher:(id)arg1 forID:(id)arg2 acknowledgementAppBundleIDs:(id)arg3 fileURL:(id)arg4;
- (BOOL)listener:(id)arg1 shouldAcceptNewConnection:(id)arg2;
- (void)finalize;
- (void)dealloc;
- (id)initWithQueue:(struct dispatch_queue_s *)arg1 rootFileAccessNode:(id)arg2;
@end
__attribute__((visibility("hidden")))
@interface NSProgressProxy : NSProgress
{
id <NSProgressPublisher> _forwarder;
BOOL _isOld;
id _unpublishingHandler;
}
- (BOOL)isOld;
- (void)acknowledgeWithSuccess:(BOOL)arg1;
- (void)pause;
- (void)cancel;
- (void)unpublish;
- (void)publish;
- (void)setKind:(id)arg1;
- (void)setUserInfoObject:(id)arg1 forKey:(id)arg2;
- (void)setPausingHandler:(id)arg1;
- (void)setCancellationHandler:(id)arg1;
- (void)setPausable:(BOOL)arg1;
- (void)setCancellable:(BOOL)arg1;
- (void)setLocalizedDescription:(id)arg1;
- (void)setCompletedUnitCount:(long long)arg1;
- (void)setTotalUnitCount:(long long)arg1;
- (void)resignCurrent;
- (void)becomeCurrentWithPendingUnitCount:(long long)arg1;
- (void)_invokeUnpublishingHandler;
- (void)_invokePublishingHandler:(id)arg1;
- (void)dealloc;
- (id)_initWithForwarder:(id)arg1 values:(id)arg2 isOld:(BOOL)arg3;
@end
@interface NSProgressSubscriber : NSObject <NSProgressSubscriber>
{
id _publishingHandler;
NSString *_subscriberID;
struct dispatch_queue_s *_queue;
NSMutableDictionary *_proxiesByPublisherID;
NSXPCConnection *_connection;
}
- (oneway void)removePublisherForID:(id)arg1;
- (oneway void)observePublisherForID:(id)arg1 value:(id)arg2 forKey:(id)arg3 inUserInfo:(BOOL)arg4;
- (oneway void)addPublisher:(id)arg1 forID:(id)arg2 withValues:(id)arg3 isOld:(BOOL)arg4;
- (void)stop;
- (void)startForFileURL:(id)arg1;
- (void)finalize;
- (void)dealloc;
- (id)initWithPublishingHandler:(id)arg1;
@end
// 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/strings/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];
}
}
if (!result) {
// Huh. At least return a local copy of the expected string.
result = string;
NSString* const kKeySuffix = @"Key";
if ([result hasSuffix:kKeySuffix])
result = [result substringToIndex:[result length] - [kKeySuffix length]];
}
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 base::FilePath& target)
: target_(target) {
progress_.reset(progress);
}
virtual ~CrNSProgressUserData() {
[progress_.get() unpublish];
}
NSProgress* progress() const { return progress_.get(); }
base::FilePath target() const { return target_; }
void setTarget(const base::FilePath& target) { target_ = target; }
private:
scoped_nsobject<NSProgress> progress_;
base::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().possibly_invalid_spec())];
base::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(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);
if (source_url) {
[progress setUserInfoObject:source_url forKey:
ProgressString(kNSProgressFileDownloadingSourceURLKey)];
}
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();
base::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) {
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));
// Only show progress if the download is IN_PROGRESS and it hasn't been
// renamed to its final name. Setting the progress after the final rename
// results in the file being stuck in an in-progress state on the dock. See
// http://crbug.com/166683.
if (download->GetState() == content::DownloadItem::IN_PROGRESS &&
!download->GetFullPath().empty() &&
download->GetFullPath() != download->GetTargetFilePath()) {
if (!progress_data)
CreateNSProgress(download);
else
UpdateNSProgress(download, progress_data);
} else {
DestroyNSProgress(download, progress_data);
}
}
// Handle downloads that ended.
if (download->GetState() != content::DownloadItem::IN_PROGRESS &&
!download->GetTargetFilePath().empty()) {
NSString* download_path =
base::mac::FilePathToNSString(download->GetTargetFilePath());
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);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment