ecca5e764024918bb2bf7fb74d8199d475fd2675
[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_page->mainFrame()->loader()->load(ResourceRequest(dragData->asURL()));
205     return true;
206 }
207     
208 DragOperation DragController::dragEnteredOrUpdated(DragData* dragData)
209 {
210     ASSERT(dragData);
211     IntPoint windowPoint = dragData->clientPosition();
212     
213     Document* newDraggingDoc = 0;
214     if (Frame* frame = m_page->mainFrame())
215         newDraggingDoc = frame->documentAtPoint(windowPoint);
216     if (m_document != newDraggingDoc) {
217         if (m_document)
218             cancelDrag();
219         m_document = newDraggingDoc;
220     }
221     
222     m_dragDestinationAction = m_client->actionMaskForDrag(dragData);
223     
224     DragOperation operation = DragOperationNone;
225     
226     if (m_dragDestinationAction == DragDestinationActionNone)
227         cancelDrag();
228     else {
229         operation = tryDocumentDrag(dragData, m_dragDestinationAction);
230         if (operation == DragOperationNone && (m_dragDestinationAction & DragDestinationActionLoad))
231             return operationForLoad(dragData);
232     }
233     
234     return operation;
235 }
236
237 static HTMLInputElement* asFileInput(Node* node)
238 {
239     ASSERT(node);
240     
241     // The button for a FILE input is a sub element with no set input type
242     // In order to get around this problem we assume any non-FILE input element
243     // is this internal button, and try querying the shadow parent node.
244     if (node->hasTagName(HTMLNames::inputTag) && node->isShadowNode() && static_cast<HTMLInputElement*>(node)->inputType() != HTMLInputElement::FILE)
245       node = node->shadowParentNode();
246     
247     if (!node || !node->hasTagName(HTMLNames::inputTag))
248         return 0;
249     
250     HTMLInputElement* inputElem = static_cast<HTMLInputElement*>(node);
251     if (inputElem->inputType() == HTMLInputElement::FILE)
252         return inputElem;
253     
254     return 0;
255 }
256     
257 DragOperation DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction actionMask)
258 {
259     ASSERT(dragData);
260     
261     if (!m_document)
262         return DragOperationNone;
263     
264     DragOperation operation = DragOperationNone;
265     if (actionMask & DragDestinationActionDHTML)
266         operation = tryDHTMLDrag(dragData);
267     m_isHandlingDrag = operation != DragOperationNone; 
268
269     RefPtr<FrameView> frameView = m_document->view();
270     if (!frameView)
271         return operation;
272     
273     if ((actionMask & DragDestinationActionEdit) && !m_isHandlingDrag && canProcessDrag(dragData)) {
274         if (dragData->containsColor()) 
275             return DragOperationGeneric;
276         
277         IntPoint dragPos = dragData->clientPosition();
278         IntPoint point = frameView->windowToContents(dragPos);
279         Element* element = m_document->elementFromPoint(point.x(), point.y());
280         ASSERT(element);
281         Frame* innerFrame = element->document()->frame();
282         ASSERT(innerFrame);
283         if (!asFileInput(element)) {
284             Selection dragCaret;
285             if (Frame* frame = m_document->frame())
286                 dragCaret = frame->visiblePositionForPoint(point);
287             m_page->dragCaretController()->setSelection(dragCaret);
288         }
289         
290         return dragIsMove(innerFrame->selectionController(), dragData) ? DragOperationMove : DragOperationCopy;
291     } 
292     
293     m_page->dragCaretController()->clear();
294     return operation;
295 }
296
297 DragSourceAction DragController::delegateDragSourceAction(const IntPoint& windowPoint)
298 {  
299     m_dragSourceAction = m_client->dragSourceActionMaskForPoint(windowPoint);
300     return m_dragSourceAction;
301 }
302     
303 DragOperation DragController::operationForLoad(DragData* dragData)
304 {
305     ASSERT(dragData);
306     Document* doc = 0;
307     doc = m_page->mainFrame()->documentAtPoint(dragData->clientPosition());
308     if (doc && (m_didInitiateDrag || doc->isPluginDocument() || (doc->frame() && doc->frame()->editor()->clientIsEditable())))
309         return DragOperationNone;
310     return dragOperation(dragData);
311 }
312
313 static bool setSelectionToDragCaret(Frame* frame, Selection& dragCaret, RefPtr<Range>& range, const IntPoint& point)
314 {
315     frame->selectionController()->setSelection(dragCaret);
316     if (frame->selectionController()->isNone()) {
317         dragCaret = frame->visiblePositionForPoint(point);
318         frame->selectionController()->setSelection(dragCaret);
319         range = dragCaret.toRange();
320     }
321     return !frame->selectionController()->isNone() && frame->selectionController()->isContentEditable();
322 }
323
324 bool DragController::concludeDrag(DragData* dragData, DragDestinationAction actionMask)
325 {
326     ASSERT(dragData);
327     ASSERT(!m_isHandlingDrag);
328     ASSERT(actionMask & DragDestinationActionEdit);
329     
330     if (!m_document)
331         return false;
332     
333     IntPoint point = m_document->view()->windowToContents(dragData->clientPosition());
334     Element* element =  m_document->elementFromPoint(point.x(), point.y());
335     ASSERT(element);
336     Frame* innerFrame = element->ownerDocument()->frame();
337     ASSERT(innerFrame);    
338
339     if (dragData->containsColor()) {
340         Color color = dragData->asColor();
341         if (!color.isValid())
342             return false;
343         if (!innerFrame)
344             return false;
345         RefPtr<Range> innerRange = innerFrame->selectionController()->toRange();
346         RefPtr<CSSStyleDeclaration> style = m_document->createCSSStyleDeclaration();
347         ExceptionCode ec;
348         style->setProperty("color", color.name(), ec);
349         if (!innerFrame->editor()->shouldApplyStyle(style.get(), innerRange.get()))
350             return false;
351         m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
352         innerFrame->editor()->applyStyle(style.get(), EditActionSetColor);
353         return true;
354     }
355     
356     if (!m_page->dragController()->canProcessDrag(dragData)) {
357         m_page->dragCaretController()->clear();
358         return false;
359     }
360     
361     if (HTMLInputElement* fileInput = asFileInput(element)) {
362         if (!dragData->containsFiles())
363             return false;
364         
365         Vector<String> filenames;
366         dragData->asFilenames(filenames);
367         if (filenames.isEmpty())
368             return false;
369         
370         // Ugly.  For security none of the API's available to us to set the input value 
371         // on file inputs.  Even forcing a change in HTMLInputElement doesn't work as
372         // RenderFileUploadControl clears the file when doing updateFromElement()
373         RenderFileUploadControl* renderer = static_cast<RenderFileUploadControl*>(fileInput->renderer());
374         
375         if (!renderer)
376             return false;
377         
378         // Only take the first filename as <input type="file" /> can only accept one
379         renderer->receiveDroppedFile(filenames[0]);
380         return true;
381     }
382
383     Selection dragCaret(m_page->dragCaretController()->selection());
384     m_page->dragCaretController()->clear();
385     RefPtr<Range> range = dragCaret.toRange();
386     
387     // For range to be null a WebKit client must have done something bad while
388     // manually controlling drag behaviour
389     if (!range)  
390         return false;
391     DocLoader* loader = range->ownerDocument()->docLoader();
392     loader->setAllowStaleResources(true);
393     if (dragIsMove(innerFrame->selectionController(), dragData) || dragCaret.isContentRichlyEditable()) { 
394         bool chosePlainText = false;
395         RefPtr<DocumentFragment> fragment = documentFragmentFromDragData(dragData, range, true, chosePlainText);
396         if (!fragment || !innerFrame->editor()->shouldInsertFragment(fragment, range, EditorInsertActionDropped)) {
397             loader->setAllowStaleResources(false);
398             return false;
399         }
400         
401         m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
402         if (dragIsMove(innerFrame->selectionController(), dragData)) {
403             bool smartMove = innerFrame->selectionGranularity() == WordGranularity 
404                           && innerFrame->editor()->smartInsertDeleteEnabled() 
405                           && dragData->canSmartReplace();
406             applyCommand(new MoveSelectionCommand(fragment, dragCaret.base(), smartMove));
407         } else {
408             if (setSelectionToDragCaret(innerFrame, dragCaret, range, point))
409                 applyCommand(new ReplaceSelectionCommand(m_document, fragment, true, dragData->canSmartReplace(), chosePlainText)); 
410         }    
411     } else {
412         String text = dragData->asPlainText();
413         if (text.isEmpty() || !innerFrame->editor()->shouldInsertText(text, range.get(), EditorInsertActionDropped)) {
414             loader->setAllowStaleResources(false);
415             return false;
416         }
417         
418         m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
419         if (setSelectionToDragCaret(innerFrame, dragCaret, range, point))
420             applyCommand(new ReplaceSelectionCommand(m_document, createFragmentFromText(range.get(), text), true, false, true)); 
421     }
422     loader->setAllowStaleResources(false);
423
424     return true;
425 }
426     
427     
428 bool DragController::canProcessDrag(DragData* dragData) 
429 {
430     ASSERT(dragData);
431
432     if (!dragData->containsCompatibleContent())
433         return false;
434     
435     IntPoint point = m_page->mainFrame()->view()->windowToContents(dragData->clientPosition());
436     HitTestResult result = HitTestResult(point);
437     if (!m_page->mainFrame()->renderer())
438         return false;
439
440     result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, true);
441     
442     if (!result.innerNonSharedNode()) 
443         return false;
444     
445     if (dragData->containsFiles() && asFileInput(result.innerNonSharedNode()))
446         return true;
447         
448     if (!result.innerNonSharedNode()->isContentEditable())
449         return false;
450         
451     if (m_didInitiateDrag && m_document == m_dragInitiator && result.isSelected())
452         return false;
453
454     return true;
455 }
456
457 DragOperation DragController::tryDHTMLDrag(DragData* dragData)
458 {   
459     ASSERT(dragData);
460     ASSERT(m_document);
461     DragOperation op = DragOperationNone;
462     RefPtr<Frame> frame = m_page->mainFrame();
463     RefPtr<FrameView> viewProtector = frame->view();
464     if (!viewProtector)
465         return DragOperationNone;
466     
467     ClipboardAccessPolicy policy = frame->loader()->baseURL().isLocalFile() ? ClipboardReadable : ClipboardTypesReadable;
468     RefPtr<Clipboard> clipboard = dragData->createClipboard(policy);
469     DragOperation srcOp = dragData->draggingSourceOperationMask();
470     clipboard->setSourceOperation(srcOp);
471     
472     PlatformMouseEvent event = createMouseEvent(dragData);
473     if (frame->eventHandler()->updateDragAndDrop(event, clipboard.get())) {
474         // *op unchanged if no source op was set
475         if (!clipboard->destinationOperation(op)) {
476             // The element accepted but they didn't pick an operation, so we pick one for them
477             // (as does WinIE).
478             if (srcOp & DragOperationCopy)
479                 op = DragOperationCopy;
480             else if (srcOp & DragOperationMove || srcOp & DragOperationGeneric)
481                 op = DragOperationMove;
482             else if (srcOp & DragOperationLink)
483                 op = DragOperationLink;
484             else
485                 op = DragOperationGeneric;
486         } else if (!(op & srcOp)) {
487             op = DragOperationNone;
488         }
489
490         clipboard->setAccessPolicy(ClipboardNumb);    // invalidate clipboard here for security
491         return op;
492     }
493     return op;
494 }
495
496 bool DragController::mayStartDragAtEventLocation(const Frame* frame, const IntPoint& framePos)
497 {
498     ASSERT(frame);
499     ASSERT(frame->settings());
500
501     if (!frame->view() || !frame->renderer())
502         return false;
503
504     HitTestResult mouseDownTarget = HitTestResult(framePos);
505
506     mouseDownTarget = frame->eventHandler()->hitTestResultAtPoint(framePos, true);
507
508     if (mouseDownTarget.image() 
509         && !mouseDownTarget.absoluteImageURL().isEmpty()
510         && frame->settings()->loadsImagesAutomatically()
511         && m_dragSourceAction & DragSourceActionImage)
512         return true;
513
514     if (!mouseDownTarget.absoluteLinkURL().isEmpty()
515         && m_dragSourceAction & DragSourceActionLink
516         && mouseDownTarget.isLiveLink())
517         return true;
518
519     if (mouseDownTarget.isSelected()
520         && m_dragSourceAction & DragSourceActionSelection)
521         return true;
522
523     return false;
524
525 }
526     
527 static CachedImage* getCachedImage(Element* element)
528 {
529     ASSERT(element);
530     RenderObject* renderer = element->renderer();
531     if (!renderer || !renderer->isImage()) 
532         return 0;
533     RenderImage* image = static_cast<RenderImage*>(renderer);
534     return image->cachedImage();
535 }
536     
537 static Image* getImage(Element* element)
538 {
539     ASSERT(element);
540     RenderObject* renderer = element->renderer();
541     if (!renderer || !renderer->isImage()) 
542         return 0;
543     
544     RenderImage* image = static_cast<RenderImage*>(renderer);
545     if (image->cachedImage() && !image->cachedImage()->errorOccurred())
546         return image->cachedImage()->image();
547     return 0;
548 }
549     
550 static void prepareClipboardForImageDrag(Frame* src, Clipboard* clipboard, Element* node, const KURL& linkURL, const KURL& imageURL, const String& label)
551 {
552     RefPtr<Range> range = src->document()->createRange();
553     ExceptionCode ec = 0;
554     range->selectNode(node, ec);
555     ASSERT(ec == 0);
556     src->selectionController()->setSelection(Selection(range.get(), DOWNSTREAM));           
557     clipboard->declareAndWriteDragImage(node, !linkURL.isEmpty() ? linkURL : imageURL, label, src);
558 }
559     
560 static IntPoint dragLocForDHTMLDrag(const IntPoint& mouseDraggedPoint, const IntPoint& dragOrigin, const IntPoint& dragImageOffset, bool isLinkImage)
561 {
562     // dragImageOffset is the cursor position relative to the lower-left corner of the image.
563 #if PLATFORM(MAC) 
564     // We add in the Y dimension because we are a flipped view, so adding moves the image down. 
565     const int yOffset = dragImageOffset.y();
566 #else
567     const int yOffset = -dragImageOffset.y();
568 #endif
569     
570     if (isLinkImage)
571         return IntPoint(mouseDraggedPoint.x() - dragImageOffset.x(), mouseDraggedPoint.y() + yOffset);
572     
573     return IntPoint(dragOrigin.x() - dragImageOffset.x(), dragOrigin.y() + yOffset);
574 }
575     
576 static IntPoint dragLocForSelectionDrag(Frame* src)
577 {
578     IntRect draggingRect = enclosingIntRect(src->selectionRect());
579     int xpos = draggingRect.right();
580     xpos = draggingRect.x() < xpos ? draggingRect.x() : xpos;
581     int ypos = draggingRect.bottom();
582 #if PLATFORM(MAC)
583     // Deal with flipped coordinates on Mac
584     ypos = draggingRect.y() > ypos ? draggingRect.y() : ypos;
585 #else
586     ypos = draggingRect.y() < ypos ? draggingRect.y() : ypos;
587 #endif
588     return IntPoint(xpos, ypos);
589 }
590     
591 bool DragController::startDrag(Frame* src, Clipboard* clipboard, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin, bool isDHTMLDrag)
592 {    
593     ASSERT(src);
594     ASSERT(clipboard);
595     
596     if (!src->view() || !src->renderer())
597         return false;
598     
599     HitTestResult dragSource = HitTestResult(dragOrigin);
600     dragSource = src->eventHandler()->hitTestResultAtPoint(dragOrigin, true);
601     KURL linkURL = dragSource.absoluteLinkURL();
602     KURL imageURL = dragSource.absoluteImageURL();
603     bool isSelected = dragSource.isSelected();
604     
605     IntPoint mouseDraggedPoint = src->view()->windowToContents(dragEvent.pos());
606     
607     m_draggingImageURL = KURL();
608     m_dragOperation = srcOp;
609     
610     DragImageRef dragImage = 0;
611     IntPoint dragLoc(0, 0);
612     IntPoint dragImageOffset(0, 0);
613     
614     if (isDHTMLDrag) 
615         dragImage = clipboard->createDragImage(dragImageOffset);
616     
617     // We allow DHTML/JS to set the drag image, even if its a link, image or text we're dragging.
618     // This is in the spirit of the IE API, which allows overriding of pasteboard data and DragOp.
619     if (dragImage) {
620         dragLoc = dragLocForDHTMLDrag(mouseDraggedPoint, dragOrigin, dragImageOffset, !linkURL.isEmpty());
621         m_dragOffset = dragImageOffset;
622     }
623     
624     bool startedDrag = true; // optimism - we almost always manage to start the drag
625     
626     Node* node = dragSource.innerNonSharedNode();
627     
628     if (!imageURL.isEmpty() && node && node->isElementNode()
629             && getImage(static_cast<Element*>(node)) 
630             && (m_dragSourceAction & DragSourceActionImage)) {
631         Element* element = static_cast<Element*>(node);
632         if (!clipboard->hasData()) {
633             m_draggingImageURL = imageURL; 
634             prepareClipboardForImageDrag(src, clipboard, element, linkURL, imageURL, dragSource.altDisplayString());
635         }
636         
637         m_client->willPerformDragSourceAction(DragSourceActionImage, dragOrigin, clipboard);
638         
639         if (!dragImage) {
640             IntRect imageRect = dragSource.imageRect();
641             imageRect.setLocation(m_page->mainFrame()->view()->windowToContents(src->view()->contentsToWindow(imageRect.location())));
642             doImageDrag(element, dragOrigin, dragSource.imageRect(), clipboard, src, m_dragOffset);
643         } else 
644             // DHTML defined drag image
645             doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false);
646
647     } else if (!linkURL.isEmpty() && (m_dragSourceAction & DragSourceActionLink)) {
648         if (!clipboard->hasData())
649             // Simplify whitespace so the title put on the clipboard resembles what the user sees
650             // on the web page. This includes replacing newlines with spaces.
651             clipboard->writeURL(linkURL, dragSource.textContent().simplifyWhiteSpace(), src);
652         
653         m_client->willPerformDragSourceAction(DragSourceActionLink, dragOrigin, clipboard);
654         if (!dragImage) {
655             dragImage = m_client->createDragImageForLink(linkURL, dragSource.textContent(), src);
656             IntSize size = dragImageSize(dragImage);
657             m_dragOffset = IntPoint(-size.width() / 2, -LinkDragBorderInset);
658             dragLoc = IntPoint(mouseDraggedPoint.x() + m_dragOffset.x(), mouseDraggedPoint.y() + m_dragOffset.y());
659         } 
660         doSystemDrag(dragImage, dragLoc, mouseDraggedPoint, clipboard, src, true);
661     } else if (isSelected && (m_dragSourceAction & DragSourceActionSelection)) {
662         RefPtr<Range> selectionRange = src->selectionController()->toRange();
663         ASSERT(selectionRange);
664         if (!clipboard->hasData()) 
665             clipboard->writeRange(selectionRange.get(), src);
666         m_client->willPerformDragSourceAction(DragSourceActionSelection, dragOrigin, clipboard);
667         if (!dragImage) {
668             dragImage = createDragImageForSelection(src);
669             dragLoc = dragLocForSelectionDrag(src);
670             m_dragOffset = IntPoint((int)(dragOrigin.x() - dragLoc.x()), (int)(dragOrigin.y() - dragLoc.y()));
671         }
672         doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false);
673     } else if (isDHTMLDrag) {
674         ASSERT(m_dragSourceAction & DragSourceActionDHTML);
675         m_client->willPerformDragSourceAction(DragSourceActionDHTML, dragOrigin, clipboard);
676         doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false);
677     } else {
678         // 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
679         // under the mousedown point, so linkURL, imageURL and isSelected are all false/empty.
680         startedDrag = false;
681     }
682     
683     if (dragImage)
684         deleteDragImage(dragImage);
685     return startedDrag;
686 }
687     
688 void DragController::doImageDrag(Element* element, const IntPoint& dragOrigin, const IntRect& rect, Clipboard* clipboard, Frame* frame, IntPoint& dragImageOffset)
689 {
690     IntPoint mouseDownPoint = dragOrigin;
691     DragImageRef dragImage;
692     IntPoint origin;
693     
694     Image* image = getImage(element);
695     if (image && image->size().height() * image->size().width() <= MaxOriginalImageArea
696         && (dragImage = createDragImageFromImage(image))) {
697         IntSize originalSize = rect.size();
698         origin = rect.location();
699         
700         dragImage = fitDragImageToMaxSize(dragImage, rect.size(), maxDragImageSize());
701         dragImage = dissolveDragImageToFraction(dragImage, DragImageAlpha);
702         IntSize newSize = dragImageSize(dragImage);
703         
704         // Properly orient the drag image and orient it differently if it's smaller than the original
705         float scale = newSize.width() / (float)originalSize.width();
706         float dx = origin.x() - mouseDownPoint.x();
707         dx *= scale;
708         origin.setX((int)(dx + 0.5));
709 #if PLATFORM(MAC)
710         //Compensate for accursed flipped coordinates in cocoa
711         origin.setY(origin.y() + originalSize.height());
712 #endif
713         float dy = origin.y() - mouseDownPoint.y();
714         dy *= scale;
715         origin.setY((int)(dy + 0.5));
716     } else {
717         dragImage = createDragImageIconForCachedImage(getCachedImage(element));
718         if (dragImage)
719             origin = IntPoint(DragIconRightInset - dragImageSize(dragImage).width(), DragIconBottomInset);
720     }
721     
722     dragImageOffset.setX(mouseDownPoint.x() + origin.x());
723     dragImageOffset.setY(mouseDownPoint.y() + origin.y());
724     doSystemDrag(dragImage, dragImageOffset, dragOrigin, clipboard, frame, false);
725     
726     deleteDragImage(dragImage);
727 }
728     
729 void DragController::doSystemDrag(DragImageRef image, const IntPoint& dragLoc, const IntPoint& eventPos, Clipboard* clipboard, Frame* frame, bool forLink)
730 {
731     m_didInitiateDrag = true;
732     m_dragInitiator = frame->document();
733     // Protect this frame and view, as a load may occur mid drag and attempt to unload this frame
734     RefPtr<Frame> frameProtector = m_page->mainFrame();
735     RefPtr<FrameView> viewProtector = frameProtector->view();
736     m_client->startDrag(image, viewProtector->windowToContents(frame->view()->contentsToWindow(dragLoc)),
737         viewProtector->windowToContents(frame->view()->contentsToWindow(eventPos)), clipboard, frameProtector.get(), forLink);
738     
739     // Drag has ended, dragEnded *should* have been called, however it is possible  
740     // for the UIDelegate to take over the drag, and fail to send the appropriate
741     // drag termination event.  As dragEnded just resets drag variables, we just 
742     // call it anyway to be on the safe side
743     dragEnded();
744 }
745     
746 // Manual drag caret manipulation
747 void DragController::placeDragCaret(const IntPoint& windowPoint)
748 {
749     Frame* mainFrame = m_page->mainFrame();    
750     Document* newDraggingDoc = mainFrame->documentAtPoint(windowPoint);
751     if (m_document != newDraggingDoc) {
752         if (m_document)
753             cancelDrag();
754         m_document = newDraggingDoc;
755     }
756     if (!m_document)
757         return;
758     Frame* frame = m_document->frame();
759     ASSERT(frame);
760     FrameView* frameView = frame->view();
761     if (!frameView)
762         return;
763     IntPoint framePoint = frameView->windowToContents(windowPoint);
764     Selection dragCaret(frame->visiblePositionForPoint(framePoint));  
765     m_page->dragCaretController()->setSelection(dragCaret);
766 }
767     
768 }