Fix http://bugs.webkit.org/show_bug.cgi?id=16816 , rdar://problem/5682985
[WebKit-https.git] / WebCore / page / DragController.cpp
1 /*
2  * Copyright (C) 2007 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "DragController.h"
28
29 #include "CSSStyleDeclaration.h"
30 #include "Clipboard.h"
31 #include "ClipboardAccessPolicy.h"
32 #include "DocLoader.h"
33 #include "Document.h"
34 #include "DocumentFragment.h"
35 #include "DragActions.h"
36 #include "DragClient.h"
37 #include "DragData.h"
38 #include "Editor.h"
39 #include "EditorClient.h"
40 #include "Element.h"
41 #include "EventHandler.h"
42 #include "FloatRect.h"
43 #include "Frame.h"
44 #include "FrameLoader.h"
45 #include "FrameView.h"
46 #include "HTMLAnchorElement.h"
47 #include "HTMLInputElement.h"
48 #include "HTMLNames.h"
49 #include "HitTestResult.h"
50 #include "Image.h"
51 #include "MoveSelectionCommand.h"
52 #include "Node.h"
53 #include "Page.h"
54 #include "PlugInInfoStore.h"
55 #include "RenderFileUploadControl.h"
56 #include "RenderImage.h"
57 #include "ReplaceSelectionCommand.h"
58 #include "ResourceRequest.h"
59 #include "SelectionController.h"
60 #include "Settings.h"
61 #include "SystemTime.h"
62 #include "Text.h"
63 #include "markup.h"
64 #include <wtf/RefPtr.h>
65
66 namespace WebCore {
67
68 static PlatformMouseEvent createMouseEvent(DragData* dragData)
69 {
70     // FIXME: We should fake modifier keys here.
71     return PlatformMouseEvent(dragData->clientPosition(), dragData->globalPosition(),
72                               LeftButton, MouseEventMoved, 0, false, false, false, false, currentTime());
73
74 }
75     
76 DragController::DragController(Page* page, DragClient* client)
77     : m_page(page)
78     , m_client(client)
79     , m_document(0)
80     , m_dragInitiator(0)
81     , m_dragDestinationAction(DragDestinationActionNone)
82     , m_dragSourceAction(DragSourceActionNone)
83     , m_didInitiateDrag(false)
84     , m_isHandlingDrag(false)
85     , m_dragOperation(DragOperationNone)
86 {
87 }
88     
89 DragController::~DragController()
90 {   
91     m_client->dragControllerDestroyed();
92 }
93     
94 static PassRefPtr<DocumentFragment> documentFragmentFromDragData(DragData* dragData, RefPtr<Range> context,
95                                           bool allowPlainText, bool& chosePlainText)
96 {
97     ASSERT(dragData);
98     chosePlainText = false;
99
100     Document* document = context->ownerDocument();
101     ASSERT(document);
102     if (document && dragData->containsCompatibleContent()) {
103         if (PassRefPtr<DocumentFragment> fragment = dragData->asFragment(document))
104             return fragment;
105
106         if (dragData->containsURL()) {
107             String title;
108             String url = dragData->asURL(&title);
109             if (!url.isEmpty()) {
110                 ExceptionCode ec;
111                 RefPtr<HTMLAnchorElement> anchor = static_cast<HTMLAnchorElement*>(document->createElement("a", ec).get());
112                 anchor->setHref(url);
113                 RefPtr<Node> anchorText = document->createTextNode(title);
114                 anchor->appendChild(anchorText, ec);
115                 RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
116                 fragment->appendChild(anchor, ec);
117                 return fragment.get();
118             }
119         }
120     }
121     if (allowPlainText && dragData->containsPlainText()) {
122         chosePlainText = true;
123         return createFragmentFromText(context.get(), dragData->asPlainText()).get();
124     }
125     
126     return 0;
127 }
128
129 bool DragController::dragIsMove(SelectionController* selectionController, DragData* dragData) 
130 {
131     return m_document == m_dragInitiator
132         && selectionController->isContentEditable()
133         && !isCopyKeyDown();
134 }
135
136 void DragController::cancelDrag()
137 {
138     m_page->dragCaretController()->clear();
139 }
140
141 void DragController::dragEnded()
142 {
143     m_dragInitiator = 0;
144     m_didInitiateDrag = false; 
145     m_page->dragCaretController()->clear(); 
146 }    
147
148 DragOperation DragController::dragEntered(DragData* dragData) 
149 {
150     return dragEnteredOrUpdated(dragData);
151 }
152     
153 void DragController::dragExited(DragData* dragData) 
154 {   
155     ASSERT(dragData);
156     Frame* mainFrame = m_page->mainFrame();
157     
158     if (RefPtr<FrameView> v = mainFrame->view()) {
159         ClipboardAccessPolicy policy = mainFrame->loader()->baseURL().isLocalFile() ? ClipboardReadable : ClipboardTypesReadable;
160         RefPtr<Clipboard> clipboard = dragData->createClipboard(policy);
161         clipboard->setSourceOperation(dragData->draggingSourceOperationMask());
162         mainFrame->eventHandler()->cancelDragAndDrop(createMouseEvent(dragData), clipboard.get());
163         clipboard->setAccessPolicy(ClipboardNumb);    // invalidate clipboard here for security
164     }
165
166     cancelDrag();
167     m_document = 0;
168 }
169     
170 DragOperation DragController::dragUpdated(DragData* dragData) 
171 {
172     return dragEnteredOrUpdated(dragData);
173 }
174     
175 bool DragController::performDrag(DragData* dragData)
176 {   
177     ASSERT(dragData);
178     m_document = m_page->mainFrame()->documentAtPoint(dragData->clientPosition());
179     if (m_isHandlingDrag) {
180         ASSERT(m_dragDestinationAction & DragDestinationActionDHTML);
181         m_client->willPerformDragDestinationAction(DragDestinationActionDHTML, dragData);
182         RefPtr<Frame> mainFrame = m_page->mainFrame();
183         if (mainFrame->view()) {
184             // Sending an event can result in the destruction of the view and part.
185             RefPtr<Clipboard> clipboard = dragData->createClipboard(ClipboardReadable);
186             clipboard->setSourceOperation(dragData->draggingSourceOperationMask());
187             mainFrame->eventHandler()->performDragAndDrop(createMouseEvent(dragData), clipboard.get());
188             clipboard->setAccessPolicy(ClipboardNumb);    // invalidate clipboard here for security
189         }
190         m_document = 0;
191         return true;
192     } 
193     
194     if ((m_dragDestinationAction & DragDestinationActionEdit) && concludeDrag(dragData, m_dragDestinationAction)) {
195         m_document = 0;
196         return true;
197     }
198     
199     m_document = 0;
200
201     if (operationForLoad(dragData) == DragOperationNone)
202         return false;
203
204     m_client->willPerformDragDestinationAction(DragDestinationActionLoad, dragData);
205     m_page->mainFrame()->loader()->load(ResourceRequest(dragData->asURL()));
206     return true;
207 }
208     
209 DragOperation DragController::dragEnteredOrUpdated(DragData* dragData)
210 {
211     ASSERT(dragData);
212     IntPoint windowPoint = dragData->clientPosition();
213     
214     Document* newDraggingDoc = 0;
215     if (Frame* frame = m_page->mainFrame())
216         newDraggingDoc = frame->documentAtPoint(windowPoint);
217     if (m_document != newDraggingDoc) {
218         if (m_document)
219             cancelDrag();
220         m_document = newDraggingDoc;
221     }
222     
223     m_dragDestinationAction = m_client->actionMaskForDrag(dragData);
224     
225     DragOperation operation = DragOperationNone;
226     
227     if (m_dragDestinationAction == DragDestinationActionNone)
228         cancelDrag();
229     else {
230         operation = tryDocumentDrag(dragData, m_dragDestinationAction);
231         if (operation == DragOperationNone && (m_dragDestinationAction & DragDestinationActionLoad))
232             return operationForLoad(dragData);
233     }
234     
235     return operation;
236 }
237
238 static HTMLInputElement* asFileInput(Node* node)
239 {
240     ASSERT(node);
241     
242     // The button for a FILE input is a sub element with no set input type
243     // In order to get around this problem we assume any non-FILE input element
244     // is this internal button, and try querying the shadow parent node.
245     if (node->hasTagName(HTMLNames::inputTag) && node->isShadowNode() && static_cast<HTMLInputElement*>(node)->inputType() != HTMLInputElement::FILE)
246       node = node->shadowParentNode();
247     
248     if (!node || !node->hasTagName(HTMLNames::inputTag))
249         return 0;
250     
251     HTMLInputElement* inputElem = static_cast<HTMLInputElement*>(node);
252     if (inputElem->inputType() == HTMLInputElement::FILE)
253         return inputElem;
254     
255     return 0;
256 }
257     
258 DragOperation DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction actionMask)
259 {
260     ASSERT(dragData);
261     
262     if (!m_document)
263         return DragOperationNone;
264     
265     DragOperation operation = DragOperationNone;
266     if (actionMask & DragDestinationActionDHTML)
267         operation = tryDHTMLDrag(dragData);
268     m_isHandlingDrag = operation != DragOperationNone; 
269
270     RefPtr<FrameView> frameView = m_document->view();
271     if (!frameView)
272         return operation;
273     
274     if ((actionMask & DragDestinationActionEdit) && !m_isHandlingDrag && canProcessDrag(dragData)) {
275         if (dragData->containsColor()) 
276             return DragOperationGeneric;
277         
278         IntPoint dragPos = dragData->clientPosition();
279         IntPoint point = frameView->windowToContents(dragPos);
280         Element* element = m_document->elementFromPoint(point.x(), point.y());
281         ASSERT(element);
282         Frame* innerFrame = element->document()->frame();
283         ASSERT(innerFrame);
284         if (!asFileInput(element)) {
285             Selection dragCaret;
286             if (Frame* frame = m_document->frame())
287                 dragCaret = frame->visiblePositionForPoint(point);
288             m_page->dragCaretController()->setSelection(dragCaret);
289         }
290         
291         return dragIsMove(innerFrame->selectionController(), dragData) ? DragOperationMove : DragOperationCopy;
292     } 
293     
294     m_page->dragCaretController()->clear();
295     return operation;
296 }
297
298 DragSourceAction DragController::delegateDragSourceAction(const IntPoint& windowPoint)
299 {  
300     m_dragSourceAction = m_client->dragSourceActionMaskForPoint(windowPoint);
301     return m_dragSourceAction;
302 }
303     
304 DragOperation DragController::operationForLoad(DragData* dragData)
305 {
306     ASSERT(dragData);
307     Document* doc = 0;
308     doc = m_page->mainFrame()->documentAtPoint(dragData->clientPosition());
309     if (doc && (m_didInitiateDrag || doc->isPluginDocument() || (doc->frame() && doc->frame()->editor()->clientIsEditable())))
310         return DragOperationNone;
311     return dragOperation(dragData);
312 }
313
314 static bool setSelectionToDragCaret(Frame* frame, Selection& dragCaret, RefPtr<Range>& range, const IntPoint& point)
315 {
316     frame->selectionController()->setSelection(dragCaret);
317     if (frame->selectionController()->isNone()) {
318         dragCaret = frame->visiblePositionForPoint(point);
319         frame->selectionController()->setSelection(dragCaret);
320         range = dragCaret.toRange();
321     }
322     return !frame->selectionController()->isNone() && frame->selectionController()->isContentEditable();
323 }
324
325 bool DragController::concludeDrag(DragData* dragData, DragDestinationAction actionMask)
326 {
327     ASSERT(dragData);
328     ASSERT(!m_isHandlingDrag);
329     ASSERT(actionMask & DragDestinationActionEdit);
330     
331     if (!m_document)
332         return false;
333     
334     IntPoint point = m_document->view()->windowToContents(dragData->clientPosition());
335     Element* element =  m_document->elementFromPoint(point.x(), point.y());
336     ASSERT(element);
337     Frame* innerFrame = element->ownerDocument()->frame();
338     ASSERT(innerFrame);    
339
340     if (dragData->containsColor()) {
341         Color color = dragData->asColor();
342         if (!color.isValid())
343             return false;
344         if (!innerFrame)
345             return false;
346         RefPtr<Range> innerRange = innerFrame->selectionController()->toRange();
347         RefPtr<CSSStyleDeclaration> style = m_document->createCSSStyleDeclaration();
348         ExceptionCode ec;
349         style->setProperty("color", color.name(), ec);
350         if (!innerFrame->editor()->shouldApplyStyle(style.get(), innerRange.get()))
351             return false;
352         m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
353         innerFrame->editor()->applyStyle(style.get(), EditActionSetColor);
354         return true;
355     }
356     
357     if (!m_page->dragController()->canProcessDrag(dragData)) {
358         m_page->dragCaretController()->clear();
359         return false;
360     }
361     
362     if (HTMLInputElement* fileInput = asFileInput(element)) {
363         if (!dragData->containsFiles())
364             return false;
365         
366         Vector<String> filenames;
367         dragData->asFilenames(filenames);
368         if (filenames.isEmpty())
369             return false;
370         
371         // Ugly.  For security none of the API's available to us to set the input value 
372         // on file inputs.  Even forcing a change in HTMLInputElement doesn't work as
373         // RenderFileUploadControl clears the file when doing updateFromElement()
374         RenderFileUploadControl* renderer = static_cast<RenderFileUploadControl*>(fileInput->renderer());
375         
376         if (!renderer)
377             return false;
378         
379         // Only take the first filename as <input type="file" /> can only accept one
380         renderer->receiveDroppedFile(filenames[0]);
381         return true;
382     }
383
384     Selection dragCaret(m_page->dragCaretController()->selection());
385     m_page->dragCaretController()->clear();
386     RefPtr<Range> range = dragCaret.toRange();
387     
388     // For range to be null a WebKit client must have done something bad while
389     // manually controlling drag behaviour
390     if (!range)  
391         return false;
392     DocLoader* loader = range->ownerDocument()->docLoader();
393     loader->setAllowStaleResources(true);
394     if (dragIsMove(innerFrame->selectionController(), dragData) || dragCaret.isContentRichlyEditable()) { 
395         bool chosePlainText = false;
396         RefPtr<DocumentFragment> fragment = documentFragmentFromDragData(dragData, range, true, chosePlainText);
397         if (!fragment || !innerFrame->editor()->shouldInsertFragment(fragment, range, EditorInsertActionDropped)) {
398             loader->setAllowStaleResources(false);
399             return false;
400         }
401         
402         m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
403         if (dragIsMove(innerFrame->selectionController(), dragData)) {
404             bool smartMove = innerFrame->selectionGranularity() == WordGranularity 
405                           && innerFrame->editor()->smartInsertDeleteEnabled() 
406                           && dragData->canSmartReplace();
407             applyCommand(new MoveSelectionCommand(fragment, dragCaret.base(), smartMove));
408         } else {
409             if (setSelectionToDragCaret(innerFrame, dragCaret, range, point))
410                 applyCommand(new ReplaceSelectionCommand(m_document, fragment, true, dragData->canSmartReplace(), chosePlainText)); 
411         }    
412     } else {
413         String text = dragData->asPlainText();
414         if (text.isEmpty() || !innerFrame->editor()->shouldInsertText(text, range.get(), EditorInsertActionDropped)) {
415             loader->setAllowStaleResources(false);
416             return false;
417         }
418         
419         m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
420         if (setSelectionToDragCaret(innerFrame, dragCaret, range, point))
421             applyCommand(new ReplaceSelectionCommand(m_document, createFragmentFromText(range.get(), text), true, false, true)); 
422     }
423     loader->setAllowStaleResources(false);
424
425     return true;
426 }
427     
428     
429 bool DragController::canProcessDrag(DragData* dragData) 
430 {
431     ASSERT(dragData);
432
433     if (!dragData->containsCompatibleContent())
434         return false;
435     
436     IntPoint point = m_page->mainFrame()->view()->windowToContents(dragData->clientPosition());
437     HitTestResult result = HitTestResult(point);
438     if (!m_page->mainFrame()->renderer())
439         return false;
440
441     result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, true);
442     
443     if (!result.innerNonSharedNode()) 
444         return false;
445     
446     if (dragData->containsFiles() && asFileInput(result.innerNonSharedNode()))
447         return true;
448         
449     if (!result.innerNonSharedNode()->isContentEditable())
450         return false;
451         
452     if (m_didInitiateDrag && m_document == m_dragInitiator && result.isSelected())
453         return false;
454
455     return true;
456 }
457
458 DragOperation DragController::tryDHTMLDrag(DragData* dragData)
459 {   
460     ASSERT(dragData);
461     ASSERT(m_document);
462     DragOperation op = DragOperationNone;
463     RefPtr<Frame> frame = m_page->mainFrame();
464     RefPtr<FrameView> viewProtector = frame->view();
465     if (!viewProtector)
466         return DragOperationNone;
467     
468     ClipboardAccessPolicy policy = frame->loader()->baseURL().isLocalFile() ? ClipboardReadable : ClipboardTypesReadable;
469     RefPtr<Clipboard> clipboard = dragData->createClipboard(policy);
470     DragOperation srcOp = dragData->draggingSourceOperationMask();
471     clipboard->setSourceOperation(srcOp);
472     
473     PlatformMouseEvent event = createMouseEvent(dragData);
474     if (frame->eventHandler()->updateDragAndDrop(event, clipboard.get())) {
475         // *op unchanged if no source op was set
476         if (!clipboard->destinationOperation(op)) {
477             // The element accepted but they didn't pick an operation, so we pick one for them
478             // (as does WinIE).
479             if (srcOp & DragOperationCopy)
480                 op = DragOperationCopy;
481             else if (srcOp & DragOperationMove || srcOp & DragOperationGeneric)
482                 op = DragOperationMove;
483             else if (srcOp & DragOperationLink)
484                 op = DragOperationLink;
485             else
486                 op = DragOperationGeneric;
487         } else if (!(op & srcOp)) {
488             op = DragOperationNone;
489         }
490
491         clipboard->setAccessPolicy(ClipboardNumb);    // invalidate clipboard here for security
492         return op;
493     }
494     return op;
495 }
496
497 bool DragController::mayStartDragAtEventLocation(const Frame* frame, const IntPoint& framePos)
498 {
499     ASSERT(frame);
500     ASSERT(frame->settings());
501
502     if (!frame->view() || !frame->renderer())
503         return false;
504
505     HitTestResult mouseDownTarget = HitTestResult(framePos);
506
507     mouseDownTarget = frame->eventHandler()->hitTestResultAtPoint(framePos, true);
508
509     if (mouseDownTarget.image() 
510         && !mouseDownTarget.absoluteImageURL().isEmpty()
511         && frame->settings()->loadsImagesAutomatically()
512         && m_dragSourceAction & DragSourceActionImage)
513         return true;
514
515     if (!mouseDownTarget.absoluteLinkURL().isEmpty()
516         && m_dragSourceAction & DragSourceActionLink
517         && mouseDownTarget.isLiveLink())
518         return true;
519
520     if (mouseDownTarget.isSelected()
521         && m_dragSourceAction & DragSourceActionSelection)
522         return true;
523
524     return false;
525
526 }
527     
528 static CachedImage* getCachedImage(Element* element)
529 {
530     ASSERT(element);
531     RenderObject* renderer = element->renderer();
532     if (!renderer || !renderer->isImage()) 
533         return 0;
534     RenderImage* image = static_cast<RenderImage*>(renderer);
535     return image->cachedImage();
536 }
537     
538 static Image* getImage(Element* element)
539 {
540     ASSERT(element);
541     RenderObject* renderer = element->renderer();
542     if (!renderer || !renderer->isImage()) 
543         return 0;
544     
545     RenderImage* image = static_cast<RenderImage*>(renderer);
546     if (image->cachedImage() && !image->cachedImage()->errorOccurred())
547         return image->cachedImage()->image();
548     return 0;
549 }
550     
551 static void prepareClipboardForImageDrag(Frame* src, Clipboard* clipboard, Element* node, const KURL& linkURL, const KURL& imageURL, const String& label)
552 {
553     RefPtr<Range> range = src->document()->createRange();
554     ExceptionCode ec = 0;
555     range->selectNode(node, ec);
556     ASSERT(ec == 0);
557     src->selectionController()->setSelection(Selection(range.get(), DOWNSTREAM));           
558     clipboard->declareAndWriteDragImage(node, !linkURL.isEmpty() ? linkURL : imageURL, label, src);
559 }
560     
561 static IntPoint dragLocForDHTMLDrag(const IntPoint& mouseDraggedPoint, const IntPoint& dragOrigin, const IntPoint& dragImageOffset, bool isLinkImage)
562 {
563     // dragImageOffset is the cursor position relative to the lower-left corner of the image.
564 #if PLATFORM(MAC) 
565     // We add in the Y dimension because we are a flipped view, so adding moves the image down. 
566     const int yOffset = dragImageOffset.y();
567 #else
568     const int yOffset = -dragImageOffset.y();
569 #endif
570     
571     if (isLinkImage)
572         return IntPoint(mouseDraggedPoint.x() - dragImageOffset.x(), mouseDraggedPoint.y() + yOffset);
573     
574     return IntPoint(dragOrigin.x() - dragImageOffset.x(), dragOrigin.y() + yOffset);
575 }
576     
577 static IntPoint dragLocForSelectionDrag(Frame* src)
578 {
579     IntRect draggingRect = enclosingIntRect(src->selectionRect());
580     int xpos = draggingRect.right();
581     xpos = draggingRect.x() < xpos ? draggingRect.x() : xpos;
582     int ypos = draggingRect.bottom();
583 #if PLATFORM(MAC)
584     // Deal with flipped coordinates on Mac
585     ypos = draggingRect.y() > ypos ? draggingRect.y() : ypos;
586 #else
587     ypos = draggingRect.y() < ypos ? draggingRect.y() : ypos;
588 #endif
589     return IntPoint(xpos, ypos);
590 }
591     
592 bool DragController::startDrag(Frame* src, Clipboard* clipboard, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin, bool isDHTMLDrag)
593 {    
594     ASSERT(src);
595     ASSERT(clipboard);
596     
597     if (!src->view() || !src->renderer())
598         return false;
599     
600     HitTestResult dragSource = HitTestResult(dragOrigin);
601     dragSource = src->eventHandler()->hitTestResultAtPoint(dragOrigin, true);
602     KURL linkURL = dragSource.absoluteLinkURL();
603     KURL imageURL = dragSource.absoluteImageURL();
604     bool isSelected = dragSource.isSelected();
605     
606     IntPoint mouseDraggedPoint = src->view()->windowToContents(dragEvent.pos());
607     
608     m_draggingImageURL = KURL();
609     m_dragOperation = srcOp;
610     
611     DragImageRef dragImage = 0;
612     IntPoint dragLoc(0, 0);
613     IntPoint dragImageOffset(0, 0);
614     
615     if (isDHTMLDrag) 
616         dragImage = clipboard->createDragImage(dragImageOffset);
617     
618     // We allow DHTML/JS to set the drag image, even if its a link, image or text we're dragging.
619     // This is in the spirit of the IE API, which allows overriding of pasteboard data and DragOp.
620     if (dragImage) {
621         dragLoc = dragLocForDHTMLDrag(mouseDraggedPoint, dragOrigin, dragImageOffset, !linkURL.isEmpty());
622         m_dragOffset = dragImageOffset;
623     }
624     
625     bool startedDrag = true; // optimism - we almost always manage to start the drag
626     
627     Node* node = dragSource.innerNonSharedNode();
628     
629     if (!imageURL.isEmpty() && node && node->isElementNode()
630             && getImage(static_cast<Element*>(node)) 
631             && (m_dragSourceAction & DragSourceActionImage)) {
632         Element* element = static_cast<Element*>(node);
633         if (!clipboard->hasData()) {
634             m_draggingImageURL = imageURL; 
635             prepareClipboardForImageDrag(src, clipboard, element, linkURL, imageURL, dragSource.altDisplayString());
636         }
637         
638         m_client->willPerformDragSourceAction(DragSourceActionImage, dragOrigin, clipboard);
639         
640         if (!dragImage) {
641             IntRect imageRect = dragSource.imageRect();
642             imageRect.setLocation(m_page->mainFrame()->view()->windowToContents(src->view()->contentsToWindow(imageRect.location())));
643             doImageDrag(element, dragOrigin, dragSource.imageRect(), clipboard, src, m_dragOffset);
644         } else 
645             // DHTML defined drag image
646             doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false);
647
648     } else if (!linkURL.isEmpty() && (m_dragSourceAction & DragSourceActionLink)) {
649         if (!clipboard->hasData())
650             // Simplify whitespace so the title put on the clipboard resembles what the user sees
651             // on the web page. This includes replacing newlines with spaces.
652             clipboard->writeURL(linkURL, dragSource.textContent().simplifyWhiteSpace(), src);
653         
654         m_client->willPerformDragSourceAction(DragSourceActionLink, dragOrigin, clipboard);
655         if (!dragImage) {
656             dragImage = m_client->createDragImageForLink(linkURL, dragSource.textContent(), src);
657             IntSize size = dragImageSize(dragImage);
658             m_dragOffset = IntPoint(-size.width() / 2, -LinkDragBorderInset);
659             dragLoc = IntPoint(mouseDraggedPoint.x() + m_dragOffset.x(), mouseDraggedPoint.y() + m_dragOffset.y());
660         } 
661         doSystemDrag(dragImage, dragLoc, mouseDraggedPoint, clipboard, src, true);
662     } else if (isSelected && (m_dragSourceAction & DragSourceActionSelection)) {
663         RefPtr<Range> selectionRange = src->selectionController()->toRange();
664         ASSERT(selectionRange);
665         if (!clipboard->hasData()) 
666             clipboard->writeRange(selectionRange.get(), src);
667         m_client->willPerformDragSourceAction(DragSourceActionSelection, dragOrigin, clipboard);
668         if (!dragImage) {
669             dragImage = createDragImageForSelection(src);
670             dragLoc = dragLocForSelectionDrag(src);
671             m_dragOffset = IntPoint((int)(dragOrigin.x() - dragLoc.x()), (int)(dragOrigin.y() - dragLoc.y()));
672         }
673         doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false);
674     } else if (isDHTMLDrag) {
675         ASSERT(m_dragSourceAction & DragSourceActionDHTML);
676         m_client->willPerformDragSourceAction(DragSourceActionDHTML, dragOrigin, clipboard);
677         doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false);
678     } else {
679         // Only way I know to get here is if to get here is if the original element clicked on in the mousedown is no longer
680         // under the mousedown point, so linkURL, imageURL and isSelected are all false/empty.
681         startedDrag = false;
682     }
683     
684     if (dragImage)
685         deleteDragImage(dragImage);
686     return startedDrag;
687 }
688     
689 void DragController::doImageDrag(Element* element, const IntPoint& dragOrigin, const IntRect& rect, Clipboard* clipboard, Frame* frame, IntPoint& dragImageOffset)
690 {
691     IntPoint mouseDownPoint = dragOrigin;
692     DragImageRef dragImage;
693     IntPoint origin;
694     
695     Image* image = getImage(element);
696     if (image && image->size().height() * image->size().width() <= MaxOriginalImageArea
697         && (dragImage = createDragImageFromImage(image))) {
698         IntSize originalSize = rect.size();
699         origin = rect.location();
700         
701         dragImage = fitDragImageToMaxSize(dragImage, rect.size(), maxDragImageSize());
702         dragImage = dissolveDragImageToFraction(dragImage, DragImageAlpha);
703         IntSize newSize = dragImageSize(dragImage);
704         
705         // Properly orient the drag image and orient it differently if it's smaller than the original
706         float scale = newSize.width() / (float)originalSize.width();
707         float dx = origin.x() - mouseDownPoint.x();
708         dx *= scale;
709         origin.setX((int)(dx + 0.5));
710 #if PLATFORM(MAC)
711         //Compensate for accursed flipped coordinates in cocoa
712         origin.setY(origin.y() + originalSize.height());
713 #endif
714         float dy = origin.y() - mouseDownPoint.y();
715         dy *= scale;
716         origin.setY((int)(dy + 0.5));
717     } else {
718         dragImage = createDragImageIconForCachedImage(getCachedImage(element));
719         if (dragImage)
720             origin = IntPoint(DragIconRightInset - dragImageSize(dragImage).width(), DragIconBottomInset);
721     }
722     
723     dragImageOffset.setX(mouseDownPoint.x() + origin.x());
724     dragImageOffset.setY(mouseDownPoint.y() + origin.y());
725     doSystemDrag(dragImage, dragImageOffset, dragOrigin, clipboard, frame, false);
726     
727     deleteDragImage(dragImage);
728 }
729     
730 void DragController::doSystemDrag(DragImageRef image, const IntPoint& dragLoc, const IntPoint& eventPos, Clipboard* clipboard, Frame* frame, bool forLink)
731 {
732     m_didInitiateDrag = true;
733     m_dragInitiator = frame->document();
734     // Protect this frame and view, as a load may occur mid drag and attempt to unload this frame
735     RefPtr<Frame> frameProtector = m_page->mainFrame();
736     RefPtr<FrameView> viewProtector = frameProtector->view();
737     m_client->startDrag(image, viewProtector->windowToContents(frame->view()->contentsToWindow(dragLoc)),
738         viewProtector->windowToContents(frame->view()->contentsToWindow(eventPos)), clipboard, frameProtector.get(), forLink);
739     
740     // Drag has ended, dragEnded *should* have been called, however it is possible  
741     // for the UIDelegate to take over the drag, and fail to send the appropriate
742     // drag termination event.  As dragEnded just resets drag variables, we just 
743     // call it anyway to be on the safe side
744     dragEnded();
745 }
746     
747 // Manual drag caret manipulation
748 void DragController::placeDragCaret(const IntPoint& windowPoint)
749 {
750     Frame* mainFrame = m_page->mainFrame();    
751     Document* newDraggingDoc = mainFrame->documentAtPoint(windowPoint);
752     if (m_document != newDraggingDoc) {
753         if (m_document)
754             cancelDrag();
755         m_document = newDraggingDoc;
756     }
757     if (!m_document)
758         return;
759     Frame* frame = m_document->frame();
760     ASSERT(frame);
761     FrameView* frameView = frame->view();
762     if (!frameView)
763         return;
764     IntPoint framePoint = frameView->windowToContents(windowPoint);
765     Selection dragCaret(frame->visiblePositionForPoint(framePoint));  
766     m_page->dragCaretController()->setSelection(dragCaret);
767 }
768     
769 }