Hi.
I have a tree panel next to a form panel and there is an iframe on the form panel as part of a TinyMceTextArea component. (If I was using the Ext HtmlEditor component there would also be an iframe.) I wanted my users to be able to drop a tree node onto the iframe. This was not possible because the Ext DD processes did not allow for it.

So, I set out to get that working. While a few people reported that they got this working, there was not much in the way of examples. However, starting with this post which was for Ext 3, I finally solved the issue. My changes are described below. I have tested it on recent versions of Chrome, FF and IE using ExtJS 4.2. It is highly likely that others might be able to improve my approach. Please post suggested improvements! :-)

What follows is specific to my situation - ie a tree node being dropped onto my customised version of the TinyMceTextArea field. However, it seems likely that the approach could easily be adapted for other situations eg dragging other Ext components and dropping onto the HtmlEditorField. Again, please post examples back here. I have included a zip of the following code blocks (but not a working app).DragAndDropTreeIframeCode.zip

1. Get the Ext.dd.DragDropMgr singleton to be aware of the iframe. In the TineMceTextArea ed.on('init', function(e) {}) function I added:
PHP Code:
        // Get a reference to the iframe and add listeners for when the
        // user drops something on it.

        /*
            // NB: the body within the iframe MUST be the height of the editor panel
            // or the drop will not be heard. See syncEditorHeight() below.
     
            // ie do the following in syncEditorHeight() below.
            // set the body height otherwise the drop onto the iframe
            // fails because since the body height is not as high as the iframe height, if
            // we drop "below" the body we are dropping into "thin air" - there is no element there 
            // so no listener is "hit"

            var edBody = edIframe.dom.contentDocument.body;
            if (edBody) {
                                // allow for the padding and margins on the body 
                var bp = Ext.fly(edBody).getPadding('tb'),
                    bm = Ext.fly(edBody).getMargin('tb'),
                                // plus a 5 px fudge factor to avoid scrollbar auto 
                                // showing for the sake of a few px
                    bh = iFrameHeight-bp-bm-5;  
                Ext.fly(edBody).setHeight(bh);
            }                            
         */

        
var edIframe Ext.get(me.getInputId() + "_ifr");
                                
// me.allowDD is a custom config of my TinyMceTextArea field set on the form panel.
                                // me.DDGroups is an array of DDgroup name strings.
        
