XUL:Specs:TransferData

From MozillaWiki
Jump to: navigation, search

Current State

  • Clipboard and drag/drop APIs require too much code, and don't really do much of the hard work for you.

Requirements

  • Should be able to do clipboard and drag/drop operations without complex code.
  • Need drag/drop APIs that are unprivileged
  • Need to have an event which is fired on the drag source when a drag is cancelled and when a drop is done on another application
  • Drag images showing what is being dragged, for example icons or treecells.
  • Shouldn't have to serialize and deserialize DOM nodes or other objects when moving between the same application.

The WhatWG defines some drag and drop functionality, based on the IE model. It mostly concerns the way the drag/drop events are fired. Need clarfication on it, but it seems that only strings can be specified as the dragged data.

Security

  • An application should only be able to retrieve items being dragged from the same origin domain. This means that a drag can occur between different domains. For different domains, the drag data is only available to a different domain after the drop.
  • Clipboard operations are only available for privileged applications, apart from built in behaviour of widgets or shortcut keys.

WHAT-WG defined API

// defined in WHAT-WG spec
interface nsIDOMDragEvent : nsIDOMMouseEvent
{
  readonly attribute nsIDOMDataTransfer dataTransfer;

  void initDragEvent(in DOMString typeArg,
                     in boolean canBubbleArg,
                     in boolean cancelableArg);

  void initDragEventNS(in DOMString namespaceURIArg,
                       in DOMString typeArg,
                       in boolean canBubbleArg,
                       in boolean cancelableArg);
};

// defined in WHAT-WG spec
interface nsIDOMDataTransfer : nsISupports
{
  attribute DOMString dropEffect;
  attribute DOMString effectAllowed;

  void clearData(in DOMString format);
  void setData(in DOMString format, in DOMString data);
  DOMString getData(in DOMString format);
  void setDragImage(in nsIDOMElement image, in long x, in long y);
  void addElement(in nsIDOMElement element);
}

DataTransfer

A DataTransfer is used to hold data for drag and drop operations. It holds a list of DataTransferItem objects each of which holds an item being dragged. The DataTransfer can be retrieved from the drag event's dataTransfer property. It will initially contain no data to be dragged.

The WHAT-WG API may be used to add new data to be dragged. When setData is called, a new item is appended if there are no items and its data is set accordingly. If an item has already been added, the setData method modifies it. The appendItem and insertItem methods may be used to add additional items to be dragged.

XUL has two requirements that the WHAT-WG specification does not cover.

  • Being able to drag data that is not text, for instance images, files, or references to nodes.
  • Being able to specify more than one object to be dragged.

For this, some additional methods are added to the DataTransfer object via an additional interface:

interface nsIDOMNSDataTransfer : nsISupports
{
  [noscript] nsIDOMNSDataTransfer clone(in PRUint32 aEventType);

  // XXXndeakin Safari also has a types attribute that holds a list of the types

  /**
   * Retrieve the data for any format associated with the item being dragged
   * at index aIndex. This is a convenient shorthand for the common case
   * where the format is not important.
   */
  nsIVariant getAnyData(in unsigned long aIndex);

  /**
   * The number of items being dragged.
   */
  readonly attribute unsigned long itemCount;

  /**
   * Add a new item to be dragged.
   *
   * @param aItem the item to add
   */
  void appendItem(in nsIDOMDataTransferItem aItem);

  /**
   * Insert an item to be dragged.
   *
   * @param aItem the item to insert
   * @param aIndex the index to insert before
   * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if aIndex is out of range
   */
  void insertItem(in unsigned long aIndex, in nsIDOMDataTransferItem aItem);

  /**
   * Remove an item to be dragged.
   *
   * @param aItem the item to remove
   * @throws NS_ERROR_DOM_NOT_FOUND_ERR if aItem isn't found
   */
  void removeItem(in nsIDOMDataTransferItem aItem);

  /**
   * Retrieve an item being dragged.
   *
   * @param aItem the item to remove
   * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if aIndex is out of range
   */
  nsIDOMDataTransferItem itemAt(in unsigned long aIndex);
};

DataTransferItem

