// 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.

// Use the <code>chrome.downloads</code> API to programmatically initiate,
// monitor, manipulate, and search for downloads.
[permissions=downloads]
namespace downloads {
  [inline_doc] dictionary HeaderNameValuePair {
    // Name of the HTTP header.
    DOMString name;

    // Value of the HTTP header.
    DOMString value;
  };

  // <dl><dt>uniquify</dt>
  //     <dd>To avoid duplication, the <code>filename</code> is changed to
  //     include a counter before the filename extension.</dd>
  //     <dt>overwrite</dt>
  //     <dd>The existing file will be overwritten with the new file.</dd>
  //     <dt>prompt</dt>
  //     <dd>The user will be prompted with a file chooser dialog.</dd>
  // </dl>
  enum FilenameConflictAction {uniquify, overwrite, prompt};

  [inline_doc] dictionary FilenameSuggestion {
    // The $(ref:DownloadItem)'s new target $(ref:DownloadItem.filename), as a path
    // relative to the user's default Downloads directory, possibly containing
    // subdirectories. Absolute paths, empty paths, and paths containing
    // back-references ".." will be ignored.
    DOMString filename;

    // The action to take if <code>filename</code> already exists.
    FilenameConflictAction? conflictAction;
  };

  [inline_doc] enum HttpMethod {GET, POST};

  enum InterruptReason {
    FILE_FAILED,
    FILE_ACCESS_DENIED,
    FILE_NO_SPACE,
    FILE_NAME_TOO_LONG,
    FILE_TOO_LARGE,
    FILE_VIRUS_INFECTED,
    FILE_TRANSIENT_ERROR,
    FILE_BLOCKED,
    FILE_SECURITY_CHECK_FAILED,
    FILE_TOO_SHORT,
    FILE_HASH_MISMATCH,
    NETWORK_FAILED,
    NETWORK_TIMEOUT,
    NETWORK_DISCONNECTED,
    NETWORK_SERVER_DOWN,
    NETWORK_INVALID_REQUEST,
    SERVER_FAILED,
    SERVER_NO_RANGE,
    SERVER_BAD_CONTENT,
    SERVER_UNAUTHORIZED,
    SERVER_CERT_PROBLEM,
    SERVER_FORBIDDEN,
    SERVER_UNREACHABLE,
    USER_CANCELED,
    USER_SHUTDOWN,
    CRASH};

  [inline_doc] dictionary DownloadOptions {
    // The URL to download.
    DOMString url;

    // A file path relative to the Downloads directory to contain the downloaded
    // file, possibly containing subdirectories. Absolute paths, empty paths,
    // and paths containing back-references ".." will cause an error.
    // $(ref:onDeterminingFilename) allows suggesting a filename after the file's
    // MIME type and a tentative filename have been determined.
    DOMString? filename;

    // The action to take if <code>filename</code> already exists.
    FilenameConflictAction? conflictAction;

    // Use a file-chooser to allow the user to select a filename regardless of
    // whether <code>filename</code> is set or already exists.
    boolean? saveAs;

    // The HTTP method to use if the URL uses the HTTP[S] protocol.
    HttpMethod? method;

    // Extra HTTP headers to send with the request if the URL uses the HTTP[s]
    // protocol. Each header is represented as a dictionary containing the keys
    // <code>name</code> and either <code>value</code> or
    // <code>binaryValue</code>, restricted to those allowed by XMLHttpRequest.
    HeaderNameValuePair[]? headers;

    // Post body.
    DOMString? body;
  };

  // <dl><dt>file</dt>
  //     <dd>The download's filename is suspicious.</dd>
  //     <dt>url</dt>
  //     <dd>The download's URL is known to be malicious.</dd>
  //     <dt>content</dt>
  //     <dd>The downloaded file is known to be malicious.</dd>
  //     <dt>uncommon</dt>
  //     <dd>The download's URL is not commonly downloaded and could be
  //     dangerous.</dd>
  //     <dt>host</dt>
  //     <dd>The download came from a host known to distribute malicious
  //     binaries and is likely dangerous.</dd>
  //     <dt>unwanted</dt>
  //     <dd>The download is potentially unwanted or unsafe. E.g. it could make
  //     changes to browser or computer settings.</dd>
  //     <dt>safe</dt>
  //     <dd>The download presents no known danger to the user's computer.</dd>
  //     <dt>accepted</dt>
  //     <dd>The user has accepted the dangerous download.</dd>
  // </dl>
  enum DangerType {
    file, url, content, uncommon, host, unwanted, safe, accepted
  };

