[whatwg] Drag and drop feedback

Ian Hickson ian at hixie.ch
Thu Jun 28 16:12:30 PDT 2012

On Fri, 17 Feb 2012, Anne van Kesteren wrote:
> * Firefox, Chrome and Opera only allow dragend to delete ranges during a 
> 'move' if the range exists within an editable element. The spec needs to 
> change to reflect this - currently it expects this anywhere within a 
> non-editable document.


> * Firefox, Chrome and Opera allow dropping in all text based inputs 
> (text search tel url email password number) not just text.

The spec allowed this -- the spec just said "text field", it didn't say 
what it meant (it just had an example that said this included textarea and 
type=text). I've made this more explicit. It may have to change if UAs use 
text fields for other controls.

> * Firefox, Chrome and Opera all make .effectAllowed writeable only when 
> the data store is in read/write mode, so that only the source node has 
> the ability to set the effectAllowed.

Done. (The way WebKit and Opera do it, not the way Firefox does it.)

> * Opera and Firefox have chosen to implement modifiers at the point of 
> dropEffect initialization during dragenter and dragover: initially set 
> dropEffect to the default value specified in the spec table, then if the 
> platform requests something else with a modifier, set the dropEffect to 
> that modified value. Therefore those events see the modified dropEffect 
> (which seems to be the intention of the spec), and can also override it 
> by writing to the dropEffect property.

Browsers ignored effectAllowed in doing this, which seems like it would be 
a source of bugs. I've adopted the idea here, but honoured effectAllowed 
in the UA requirements here.

> * When dragging selections, Firefox, Chrome and Opera populate text/html 
> with the minimal markup intersected by the selection (the smallest 
> possible markup that contains all of the selection, without any parent 
> elements that contain both ends of the selection).

I haven't attempted to define this, since even a detailed explanation 
would still be incomplete unless we also defined all the whitelisting done 
for security reasons, and I don't intend to try to play that game.

> * Opera populates text/html when dragging an element. This means that it 
> is possible to drag HTML into another application without needing 
> JavaScript.

I've allowed this, but in no detail.

> * Opera recognises dropzone on parent elements of the immediate user selection
> - the spec currently requires it to be the specific element, which means that
> child elements of the dropzone are not within the dropzone. This
> implementation is as simple as replacing this line in the specification:
> "If the immediate user selection is an element with a dropzone attribute that
> matches the drag data store -> Set the current target element to the immediate
> user selection anyway."
> With this line:
> "If the immediate user selection is an element with a dropzone attribute that
> matches the drag data store, or if the immediate user selection has an
> ancestor element with a dropzone attribute that matches the drag data store ->
> Set the current target element to the immediate user selection anyway."

The proposed change wouldn't work (you'd end up sending the drop events 
to the wrong element, which matters e.g. if conflicting drop zones are 
nested). But I've done something similar, so that dropzone does work even 
when the element has descendants.

> * When there is no body, Opera uses the root node as the fallback target 
> (when the immediate user selection does not cancel dragenter). If there 
> is no root node, Opera uses null. This allows documents with no body to 
> function correctly, where the spec reverts to using the document object. 
> This means that when subsequently moving the drag over another 
> non-cancelling element, it will once again fire dragenter at the 
> document. And again when the mouse drags over another non-cancelling 
> element, etc. etc.
> Our proposal is to replace this:
> "If the current target element is the body element"
> with this:
> If the current target element is the body element, or if there is no body
> element and the current target is the html element
> And then replace this:
> "Otherwise; Fire a DND event named dragenter at the body element, if there is
> one, or at the Document object, if not. Then, and set the current target
> element to the body element, regardless of whether that event was canceled or
> not."
> with this:
> If the body element or html element exist; Fire a DND event named dragenter at
> the first of the following objects that exists, and set the current target
> element to that object regardless of whether that event was canceled or not:
> * The body element
> * The html element [interpreted as the root node in non-html documents]
> Otherwise; set the current target element to null
> (Note that it is possible for a document containing an immediate user 
> selection to have neither a body element nor a root element, if the 
> dragenter event handler for the immediate user selection deletes the 
> root element.)

I don't understand the problem this is fixing.

On Fri, 17 Feb 2012, Anne van Kesteren wrote:
> The spec bases its data store protection on the event type, and not the 
> actual dnd status, which would allow (eg.) a dragenter event to create a 
> synthetic dragstart event using the existing dataTransfer, and put the 
> data store into read/write mode.

No, this is incorrect. Only one thing can change the drag data store mode, 
and that's the "fire a DND event" algorithm, which is only invoked for 
real UA events. Synthetic events have no effect on the mode.

> The spec does not provide any way to create a fake data store.

This doesn't seem to be a problem.

> For compatibility with Firefox, we implement the following for the
> initDragEvent method's dataTransfer parameter:
> number as dataTransfer must throw an error
> string as dataTransfer must throw an error
> boolean as dataTransfer must throw an error
> object as dataTransfer must create a synthetic dataTransfer

Why is compatibility with this non-standard interface needed? As far as I 
can see, there is no need for an initDragEvent() method.

> In Firefox, a synthetic dataTransfer seems to be always in protected 
> mode (or perhaps just not associated with a drag data store), but Opera 
> creates a detatched data store in the expected mode for the synthetic 
> event type. This allows a script to synthesise an entire drag event 
> sequence, complete with drag data.