if (edIframe && me.allowDD) {

            var 
iFrameNode edIframe.dom;

            if (
iFrameNode) {
                
// This handles drops from OUTSIDE the browser. eg files from the OS.
                // contentDocument in iframe is the equivalent of document in window
                
var doc iFrameNode.contentDocument;
                
doc.ondrop = function(e) {
                    
e.preventDefault();
                    
//console.log('DROPPEEEEEDDDD')
                    
me.handleExternalDrop(e);  // custom func. See below
                    
return false;
                };

                
// This enables a drop onto the iframe from INSIDE the ExtJS scope

                // There is no point setting the following up if we dont have
                // any Ext DDGroup names supplied.
                
if (me.DDGroups.length) {

                    
// Get a ref to the Ext.dd.DragDropMgr singleton
                    
var ddm Ext.dd.DragDropMgr;

                    
// Add these listeners so that the iFrame document element is "registered"
                    // and will be referenced in the Ext.dd.DragDropMgr for MouseMove and MouseUp events

                    //       on( el, eventName,     [handler],       [scope],  [options] ) 
                    
Ext.EventManager.on(doc"mousemove"ddm.handleMouseMoveddmtrue);
                    
Ext.EventManager.on(doc"mouseup"ddm.handleMouseUpddmtrue);

                    
// iFrameParent is the div in the containing ExtJS field that holds the iFrame.
                    // (ie this TinyMceTextArea field) 
                    
var iFrameParent iFrameNode.parentElement,

                        
// iFrameCt is the Ext.dom.Element for that iFrame's container. 
                        
iFrameCt Ext.get(iFrameParent.id);

                    
// Attach a dropZone to the iFrame's container.
                    // Since this node is within the Ext scope we are allowed to do this.
                    
var edDropZone = new Ext.dd.DropZone(iFrameCt, {
                        
// Added below
                        
ddGroup"",

                        
// This is used in the DragDropManager override to be explicit about
                        // the fact that we want to pay attention to this iFrame. 
                        
iframeEliFrameNode,

                        
// Save this tinymce editor on the dropZone. 
                        // It makes it easy to find it again!
                        // Used in the onNodeDrop() listener below.
                        
uxEdme,

                        
// If the mouse is over a target node, return that node. This is
                        // provided as the target parameter in all "onNodeXXXX" node event handling functions
                        
getTargetFromEvent: function(e) {
                            return 
e.getTarget();
                        },

                        
// On node drop, we can interrogate the target node to find the underlying
                        // application object that is the target of the dragged data.
                        // We can use the data set up by the DragZone's getDragData method to read
                        // any data we decided to attach.
                        
onNodeDrop: function(targetddedata) {
                            var 
dropField this.uxEd,
                                
dropData data;

                            
//console.log('TinyMce heard the drop ',dropData);

                            // Fire an event so a listener can do something with
                            // the dropped data
                            
me.fireEvent('oneditordrop'dropFielddropDatae);
                            return 
true;
                        }

                    });

                    
// Add the supplied DDgroup name(s) (strings)
                    
Ext.Array.each(me.DDGroups, function(groupName) {
                        
edDropZone.addToGroup(groupName)
                    });
                }


            } 
// if (iFrameNode)

        
// if (edIframe && me.allowDD) 
Pay attention to the comment at the head of the block above about the need to make sure your iframe body is 100% height (or close) of the iframe. If you are not using the TintMceTextArea component just use the above as a guide to using your iframe - eg in an 'after render' listener.

Side issue: While it is outside the scope of this article, for completeness this is the method I use to handle drops of files from the OS onto the iframe. This doesnt need the rest of this post in order to work. It only needs the doc.ondrop = function(e) above:
PHP Code:
/**
 * Something was dropped on either the editor field or the editor's iframe 
 * of the editor area. Handle that drop by firing the appropriate event that 
 * a listener can use. eg on the form panel
 * 
 * @return {[type]} [description]
 */
handleExternalDrop: function(e) {
    
console.log('handleExternalDrop'e)
    var 
me this;
    if (
e.dataTransfer) {
        
// Analyse the event to work out what was dropped
        
var files e.dataTransfer.files;
        if (
files && files.length) {
            
// Grab the files array and pass it on to the listener in the client                 
            
me.fireEvent('editordropfiles'mefilese);
        }
    }
}, 
2. Override the Ext.dd.DragDropManager.fireEvents() method to include a check for our designated iframe. Basically, if the mouse is above, or is dropped on, a target element that is within the designated iframe, include the dropZone as a "hit" and fire the appropriate events accordingly. Without this extra check the DragDropManager will not recognise that the iframe should be included in it's checking. It is a long method but there is just one change block as indicated at about halfway down:
PHP Code:
Ext.define('Ext.overrides.dd.DragDropManager', {
    
override'Ext.dd.DragDropManager',
    
/**
     * Override to better handle DD from Ext to an embedded iFrame
     * eg the TinyMceTextArea component
     *
     * Changes to this method are indicated by //MH comments
     * 
     * Iterates over all of the DragDrop elements to find ones we are
     * hovering over or dropping on
     * @param {Event} e the event
     * @param {Boolean} isDrop is this a drop op or a mouseover op?
     * @private
     */
    
fireEvents: function(eisDrop) {
        var 
me this,
            
dragCurrent me.dragCurrent,
            
dragEl,
            
oldDragElTop,
            
mousePoint e.getPoint(),
            
overTarget,
            
overTargetEl,
            
allTargets = [],
            
oldOvers = [], // cache the previous dragOver array
            
outEvts = [],
            
overEvts = [],
            
dropEvts = [],
            
enterEvts = [],
            
xy,
            
needsSort,
            
i,
            
len,
            
sGroup;

        
// If the user did the mouse up outside of the window, we could
        // get here even though we have ended the drag.
        
if (!dragCurrent || dragCurrent.isLocked()) {
            return;
        }

        
// If we need to use the current mousemove target to find the over el,
        // but pointer-events is not supported, AND the delta position does not place the mouse outside of the dragEl,
        // temporarily move the dragEl away, and fake the mousemove target by using document.elementFromPoint
        // while it's out of the way.
        // The pointer events implementation is bugged in IE9/10 and opera, so fallback even if they report that they support it.
        // IE8m do not support it so they will auto fall back
        
if (!me.notifyOccluded && (!Ext.supports.PointerEvents || Ext.isIE10m || Ext.isOpera) && !(dragCurrent.deltaX || dragCurrent.deltaY 0)) {
            
dragEl dragCurrent.getDragEl();
            
oldDragElTop dragEl.style.top;
            
dragEl.style.top '-10000px';
            
xy e.getXY();
            
e.target document.elementFromPoint(xy[0], xy[1]);
            
dragEl.style.top oldDragElTop;
        }

        
// Check to see if the object(s) we were hovering over is no longer
        // being hovered over so we can fire the onDragOut event
        
for (i in me.dragOvers) {

            
overTarget me.dragOvers[i];

            if (!
me.isTypeOfDD(overTarget)) {
                continue;
            }

            
// If notifyOccluded set, we use mouse position
            
if (me.notifyOccluded) {
                if (!
this.isOverTarget(mousePointoverTargetme.mode)) {
                    
outEvts.push(overTarget);
                }
            }
            
// Otherwise we use event source of the mousemove event
            
else {
                if (!
e.within(overTarget.getEl())) {
                    
outEvts.push(overTarget);
                }
            }

            
oldOvers[i] = true;
            
delete me.dragOvers[i];
        }

        
// Collect all targets which are members of the same ddGoups that the dragCurrent is a member of, and which may recieve mouseover and drop notifications.
        // This is preparatory to seeing which one(s) we are currently over
        // Begin by iterating through the ddGroups of which the dragCurrent is a member
        
for (sGroup in dragCurrent.groups) {

            if (
"string" != typeof sGroup) {
                continue;
            }

            
// Loop over the registered members of each group, testing each as a potential target
            
for (i in me.ids[sGroup]) {
                
overTarget me.ids[sGroup][i];

                
// The target is valid if it is a DD type
                // And it's got a DOM element
                // And it's configured to be a drop target
                // And it's not locked
                // And the DOM element is fully visible with no hidden ancestors
                // And it's either not the dragCurrent, or, if it is, tha dragCurrent is configured to not ignore itself.
                
if (me.isTypeOfDD(overTarget) &&
                    (
overTargetEl overTarget.getEl()) &&
                    (
overTarget.isTarget) &&
                    (!
overTarget.isLocked()) &&
                    (
Ext.fly(overTargetEl).isVisible(true)) &&
                    ((
overTarget != dragCurrent) || (dragCurrent.ignoreSelf === false))) {

                    
// If notifyOccluded set, we use mouse position
                    
if (me.notifyOccluded) {

                        
// Only sort by zIndex if there were some which had a floating zIndex value
                        
if ((overTarget.zIndex me.getZIndex(overTargetEl)) !== -1) {
                            
needsSort true;
                        }
                        
allTargets.push(overTarget);
                    }
                    
// Otherwise we use event source of the mousemove event
                    
else {

                        
// Start MH change block ====================

                        // FYI: overTarget is an instance of "Ext.dd.DropZone"
                        // Look for the iFrame element that we added to this
                        // dropZone when we created it.

                        
if (overTarget.iframeEl) {

                            
// This uses the browser DOM native contains() method to see if the 
                            // target on the mouse event is contained within the iFrame body 
                            // we are interested in.
                            
if (overTarget.iframeEl.contentDocument.body.contains(e.target)) {

                                
// Get the location of the iFrame
                                
var tLoc this.getLocation(overTarget);

                                
// At this point the xy mouse coordinates are relative to the iFrame doc.
                                // Adjust the mousepoint to be relative to the main window body
                                // as it would normally be if we didnt just mouse over the iFrame.
                                
var mousePoint.tLoc.1,
                                    
mousePoint.tLoc.1;
                                
mousePoint[0] = mousePoint.left mousePoint.right mousePoint.x;
                                
mousePoint[1] = mousePoint.bottom mousePoint.top mousePoint.y;

                                
allTargets.push(overTarget);
                                break;
                            } 
// else no iFrame match, therefore dont push the target

                        
} else {
                            
// As per original code. Non-iframe nodes.
                            
if (e.within(overTarget.getEl())) {
                                
allTargets.push(overTarget);
                                break;
                            }

                        }

                        
/* MH: This is the orginal code that was in this else block:

                            if (e.within(overTarget.getEl())) {
                                allTargets.push(overTarget);
                                break;
                            } 

                        */
                        // end MH change block ===============
                    
}
                }
            }
        }

        
// If there were floating targets, sort the highest zIndex to the top
        
if (needsSort) {
            
Ext.Array.sort(allTargetsme.byZIndex);
        }

        
// Loop through possible targets, notifying the one(s) we are over.
        // Usually we only deliver events to the topmost.
        
for (0len allTargets.lengthleni++) {
            
overTarget allTargets[i];

            
// If we are over the overTarget, queue it up to recieve an event of whatever type we are handling
            
if (me.isOverTarget(mousePointoverTargetme.mode)) {
                
// look for drop interactions
                
if (isDrop) {
                    
dropEvts.push(overTarget);
                    
// look for drag enter and drag over interactions
                
} else {

                    
// initial drag over: dragEnter fires
                    
if (!oldOvers[overTarget.id]) {
                        
enterEvts.push(overTarget);
                        
// subsequent drag overs: dragOver fires
                    
} else {
                        
overEvts.push(overTarget);
                    }
                    
me.dragOvers[overTarget.id] = overTarget;
                }

                
// Unless this DragDropManager has been explicitly configured to deliver events to multiple targets, then we are done.
                
if (!me.notifyOccluded) {
                    break;
                }
            }
        }

        if (
me.mode) {
            if (
outEvts.length) {
                
dragCurrent.b4DragOut(eoutEvts);
                
dragCurrent.onDragOut(eoutEvts);
            }

            if (
enterEvts.length) {
                
dragCurrent.onDragEnter(eenterEvts);
            }

            if (
overEvts.length) {
                
dragCurrent.b4DragOver(eoverEvts);
                
dragCurrent.onDragOver(eoverEvts);
            }

            if (
dropEvts.length) {
                
dragCurrent.b4DragDrop(edropEvts);
                
dragCurrent.onDragDrop(edropEvts);
            }

        } else {
            
// fire dragout events
            
for (0len outEvts.lengthlen; ++i) {
                
dragCurrent.b4DragOut(eoutEvts[i].id);
                
dragCurrent.onDragOut(eoutEvts[i].id);
            }

            
// fire enter events
            
for (0len enterEvts.lengthlen; ++i) {
                
// dc.b4DragEnter(e, oDD.id);
                
dragCurrent.onDragEnter(eenterEvts[i].id);
            }

            
// fire over events
            
for (0len overEvts.lengthlen; ++i) {
                
dragCurrent.b4DragOver(eoverEvts[i].id);
                
dragCurrent.onDragOver(eoverEvts[i].id);
            }

            
// fire drop events
            
for (0len dropEvts.lengthlen; ++i) {
                
dragCurrent.b4DragDrop(edropEvts[i].id);
                
dragCurrent.onDragDrop(edropEvts[i].id);
            }

        }

        
// notify about a drop that did not find a target
        
if (isDrop && !dropEvts.length) {
            
dragCurrent.onInvalidDrop(e);
        }

    }
}); 
You will need to put this override wherever you keep yours and amend the ext.define() at the top.

3. Configure your tree panel to use the 'treeviewdragdrop' plugin as normal. Add a 'viewready' listener and a new function:
PHP Code:
initComponent: function() {
    var 
me this;

    
// ... your other setup
    
    
me.viewConfig = {
        
plugins: {
            
ptype'treeviewdragdrop',
            
pluginId'uxtvddTreeDD'
        
}
    };

    
me.callParent(arguments);

    
me.view.on('viewready', function(view) {
        var 
view.getPlugin("uxtvddTreeDD");
        var 
dragZone p.dragZone;

            
// Override the b4Drag function to check if we are over one of our designated iFrames
            // and then amend the xy location of the DDProxy if we are.
        
dragZone.b4Drag = function(e) {
            
// Original code was:
            // this.setDragElPos(e.getPageX(), e.getPageY());

            // MH changed to:
            
var e.getPageX(),
                
e.getPageY();

            var 
iFrameLocation me.getIFrameLocation(ethis)
            if (
iFrameLocation) {
                
// Fix the location of the DDProxy (ghost element).
                // If you are on an iframe body that is a dropZone in this DDgroup,
                // mouse X and Y are relative to the iframe doc, not the app doc.
                // So, we need to know the left and top of the iFrame so we can
                // adjust these x y cordinates to be relative to the main window rather than the iframe
                // so the DDProxy element (ghost) tracks the mouse even as you move over the iFrame

                // Adjust the x y to include the position of iFrame 
                
iFrameLocation.1;
                
iFrameLocation.1;
            }
            
// Position the DDProxy
            
this.setDragElPos(xy);


        }

    }, 
this);
},

/**
 * New function to return the location of the iframe we are interested in 
 * that the mouse is over.
 * 
 * @param  {[type]} e           [description]
 * @param  {[type]} dragCurrent [description]
 * @return {[type]}             [description]
 */
getIFrameLocation: function(edragCurrent) {
    var 
ddmgr Ext.dd.DragDropManager,
        
sGroup dragCurrent.ddGroup,
        
iFrameLocation null;

    
// Loop over all the dropZones that are members of this DDgroup
    
for (i in ddmgr.ids[sGroup]) {
        var 
overTarget ddmgr.ids[sGroup][i];

        
// If this target is an iFrame...
        
if (overTarget.iframeEl) {
            
// and that iframe body contains the event target 
            // (ie the element that the mouse) id hovering over...
            
if (overTarget.iframeEl.contentDocument.body.contains(e.target)) {
                
// Get the location of that iFrame and return it
                
iFrameLocation ddmgr.getLocation(overTarget);
                break;
            }
        }
    }

    return 
iFrameLocation;

(One improvement here could be to check e.target before calling getIFrameLocation() to see if the target element is in an iframe. I couldn't find a consistent way to check that. It works fine as it is.)

The dragZone.b4Drag() function above is called indirectly by the DragDropManager. We override it to reposition the DDProxy "ghost" element so that it tracks the mouse properly as you "cross the border" between DOMs.

4. In the relevant controller in your app add a listener for the custom TinyMceTextArea events:
PHP Code:
// A listener for the form 'vOpsItemForm' field 'vTinyMceField[itemId=opdetails]' by xtype selector
'vOpsItemForm vTinyMceField[itemId=opdetails]': {
    
editordropfilesthis.onEditorDropFiles// Files from OS drop
    
oneditordropthis.onEditorDropPanel // An ExtJS panel or tree node or .... 
}

// Your handlers
onEditorDropFiles: function(edfilese){
    
console.log('in onEditorDropFiles',arguments)  
    
// Your code ...  
},
onEditorDropPanel: function(eddropDatae){
    
console.log('in onEditorDropPanel',arguments)  
    
// Your code ...      

Cheers,
Murray