A DataTransferItem holds data to be used for inter-application transfer, such as the data being dragged during a drag and drop operation or the data on the clipboard. The data may be provided in multiple formats, where each format is a MIME type, such as text/plain or image/png. Multiple formats are used to provide data in more specific formats for those applications that can receive them, and more general formats for applications that can only handle certain formats.

The format text/plain should be used for string data. For compatability, the format text/unicode is equivalent to text/plain. Data stored in either will be retrievable in either format as well.

The data may be any object, stored as an nsIVariant. In script, nsIVariant objects may hold any type of data, and will automatically convert between the script type and the nsIVariant. For instance, a string may supplied directly to the methods below which accept an nsIVariant as an argument.

Generally, only objects that implement nsISupports and strings are stored. Other primitive formats such as boolean and integer are converted to strings when stored.

String data may be added to the DataTransferItem using the setData method. Data is stored in the order that it is added. Thus, data of formats with greater specificity should be added first while data that is more general should be added last. Generally, text/plain string data is the most likely format a drop target will be able to support at minimum, so this format should be added last.

All data added to the DataTransferItem is stored with the principal of the caller adding the data. Code with lower privileges that cannot access objects of that principal will not be able to access or change the data, and a security exception will be thrown. For example, if a privileged application adds data, an ordinary web page will not be able to access data via that format, however, it may add data of other formats.

interface nsIDOMDataTransferItem : nsISupports
{
  /**
   * Converts the data into an nsITransferable to be used for drag and drop or
   * clipboard operations. Usually, you don't need to call this yourself.
   */
  readonly attribute nsITransferable transferable;

  /**
   * Holds a list of the formats of the data that is stored, in the same order
   * the data was added.
   */
  readonly attribute nsIDOMDOMStringList formats;

  /**
   * Determine whether data of the given format is present.
   *
   * @param format format of the data to look up
   * @returns true if data of the given format exists
   * @throws NS_ERROR_DOM_INVALID_CHARACTER_ERR for empty format strings
   */
  boolean hasData(in AString format);

  /**
   * Retrieve the data for a particular format, or null if it doesn't exist.
   *
   * @param format the format of the data to look up
   * @returns the data of the given format, or null if it doesn't exist.
   * @throws NS_ERROR_DOM_INVALID_CHARACTER_ERR for empty format strings
   */
  nsIVariant getData(in AString format);

  /**
   * Retrieve the data from a set of formats, or null if it doesn't exist.
   * The set of formats is supplied in the formats argument, and is a space
   * separated list of formats to look for. This method will return the
   * data corresponding to the first format that is found, or null if
   * the data was not found. The found format is set in the out argument
   * format.
   *
   * If the formats argument is an empty string, then the first data of
   * any format is returned.
   *  
   * This method is most useful when the caller wants to retrieve the best
   * data given a set of formats that the caller knows can be supported.
   *
   * @param formats a space separated list of formats to look for
   * @param foundFormat [out] the format that was found, or empty if not found
   * @returns the data of the first found format, or null if none exist.
   */
  nsIVariant getDataInSet(in AString formats, out AString foundFormat);

  /**
   * Add data to the transferable of the given format. Data should be added in
   * order of preference, with the most specific format first and the least
   * specific format last. If data of the given format already exists, it is
   * replaced in the same position as the old data.
   *
   * If data implements nsIFlavorDataProvider, the real data will be requested
   * through the flavor data provider only when it is accessed. This would be
   * used when the data is large or when it would be a lengthy computation to
   * compute it.
   *
   * @param format the format to add
   * @param data the data to add
   * @throws NS_ERROR_DOM_INVALID_CHARACTER_ERR for empty format strings
   * @throws NS_ERROR_NULL_POINTER if the data is null
   */
  void setData(in AString format, in nsIVariant data);

  /**
   * Remove the data associated with the given format.
   *
   * @param format the format to remove
   * @throws NS_ERROR_DOM_INVALID_CHARACTER_ERR for empty format strings
   * @throws NS_ERROR_DOM_NOT_FOUND_ERR if the data of the format doesn't exist 
   */
  void clearData(in AString format);

  /**
   * Indicate that any caller may access the data contained within the
   * transferable, and not just those with appropriate principals. This
   * effectively clears the principals that were stored with the data.
   * This is called when dropping the data.
   */
  [noscript] void allowAnyAccess();
};