Since there's no way to create a DataTransfer object, this seems like a 
completely moot point.

> Firefox clones only the dropEffect and effectAllowed properties of the 
> dataTransfer object when creating a synthetic dataTransfer.

There is no way to create a synthetic dataTransfer as far as I can tell.

> Real dataTransfer (an actual dnd operation) as dataTransfer should use 
> the protection mode from the real dnd, not modify it. This protects 
> against the security issue mentioned earlier.

I do not understand this sentence.

> Detatched (synthetic) dataTransfer as dataTransfer should use the 
> protection mode from the current event name, not the event where it was 
> created.

Nor this one.

On Fri, 17 Feb 2012, Anne van Kesteren wrote:
> This is a proposal for what we see as one of the most significant 
> limitations of HTML5 drag and drop.
> The current drag and drop API has no way to restrict what documents may 
> become drop targets, and has no way for a drop target to know what 
> document was the source of the drag. This is intended to allow drag and 
> drop to operate freely between documents, between origins, and between 
> host applications. However, this has the effect of preventing sites from 
> safely using drag and drop for any potentially sensitive actions, such 
> as manipulating the contents of an address book, or initiating financial 
> transfers.
> Imagine your bank site wants to use this API, to allow you to drag a 
> "payment", to a "recipient". Drop it, and they start transferring the 
> money. But the API does not provide any way for your bank site to be 
> sure that you started that drag operation on their page. For example, 
> evilsite puts banksite in an invisible iframe or obscured popup 
> (clickjacking protection is not going to help), convinces you to drag an 
> element from evilsite onto the iframe, dropping it on the "recipient" - 
> a transfer is started.

This is trivially avoided by just having the bank set internal state when 
the drag is started, and checking it when the drag is ended, in a manner 
analogous to CSRF protection.

> So your bank can include a password/id token in the transfer data, and 
> refuse to work if that does not exist. But that then opens the reverse 
> hole. If evilsite convinces you to start a drag operation over a 
> banksite "payment", and end over evilsite, evilsite will be handed the 
> token, which it can then use.

Don't put the token in the drag-and-drop data.

> (Remember that this is supposed to allow drag/drop between pages that 
> may trust each other, even if they are not same-origin, or if they 
> cannot access each other through the frames hierarchy. Storing variables 
> on the page would not be sufficient for these cases.)

The scenario that was listed was a bank starting a transaction. What 
scenario involves multiple cross-origin sites that trust each other but 
cannot access each other?

> Our proposal takes its cues and algorithms from the postMessage API, and 
> allows the source site to restrict drop targets to only those origins 
> which it trusts, and allows drop targets to see which origin was the 
> source of a drag. The majority of the algorithm can be copied from 
> postMessage, including the syntax for allowed target origins.

The postMessage() security mechanism is generally considered to have been 
poorly designed, so I don't think that's a good plan.

See, e.g.: http://seclab.stanford.edu/websec/frames/post-message.pdf

However, it's not clear to me that there is really a problem to solve 
here, so I haven't examined the proposal in detail to see if it suffers 
from the same problems. I think a deeper discussion of the use cases is 
required first.

In general, though, I would expect us to address the general area using 
promises and message ports, leveraging much of the existing infrastructure 
rather than introducing yet another security mechanism.

On Fri, 17 Feb 2012, Anne van Kesteren wrote:
> * Firefox chooses to reinitialise dropEffect in dragend to have the same 
> value it had at the start of drop. Opera and Chrome choose to be 
> consistent, and accept the last value it had - this is unspecified per 
> spec

As far as I can tell, this is specified precisely. In 'dragend' you use 
the current drag operation value.

> =Dragging inputs and interactive elements=
> * We have decided to make certain interactive elements be "special", for
> compatibility with other browsers, and user expectations. This is not covered
> by the spec. A page is highly unlikely to make an editable element be
> draggable but it's quite possible to have an input somewhere inside a
> draggable element. The user still wants to be able to select text in that
> element and interact normally with it.
>  * Input/select/textarea/button cannot be dragged.
>  * ContentEditable elements cannot be dragged.
>  * Editable SVG elements cannot be dragged.
>  * Scrollbars must respond as scrollbars, not draggable points.

Why would you explicitly ignore "draggable" on an element that has it? If 
the author didn't want it draggable, he presumably wouldn't set it that way.

> [lots of other bugs in browsers]

Please do file bugs on these!

On Thu, 23 Feb 2012, Daniel Cheng wrote:
> For a long time, WebKit returned types as an Array rather than a 
> DOMStringList. I fixed this recently, but arv pointed out that 
> DOMStringList is deprecated in favor of Array: 
> http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#interface-domstringlist
> Given that, wouldn't it make sense to change event.dataTransfer.types to be
> a live Array instead of a live DOMStringList? It's not needed for legacy
> compatibility with IE, which didn't have .types. It was implemented
> differently in Gecko and WebKit, so pages ought to be checking for this
> already with:
> if (event.dataTransfer.contains) {
>   ...
> } else if (event.dataTransfer.indexOf) {
>   ...
> }
> As a result, the burden of such a change to well-behaved web developers
> should be minimal.


Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

More information about the whatwg mailing list