chromium/chrome/updater/mac/client_lib/CRURegistration.h

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_UPDATER_MAC_CLIENT_LIB_CRUREGISTRATION_H_
#define CHROME_UPDATER_MAC_CLIENT_LIB_CRUREGISTRATION_H_

#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>

NS_ASSUME_NONNULL_BEGIN

/** The domain for user or system errors reported by CRURegistration. */
extern NSString* const CRURegistrationErrorDomain;

/**
 * The domain for internal errors from CRURegistration. Clients should never
 * encounter these; please file a bug if you get errors in this domain.
 */
extern NSString* const CRURegistrationInternalErrorDomain;

/**
 * NSError userInfo dict key mapped to the POSIX errno for NSErrors in
 * CRURegistrationErrorDomain with underlying POSIX causes.
 */
extern NSString* const CRUErrnoKey;

/**
 * NSError userInfo dict key mapped to the string captured from the stderr
 * output of a command-line tool invoked by CRURegistration. This error is
 * usually intended to be human-readable, rather than machine-readable.
 */
extern NSString* const CRUStderrKey;

/**
 * NSError userInfo dict key mapped to the string captured from the stdout
 * output of a command-line tool invoked by CRURegistration. May contain partial
 * or incorrect output from the command that failed.
 */
extern NSString* const CRUStdoutKey;

/**
 * NSError userInfo dict key mapped to the return code of a failing task (as
 * an NSNumber wrapping NSInteger).
 */
extern NSString* const CRUReturnCodeKey;

typedef NS_ERROR_ENUM(CRURegistrationErrorDomain, CRURegistrationError){
    /**
     * `CRURegistration` couldn't read a stream (stdout or stderr) when running
     * a subprocess. The POSIX error code for the error is available in the
     * error's user data under `CRUErrnoKey`.
     */
    CRURegistrationErrorTaskStreamUnreadable = 1,

    /**
     * `CRURegistration` couldn't find the updater or the updater installer.
     */
    CRURegistrationErrorHelperNotFound = 2,

    /**
     * The updater component invoked by `CRURegistration` returned an error. Its
     * error message is available in the error's user data under `CRUStderrKey`.
     * Any partial output, usable for debugging purposes, is available under
     * `CRUStdoutKey`.
     */
    CRURegistrationErrorTaskFailed = 3,

    /**
     * Parameters for a CRURegistration operation were invalid. Details about
     * the invalid parameters are available in the error's `userInfo` dict
     * under `NSDebugDescriptionErrorKey`.
     *
     * Invalid arguments may also show up as `CRURegistrationErrorTaskFailed`,
     * if they are detected by the underlying helper binary instead of
     * `CRURegistration` itself. Whenever `CRURegistration` replies with this
     * error, it also fails an `NSAssert` so the issue can be discovered
     * in debug builds while the faulty call is still on the stack.
     */
    CRURegistrationErrorInvalidArgument = 4,

    /**
     * A macOS API failed in a way that implies that important parts of the
     * filesystem are not usable for the requested operation, which
     * `CRURegistration` was running in-process. The issue is something
     * different from "could not find the updater".
     *
     * File system errors encountered while trying to find a helper to run a
     * task out-of-process are reported as `CRURegistrationErrorHelperNotFound`.
     * Errors encountered by such helpers are always reported as
     * `CRURegistrationErrorTaskFailed` instead.
     */
    CRURegistrationErrorFilesystem = 5,
};

/**
 * CRURegistration interfaces with Chromium Updater to configure and retrieve
 * information about an app, or to install the updater for the current user. Its
 * methods can be invoked from any thread or queue.
 *
 * Do not block CRURegistration's target queue synchronously waiting for a
 * callback from CRURegistration; this causes deadlock. Invoking CRURegistration
 * methods on this (or any) queue without subsequently synchronously waiting for
 * a provided callback to execute is safe. CRURegistration does not block its
 * target queue.
 */
@interface CRURegistration : NSObject

/**
 * Initializes a CRURegistration instance to manage Chromium Updater's
 * information about the app with the provided ID, using a specified queue
 * for execution and callbacks. This queue can be serial or concurrent, but
 * typically should not be the main queue.
 *
 * @param appId The ID of the app this CRURegistration instance operates on.
 * @param xcPath The absolute path of the application bundle for the instance
 *     of the app this CRURegistration operates on, or another path the updater
 *     registers to verify the existence of an app on disk.
 * @param targetQueue Dispatch queue for callbacks and internal operations.
 *     If this queue is blocked, CRURegistration will get stuck.
 */