  // <dl><dt>in_progress</dt>
  //     <dd>The download is currently receiving data from the server.</dd>
  //     <dt>interrupted</dt>
  //     <dd>An error broke the connection with the file host.</dd>
  //     <dt>complete</dt>
  //     <dd>The download completed successfully.</dd>
  // </dl>
  enum State {in_progress, interrupted, complete};

  // The state of the process of downloading a file.
  dictionary DownloadItem {
    // An identifier that is persistent across browser sessions.
    long id;

    // Absolute URL.
    DOMString url;

    // Absolute URL.
    DOMString referrer;

    // Absolute local path.
    DOMString filename;

    // False if this download is recorded in the history, true if it is not
    // recorded.
    boolean incognito;

    // Indication of whether this download is thought to be safe or known to be
    // suspicious.
    DangerType danger;

    // The file's MIME type.
    DOMString mime;

    // The time when the download began in ISO 8601 format. May be passed
    // directly to the Date constructor: <code>chrome.downloads.search({},
    // function(items){items.forEach(function(item){console.log(new
    // Date(item.startTime))})})</code>
    DOMString startTime;

    // The time when the download ended in ISO 8601 format. May be passed
    // directly to the Date constructor: <code>chrome.downloads.search({},
    // function(items){items.forEach(function(item){if (item.endTime)
    // console.log(new Date(item.endTime))})})</code>
    DOMString? endTime;

    // Estimated time when the download will complete in ISO 8601 format. May be
    // passed directly to the Date constructor:
    // <code>chrome.downloads.search({},
    // function(items){items.forEach(function(item){if (item.estimatedEndTime)
    // console.log(new Date(item.estimatedEndTime))})})</code>
    DOMString? estimatedEndTime;

    // Indicates whether the download is progressing, interrupted, or complete.
    State state;

    // True if the download has stopped reading data from the host, but kept the
    // connection open.
    boolean paused;

    // True if the download is in progress and paused, or else if it is
    // interrupted and can be resumed starting from where it was interrupted.
    boolean canResume;

    // Why the download was interrupted. Several kinds of HTTP errors may be
    // grouped under one of the errors beginning with <code>SERVER_</code>.
    // Errors relating to the network begin with <code>NETWORK_</code>, errors
    // relating to the process of writing the file to the file system begin with
    // <code>FILE_</code>, and interruptions initiated by the user begin with
    // <code>USER_</code>.
    InterruptReason? error;

    // Number of bytes received so far from the host, without considering file
    // compression.
    double bytesReceived;

    // Number of bytes in the whole file, without considering file compression,
    // or -1 if unknown.
    double totalBytes;

    // Number of bytes in the whole file post-decompression, or -1 if unknown.
    double fileSize;

    // Whether the downloaded file still exists. This information may be out of
    // date because Chrome does not automatically watch for file removal. Call
    // $(ref:search)() in order to trigger the check for file existence. When the
    // existence check completes, if the file has been deleted, then an
    // $(ref:onChanged) event will fire. Note that $(ref:search)() does not wait
    // for the existence check to finish before returning, so results from
    // $(ref:search)() may not accurately reflect the file system. Also,
    // $(ref:search)() may be called as often as necessary, but will not check for
    // file existence any more frequently than once every 10 seconds.
    boolean exists;

    // The identifier for the extension that initiated this download if this
    // download was initiated by an extension. Does not change once it is set.
    DOMString? byExtensionId;

    // The localized name of the extension that initiated this download if this
    // download was initiated by an extension. May change if the extension
    // changes its name or if the user changes their locale.
    DOMString? byExtensionName;
  };

