Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / page / DragController.cpp
1 /*
2  * Copyright (C) 2007, 2009, 2010, 2013, 2015 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 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 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 "HTMLAnchorElement.h"
30 #include "SVGAElement.h"
31
32 #if ENABLE(DRAG_SUPPORT)
33 #include "CachedImage.h"
34 #include "CachedResourceLoader.h"
35 #include "DataTransfer.h"
36 #include "Document.h"
37 #include "DocumentFragment.h"
38 #include "DragActions.h"
39 #include "DragClient.h"
40 #include "DragData.h"
41 #include "DragImage.h"
42 #include "DragState.h"
43 #include "Editor.h"
44 #include "EditorClient.h"
45 #include "EventHandler.h"
46 #include "ExceptionCodePlaceholder.h"
47 #include "FloatRect.h"
48 #include "FrameLoadRequest.h"
49 #include "FrameLoader.h"
50 #include "FrameSelection.h"
51 #include "FrameView.h"
52 #include "HTMLAttachmentElement.h"
53 #include "HTMLImageElement.h"
54 #include "HTMLInputElement.h"
55 #include "HTMLNames.h"
56 #include "HTMLPlugInElement.h"
57 #include "HitTestRequest.h"
58 #include "HitTestResult.h"
59 #include "Image.h"
60 #include "ImageOrientation.h"
61 #include "MainFrame.h"
62 #include "MoveSelectionCommand.h"
63 #include "Page.h"
64 #include "Pasteboard.h"
65 #include "PlatformKeyboardEvent.h"
66 #include "PluginDocument.h"
67 #include "PluginViewBase.h"
68 #include "RenderFileUploadControl.h"
69 #include "RenderImage.h"
70 #include "RenderView.h"
71 #include "ReplaceSelectionCommand.h"
72 #include "ResourceRequest.h"
73 #include "SecurityOrigin.h"
74 #include "Settings.h"
75 #include "ShadowRoot.h"
76 #include "StyleProperties.h"
77 #include "Text.h"
78 #include "TextEvent.h"
79 #include "htmlediting.h"
80 #include "markup.h"
81 #include <wtf/CurrentTime.h>
82 #include <wtf/RefPtr.h>
83 #endif
84
85 namespace WebCore {
86
87 bool isDraggableLink(const Element& element)
88 {
89     if (is<HTMLAnchorElement>(element))
90         return downcast<HTMLAnchorElement>(element).isLiveLink();
91     if (is<SVGAElement>(element))
92         return element.isLink();
93     return false;
94 }
95
96 #if ENABLE(DRAG_SUPPORT)
97     
98 static PlatformMouseEvent createMouseEvent(DragData& dragData)
99 {
100     int keyState = dragData.modifierKeyState();
101     bool shiftKey = static_cast<bool>(keyState & PlatformEvent::ShiftKey);
102     bool ctrlKey = static_cast<bool>(keyState & PlatformEvent::CtrlKey);
103     bool altKey = static_cast<bool>(keyState & PlatformEvent::AltKey);
104     bool metaKey = static_cast<bool>(keyState & PlatformEvent::MetaKey);
105
106     return PlatformMouseEvent(dragData.clientPosition(), dragData.globalPosition(),
107                               LeftButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey,
108                               metaKey, currentTime(), ForceAtClick);
109 }
110
111 DragController::DragController(Page& page, DragClient& client)
112     : m_page(page)
113     , m_client(client)
114     , m_numberOfItemsToBeAccepted(0)
115     , m_documentIsHandlingDrag(false)
116     , m_dragDestinationAction(DragDestinationActionNone)
117     , m_dragSourceAction(DragSourceActionNone)
118     , m_didInitiateDrag(false)
119     , m_sourceDragOperation(DragOperationNone)
120 {
121 }
122
123 DragController::~DragController()
124 {
125     m_client.dragControllerDestroyed();
126 }
127
128 static PassRefPtr<DocumentFragment> documentFragmentFromDragData(DragData& dragData, Frame& frame, Range& context, bool allowPlainText, bool& chosePlainText)
129 {
130     chosePlainText = false;
131
132     Document& document = context.ownerDocument();
133     if (dragData.containsCompatibleContent()) {
134         if (PassRefPtr<DocumentFragment> fragment = frame.editor().webContentFromPasteboard(*Pasteboard::createForDragAndDrop(dragData), context, allowPlainText, chosePlainText))
135             return fragment;
136
137         if (dragData.containsURL(DragData::DoNotConvertFilenames)) {
138             String title;
139             String url = dragData.asURL(DragData::DoNotConvertFilenames, &title);
140             if (!url.isEmpty()) {
141                 Ref<HTMLAnchorElement> anchor = HTMLAnchorElement::create(document);
142                 anchor->setHref(url);
143                 if (title.isEmpty()) {
144                     // Try the plain text first because the url might be normalized or escaped.
145                     if (dragData.containsPlainText())
146                         title = dragData.asPlainText();
147                     if (title.isEmpty())
148                         title = url;
149                 }
150                 anchor->appendChild(document.createTextNode(title), IGNORE_EXCEPTION);
151                 Ref<DocumentFragment> fragment = document.createDocumentFragment();
152                 fragment->appendChild(WTFMove(anchor), IGNORE_EXCEPTION);
153                 return fragment.ptr();
154             }
155         }
156     }
157     if (allowPlainText && dragData.containsPlainText()) {
158         chosePlainText = true;
159         return createFragmentFromText(context, dragData.asPlainText()).ptr();
160     }
161
162     return nullptr;
163 }
164
165 bool DragController::dragIsMove(FrameSelection& selection, DragData& dragData)
166 {
167     const VisibleSelection& visibleSelection = selection.selection();
168     return m_documentUnderMouse == m_dragInitiator && visibleSelection.isContentEditable() && visibleSelection.isRange() && !isCopyKeyDown(dragData);
169 }
170
171 void DragController::clearDragCaret()
172 {
173     m_page.dragCaretController().clear();
174 }
175
176 void DragController::dragEnded()
177 {
178     m_dragInitiator = nullptr;
179     m_didInitiateDrag = false;
180     clearDragCaret();
181     
182     m_client.dragEnded();
183 }
184
185 DragOperation DragController::dragEntered(DragData& dragData)
186 {
187     return dragEnteredOrUpdated(dragData);
188 }
189
190 void DragController::dragExited(DragData& dragData)
191 {
192     if (RefPtr<FrameView> v = m_page.mainFrame().view()) {
193 #if ENABLE(DASHBOARD_SUPPORT)
194         DataTransferAccessPolicy policy = (m_page.mainFrame().settings().usesDashboardBackwardCompatibilityMode() && (!m_documentUnderMouse || m_documentUnderMouse->securityOrigin()->isLocal()))
195             ? DataTransferAccessPolicy::Readable : DataTransferAccessPolicy::TypesReadable;
196 #else
197         DataTransferAccessPolicy policy = DataTransferAccessPolicy::TypesReadable;
198 #endif
199         RefPtr<DataTransfer> dataTransfer = DataTransfer::createForDragAndDrop(policy, dragData);
200         dataTransfer->setSourceOperation(dragData.draggingSourceOperationMask());
201         m_page.mainFrame().eventHandler().cancelDragAndDrop(createMouseEvent(dragData), dataTransfer.get());
202         dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security.
203     }
204     mouseMovedIntoDocument(nullptr);
205     if (m_fileInputElementUnderMouse)
206         m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false);
207     m_fileInputElementUnderMouse = nullptr;
208 }
209
210 DragOperation DragController::dragUpdated(DragData& dragData)
211 {
212     return dragEnteredOrUpdated(dragData);
213 }
214
215 bool DragController::performDragOperation(DragData& dragData)
216 {
217     m_documentUnderMouse = m_page.mainFrame().documentAtPoint(dragData.clientPosition());
218
219     ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy = ShouldOpenExternalURLsPolicy::ShouldNotAllow;
220     if (m_documentUnderMouse)
221         shouldOpenExternalURLsPolicy = m_documentUnderMouse->shouldOpenExternalURLsPolicyToPropagate();
222
223     if ((m_dragDestinationAction & DragDestinationActionDHTML) && m_documentIsHandlingDrag) {
224         m_client.willPerformDragDestinationAction(DragDestinationActionDHTML, dragData);
225         Ref<MainFrame> mainFrame(m_page.mainFrame());
226         bool preventedDefault = false;
227         if (mainFrame->view()) {
228             // Sending an event can result in the destruction of the view and part.
229             RefPtr<DataTransfer> dataTransfer = DataTransfer::createForDragAndDrop(DataTransferAccessPolicy::Readable, dragData);
230             dataTransfer->setSourceOperation(dragData.draggingSourceOperationMask());
231             preventedDefault = mainFrame->eventHandler().performDragAndDrop(createMouseEvent(dragData), dataTransfer.get());
232             dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security.
233         }
234         if (preventedDefault) {
235             clearDragCaret();
236             m_documentUnderMouse = nullptr;
237             return true;
238         }
239     }
240
241     if ((m_dragDestinationAction & DragDestinationActionEdit) && concludeEditDrag(dragData)) {
242         m_documentUnderMouse = nullptr;
243         return true;
244     }
245
246     m_documentUnderMouse = nullptr;
247
248     if (operationForLoad(dragData) == DragOperationNone)
249         return false;
250
251     m_client.willPerformDragDestinationAction(DragDestinationActionLoad, dragData);
252     m_page.mainFrame().loader().load(FrameLoadRequest(&m_page.mainFrame(), ResourceRequest(dragData.asURL()), shouldOpenExternalURLsPolicy));
253     return true;
254 }
255
256 void DragController::mouseMovedIntoDocument(Document* newDocument)
257 {
258     if (m_documentUnderMouse == newDocument)
259         return;
260
261     // If we were over another document clear the selection
262     if (m_documentUnderMouse)
263         clearDragCaret();
264     m_documentUnderMouse = newDocument;
265 }
266
267 DragOperation DragController::dragEnteredOrUpdated(DragData& dragData)
268 {
269     mouseMovedIntoDocument(m_page.mainFrame().documentAtPoint(dragData.clientPosition()));
270
271     m_dragDestinationAction = m_client.actionMaskForDrag(dragData);
272     if (m_dragDestinationAction == DragDestinationActionNone) {
273         clearDragCaret(); // FIXME: Why not call mouseMovedIntoDocument(nullptr)?
274         return DragOperationNone;
275     }
276
277     DragOperation dragOperation = DragOperationNone;
278     m_documentIsHandlingDrag = tryDocumentDrag(dragData, m_dragDestinationAction, dragOperation);
279     if (!m_documentIsHandlingDrag && (m_dragDestinationAction & DragDestinationActionLoad))
280         dragOperation = operationForLoad(dragData);
281     return dragOperation;
282 }
283
284 static HTMLInputElement* asFileInput(Node& node)
285 {
286     if (!is<HTMLInputElement>(node))
287         return nullptr;
288
289     auto* inputElement = &downcast<HTMLInputElement>(node);
290
291     // If this is a button inside of the a file input, move up to the file input.
292     if (inputElement->isTextButton() && is<ShadowRoot>(inputElement->treeScope().rootNode())) {
293         auto& host = *downcast<ShadowRoot>(inputElement->treeScope().rootNode()).host();
294         inputElement = is<HTMLInputElement>(host) ? &downcast<HTMLInputElement>(host) : nullptr;
295     }
296
297     return inputElement && inputElement->isFileUpload() ? inputElement : nullptr;
298 }
299
300 // This can return null if an empty document is loaded.
301 static Element* elementUnderMouse(Document* documentUnderMouse, const IntPoint& p)
302 {
303     Frame* frame = documentUnderMouse->frame();
304     float zoomFactor = frame ? frame->pageZoomFactor() : 1;
305     LayoutPoint point(p.x() * zoomFactor, p.y() * zoomFactor);
306
307     HitTestResult result(point);
308     documentUnderMouse->renderView()->hitTest(HitTestRequest(), result);
309
310     Node* node = result.innerNode();
311     while (node && !is<Element>(*node))
312         node = node->parentNode();
313     if (node)
314         node = node->deprecatedShadowAncestorNode();
315
316     return downcast<Element>(node);
317 }
318
319 bool DragController::tryDocumentDrag(DragData& dragData, DragDestinationAction actionMask, DragOperation& dragOperation)
320 {
321     if (!m_documentUnderMouse)
322         return false;
323
324     if (m_dragInitiator && !m_documentUnderMouse->securityOrigin()->canReceiveDragData(m_dragInitiator->securityOrigin()))
325         return false;
326
327     bool isHandlingDrag = false;
328     if (actionMask & DragDestinationActionDHTML) {
329         isHandlingDrag = tryDHTMLDrag(dragData, dragOperation);
330         // Do not continue if m_documentUnderMouse has been reset by tryDHTMLDrag.
331         // tryDHTMLDrag fires dragenter event. The event listener that listens
332         // to this event may create a nested message loop (open a modal dialog),
333         // which could process dragleave event and reset m_documentUnderMouse in
334         // dragExited.
335         if (!m_documentUnderMouse)
336             return false;
337     }
338
339     // It's unclear why this check is after tryDHTMLDrag.
340     // We send drag events in tryDHTMLDrag and that may be the reason.
341     RefPtr<FrameView> frameView = m_documentUnderMouse->view();
342     if (!frameView)
343         return false;
344
345     if (isHandlingDrag) {
346         clearDragCaret();
347         return true;
348     }
349
350     if ((actionMask & DragDestinationActionEdit) && canProcessDrag(dragData)) {
351         if (dragData.containsColor()) {
352             dragOperation = DragOperationGeneric;
353             return true;
354         }
355
356         IntPoint point = frameView->windowToContents(dragData.clientPosition());
357         Element* element = elementUnderMouse(m_documentUnderMouse.get(), point);
358         if (!element)
359             return false;
360         
361         HTMLInputElement* elementAsFileInput = asFileInput(*element);
362         if (m_fileInputElementUnderMouse != elementAsFileInput) {
363             if (m_fileInputElementUnderMouse)
364                 m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false);
365             m_fileInputElementUnderMouse = elementAsFileInput;
366         }
367         
368         if (!m_fileInputElementUnderMouse)
369             m_page.dragCaretController().setCaretPosition(m_documentUnderMouse->frame()->visiblePositionForPoint(point));
370         else
371             clearDragCaret();
372
373         Frame* innerFrame = element->document().frame();
374         dragOperation = dragIsMove(innerFrame->selection(), dragData) ? DragOperationMove : DragOperationCopy;
375         m_numberOfItemsToBeAccepted = 0;
376
377         unsigned numberOfFiles = dragData.numberOfFiles();
378         if (m_fileInputElementUnderMouse) {
379             if (m_fileInputElementUnderMouse->isDisabledFormControl())
380                 m_numberOfItemsToBeAccepted = 0;
381             else if (m_fileInputElementUnderMouse->multiple())
382                 m_numberOfItemsToBeAccepted = numberOfFiles;
383             else if (numberOfFiles > 1)
384                 m_numberOfItemsToBeAccepted = 0;
385             else
386                 m_numberOfItemsToBeAccepted = 1;
387             
388             if (!m_numberOfItemsToBeAccepted)
389                 dragOperation = DragOperationNone;
390             m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(m_numberOfItemsToBeAccepted);
391         } else {
392             // We are not over a file input element. The dragged item(s) will only
393             // be loaded into the view the number of dragged items is 1.
394             m_numberOfItemsToBeAccepted = numberOfFiles != 1 ? 0 : 1;
395         }
396         
397         return true;
398     }
399     
400     // We are not over an editable region. Make sure we're clearing any prior drag cursor.
401     clearDragCaret();
402     if (m_fileInputElementUnderMouse)
403         m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false);
404     m_fileInputElementUnderMouse = nullptr;
405     return false;
406 }
407
408 DragSourceAction DragController::delegateDragSourceAction(const IntPoint& rootViewPoint)
409 {
410     m_dragSourceAction = m_client.dragSourceActionMaskForPoint(rootViewPoint);
411     return m_dragSourceAction;
412 }
413
414 DragOperation DragController::operationForLoad(DragData& dragData)
415 {
416     Document* document = m_page.mainFrame().documentAtPoint(dragData.clientPosition());
417
418     bool pluginDocumentAcceptsDrags = false;
419
420     if (is<PluginDocument>(document)) {
421         const Widget* widget = downcast<PluginDocument>(*document).pluginWidget();
422         const PluginViewBase* pluginView = is<PluginViewBase>(widget) ? downcast<PluginViewBase>(widget) : nullptr;
423
424         if (pluginView)
425             pluginDocumentAcceptsDrags = pluginView->shouldAllowNavigationFromDrags();
426     }
427
428     if (document && (m_didInitiateDrag || (is<PluginDocument>(*document) && !pluginDocumentAcceptsDrags) || document->hasEditableStyle()))
429         return DragOperationNone;
430     return dragOperation(dragData);
431 }
432
433 static bool setSelectionToDragCaret(Frame* frame, VisibleSelection& dragCaret, RefPtr<Range>& range, const IntPoint& point)
434 {
435     frame->selection().setSelection(dragCaret);
436     if (frame->selection().selection().isNone()) {
437         dragCaret = frame->visiblePositionForPoint(point);
438         frame->selection().setSelection(dragCaret);
439         range = dragCaret.toNormalizedRange();
440     }
441     return !frame->selection().isNone() && frame->selection().selection().isContentEditable();
442 }
443
444 bool DragController::dispatchTextInputEventFor(Frame* innerFrame, DragData& dragData)
445 {
446     ASSERT(m_page.dragCaretController().hasCaret());
447     String text = m_page.dragCaretController().isContentRichlyEditable() ? emptyString() : dragData.asPlainText();
448     Node* target = innerFrame->editor().findEventTargetFrom(m_page.dragCaretController().caretPosition());
449     return target->dispatchEvent(TextEvent::createForDrop(innerFrame->document()->domWindow(), text));
450 }
451
452 bool DragController::concludeEditDrag(DragData& dragData)
453 {
454     RefPtr<HTMLInputElement> fileInput = m_fileInputElementUnderMouse;
455     if (m_fileInputElementUnderMouse) {
456         m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false);
457         m_fileInputElementUnderMouse = nullptr;
458     }
459
460     if (!m_documentUnderMouse)
461         return false;
462
463     IntPoint point = m_documentUnderMouse->view()->windowToContents(dragData.clientPosition());
464     Element* element = elementUnderMouse(m_documentUnderMouse.get(), point);
465     if (!element)
466         return false;
467     RefPtr<Frame> innerFrame = element->document().frame();
468     ASSERT(innerFrame);
469
470     if (m_page.dragCaretController().hasCaret() && !dispatchTextInputEventFor(innerFrame.get(), dragData))
471         return true;
472
473     if (dragData.containsColor()) {
474         Color color = dragData.asColor();
475         if (!color.isValid())
476             return false;
477         RefPtr<Range> innerRange = innerFrame->selection().toNormalizedRange();
478         RefPtr<MutableStyleProperties> style = MutableStyleProperties::create();
479         style->setProperty(CSSPropertyColor, color.serialized(), false);
480         if (!innerFrame->editor().shouldApplyStyle(style.get(), innerRange.get()))
481             return false;
482         m_client.willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
483         innerFrame->editor().applyStyle(style.get(), EditActionSetColor);
484         return true;
485     }
486
487     if (dragData.containsFiles() && fileInput) {
488         // fileInput should be the element we hit tested for, unless it was made
489         // display:none in a drop event handler.
490         ASSERT(fileInput == element || !fileInput->renderer());
491         if (fileInput->isDisabledFormControl())
492             return false;
493
494         return fileInput->receiveDroppedFiles(dragData);
495     }
496
497     if (!m_page.dragController().canProcessDrag(dragData)) {
498         clearDragCaret();
499         return false;
500     }
501
502     VisibleSelection dragCaret = m_page.dragCaretController().caretPosition();
503     clearDragCaret();
504     RefPtr<Range> range = dragCaret.toNormalizedRange();
505     RefPtr<Element> rootEditableElement = innerFrame->selection().selection().rootEditableElement();
506
507     // For range to be null a WebKit client must have done something bad while
508     // manually controlling drag behaviour
509     if (!range)
510         return false;
511
512     ResourceCacheValidationSuppressor validationSuppressor(range->ownerDocument().cachedResourceLoader());
513     if (dragIsMove(innerFrame->selection(), dragData) || dragCaret.isContentRichlyEditable()) {
514         bool chosePlainText = false;
515         RefPtr<DocumentFragment> fragment = documentFragmentFromDragData(dragData, *innerFrame, *range, true, chosePlainText);
516         if (!fragment || !innerFrame->editor().shouldInsertFragment(fragment, range, EditorInsertActionDropped)) {
517             return false;
518         }
519
520         m_client.willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
521         if (dragIsMove(innerFrame->selection(), dragData)) {
522             // NSTextView behavior is to always smart delete on moving a selection,
523             // but only to smart insert if the selection granularity is word granularity.
524             bool smartDelete = innerFrame->editor().smartInsertDeleteEnabled();
525             bool smartInsert = smartDelete && innerFrame->selection().granularity() == WordGranularity && dragData.canSmartReplace();
526             applyCommand(MoveSelectionCommand::create(fragment, dragCaret.base(), smartInsert, smartDelete));
527         } else {
528             if (setSelectionToDragCaret(innerFrame.get(), dragCaret, range, point)) {
529                 ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::PreventNesting;
530                 if (dragData.canSmartReplace())
531                     options |= ReplaceSelectionCommand::SmartReplace;
532                 if (chosePlainText)
533                     options |= ReplaceSelectionCommand::MatchStyle;
534                 applyCommand(ReplaceSelectionCommand::create(*m_documentUnderMouse, WTFMove(fragment), options));
535             }
536         }
537     } else {
538         String text = dragData.asPlainText();
539         if (text.isEmpty() || !innerFrame->editor().shouldInsertText(text, range.get(), EditorInsertActionDropped)) {
540             return false;
541         }
542
543         m_client.willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
544         if (setSelectionToDragCaret(innerFrame.get(), dragCaret, range, point))
545             applyCommand(ReplaceSelectionCommand::create(*m_documentUnderMouse, createFragmentFromText(*range, text),  ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting));
546     }
547
548     if (rootEditableElement) {
549         if (Frame* frame = rootEditableElement->document().frame())
550             frame->eventHandler().updateDragStateAfterEditDragIfNeeded(rootEditableElement.get());
551     }
552
553     return true;
554 }
555
556 bool DragController::canProcessDrag(DragData& dragData)
557 {
558     if (!dragData.containsCompatibleContent())
559         return false;
560
561     IntPoint point = m_page.mainFrame().view()->windowToContents(dragData.clientPosition());
562     HitTestResult result = HitTestResult(point);
563     if (!m_page.mainFrame().contentRenderer())
564         return false;
565
566     result = m_page.mainFrame().eventHandler().hitTestResultAtPoint(point, HitTestRequest::ReadOnly | HitTestRequest::Active);
567
568     if (!result.innerNonSharedNode())
569         return false;
570
571     if (dragData.containsFiles() && asFileInput(*result.innerNonSharedNode()))
572         return true;
573
574     if (is<HTMLPlugInElement>(*result.innerNonSharedNode())) {
575         if (!downcast<HTMLPlugInElement>(result.innerNonSharedNode())->canProcessDrag() && !result.innerNonSharedNode()->hasEditableStyle())
576             return false;
577     } else if (!result.innerNonSharedNode()->hasEditableStyle())
578         return false;
579
580     if (m_didInitiateDrag && m_documentUnderMouse == m_dragInitiator && result.isSelected())
581         return false;
582
583     return true;
584 }
585
586 static DragOperation defaultOperationForDrag(DragOperation srcOpMask)
587 {
588     // This is designed to match IE's operation fallback for the case where
589     // the page calls preventDefault() in a drag event but doesn't set dropEffect.
590     if (srcOpMask == DragOperationEvery)
591         return DragOperationCopy;
592     if (srcOpMask == DragOperationNone)
593         return DragOperationNone;
594     if (srcOpMask & DragOperationMove || srcOpMask & DragOperationGeneric)
595         return DragOperationMove;
596     if (srcOpMask & DragOperationCopy)
597         return DragOperationCopy;
598     if (srcOpMask & DragOperationLink)
599         return DragOperationLink;
600     
601     // FIXME: Does IE really return "generic" even if no operations were allowed by the source?
602     return DragOperationGeneric;
603 }
604
605 bool DragController::tryDHTMLDrag(DragData& dragData, DragOperation& operation)
606 {
607     ASSERT(m_documentUnderMouse);
608     Ref<MainFrame> mainFrame(m_page.mainFrame());
609     RefPtr<FrameView> viewProtector = mainFrame->view();
610     if (!viewProtector)
611         return false;
612
613 #if ENABLE(DASHBOARD_SUPPORT)
614     DataTransferAccessPolicy policy = (mainFrame->settings().usesDashboardBackwardCompatibilityMode() && m_documentUnderMouse->securityOrigin()->isLocal()) ?
615         DataTransferAccessPolicy::Readable : DataTransferAccessPolicy::TypesReadable;
616 #else
617     DataTransferAccessPolicy policy = DataTransferAccessPolicy::TypesReadable;
618 #endif
619     RefPtr<DataTransfer> dataTransfer = DataTransfer::createForDragAndDrop(policy, dragData);
620     DragOperation srcOpMask = dragData.draggingSourceOperationMask();
621     dataTransfer->setSourceOperation(srcOpMask);
622
623     PlatformMouseEvent event = createMouseEvent(dragData);
624     if (!mainFrame->eventHandler().updateDragAndDrop(event, dataTransfer.get())) {
625         dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security.
626         return false;
627     }
628
629     operation = dataTransfer->destinationOperation();
630     if (dataTransfer->dropEffectIsUninitialized())
631         operation = defaultOperationForDrag(srcOpMask);
632     else if (!(srcOpMask & operation)) {
633         // The element picked an operation which is not supported by the source
634         operation = DragOperationNone;
635     }
636
637     dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security.
638     return true;
639 }
640
641 Element* DragController::draggableElement(const Frame* sourceFrame, Element* startElement, const IntPoint& dragOrigin, DragState& state) const
642 {
643     state.type = (sourceFrame->selection().contains(dragOrigin)) ? DragSourceActionSelection : DragSourceActionNone;
644     if (!startElement)
645         return nullptr;
646 #if ENABLE(ATTACHMENT_ELEMENT)
647     // Unlike image elements, attachment elements are immediately selected upon mouse down,
648     // but for those elements we still want to use the single element drag behavior as long as
649     // the element is the only content of the selection.
650     const VisibleSelection& selection = sourceFrame->selection().selection();
651     if (selection.isRange() && is<HTMLAttachmentElement>(selection.start().anchorNode()) && selection.start().anchorNode() == selection.end().anchorNode())
652         state.type = DragSourceActionNone;
653 #endif
654
655     for (auto* renderer = startElement->renderer(); renderer; renderer = renderer->parent()) {
656         Element* element = renderer->nonPseudoElement();
657         if (!element) {
658             // Anonymous render blocks don't correspond to actual DOM elements, so we skip over them
659             // for the purposes of finding a draggable element.
660             continue;
661         }
662         EUserDrag dragMode = renderer->style().userDrag();
663         if ((m_dragSourceAction & DragSourceActionDHTML) && dragMode == DRAG_ELEMENT) {
664             state.type = static_cast<DragSourceAction>(state.type | DragSourceActionDHTML);
665             return element;
666         }
667         if (dragMode == DRAG_AUTO) {
668             if ((m_dragSourceAction & DragSourceActionImage)
669                 && is<HTMLImageElement>(*element)
670                 && sourceFrame->settings().loadsImagesAutomatically()) {
671                 state.type = static_cast<DragSourceAction>(state.type | DragSourceActionImage);
672                 return element;
673             }
674             if ((m_dragSourceAction & DragSourceActionLink) && isDraggableLink(*element)) {
675                 state.type = static_cast<DragSourceAction>(state.type | DragSourceActionLink);
676                 return element;
677             }
678 #if ENABLE(ATTACHMENT_ELEMENT)
679             if ((m_dragSourceAction & DragSourceActionAttachment)
680                 && is<HTMLAttachmentElement>(*element)
681                 && downcast<HTMLAttachmentElement>(*element).file()) {
682                 state.type = static_cast<DragSourceAction>(state.type | DragSourceActionAttachment);
683                 return element;
684             }
685 #endif
686         }
687     }
688
689     // We either have nothing to drag or we have a selection and we're not over a draggable element.
690     return (state.type & DragSourceActionSelection) ? startElement : nullptr;
691 }
692
693 static CachedImage* getCachedImage(Element& element)
694 {
695     RenderObject* renderer = element.renderer();
696     if (!is<RenderImage>(renderer))
697         return nullptr;
698     auto& image = downcast<RenderImage>(*renderer);
699     return image.cachedImage();
700 }
701
702 static Image* getImage(Element& element)
703 {
704     CachedImage* cachedImage = getCachedImage(element);
705     // Don't use cachedImage->imageForRenderer() here as that may return BitmapImages for cached SVG Images.
706     // Users of getImage() want access to the SVGImage, in order to figure out the filename extensions,
707     // which would be empty when asking the cached BitmapImages.
708     return (cachedImage && !cachedImage->errorOccurred()) ?
709         cachedImage->image() : nullptr;
710 }
711
712 static void selectElement(Element& element)
713 {
714     RefPtr<Range> range = element.document().createRange();
715     range->selectNode(&element);
716     element.document().frame()->selection().setSelection(VisibleSelection(*range, DOWNSTREAM));
717 }
718
719 static IntPoint dragLocForDHTMLDrag(const IntPoint& mouseDraggedPoint, const IntPoint& dragOrigin, const IntPoint& dragImageOffset, bool isLinkImage)
720 {
721     // dragImageOffset is the cursor position relative to the lower-left corner of the image.
722 #if PLATFORM(COCOA)
723     // We add in the Y dimension because we are a flipped view, so adding moves the image down.
724     const int yOffset = dragImageOffset.y();
725 #else
726     const int yOffset = -dragImageOffset.y();
727 #endif
728
729     if (isLinkImage)
730         return IntPoint(mouseDraggedPoint.x() - dragImageOffset.x(), mouseDraggedPoint.y() + yOffset);
731
732     return IntPoint(dragOrigin.x() - dragImageOffset.x(), dragOrigin.y() + yOffset);
733 }
734
735 static IntPoint dragLocForSelectionDrag(Frame& src)
736 {
737     IntRect draggingRect = enclosingIntRect(src.selection().selectionBounds());
738     int xpos = draggingRect.maxX();
739     xpos = draggingRect.x() < xpos ? draggingRect.x() : xpos;
740     int ypos = draggingRect.maxY();
741 #if PLATFORM(COCOA)
742     // Deal with flipped coordinates on Mac
743     ypos = draggingRect.y() > ypos ? draggingRect.y() : ypos;
744 #else
745     ypos = draggingRect.y() < ypos ? draggingRect.y() : ypos;
746 #endif
747     return IntPoint(xpos, ypos);
748 }
749
750 bool DragController::startDrag(Frame& src, const DragState& state, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin)
751 {
752     if (!src.view() || !src.contentRenderer() || !state.source)
753         return false;
754
755     HitTestResult hitTestResult = src.eventHandler().hitTestResultAtPoint(dragOrigin, HitTestRequest::ReadOnly | HitTestRequest::Active);
756
757     // FIXME(136836): Investigate whether all elements should use the containsIncludingShadowDOM() path here.
758     bool includeShadowDOM = false;
759 #if ENABLE(VIDEO)
760     includeShadowDOM = state.source->isMediaElement();
761 #endif
762     bool sourceContainsHitNode;
763     if (!includeShadowDOM)
764         sourceContainsHitNode = state.source->contains(hitTestResult.innerNode());
765     else
766         sourceContainsHitNode = state.source->containsIncludingShadowDOM(hitTestResult.innerNode());
767
768     if (!sourceContainsHitNode)
769         // The original node being dragged isn't under the drag origin anymore... maybe it was
770         // hidden or moved out from under the cursor. Regardless, we don't want to start a drag on
771         // something that's not actually under the drag origin.
772         return false;
773     URL linkURL = hitTestResult.absoluteLinkURL();
774     URL imageURL = hitTestResult.absoluteImageURL();
775 #if ENABLE(ATTACHMENT_ELEMENT)
776     URL attachmentURL = hitTestResult.absoluteAttachmentURL();
777     m_draggingAttachmentURL = URL();
778 #endif
779
780     IntPoint mouseDraggedPoint = src.view()->windowToContents(dragEvent.position());
781
782     m_draggingImageURL = URL();
783     m_sourceDragOperation = srcOp;
784
785     DragImageRef dragImage = nullptr;
786     IntPoint dragLoc(0, 0);
787     IntPoint dragImageOffset(0, 0);
788
789     ASSERT(state.dataTransfer);
790
791     DataTransfer& dataTransfer = *state.dataTransfer;
792     if (state.type == DragSourceActionDHTML)
793         dragImage = dataTransfer.createDragImage(dragImageOffset);
794     if (state.type == DragSourceActionSelection || !imageURL.isEmpty() || !linkURL.isEmpty())
795         // Selection, image, and link drags receive a default set of allowed drag operations that
796         // follows from:
797         // http://trac.webkit.org/browser/trunk/WebKit/mac/WebView/WebHTMLView.mm?rev=48526#L3430
798         m_sourceDragOperation = static_cast<DragOperation>(m_sourceDragOperation | DragOperationGeneric | DragOperationCopy);
799
800     // We allow DHTML/JS to set the drag image, even if its a link, image or text we're dragging.
801     // This is in the spirit of the IE API, which allows overriding of pasteboard data and DragOp.
802     if (dragImage) {
803         dragLoc = dragLocForDHTMLDrag(mouseDraggedPoint, dragOrigin, dragImageOffset, !linkURL.isEmpty());
804         m_dragOffset = dragImageOffset;
805     }
806
807     bool startedDrag = true; // optimism - we almost always manage to start the drag
808
809     ASSERT(state.source);
810     Element& element = *state.source;
811
812     Image* image = getImage(element);
813     if (state.type == DragSourceActionSelection) {
814         if (!dataTransfer.pasteboard().hasData()) {
815             // FIXME: This entire block is almost identical to the code in Editor::copy, and the code should be shared.
816
817             RefPtr<Range> selectionRange = src.selection().toNormalizedRange();
818             ASSERT(selectionRange);
819
820             src.editor().willWriteSelectionToPasteboard(selectionRange.get());
821
822             if (enclosingTextFormControl(src.selection().selection().start()))
823                 dataTransfer.pasteboard().writePlainText(src.editor().selectedTextForDataTransfer(), Pasteboard::CannotSmartReplace);
824             else {
825 #if PLATFORM(COCOA) || PLATFORM(EFL) || PLATFORM(GTK)
826                 src.editor().writeSelectionToPasteboard(dataTransfer.pasteboard());
827 #else
828                 // FIXME: Convert all other platforms to match Mac and delete this.
829                 dataTransfer.pasteboard().writeSelection(*selectionRange, src.editor().canSmartCopyOrDelete(), src, IncludeImageAltTextForDataTransfer);
830 #endif
831             }
832
833             src.editor().didWriteSelectionToPasteboard();
834         }
835         m_client.willPerformDragSourceAction(DragSourceActionSelection, dragOrigin, dataTransfer);
836         if (!dragImage) {
837             dragImage = dissolveDragImageToFraction(createDragImageForSelection(src), DragImageAlpha);
838             dragLoc = dragLocForSelectionDrag(src);
839             m_dragOffset = IntPoint(dragOrigin.x() - dragLoc.x(), dragOrigin.y() - dragLoc.y());
840         }
841         doSystemDrag(dragImage, dragLoc, dragOrigin, dataTransfer, src, false);
842     } else if (!src.document()->securityOrigin()->canDisplay(linkURL)) {
843         src.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Not allowed to drag local resource: " + linkURL.stringCenterEllipsizedToLength());
844         startedDrag = false;
845     } else if (!imageURL.isEmpty() && image && !image->isNull() && (m_dragSourceAction & DragSourceActionImage)) {
846         // We shouldn't be starting a drag for an image that can't provide an extension.
847         // This is an early detection for problems encountered later upon drop.
848         ASSERT(!image->filenameExtension().isEmpty());
849         if (!dataTransfer.pasteboard().hasData()) {
850             m_draggingImageURL = imageURL;
851             if (element.isContentRichlyEditable())
852                 selectElement(element);
853             declareAndWriteDragImage(dataTransfer, element, !linkURL.isEmpty() ? linkURL : imageURL, hitTestResult.altDisplayString());
854         }
855
856         m_client.willPerformDragSourceAction(DragSourceActionImage, dragOrigin, dataTransfer);
857
858         if (!dragImage) {
859             IntRect imageRect = hitTestResult.imageRect();
860             imageRect.setLocation(m_page.mainFrame().view()->rootViewToContents(src.view()->contentsToRootView(imageRect.location())));
861             doImageDrag(element, dragOrigin, hitTestResult.imageRect(), dataTransfer, src, m_dragOffset);
862         } else {
863             // DHTML defined drag image
864             doSystemDrag(dragImage, dragLoc, dragOrigin, dataTransfer, src, false);
865         }
866     } else if (!linkURL.isEmpty() && (m_dragSourceAction & DragSourceActionLink)) {
867         if (!dataTransfer.pasteboard().hasData()) {
868             // Simplify whitespace so the title put on the dataTransfer resembles what the user sees
869             // on the web page. This includes replacing newlines with spaces.
870             src.editor().copyURL(linkURL, hitTestResult.textContent().simplifyWhiteSpace(), dataTransfer.pasteboard());
871         }
872
873         const VisibleSelection& sourceSelection = src.selection().selection();
874         if (sourceSelection.isCaret() && sourceSelection.isContentEditable()) {
875             // a user can initiate a drag on a link without having any text
876             // selected.  In this case, we should expand the selection to
877             // the enclosing anchor element
878             Position pos = sourceSelection.base();
879             Node* node = enclosingAnchorElement(pos);
880             if (node)
881                 src.selection().setSelection(VisibleSelection::selectionFromContentsOfNode(node));
882         }
883
884         m_client.willPerformDragSourceAction(DragSourceActionLink, dragOrigin, dataTransfer);
885         if (!dragImage) {
886             dragImage = createDragImageForLink(linkURL, hitTestResult.textContent(), src.settings().fontRenderingMode());
887             IntSize size = dragImageSize(dragImage);
888             m_dragOffset = IntPoint(-size.width() / 2, -LinkDragBorderInset);
889             dragLoc = IntPoint(mouseDraggedPoint.x() + m_dragOffset.x(), mouseDraggedPoint.y() + m_dragOffset.y());
890             // Later code expects the drag image to be scaled by device's scale factor.
891             dragImage = scaleDragImage(dragImage, FloatSize(m_page.deviceScaleFactor(), m_page.deviceScaleFactor()));
892         }
893         doSystemDrag(dragImage, dragLoc, mouseDraggedPoint, dataTransfer, src, true);
894 #if ENABLE(ATTACHMENT_ELEMENT)
895     } else if (!attachmentURL.isEmpty() && (m_dragSourceAction & DragSourceActionAttachment)) {
896         if (!dataTransfer.pasteboard().hasData()) {
897             m_draggingAttachmentURL = attachmentURL;
898             selectElement(element);
899             declareAndWriteAttachment(dataTransfer, element, attachmentURL);
900         }
901         
902         m_client.willPerformDragSourceAction(DragSourceActionAttachment, dragOrigin, dataTransfer);
903         
904         if (!dragImage) {
905             dragImage = dissolveDragImageToFraction(createDragImageForSelection(src), DragImageAlpha);
906             dragLoc = dragLocForSelectionDrag(src);
907             m_dragOffset = IntPoint(dragOrigin.x() - dragLoc.x(), dragOrigin.y() - dragLoc.y());
908         }
909         doSystemDrag(dragImage, dragLoc, dragOrigin, dataTransfer, src, false);
910 #endif
911     } else if (state.type == DragSourceActionDHTML) {
912         if (dragImage) {
913             ASSERT(m_dragSourceAction & DragSourceActionDHTML);
914             m_client.willPerformDragSourceAction(DragSourceActionDHTML, dragOrigin, dataTransfer);
915             doSystemDrag(dragImage, dragLoc, dragOrigin, dataTransfer, src, false);
916         } else
917             startedDrag = false;
918     } else {
919         // draggableElement() determined an image or link node was draggable, but it turns out the
920         // image or link had no URL, so there is nothing to drag.
921         startedDrag = false;
922     }
923
924     if (dragImage)
925         deleteDragImage(dragImage);
926     return startedDrag;
927 }
928
929 void DragController::doImageDrag(Element& element, const IntPoint& dragOrigin, const IntRect& layoutRect, DataTransfer& dataTransfer, Frame& frame, IntPoint& dragImageOffset)
930 {
931     IntPoint mouseDownPoint = dragOrigin;
932     DragImageRef dragImage = nullptr;
933     IntPoint scaledOrigin;
934
935     if (!element.renderer())
936         return;
937
938     ImageOrientationDescription orientationDescription(element.renderer()->shouldRespectImageOrientation());
939 #if ENABLE(CSS_IMAGE_ORIENTATION)
940     orientationDescription.setImageOrientationEnum(element.renderer()->style().imageOrientation());
941 #endif
942
943     Image* image = getImage(element);
944     if (image && image->size().height() * image->size().width() <= MaxOriginalImageArea
945         && (dragImage = createDragImageFromImage(image, element.renderer() ? orientationDescription : ImageOrientationDescription()))) {
946
947         dragImage = fitDragImageToMaxSize(dragImage, layoutRect.size(), maxDragImageSize());
948         IntSize fittedSize = dragImageSize(dragImage);
949
950         dragImage = scaleDragImage(dragImage, FloatSize(m_page.deviceScaleFactor(), m_page.deviceScaleFactor()));
951         dragImage = dissolveDragImageToFraction(dragImage, DragImageAlpha);
952
953         // Properly orient the drag image and orient it differently if it's smaller than the original.
954         float scale = fittedSize.width() / (float)layoutRect.width();
955         float dx = scale * (layoutRect.x() - mouseDownPoint.x());
956         float originY = layoutRect.y();
957 #if PLATFORM(COCOA)
958         // Compensate for accursed flipped coordinates in Cocoa.
959         originY += layoutRect.height();
960 #endif
961         float dy = scale * (originY - mouseDownPoint.y());
962         scaledOrigin = IntPoint((int)(dx + 0.5), (int)(dy + 0.5));
963     } else {
964         if (CachedImage* cachedImage = getCachedImage(element)) {
965             dragImage = createDragImageIconForCachedImageFilename(cachedImage->response().suggestedFilename());
966             if (dragImage)
967                 scaledOrigin = IntPoint(DragIconRightInset - dragImageSize(dragImage).width(), DragIconBottomInset);
968         }
969     }
970
971     dragImageOffset = mouseDownPoint + scaledOrigin;
972     doSystemDrag(dragImage, dragImageOffset, dragOrigin, dataTransfer, frame, false);
973
974     deleteDragImage(dragImage);
975 }
976
977 void DragController::doSystemDrag(DragImageRef image, const IntPoint& dragLoc, const IntPoint& eventPos, DataTransfer& dataTransfer, Frame& frame, bool forLink)
978 {
979     m_didInitiateDrag = true;
980     m_dragInitiator = frame.document();
981     // Protect this frame and view, as a load may occur mid drag and attempt to unload this frame
982     Ref<MainFrame> frameProtector(m_page.mainFrame());
983     RefPtr<FrameView> viewProtector = frameProtector->view();
984     m_client.startDrag(image, viewProtector->rootViewToContents(frame.view()->contentsToRootView(dragLoc)),
985         viewProtector->rootViewToContents(frame.view()->contentsToRootView(eventPos)), dataTransfer, frameProtector.get(), forLink);
986     // DragClient::startDrag can cause our Page to dispear, deallocating |this|.
987     if (!frameProtector->page())
988         return;
989
990     cleanupAfterSystemDrag();
991 }
992
993 // Manual drag caret manipulation
994 void DragController::placeDragCaret(const IntPoint& windowPoint)
995 {
996     mouseMovedIntoDocument(m_page.mainFrame().documentAtPoint(windowPoint));
997     if (!m_documentUnderMouse)
998         return;
999     Frame* frame = m_documentUnderMouse->frame();
1000     FrameView* frameView = frame->view();
1001     if (!frameView)
1002         return;
1003     IntPoint framePoint = frameView->windowToContents(windowPoint);
1004
1005     m_page.dragCaretController().setCaretPosition(frame->visiblePositionForPoint(framePoint));
1006 }
1007
1008 #endif // ENABLE(DRAG_SUPPORT)
1009
1010 } // namespace WebCore