- (instancetype)initWithAppId:(NSString*)appId
         existenceCheckerPath:(NSString*)xcPath
                  targetQueue:(dispatch_queue_t)targetQueue
    NS_DESIGNATED_INITIALIZER;

/**
 * Initializes a CRURegistration instance to manage Chromium Updater's
 * information about the app with the provided ID, using a global concurrent
 * queue for execution (with the specified quality of service).
 *
 * @param appId The ID of the app this CRURegistration instance operates on.
 * @param xcPath The absolute path of the application bundle for the instance
 *     of the app this CRURegistration operates on, or another path the updater
 *     registers to verify the existence of an app on disk.
 * @param qos Identifier for the global concurrent queue to use for callbacks
 *     and internal operations. See Apple's documentation for
 *     `dispatch_get_global_queue` for more details:
 *     https://developer.apple.com/documentation/dispatch/1452927-dispatch_get_global_queue
 */
- (instancetype)initWithAppId:(NSString*)appId
         existenceCheckerPath:(NSString*)xcPath
                          qos:(dispatch_qos_class_t)qos;

/**
 * Initializes a CRURegistration instance to manage Chromium Updater's
 * information about the app with the provided ID, using the `QOS_CLASS_UTILITY`
 * global concurrent queue for execution.
 *
 * @param appId The ID of the app this CRURegistration instance operates on.
 * @param xcPath The absolute path of the application bundle for the instance
 *     of the app this CRURegistration operates on, or another path the updater
 *     registers to verify the existence of an app on disk.
 */
- (instancetype)initWithAppId:(NSString*)appId
         existenceCheckerPath:(NSString*)xcPath;

/**
 * CRURegistration cannot be initialized without an app ID and existence
 * checker path.
 */
- (instancetype)init NS_UNAVAILABLE;

/**
 * Asynchronously registers the app for updates, owned by the current user.
 *
 * If the app is already registered for the current user, this updates the
 * registration.
 *
 * When registration has completed, `reply` will be dispatched to the target
 * queue. If registration succeeds, the `NSError*` argument will be nil.
 * If it does not, the `NSError*` argument to `reply` will contain a
 * descriptive error.
 *
 * `reply` may be nil if the application is not interested in the result of
 * registration.
 */
- (void)registerVersion:(NSString*)version
                  reply:(void (^_Nullable)(NSError* _Nullable))reply;

/**
 * Asynchronously retrieves the tag for the registered app.
 *
 * `reply` will be dispatched to the target queue when tag reading has completed
 * or failed. If the tag can be read successfully, it will be passed as the
 * NSString* argument of the reply block and the NSError* argument will be nil.
 * If the tag cannot be read, the NSString* will be nil while the NSError* will
 * contain a descriptive error.
 *
 * If the tag is empty, the NSString* argument will be the empty string (and
 * there is no error). If no app with the provided ID is registered or the
 * updater is not installed, an error occurs.
 */
- (void)fetchTagWithReply:(void (^)(NSString* _Nullable,
                                    NSError* _Nullable))reply;

/**
 * Asynchronously installs the updater, as the current user.
 *
 * `reply` will be dispatched to the target queue when installation has
 * completed or failed. If the installation is skipped because there is already
 * an updater present at the same or higher version, it is considered to have
 * completed successfully.
 *
 * If installation succeeds (or is skipped), the `NSError*` argument to `reply`
 * will be nil. If it fails, the argument will contain a descriptive error.
 */
- (void)installUpdaterWithReply:(void (^_Nullable)(NSError* _Nullable))reply;

/**
 * Asynchronously marks the current product active for the current user.
 *
 * This may run concurrently with other operations performed by CRURegistration
 * and return out-of-order, although it will still run under, and reply on,
 * the target queue. `reply` will be dispatched to the target queue when the
 * "active" file for the provided app ID has been written (or the attempt to
 * do so has failed).
 */
- (void)markActiveWithReply:(void (^_Nullable)(NSError* _Nullable))reply;
@end

NS_ASSUME_NONNULL_END

#endif  // CHROME_UPDATER_MAC_CLIENT_LIB_CRUREGISTRATION_H_