  [inline_doc] dictionary DownloadQuery {
    // This array of search terms limits results to $(ref:DownloadItem) whose
    // <code>filename</code> or <code>url</code> contain all of the search terms
    // that do not begin with a dash '-' and none of the search terms that do
    // begin with a dash.
    DOMString[]? query;

    // Limits results to $(ref:DownloadItem) that
    // started before the given ms since the epoch.
    DOMString? startedBefore;

    // Limits results to $(ref:DownloadItem) that
    // started after the given ms since the epoch.
    DOMString? startedAfter;

    // Limits results to $(ref:DownloadItem) that ended before the given ms since the
    // epoch.
    DOMString? endedBefore;

    // Limits results to $(ref:DownloadItem) that ended after the given ms since the
    // epoch.
    DOMString? endedAfter;

    // Limits results to $(ref:DownloadItem) whose
    // <code>totalBytes</code> is greater than the given integer.
    double? totalBytesGreater;

    // Limits results to $(ref:DownloadItem) whose
    // <code>totalBytes</code> is less than the given integer.
    double? totalBytesLess;

    // Limits results to $(ref:DownloadItem) whose
    // <code>filename</code> matches the given regular expression.
    DOMString? filenameRegex;

    // Limits results to $(ref:DownloadItem) whose
    // <code>url</code> matches the given regular expression.
    DOMString? urlRegex;

    // The maximum number of matching $(ref:DownloadItem) returned. Defaults to
    // 1000. Set to 0 in order to return all matching $(ref:DownloadItem). See
    // $(ref:search) for how to page through results.
    long? limit;

    // Set elements of this array to $(ref:DownloadItem) properties in order to
    // sort search results. For example, setting
    // <code>orderBy=['startTime']</code> sorts the $(ref:DownloadItem) by their
    // start time in ascending order. To specify descending order, prefix with a
    // hyphen: '-startTime'.
    DOMString[]? orderBy;

    // The <code>id</code> of the $(ref:DownloadItem) to query.
    long? id;

    // Absolute URL.
    DOMString? url;

    // Absolute local path.
    DOMString? filename;

    // Indication of whether this download is thought to be safe or known to be
    // suspicious.
    DangerType? danger;

    // The file's MIME type.
    DOMString? mime;

    // The time when the download began in ISO 8601 format.
    DOMString? startTime;

    // The time when the download ended in ISO 8601 format.
    DOMString? endTime;

    // Indicates whether the download is progressing, interrupted, or complete.
    State? state;

    // True if the download has stopped reading data from the host, but kept the
    // connection open.
    boolean? paused;

    // Why a download was interrupted.
    InterruptReason? error;

    // Number of bytes received so far from the host, without considering file
    // compression.
    double? bytesReceived;

    // Number of bytes in the whole file, without considering file compression,
    // or -1 if unknown.
    double? totalBytes;

    // Number of bytes in the whole file post-decompression, or -1 if unknown.
    double? fileSize;

    // Whether the downloaded file exists;
    boolean? exists;
  };

  dictionary StringDelta {
    DOMString? previous;
    DOMString? current;
  };

  dictionary DoubleDelta {
    double? previous;
    double? current;
  };

  dictionary BooleanDelta {
    boolean? previous;
    boolean? current;
  };

  // Encapsulates a change in a DownloadItem.
  [inline_doc] dictionary DownloadDelta {
    // The <code>id</code> of the $(ref:DownloadItem)
    // that changed.
    long id;

    // The change in <code>url</code>, if any.
    StringDelta? url;

    // The change in <code>filename</code>, if any.
    StringDelta? filename;

    // The change in <code>danger</code>, if any.
    StringDelta? danger;

    // The change in <code>mime</code>, if any.
    StringDelta? mime;

    // The change in <code>startTime</code>, if any.
    StringDelta? startTime;

    // The change in <code>endTime</code>, if any.
    StringDelta? endTime;

    // The change in <code>state</code>, if any.
    StringDelta? state;

    // The change in <code>canResume</code>, if any.
    BooleanDelta? canResume;

    // The change in <code>paused</code>, if any.
    BooleanDelta? paused;

    // The change in <code>error</code>, if any.
    StringDelta? error;

    // The change in <code>totalBytes</code>, if any.
    DoubleDelta? totalBytes;

    // The change in <code>fileSize</code>, if any.
    DoubleDelta? fileSize;

    // The change in <code>exists</code>, if any.
    BooleanDelta? exists;
  };

