Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 // -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 ////////////////////////////////////////////////////////////////////////
8 //
9 // USE OF THIS API FOR DRAG AND DROP IS DEPRECATED!
10 // Do not use this file for new code.
11 //
12 // For documentation about what to use instead, see:
13 // http://developer.mozilla.org/En/DragDrop/Drag_and_Drop
14 //
15 ////////////////////////////////////////////////////////////////////////
18 /**
19 * nsTransferable - a wrapper for nsITransferable that simplifies
20 * javascript clipboard and drag&drop. for use in
21 * these situations you should use the nsClipboard
22 * and nsDragAndDrop wrappers for more convenience
23 **/
25 var nsTransferable = {
26 /**
27 * nsITransferable set (TransferData aTransferData) ;
28 *
29 * Creates a transferable with data for a list of supported types ("flavours")
30 *
31 * @param TransferData aTransferData
32 * a javascript object in the format described above
33 **/
34 set: function (aTransferDataSet)
35 {
36 var trans = this.createTransferable();
37 for (var i = 0; i < aTransferDataSet.dataList.length; ++i)
38 {
39 var currData = aTransferDataSet.dataList[i];
40 var currFlavour = currData.flavour.contentType;
41 trans.addDataFlavor(currFlavour);
42 var supports = null; // nsISupports data
43 var length = 0;
44 if (currData.flavour.dataIIDKey == "nsISupportsString")
45 {
46 supports = Components.classes["@mozilla.org/supports-string;1"]
47 .createInstance(Components.interfaces.nsISupportsString);
49 supports.data = currData.supports;
50 length = supports.data.length;
51 }
52 else
53 {
54 // non-string data.
55 supports = currData.supports;
56 length = 0; // kFlavorHasDataProvider
57 }
58 trans.setTransferData(currFlavour, supports, length * 2);
59 }
60 return trans;
61 },
63 /**
64 * TransferData/TransferDataSet get (FlavourSet aFlavourSet,
65 * Function aRetrievalFunc, Boolean aAnyFlag) ;
66 *
67 * Retrieves data from the transferable provided in aRetrievalFunc, formatted
68 * for more convenient access.
69 *
70 * @param FlavourSet aFlavourSet
71 * a FlavourSet object that contains a list of supported flavours.
72 * @param Function aRetrievalFunc
73 * a reference to a function that returns a nsISupportsArray of nsITransferables
74 * for each item from the specified source (clipboard/drag&drop etc)
75 * @param Boolean aAnyFlag
76 * a flag specifying whether or not a specific flavour is requested. If false,
77 * data of the type of the first flavour in the flavourlist parameter is returned,
78 * otherwise the best flavour supported will be returned.
79 **/
80 get: function (aFlavourSet, aRetrievalFunc, aAnyFlag)
81 {
82 if (!aRetrievalFunc)
83 throw "No data retrieval handler provided!";
85 var supportsArray = aRetrievalFunc(aFlavourSet);
86 var dataArray = [];
87 var count = supportsArray.Count();
89 // Iterate over the number of items returned from aRetrievalFunc. For
90 // clipboard operations, this is 1, for drag and drop (where multiple
91 // items may have been dragged) this could be >1.
92 for (var i = 0; i < count; i++)
93 {
94 var trans = supportsArray.GetElementAt(i);
95 if (!trans) continue;
96 trans = trans.QueryInterface(Components.interfaces.nsITransferable);
98 var data = { };
99 var length = { };
101 var currData = null;
102 if (aAnyFlag)
103 {
104 var flavour = { };
105 trans.getAnyTransferData(flavour, data, length);
106 if (data && flavour)
107 {
108 var selectedFlavour = aFlavourSet.flavourTable[flavour.value];
109 if (selectedFlavour)
110 dataArray[i] = FlavourToXfer(data.value, length.value, selectedFlavour);
111 }
112 }
113 else
114 {
115 var firstFlavour = aFlavourSet.flavours[0];
116 trans.getTransferData(firstFlavour, data, length);
117 if (data && firstFlavour)
118 dataArray[i] = FlavourToXfer(data.value, length.value, firstFlavour);
119 }
120 }
121 return new TransferDataSet(dataArray);
122 },
124 /**
125 * nsITransferable createTransferable (void) ;
126 *
127 * Creates and returns a transferable object.
128 **/
129 createTransferable: function ()
130 {
131 const kXferableContractID = "@mozilla.org/widget/transferable;1";
132 const kXferableIID = Components.interfaces.nsITransferable;
133 var trans = Components.classes[kXferableContractID].createInstance(kXferableIID);
134 trans.init(null);
135 return trans;
136 }
137 };
139 /**
140 * A FlavourSet is a simple type that represents a collection of Flavour objects.
141 * FlavourSet is constructed from an array of Flavours, and stores this list as
142 * an array and a hashtable. The rationale for the dual storage is as follows:
143 *
144 * Array: Ordering is important when adding data flavours to a transferable.
145 * Flavours added first are deemed to be 'preferred' by the client.
146 * Hash: Convenient lookup of flavour data using the content type (MIME type)
147 * of data as a key.
148 */
149 function FlavourSet(aFlavourList)
150 {
151 this.flavours = aFlavourList || [];
152 this.flavourTable = { };
154 this._XferID = "FlavourSet";
156 for (var i = 0; i < this.flavours.length; ++i)
157 this.flavourTable[this.flavours[i].contentType] = this.flavours[i];
158 }
160 FlavourSet.prototype = {
161 appendFlavour: function (aFlavour, aFlavourIIDKey)
162 {
163 var flavour = new Flavour (aFlavour, aFlavourIIDKey);
164 this.flavours.push(flavour);
165 this.flavourTable[flavour.contentType] = flavour;
166 }
167 };
169 /**
170 * A Flavour is a simple type that represents a data type that can be handled.
171 * It takes a content type (MIME type) which is used when storing data on the
172 * system clipboard/drag and drop, and an IIDKey (string interface name
173 * which is used to QI data to an appropriate form. The default interface is
174 * assumed to be wide-string.
175 */
176 function Flavour(aContentType, aDataIIDKey)
177 {
178 this.contentType = aContentType;
179 this.dataIIDKey = aDataIIDKey || "nsISupportsString";
181 this._XferID = "Flavour";
182 }
184 function TransferDataBase() {}
185 TransferDataBase.prototype = {
186 push: function (aItems)
187 {
188 this.dataList.push(aItems);
189 },
191 get first ()
192 {
193 return "dataList" in this && this.dataList.length ? this.dataList[0] : null;
194 }
195 };
197 /**
198 * TransferDataSet is a list (array) of TransferData objects, which represents
199 * data dragged from one or more elements.
200 */
201 function TransferDataSet(aTransferDataList)
202 {
203 this.dataList = aTransferDataList || [];
205 this._XferID = "TransferDataSet";
206 }
207 TransferDataSet.prototype = TransferDataBase.prototype;
209 /**
210 * TransferData is a list (array) of FlavourData for all the applicable content
211 * types associated with a drag from a single item.
212 */
213 function TransferData(aFlavourDataList)
214 {
215 this.dataList = aFlavourDataList || [];
217 this._XferID = "TransferData";
218 }
219 TransferData.prototype = {
220 __proto__: TransferDataBase.prototype,
222 addDataForFlavour: function (aFlavourString, aData, aLength, aDataIIDKey)
223 {
224 this.dataList.push(new FlavourData(aData, aLength,
225 new Flavour(aFlavourString, aDataIIDKey)));
226 }
227 };
229 /**
230 * FlavourData is a type that represents data retrieved from the system
231 * clipboard or drag and drop. It is constructed internally by the Transferable
232 * using the raw (nsISupports) data from the clipboard, the length of the data,
233 * and an object of type Flavour representing the type. Clients implementing
234 * IDragDropObserver receive an object of this type in their implementation of
235 * onDrop. They access the 'data' property to retrieve data, which is either data
236 * QI'ed to a usable form, or unicode string.
237 */
238 function FlavourData(aData, aLength, aFlavour)
239 {
240 this.supports = aData;
241 this.contentLength = aLength;
242 this.flavour = aFlavour || null;
244 this._XferID = "FlavourData";
245 }
247 FlavourData.prototype = {
248 get data ()
249 {
250 if (this.flavour &&
251 this.flavour.dataIIDKey != "nsISupportsString")
252 return this.supports.QueryInterface(Components.interfaces[this.flavour.dataIIDKey]);
254 var supports = this.supports;
255 if (supports instanceof Components.interfaces.nsISupportsString)
256 return supports.data.substring(0, this.contentLength/2);
258 return supports;
259 }
260 }
262 /**
263 * Create a TransferData object with a single FlavourData entry. Used when
264 * unwrapping data of a specific flavour from the drag service.
265 */
266 function FlavourToXfer(aData, aLength, aFlavour)
267 {
268 return new TransferData([new FlavourData(aData, aLength, aFlavour)]);
269 }
271 var transferUtils = {
273 retrieveURLFromData: function (aData, flavour)
274 {
275 switch (flavour) {
276 case "text/unicode":
277 case "text/plain":
278 case "text/x-moz-text-internal":
279 return aData.replace(/^\s+|\s+$/g, "");
280 case "text/x-moz-url":
281 return ((aData instanceof Components.interfaces.nsISupportsString) ? aData.toString() : aData).split("\n")[0];
282 case "application/x-moz-file":
283 var ioService = Components.classes["@mozilla.org/network/io-service;1"]
284 .getService(Components.interfaces.nsIIOService);
285 var fileHandler = ioService.getProtocolHandler("file")
286 .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
287 return fileHandler.getURLSpecFromFile(aData);
288 }
289 return null;
290 }
292 }
294 /**
295 * nsDragAndDrop - a convenience wrapper for nsTransferable, nsITransferable
296 * and nsIDragService/nsIDragSession.
297 *
298 * Use: map the handler functions to the 'ondraggesture', 'ondragover' and
299 * 'ondragdrop' event handlers on your XML element, e.g.
300 * <xmlelement ondraggesture="nsDragAndDrop.startDrag(event, observer);"
301 * ondragover="nsDragAndDrop.dragOver(event, observer);"
302 * ondragdrop="nsDragAndDrop.drop(event, observer);"/>
303 *
304 * You need to create an observer js object with the following member
305 * functions:
306 * Object onDragStart (event) // called when drag initiated,
307 * // returns flavour list with data
308 * // to stuff into transferable
309 * void onDragOver (Object flavour) // called when element is dragged
310 * // over, so that it can perform
311 * // any drag-over feedback for provided
312 * // flavour
313 * void onDrop (Object data) // formatted data object dropped.
314 * Object getSupportedFlavours () // returns a flavour list so that
315 * // nsTransferable can determine
316 * // whether or not to accept drop.
317 **/
319 var nsDragAndDrop = {
321 _mDS: null,
322 get mDragService()
323 {
324 if (!this._mDS)
325 {
326 const kDSContractID = "@mozilla.org/widget/dragservice;1";
327 const kDSIID = Components.interfaces.nsIDragService;
328 this._mDS = Components.classes[kDSContractID].getService(kDSIID);
329 }
330 return this._mDS;
331 },
333 /**
334 * void startDrag (DOMEvent aEvent, Object aDragDropObserver) ;
335 *
336 * called when a drag on an element is started.
337 *
338 * @param DOMEvent aEvent
339 * the DOM event fired by the drag init
340 * @param Object aDragDropObserver
341 * javascript object of format described above that specifies
342 * the way in which the element responds to drag events.
343 **/
344 startDrag: function (aEvent, aDragDropObserver)
345 {
346 if (!("onDragStart" in aDragDropObserver))
347 return;
349 const kDSIID = Components.interfaces.nsIDragService;
350 var dragAction = { action: kDSIID.DRAGDROP_ACTION_COPY + kDSIID.DRAGDROP_ACTION_MOVE + kDSIID.DRAGDROP_ACTION_LINK };
352 var transferData = { data: null };
353 try
354 {
355 aDragDropObserver.onDragStart(aEvent, transferData, dragAction);
356 }
357 catch (e)
358 {
359 return; // not a draggable item, bail!
360 }
362 if (!transferData.data) return;
363 transferData = transferData.data;
365 var dt = aEvent.dataTransfer;
366 var count = 0;
367 do {
368 var tds = transferData._XferID == "TransferData"
369 ? transferData
370 : transferData.dataList[count]
371 for (var i = 0; i < tds.dataList.length; ++i)
372 {
373 var currData = tds.dataList[i];
374 var currFlavour = currData.flavour.contentType;
375 var value = currData.supports;
376 if (value instanceof Components.interfaces.nsISupportsString)
377 value = value.toString();
378 dt.mozSetDataAt(currFlavour, value, count);
379 }
381 count++;
382 }
383 while (transferData._XferID == "TransferDataSet" &&
384 count < transferData.dataList.length);
386 dt.effectAllowed = "all";
387 // a drag targeted at a tree should instead use the treechildren so that
388 // the current selection is used as the drag feedback
389 dt.addElement(aEvent.originalTarget.localName == "treechildren" ?
390 aEvent.originalTarget : aEvent.target);
391 aEvent.stopPropagation();
392 },
394 /**
395 * void dragOver (DOMEvent aEvent, Object aDragDropObserver) ;
396 *
397 * called when a drag passes over this element
398 *
399 * @param DOMEvent aEvent
400 * the DOM event fired by passing over the element
401 * @param Object aDragDropObserver
402 * javascript object of format described above that specifies
403 * the way in which the element responds to drag events.
404 **/
405 dragOver: function (aEvent, aDragDropObserver)
406 {
407 if (!("onDragOver" in aDragDropObserver))
408 return;
409 if (!this.checkCanDrop(aEvent, aDragDropObserver))
410 return;
411 var flavourSet = aDragDropObserver.getSupportedFlavours();
412 for (var flavour in flavourSet.flavourTable)
413 {
414 if (this.mDragSession.isDataFlavorSupported(flavour))
415 {
416 aDragDropObserver.onDragOver(aEvent,
417 flavourSet.flavourTable[flavour],
418 this.mDragSession);
419 aEvent.stopPropagation();
420 aEvent.preventDefault();
421 break;
422 }
423 }
424 },
426 mDragSession: null,
428 /**
429 * void drop (DOMEvent aEvent, Object aDragDropObserver) ;
430 *
431 * called when the user drops on the element
432 *
433 * @param DOMEvent aEvent
434 * the DOM event fired by the drop
435 * @param Object aDragDropObserver
436 * javascript object of format described above that specifies
437 * the way in which the element responds to drag events.
438 **/
439 drop: function (aEvent, aDragDropObserver)
440 {
441 if (!("onDrop" in aDragDropObserver))
442 return;
443 if (!this.checkCanDrop(aEvent, aDragDropObserver))
444 return;
446 var flavourSet = aDragDropObserver.getSupportedFlavours();
448 var dt = aEvent.dataTransfer;
449 var dataArray = [];
450 var count = dt.mozItemCount;
451 for (var i = 0; i < count; ++i) {
452 var types = dt.mozTypesAt(i);
453 for (var j = 0; j < flavourSet.flavours.length; j++) {
454 var type = flavourSet.flavours[j].contentType;
455 // dataTransfer uses text/plain but older code used text/unicode, so
456 // switch this for compatibility
457 var modtype = (type == "text/unicode") ? "text/plain" : type;
458 if (Array.indexOf(types, modtype) >= 0) {
459 var data = dt.mozGetDataAt(modtype, i);
460 if (data) {
461 // Non-strings need some non-zero value used for their data length.
462 const kNonStringDataLength = 4;
464 var length = (typeof data == "string") ? data.length : kNonStringDataLength;
465 dataArray[i] = FlavourToXfer(data, length, flavourSet.flavourTable[type]);
466 break;
467 }
468 }
469 }
470 }
472 var transferData = new TransferDataSet(dataArray)
474 // hand over to the client to respond to dropped data
475 var multiple = "canHandleMultipleItems" in aDragDropObserver && aDragDropObserver.canHandleMultipleItems;
476 var dropData = multiple ? transferData : transferData.first.first;
477 aDragDropObserver.onDrop(aEvent, dropData, this.mDragSession);
478 aEvent.stopPropagation();
479 },
481 /**
482 * void dragExit (DOMEvent aEvent, Object aDragDropObserver) ;
483 *
484 * called when a drag leaves this element
485 *
486 * @param DOMEvent aEvent
487 * the DOM event fired by leaving the element
488 * @param Object aDragDropObserver
489 * javascript object of format described above that specifies
490 * the way in which the element responds to drag events.
491 **/
492 dragExit: function (aEvent, aDragDropObserver)
493 {
494 if (!this.checkCanDrop(aEvent, aDragDropObserver))
495 return;
496 if ("onDragExit" in aDragDropObserver)
497 aDragDropObserver.onDragExit(aEvent, this.mDragSession);
498 },
500 /**
501 * void dragEnter (DOMEvent aEvent, Object aDragDropObserver) ;
502 *
503 * called when a drag enters in this element
504 *
505 * @param DOMEvent aEvent
506 * the DOM event fired by entering in the element
507 * @param Object aDragDropObserver
508 * javascript object of format described above that specifies
509 * the way in which the element responds to drag events.
510 **/
511 dragEnter: function (aEvent, aDragDropObserver)
512 {
513 if (!this.checkCanDrop(aEvent, aDragDropObserver))
514 return;
515 if ("onDragEnter" in aDragDropObserver)
516 aDragDropObserver.onDragEnter(aEvent, this.mDragSession);
517 },
519 /**
520 * Boolean checkCanDrop (DOMEvent aEvent, Object aDragDropObserver) ;
521 *
522 * Sets the canDrop attribute for the drag session.
523 * returns false if there is no current drag session.
524 *
525 * @param DOMEvent aEvent
526 * the DOM event fired by the drop
527 * @param Object aDragDropObserver
528 * javascript object of format described above that specifies
529 * the way in which the element responds to drag events.
530 **/
531 checkCanDrop: function (aEvent, aDragDropObserver)
532 {
533 if (!this.mDragSession)
534 this.mDragSession = this.mDragService.getCurrentSession();
535 if (!this.mDragSession)
536 return false;
537 this.mDragSession.canDrop = this.mDragSession.sourceNode != aEvent.target;
538 if ("canDrop" in aDragDropObserver)
539 this.mDragSession.canDrop &= aDragDropObserver.canDrop(aEvent, this.mDragSession);
540 return true;
541 },
543 /**
544 * Do a security check for drag n' drop. Make sure the source document
545 * can load the dragged link.
546 *
547 * @param DOMEvent aEvent
548 * the DOM event fired by leaving the element
549 * @param Object aDragDropObserver
550 * javascript object of format described above that specifies
551 * the way in which the element responds to drag events.
552 * @param String aDraggedText
553 * the text being dragged
554 **/
555 dragDropSecurityCheck: function (aEvent, aDragSession, aDraggedText)
556 {
557 // Strip leading and trailing whitespace, then try to create a
558 // URI from the dropped string. If that succeeds, we're
559 // dropping a URI and we need to do a security check to make
560 // sure the source document can load the dropped URI. We don't
561 // so much care about creating the real URI here
562 // (i.e. encoding differences etc don't matter), we just want
563 // to know if aDraggedText really is a URI.
565 aDraggedText = aDraggedText.replace(/^\s*|\s*$/g, '');
567 var uri;
568 var ioService = Components.classes["@mozilla.org/network/io-service;1"]
569 .getService(Components.interfaces.nsIIOService);
570 try {
571 uri = ioService.newURI(aDraggedText, null, null);
572 } catch (e) {
573 }
575 if (!uri)
576 return;
578 // aDraggedText is a URI, do the security check.
579 const nsIScriptSecurityManager = Components.interfaces
580 .nsIScriptSecurityManager;
581 var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
582 .getService(nsIScriptSecurityManager);
584 if (!aDragSession)
585 aDragSession = this.mDragService.getCurrentSession();
587 var sourceDoc = aDragSession.sourceDocument;
588 // Use "file:///" as the default sourceURI so that drops of file:// URIs
589 // are always allowed.
590 var principal = sourceDoc ? sourceDoc.nodePrincipal
591 : secMan.getSimpleCodebasePrincipal(ioService.newURI("file:///", null, null));
593 try {
594 secMan.checkLoadURIStrWithPrincipal(principal, aDraggedText,
595 nsIScriptSecurityManager.STANDARD);
596 } catch (e) {
597 // Stop event propagation right here.
598 aEvent.stopPropagation();
600 throw "Drop of " + aDraggedText + " denied.";
601 }
602 }
603 };