toolkit/content/nsDragAndDrop.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/content/nsDragAndDrop.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,604 @@
     1.4 +// -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 +
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +////////////////////////////////////////////////////////////////////////
    1.11 +//
    1.12 +// USE OF THIS API FOR DRAG AND DROP IS DEPRECATED!
    1.13 +// Do not use this file for new code.
    1.14 +//
    1.15 +// For documentation about what to use instead, see:
    1.16 +//   http://developer.mozilla.org/En/DragDrop/Drag_and_Drop
    1.17 +//
    1.18 +////////////////////////////////////////////////////////////////////////
    1.19 +
    1.20 +
    1.21 +/** 
    1.22 + *  nsTransferable - a wrapper for nsITransferable that simplifies
    1.23 + *                   javascript clipboard and drag&drop. for use in
    1.24 + *                   these situations you should use the nsClipboard
    1.25 + *                   and nsDragAndDrop wrappers for more convenience
    1.26 + **/ 
    1.27 + 
    1.28 +var nsTransferable = {
    1.29 +  /**
    1.30 +   * nsITransferable set (TransferData aTransferData) ;
    1.31 +   *
    1.32 +   * Creates a transferable with data for a list of supported types ("flavours")
    1.33 +   * 
    1.34 +   * @param TransferData aTransferData
    1.35 +   *        a javascript object in the format described above 
    1.36 +   **/ 
    1.37 +  set: function (aTransferDataSet)
    1.38 +    {
    1.39 +      var trans = this.createTransferable();
    1.40 +      for (var i = 0; i < aTransferDataSet.dataList.length; ++i) 
    1.41 +        {
    1.42 +          var currData = aTransferDataSet.dataList[i];
    1.43 +          var currFlavour = currData.flavour.contentType;
    1.44 +          trans.addDataFlavor(currFlavour);
    1.45 +          var supports = null; // nsISupports data
    1.46 +          var length = 0;
    1.47 +          if (currData.flavour.dataIIDKey == "nsISupportsString")
    1.48 +            {
    1.49 +              supports = Components.classes["@mozilla.org/supports-string;1"]
    1.50 +                                   .createInstance(Components.interfaces.nsISupportsString);
    1.51 +
    1.52 +              supports.data = currData.supports;
    1.53 +              length = supports.data.length;
    1.54 +            }
    1.55 +          else 
    1.56 +            {
    1.57 +              // non-string data.
    1.58 +              supports = currData.supports;
    1.59 +              length = 0; // kFlavorHasDataProvider
    1.60 +            }
    1.61 +          trans.setTransferData(currFlavour, supports, length * 2);
    1.62 +        }
    1.63 +      return trans;
    1.64 +    },
    1.65 +  
    1.66 +  /**
    1.67 +   * TransferData/TransferDataSet get (FlavourSet aFlavourSet, 
    1.68 +   *                                   Function aRetrievalFunc, Boolean aAnyFlag) ;
    1.69 +   *
    1.70 +   * Retrieves data from the transferable provided in aRetrievalFunc, formatted
    1.71 +   * for more convenient access.
    1.72 +   *
    1.73 +   * @param FlavourSet aFlavourSet
    1.74 +   *        a FlavourSet object that contains a list of supported flavours.
    1.75 +   * @param Function aRetrievalFunc
    1.76 +   *        a reference to a function that returns a nsISupportsArray of nsITransferables
    1.77 +   *        for each item from the specified source (clipboard/drag&drop etc)
    1.78 +   * @param Boolean aAnyFlag
    1.79 +   *        a flag specifying whether or not a specific flavour is requested. If false,
    1.80 +   *        data of the type of the first flavour in the flavourlist parameter is returned,
    1.81 +   *        otherwise the best flavour supported will be returned.
    1.82 +   **/
    1.83 +  get: function (aFlavourSet, aRetrievalFunc, aAnyFlag)
    1.84 +    {
    1.85 +      if (!aRetrievalFunc) 
    1.86 +        throw "No data retrieval handler provided!";
    1.87 +      
    1.88 +      var supportsArray = aRetrievalFunc(aFlavourSet);
    1.89 +      var dataArray = [];
    1.90 +      var count = supportsArray.Count();
    1.91 +      
    1.92 +      // Iterate over the number of items returned from aRetrievalFunc. For
    1.93 +      // clipboard operations, this is 1, for drag and drop (where multiple
    1.94 +      // items may have been dragged) this could be >1.
    1.95 +      for (var i = 0; i < count; i++)
    1.96 +        {
    1.97 +          var trans = supportsArray.GetElementAt(i);
    1.98 +          if (!trans) continue;
    1.99 +          trans = trans.QueryInterface(Components.interfaces.nsITransferable);
   1.100 +            
   1.101 +          var data = { };
   1.102 +          var length = { };
   1.103 +          
   1.104 +          var currData = null;
   1.105 +          if (aAnyFlag)
   1.106 +            { 
   1.107 +              var flavour = { };
   1.108 +              trans.getAnyTransferData(flavour, data, length);
   1.109 +              if (data && flavour)
   1.110 +                {
   1.111 +                  var selectedFlavour = aFlavourSet.flavourTable[flavour.value];
   1.112 +                  if (selectedFlavour) 
   1.113 +                    dataArray[i] = FlavourToXfer(data.value, length.value, selectedFlavour);
   1.114 +                }
   1.115 +            }
   1.116 +          else
   1.117 +            {
   1.118 +              var firstFlavour = aFlavourSet.flavours[0];
   1.119 +              trans.getTransferData(firstFlavour, data, length);
   1.120 +              if (data && firstFlavour)
   1.121 +                dataArray[i] = FlavourToXfer(data.value, length.value, firstFlavour);
   1.122 +            }
   1.123 +        }
   1.124 +      return new TransferDataSet(dataArray);
   1.125 +    },
   1.126 +
   1.127 +  /** 
   1.128 +   * nsITransferable createTransferable (void) ;
   1.129 +   *
   1.130 +   * Creates and returns a transferable object.
   1.131 +   **/    
   1.132 +  createTransferable: function ()
   1.133 +    {
   1.134 +      const kXferableContractID = "@mozilla.org/widget/transferable;1";
   1.135 +      const kXferableIID = Components.interfaces.nsITransferable;
   1.136 +      var trans = Components.classes[kXferableContractID].createInstance(kXferableIID);
   1.137 +      trans.init(null);
   1.138 +      return trans;
   1.139 +    }
   1.140 +};  
   1.141 +
   1.142 +/** 
   1.143 + * A FlavourSet is a simple type that represents a collection of Flavour objects.
   1.144 + * FlavourSet is constructed from an array of Flavours, and stores this list as
   1.145 + * an array and a hashtable. The rationale for the dual storage is as follows:
   1.146 + * 
   1.147 + * Array: Ordering is important when adding data flavours to a transferable. 
   1.148 + *        Flavours added first are deemed to be 'preferred' by the client. 
   1.149 + * Hash:  Convenient lookup of flavour data using the content type (MIME type)
   1.150 + *        of data as a key. 
   1.151 + */
   1.152 +function FlavourSet(aFlavourList)
   1.153 +{
   1.154 +  this.flavours = aFlavourList || [];
   1.155 +  this.flavourTable = { };
   1.156 +
   1.157 +  this._XferID = "FlavourSet";
   1.158 +  
   1.159 +  for (var i = 0; i < this.flavours.length; ++i)
   1.160 +    this.flavourTable[this.flavours[i].contentType] = this.flavours[i];
   1.161 +}
   1.162 +
   1.163 +FlavourSet.prototype = {
   1.164 +  appendFlavour: function (aFlavour, aFlavourIIDKey)
   1.165 +  {
   1.166 +    var flavour = new Flavour (aFlavour, aFlavourIIDKey);
   1.167 +    this.flavours.push(flavour);
   1.168 +    this.flavourTable[flavour.contentType] = flavour;
   1.169 +  }
   1.170 +};
   1.171 +
   1.172 +/** 
   1.173 + * A Flavour is a simple type that represents a data type that can be handled. 
   1.174 + * It takes a content type (MIME type) which is used when storing data on the
   1.175 + * system clipboard/drag and drop, and an IIDKey (string interface name
   1.176 + * which is used to QI data to an appropriate form. The default interface is
   1.177 + * assumed to be wide-string.
   1.178 + */ 
   1.179 +function Flavour(aContentType, aDataIIDKey)
   1.180 +{
   1.181 +  this.contentType = aContentType;
   1.182 +  this.dataIIDKey = aDataIIDKey || "nsISupportsString";
   1.183 +
   1.184 +  this._XferID = "Flavour";
   1.185 +}
   1.186 +
   1.187 +function TransferDataBase() {}
   1.188 +TransferDataBase.prototype = {
   1.189 +  push: function (aItems)
   1.190 +  {
   1.191 +    this.dataList.push(aItems);
   1.192 +  },
   1.193 +
   1.194 +  get first ()
   1.195 +  {
   1.196 +    return "dataList" in this && this.dataList.length ? this.dataList[0] : null;
   1.197 +  }
   1.198 +};
   1.199 +
   1.200 +/** 
   1.201 + * TransferDataSet is a list (array) of TransferData objects, which represents
   1.202 + * data dragged from one or more elements. 
   1.203 + */
   1.204 +function TransferDataSet(aTransferDataList)
   1.205 +{
   1.206 +  this.dataList = aTransferDataList || [];
   1.207 +
   1.208 +  this._XferID = "TransferDataSet";
   1.209 +}
   1.210 +TransferDataSet.prototype = TransferDataBase.prototype;
   1.211 +
   1.212 +/** 
   1.213 + * TransferData is a list (array) of FlavourData for all the applicable content
   1.214 + * types associated with a drag from a single item. 
   1.215 + */
   1.216 +function TransferData(aFlavourDataList)
   1.217 +{
   1.218 +  this.dataList = aFlavourDataList || [];
   1.219 +
   1.220 +  this._XferID = "TransferData";
   1.221 +}
   1.222 +TransferData.prototype = {
   1.223 +  __proto__: TransferDataBase.prototype,
   1.224 +  
   1.225 +  addDataForFlavour: function (aFlavourString, aData, aLength, aDataIIDKey)
   1.226 +  {
   1.227 +    this.dataList.push(new FlavourData(aData, aLength, 
   1.228 +                       new Flavour(aFlavourString, aDataIIDKey)));
   1.229 +  }
   1.230 +};
   1.231 +
   1.232 +/** 
   1.233 + * FlavourData is a type that represents data retrieved from the system 
   1.234 + * clipboard or drag and drop. It is constructed internally by the Transferable
   1.235 + * using the raw (nsISupports) data from the clipboard, the length of the data,
   1.236 + * and an object of type Flavour representing the type. Clients implementing
   1.237 + * IDragDropObserver receive an object of this type in their implementation of
   1.238 + * onDrop. They access the 'data' property to retrieve data, which is either data 
   1.239 + * QI'ed to a usable form, or unicode string. 
   1.240 + */
   1.241 +function FlavourData(aData, aLength, aFlavour) 
   1.242 +{
   1.243 +  this.supports = aData;
   1.244 +  this.contentLength = aLength;
   1.245 +  this.flavour = aFlavour || null;
   1.246 +  
   1.247 +  this._XferID = "FlavourData";
   1.248 +}
   1.249 +
   1.250 +FlavourData.prototype = {
   1.251 +  get data ()
   1.252 +  {
   1.253 +    if (this.flavour &&
   1.254 +        this.flavour.dataIIDKey != "nsISupportsString")
   1.255 +      return this.supports.QueryInterface(Components.interfaces[this.flavour.dataIIDKey]); 
   1.256 +
   1.257 +    var supports = this.supports;
   1.258 +    if (supports instanceof Components.interfaces.nsISupportsString)
   1.259 +      return supports.data.substring(0, this.contentLength/2);
   1.260 +     
   1.261 +    return supports;
   1.262 +  }
   1.263 +}
   1.264 +
   1.265 +/** 
   1.266 + * Create a TransferData object with a single FlavourData entry. Used when 
   1.267 + * unwrapping data of a specific flavour from the drag service. 
   1.268 + */
   1.269 +function FlavourToXfer(aData, aLength, aFlavour) 
   1.270 +{
   1.271 +  return new TransferData([new FlavourData(aData, aLength, aFlavour)]);
   1.272 +}
   1.273 +
   1.274 +var transferUtils = {
   1.275 +
   1.276 +  retrieveURLFromData: function (aData, flavour)
   1.277 +  {
   1.278 +    switch (flavour) {
   1.279 +      case "text/unicode":
   1.280 +      case "text/plain":
   1.281 +      case "text/x-moz-text-internal":
   1.282 +        return aData.replace(/^\s+|\s+$/g, "");
   1.283 +      case "text/x-moz-url":
   1.284 +        return ((aData instanceof Components.interfaces.nsISupportsString) ? aData.toString() : aData).split("\n")[0];
   1.285 +      case "application/x-moz-file":
   1.286 +        var ioService = Components.classes["@mozilla.org/network/io-service;1"]
   1.287 +                                  .getService(Components.interfaces.nsIIOService);
   1.288 +        var fileHandler = ioService.getProtocolHandler("file")
   1.289 +                                   .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
   1.290 +        return fileHandler.getURLSpecFromFile(aData);
   1.291 +    }
   1.292 +    return null;                                                   
   1.293 +  }
   1.294 +
   1.295 +}
   1.296 +
   1.297 +/**
   1.298 + * nsDragAndDrop - a convenience wrapper for nsTransferable, nsITransferable
   1.299 + *                 and nsIDragService/nsIDragSession. 
   1.300 + *
   1.301 + * Use: map the handler functions to the 'ondraggesture', 'ondragover' and
   1.302 + *   'ondragdrop' event handlers on your XML element, e.g.                   
   1.303 + *   <xmlelement ondraggesture="nsDragAndDrop.startDrag(event, observer);"   
   1.304 + *               ondragover="nsDragAndDrop.dragOver(event, observer);"      
   1.305 + *               ondragdrop="nsDragAndDrop.drop(event, observer);"/>         
   1.306 + *                                                                           
   1.307 + *   You need to create an observer js object with the following member      
   1.308 + *   functions:                                                              
   1.309 + *     Object onDragStart (event)        // called when drag initiated,      
   1.310 + *                                       // returns flavour list with data   
   1.311 + *                                       // to stuff into transferable      
   1.312 + *     void onDragOver (Object flavour)  // called when element is dragged   
   1.313 + *                                       // over, so that it can perform     
   1.314 + *                                       // any drag-over feedback for provided
   1.315 + *                                       // flavour                          
   1.316 + *     void onDrop (Object data)         // formatted data object dropped.   
   1.317 + *     Object getSupportedFlavours ()    // returns a flavour list so that   
   1.318 + *                                       // nsTransferable can determine
   1.319 + *                                       // whether or not to accept drop. 
   1.320 + **/   
   1.321 +
   1.322 +var nsDragAndDrop = {
   1.323 +  
   1.324 +  _mDS: null,
   1.325 +  get mDragService()
   1.326 +    {
   1.327 +      if (!this._mDS) 
   1.328 +        {
   1.329 +          const kDSContractID = "@mozilla.org/widget/dragservice;1";
   1.330 +          const kDSIID = Components.interfaces.nsIDragService;
   1.331 +          this._mDS = Components.classes[kDSContractID].getService(kDSIID);
   1.332 +        }
   1.333 +      return this._mDS;
   1.334 +    },
   1.335 +
   1.336 +  /**
   1.337 +   * void startDrag (DOMEvent aEvent, Object aDragDropObserver) ;
   1.338 +   *
   1.339 +   * called when a drag on an element is started.
   1.340 +   *
   1.341 +   * @param DOMEvent aEvent
   1.342 +   *        the DOM event fired by the drag init
   1.343 +   * @param Object aDragDropObserver
   1.344 +   *        javascript object of format described above that specifies
   1.345 +   *        the way in which the element responds to drag events.
   1.346 +   **/  
   1.347 +  startDrag: function (aEvent, aDragDropObserver)
   1.348 +    {
   1.349 +      if (!("onDragStart" in aDragDropObserver))
   1.350 +        return;
   1.351 +
   1.352 +      const kDSIID = Components.interfaces.nsIDragService;
   1.353 +      var dragAction = { action: kDSIID.DRAGDROP_ACTION_COPY + kDSIID.DRAGDROP_ACTION_MOVE + kDSIID.DRAGDROP_ACTION_LINK };
   1.354 +
   1.355 +      var transferData = { data: null };
   1.356 +      try 
   1.357 +        {
   1.358 +          aDragDropObserver.onDragStart(aEvent, transferData, dragAction);
   1.359 +        }
   1.360 +      catch (e) 
   1.361 +        {
   1.362 +          return;  // not a draggable item, bail!
   1.363 +        }
   1.364 +
   1.365 +      if (!transferData.data) return;
   1.366 +      transferData = transferData.data;
   1.367 +
   1.368 +      var dt = aEvent.dataTransfer;
   1.369 +      var count = 0;
   1.370 +      do {
   1.371 +        var tds = transferData._XferID == "TransferData" 
   1.372 +                                         ? transferData 
   1.373 +                                         : transferData.dataList[count]
   1.374 +        for (var i = 0; i < tds.dataList.length; ++i) 
   1.375 +        {
   1.376 +          var currData = tds.dataList[i];
   1.377 +          var currFlavour = currData.flavour.contentType;
   1.378 +          var value = currData.supports;
   1.379 +          if (value instanceof Components.interfaces.nsISupportsString)
   1.380 +            value = value.toString();
   1.381 +          dt.mozSetDataAt(currFlavour, value, count);
   1.382 +        }
   1.383 +
   1.384 +        count++;
   1.385 +      }
   1.386 +      while (transferData._XferID == "TransferDataSet" && 
   1.387 +             count < transferData.dataList.length);
   1.388 +
   1.389 +      dt.effectAllowed = "all";
   1.390 +      // a drag targeted at a tree should instead use the treechildren so that
   1.391 +      // the current selection is used as the drag feedback
   1.392 +      dt.addElement(aEvent.originalTarget.localName == "treechildren" ?
   1.393 +                    aEvent.originalTarget : aEvent.target);
   1.394 +      aEvent.stopPropagation();
   1.395 +    },
   1.396 +
   1.397 +  /** 
   1.398 +   * void dragOver (DOMEvent aEvent, Object aDragDropObserver) ;
   1.399 +   *
   1.400 +   * called when a drag passes over this element
   1.401 +   *
   1.402 +   * @param DOMEvent aEvent
   1.403 +   *        the DOM event fired by passing over the element
   1.404 +   * @param Object aDragDropObserver
   1.405 +   *        javascript object of format described above that specifies
   1.406 +   *        the way in which the element responds to drag events.
   1.407 +   **/
   1.408 +  dragOver: function (aEvent, aDragDropObserver)
   1.409 +    { 
   1.410 +      if (!("onDragOver" in aDragDropObserver)) 
   1.411 +        return;
   1.412 +      if (!this.checkCanDrop(aEvent, aDragDropObserver))
   1.413 +        return;
   1.414 +      var flavourSet = aDragDropObserver.getSupportedFlavours();
   1.415 +      for (var flavour in flavourSet.flavourTable)
   1.416 +        {
   1.417 +          if (this.mDragSession.isDataFlavorSupported(flavour))
   1.418 +            {
   1.419 +              aDragDropObserver.onDragOver(aEvent, 
   1.420 +                                           flavourSet.flavourTable[flavour], 
   1.421 +                                           this.mDragSession);
   1.422 +              aEvent.stopPropagation();
   1.423 +              aEvent.preventDefault();
   1.424 +              break;
   1.425 +            }
   1.426 +        }
   1.427 +    },
   1.428 +
   1.429 +  mDragSession: null,
   1.430 +
   1.431 +  /** 
   1.432 +   * void drop (DOMEvent aEvent, Object aDragDropObserver) ;
   1.433 +   *
   1.434 +   * called when the user drops on the element
   1.435 +   *
   1.436 +   * @param DOMEvent aEvent
   1.437 +   *        the DOM event fired by the drop
   1.438 +   * @param Object aDragDropObserver
   1.439 +   *        javascript object of format described above that specifies
   1.440 +   *        the way in which the element responds to drag events.
   1.441 +   **/
   1.442 +  drop: function (aEvent, aDragDropObserver)
   1.443 +    {
   1.444 +      if (!("onDrop" in aDragDropObserver))
   1.445 +        return;
   1.446 +      if (!this.checkCanDrop(aEvent, aDragDropObserver))
   1.447 +        return;  
   1.448 +
   1.449 +      var flavourSet = aDragDropObserver.getSupportedFlavours();
   1.450 +
   1.451 +      var dt = aEvent.dataTransfer;
   1.452 +      var dataArray = [];
   1.453 +      var count = dt.mozItemCount;
   1.454 +      for (var i = 0; i < count; ++i) {
   1.455 +        var types = dt.mozTypesAt(i);
   1.456 +        for (var j = 0; j < flavourSet.flavours.length; j++) {
   1.457 +          var type = flavourSet.flavours[j].contentType;
   1.458 +          // dataTransfer uses text/plain but older code used text/unicode, so
   1.459 +          // switch this for compatibility
   1.460 +          var modtype = (type == "text/unicode") ? "text/plain" : type;
   1.461 +          if (Array.indexOf(types, modtype) >= 0) {
   1.462 +            var data = dt.mozGetDataAt(modtype, i);
   1.463 +            if (data) {
   1.464 +              // Non-strings need some non-zero value used for their data length.
   1.465 +              const kNonStringDataLength = 4;
   1.466 +
   1.467 +              var length = (typeof data == "string") ? data.length : kNonStringDataLength;
   1.468 +              dataArray[i] = FlavourToXfer(data, length, flavourSet.flavourTable[type]);
   1.469 +              break;
   1.470 +            }
   1.471 +          }
   1.472 +        }
   1.473 +      }
   1.474 +
   1.475 +      var transferData = new TransferDataSet(dataArray)
   1.476 +
   1.477 +      // hand over to the client to respond to dropped data
   1.478 +      var multiple = "canHandleMultipleItems" in aDragDropObserver && aDragDropObserver.canHandleMultipleItems;
   1.479 +      var dropData = multiple ? transferData : transferData.first.first;
   1.480 +      aDragDropObserver.onDrop(aEvent, dropData, this.mDragSession);
   1.481 +      aEvent.stopPropagation();
   1.482 +    },
   1.483 +
   1.484 +  /** 
   1.485 +   * void dragExit (DOMEvent aEvent, Object aDragDropObserver) ;
   1.486 +   *
   1.487 +   * called when a drag leaves this element
   1.488 +   *
   1.489 +   * @param DOMEvent aEvent
   1.490 +   *        the DOM event fired by leaving the element
   1.491 +   * @param Object aDragDropObserver
   1.492 +   *        javascript object of format described above that specifies
   1.493 +   *        the way in which the element responds to drag events.
   1.494 +   **/
   1.495 +  dragExit: function (aEvent, aDragDropObserver)
   1.496 +    {
   1.497 +      if (!this.checkCanDrop(aEvent, aDragDropObserver))
   1.498 +        return;
   1.499 +      if ("onDragExit" in aDragDropObserver)
   1.500 +        aDragDropObserver.onDragExit(aEvent, this.mDragSession);
   1.501 +    },  
   1.502 +    
   1.503 +  /** 
   1.504 +   * void dragEnter (DOMEvent aEvent, Object aDragDropObserver) ;
   1.505 +   *
   1.506 +   * called when a drag enters in this element
   1.507 +   *
   1.508 +   * @param DOMEvent aEvent
   1.509 +   *        the DOM event fired by entering in the element
   1.510 +   * @param Object aDragDropObserver
   1.511 +   *        javascript object of format described above that specifies
   1.512 +   *        the way in which the element responds to drag events.
   1.513 +   **/
   1.514 +  dragEnter: function (aEvent, aDragDropObserver)
   1.515 +    {
   1.516 +      if (!this.checkCanDrop(aEvent, aDragDropObserver))
   1.517 +        return;
   1.518 +      if ("onDragEnter" in aDragDropObserver)
   1.519 +        aDragDropObserver.onDragEnter(aEvent, this.mDragSession);
   1.520 +    },  
   1.521 +
   1.522 +  /** 
   1.523 +   * Boolean checkCanDrop (DOMEvent aEvent, Object aDragDropObserver) ;
   1.524 +   *
   1.525 +   * Sets the canDrop attribute for the drag session.
   1.526 +   * returns false if there is no current drag session.
   1.527 +   *
   1.528 +   * @param DOMEvent aEvent
   1.529 +   *        the DOM event fired by the drop
   1.530 +   * @param Object aDragDropObserver
   1.531 +   *        javascript object of format described above that specifies
   1.532 +   *        the way in which the element responds to drag events.
   1.533 +   **/
   1.534 +  checkCanDrop: function (aEvent, aDragDropObserver)
   1.535 +    {
   1.536 +      if (!this.mDragSession) 
   1.537 +        this.mDragSession = this.mDragService.getCurrentSession();
   1.538 +      if (!this.mDragSession) 
   1.539 +        return false;
   1.540 +      this.mDragSession.canDrop = this.mDragSession.sourceNode != aEvent.target;
   1.541 +      if ("canDrop" in aDragDropObserver)
   1.542 +        this.mDragSession.canDrop &= aDragDropObserver.canDrop(aEvent, this.mDragSession);
   1.543 +      return true;
   1.544 +    },
   1.545 +
   1.546 +  /**
   1.547 +   * Do a security check for drag n' drop. Make sure the source document
   1.548 +   * can load the dragged link.
   1.549 +   *
   1.550 +   * @param DOMEvent aEvent
   1.551 +   *        the DOM event fired by leaving the element
   1.552 +   * @param Object aDragDropObserver
   1.553 +   *        javascript object of format described above that specifies
   1.554 +   *        the way in which the element responds to drag events.
   1.555 +   * @param String aDraggedText
   1.556 +   *        the text being dragged
   1.557 +   **/
   1.558 +  dragDropSecurityCheck: function (aEvent, aDragSession, aDraggedText)
   1.559 +    {
   1.560 +      // Strip leading and trailing whitespace, then try to create a
   1.561 +      // URI from the dropped string. If that succeeds, we're
   1.562 +      // dropping a URI and we need to do a security check to make
   1.563 +      // sure the source document can load the dropped URI. We don't
   1.564 +      // so much care about creating the real URI here
   1.565 +      // (i.e. encoding differences etc don't matter), we just want
   1.566 +      // to know if aDraggedText really is a URI.
   1.567 +
   1.568 +      aDraggedText = aDraggedText.replace(/^\s*|\s*$/g, '');
   1.569 +
   1.570 +      var uri;
   1.571 +      var ioService = Components.classes["@mozilla.org/network/io-service;1"]
   1.572 +                                .getService(Components.interfaces.nsIIOService);
   1.573 +      try {
   1.574 +        uri = ioService.newURI(aDraggedText, null, null);
   1.575 +      } catch (e) {
   1.576 +      }
   1.577 +
   1.578 +      if (!uri)
   1.579 +        return;
   1.580 +
   1.581 +      // aDraggedText is a URI, do the security check.
   1.582 +      const nsIScriptSecurityManager = Components.interfaces
   1.583 +                                                 .nsIScriptSecurityManager;
   1.584 +      var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
   1.585 +                             .getService(nsIScriptSecurityManager);
   1.586 +
   1.587 +      if (!aDragSession)
   1.588 +        aDragSession = this.mDragService.getCurrentSession();
   1.589 +
   1.590 +      var sourceDoc = aDragSession.sourceDocument;
   1.591 +      // Use "file:///" as the default sourceURI so that drops of file:// URIs
   1.592 +      // are always allowed.
   1.593 +      var principal = sourceDoc ? sourceDoc.nodePrincipal
   1.594 +                                : secMan.getSimpleCodebasePrincipal(ioService.newURI("file:///", null, null));
   1.595 +
   1.596 +      try {
   1.597 +        secMan.checkLoadURIStrWithPrincipal(principal, aDraggedText,
   1.598 +                                            nsIScriptSecurityManager.STANDARD);
   1.599 +      } catch (e) {
   1.600 +        // Stop event propagation right here.
   1.601 +        aEvent.stopPropagation();
   1.602 +
   1.603 +        throw "Drop of " + aDraggedText + " denied.";
   1.604 +      }
   1.605 +    }
   1.606 +};
   1.607 +

mercurial