  [inline_doc] dictionary GetFileIconOptions {
    // The size of the returned icon. The icon will be square with dimensions
    // size * size pixels. The default and largest size for the icon is 32x32
    // pixels. The only supported sizes are 16 and 32. It is an error to specify
    // any other size.
    [legalValues=(16,32)] long? size;
  };

  callback DownloadCallback = void(long downloadId);
  callback SearchCallback = void(DownloadItem[] results);
  callback EraseCallback = void(long[] erasedIds);
  callback NullCallback = void();
  callback GetFileIconCallback = void(optional DOMString iconURL);
  callback SuggestFilenameCallback = void(
    optional FilenameSuggestion suggestion);

  interface Functions {
    // Download a URL. If the URL uses the HTTP[S] protocol, then the request
    // will include all cookies currently set for its hostname. If both
    // <code>filename</code> and <code>saveAs</code> are specified, then the
    // Save As dialog will be displayed, pre-populated with the specified
    // <code>filename</code>. If the download started successfully,
    // <code>callback</code> will be called with the new $(ref:DownloadItem)'s
    // <code>downloadId</code>. If there was an error starting the download,
    // then <code>callback</code> will be called with
    // <code>downloadId=undefined</code> and $(ref:runtime.lastError) will contain
    // a descriptive string. The error strings are not guaranteed to remain
    // backwards compatible between releases. Extensions must not parse it.
    // |options|: What to download and how.
    // |callback|: Called with the id of the new $(ref:DownloadItem).
    static void download(DownloadOptions options,
                         optional DownloadCallback callback);

    // Find $(ref:DownloadItem). Set <code>query</code> to the empty object to get
    // all $(ref:DownloadItem). To get a specific $(ref:DownloadItem), set only the
    // <code>id</code> field. To page through a large number of items, set
    // <code>orderBy: ['-startTime']</code>, set <code>limit</code> to the
    // number of items per page, and set <code>startedAfter</code> to the
    // <code>startTime</code> of the last item from the last page.
    static void search(DownloadQuery query, SearchCallback callback);

    // Pause the download. If the request was successful the download is in a
    // paused state. Otherwise $(ref:runtime.lastError) contains an error message.
    // The request will fail if the download is not active.
    // |downloadId|: The id of the download to pause.
    // |callback|: Called when the pause request is completed.
    static void pause(long downloadId, optional NullCallback callback);

    // Resume a paused download. If the request was successful the download is
    // in progress and unpaused. Otherwise $(ref:runtime.lastError) contains an
    // error message. The request will fail if the download is not active.
    // |downloadId|: The id of the download to resume.
    // |callback|: Called when the resume request is completed.
    static void resume(long downloadId, optional NullCallback callback);

    // Cancel a download. When <code>callback</code> is run, the download is
    // cancelled, completed, interrupted or doesn't exist anymore.
    // |downloadId|: The id of the download to cancel.
    // |callback|: Called when the cancel request is completed.
    static void cancel(long downloadId, optional NullCallback callback);

    // Retrieve an icon for the specified download. For new downloads, file
    // icons are available after the $(ref:onCreated) event has been received. The
    // image returned by this function while a download is in progress may be
    // different from the image returned after the download is complete. Icon
    // retrieval is done by querying the underlying operating system or toolkit
    // depending on the platform. The icon that is returned will therefore
    // depend on a number of factors including state of the download, platform,
    // registered file types and visual theme. If a file icon cannot be
    // determined, $(ref:runtime.lastError) will contain an error message.
    // |downloadId|: The identifier for the download.
    // |callback|: A URL to an image that represents the download.
    static void getFileIcon(long downloadId,
                            optional GetFileIconOptions options,
                            GetFileIconCallback callback);

    // Open the downloaded file now if the $(ref:DownloadItem) is complete;
    // otherwise returns an error through $(ref:runtime.lastError). Requires the
    // <code>"downloads.open"</code> permission in addition to the
    // <code>"downloads"</code> permission. An $(ref:onChanged) event will fire
    // when the item is opened for the first time.
    // |downloadId|: The identifier for the downloaded file.
    static void open(long downloadId);

    // Show the downloaded file in its folder in a file manager.
    // |downloadId|: The identifier for the downloaded file.
    static void show(long downloadId);

    // Show the default Downloads folder in a file manager.
    static void showDefaultFolder();

    // Erase matching $(ref:DownloadItem) from history without deleting the
    // downloaded file. An $(ref:onErased) event will fire for each
    // $(ref:DownloadItem) that matches <code>query</code>, then
    // <code>callback</code> will be called.
    static void erase(DownloadQuery query, optional EraseCallback callback);

    // Remove the downloaded file if it exists and the $(ref:DownloadItem) is
    // complete; otherwise return an error through $(ref:runtime.lastError).
    static void removeFile(long downloadId, optional NullCallback callback);

    // Prompt the user to accept a dangerous download. Can only be called from a
    // visible context (tab, window, or page/browser action popup). Does not
    // automatically accept dangerous downloads. If the download is accepted,
    // then an $(ref:onChanged) event will fire, otherwise nothing will happen.
    // When all the data is fetched into a temporary file and either the
    // download is not dangerous or the danger has been accepted, then the
    // temporary file is renamed to the target filename, the |state| changes to
    // 'complete', and $(ref:onChanged) fires.
    // |downloadId|: The identifier for the $(ref:DownloadItem).
    // |callback|: Called when the danger prompt dialog closes.
    static void acceptDanger(long downloadId, optional NullCallback callback);

    // Initiate dragging the downloaded file to another application. Call in a
    // javascript <code>ondragstart</code> handler.
    static void drag(long downloadId);

    // Enable or disable the gray shelf at the bottom of every window associated
    // with the current browser profile. The shelf will be disabled as long as
    // at least one extension has disabled it. Enabling the shelf while at least
    // one other extension has disabled it will return an error through
    // $(ref:runtime.lastError). Requires the <code>"downloads.shelf"</code>
    // permission in addition to the <code>"downloads"</code> permission.
    static void setShelfEnabled(boolean enabled);
  };

  interface Events {
    // This event fires with the $(ref:DownloadItem) object when a download
    // begins.
    static void onCreated(DownloadItem downloadItem);

    // Fires with the <code>downloadId</code> when a download is erased from
    // history.
    // |downloadId|: The <code>id</code> of the $(ref:DownloadItem) that was
    // erased.
    static void onErased(long downloadId);

    // When any of a $(ref:DownloadItem)'s properties except
    // <code>bytesReceived</code> and <code>estimatedEndTime</code> changes,
    // this event fires with the <code>downloadId</code> and an object
    // containing the properties that changed.
    static void onChanged(DownloadDelta downloadDelta);

    // During the filename determination process, extensions will be given the
    // opportunity to override the target $(ref:DownloadItem.filename). Each
    // extension may not register more than one listener for this event. Each
    // listener must call <code>suggest</code> exactly once, either
    // synchronously or asynchronously. If the listener calls
    // <code>suggest</code> asynchronously, then it must return
    // <code>true</code>. If the listener neither calls <code>suggest</code>
    // synchronously nor returns <code>true</code>, then <code>suggest</code>
    // will be called automatically. The $(ref:DownloadItem) will not complete
    // until all listeners have called <code>suggest</code>. Listeners may call
    // <code>suggest</code> without any arguments in order to allow the download
    // to use <code>downloadItem.filename</code> for its filename, or pass a
    // <code>suggestion</code> object to <code>suggest</code> in order to
    // override the target filename. If more than one extension overrides the
    // filename, then the last extension installed whose listener passes a
    // <code>suggestion</code> object to <code>suggest</code> wins. In order to
    // avoid confusion regarding which extension will win, users should not
    // install extensions that may conflict. If the download is initiated by
    // $(ref:download) and the target filename is known before the MIME type and
    // tentative filename have been determined, pass <code>filename</code> to
    // $(ref:download) instead.
    [maxListeners=1] static void onDeterminingFilename(
        DownloadItem downloadItem, SuggestFilenameCallback suggest);
  };
};
