diff options
author | Konstantin Tokarev <annulen@yandex.ru> | 2016-08-25 19:20:41 +0300 |
---|---|---|
committer | Konstantin Tokarev <annulen@yandex.ru> | 2017-02-02 12:30:55 +0000 |
commit | 6882a04fb36642862b11efe514251d32070c3d65 (patch) | |
tree | b7959826000b061fd5ccc7512035c7478742f7b0 /Source/WebCore/page/DragController.cpp | |
parent | ab6df191029eeeb0b0f16f127d553265659f739e (diff) | |
download | qtwebkit-6882a04fb36642862b11efe514251d32070c3d65.tar.gz |
Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443)
Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f
Reviewed-by: Konstantin Tokarev <annulen@yandex.ru>
Diffstat (limited to 'Source/WebCore/page/DragController.cpp')
-rw-r--r-- | Source/WebCore/page/DragController.cpp | 746 |
1 files changed, 404 insertions, 342 deletions
diff --git a/Source/WebCore/page/DragController.cpp b/Source/WebCore/page/DragController.cpp index 2537040d8..c4cbb67ef 100644 --- a/Source/WebCore/page/DragController.cpp +++ b/Source/WebCore/page/DragController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009, 2010, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2009, 2010, 2013, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,31 +26,30 @@ #include "config.h" #include "DragController.h" -#if ENABLE(DRAG_SUPPORT) +#include "HTMLAnchorElement.h" +#include "SVGAElement.h" +#if ENABLE(DRAG_SUPPORT) #include "CachedImage.h" -#include "Clipboard.h" -#include "ClipboardAccessPolicy.h" #include "CachedResourceLoader.h" +#include "DataTransfer.h" #include "Document.h" #include "DocumentFragment.h" #include "DragActions.h" #include "DragClient.h" #include "DragData.h" -#include "DragSession.h" +#include "DragImage.h" #include "DragState.h" #include "Editor.h" #include "EditorClient.h" -#include "Element.h" #include "EventHandler.h" #include "ExceptionCodePlaceholder.h" #include "FloatRect.h" -#include "Frame.h" #include "FrameLoadRequest.h" #include "FrameLoader.h" #include "FrameSelection.h" #include "FrameView.h" -#include "HTMLAnchorElement.h" +#include "HTMLAttachmentElement.h" #include "HTMLImageElement.h" #include "HTMLInputElement.h" #include "HTMLNames.h" @@ -59,8 +58,10 @@ #include "HitTestResult.h" #include "Image.h" #include "ImageOrientation.h" +#include "MainFrame.h" #include "MoveSelectionCommand.h" #include "Page.h" +#include "Pasteboard.h" #include "PlatformKeyboardEvent.h" #include "PluginDocument.h" #include "PluginViewBase.h" @@ -72,178 +73,183 @@ #include "SecurityOrigin.h" #include "Settings.h" #include "ShadowRoot.h" -#include "StylePropertySet.h" +#include "StyleProperties.h" #include "Text.h" #include "TextEvent.h" #include "htmlediting.h" #include "markup.h" #include <wtf/CurrentTime.h> #include <wtf/RefPtr.h> +#endif namespace WebCore { -static PlatformMouseEvent createMouseEvent(DragData* dragData) +bool isDraggableLink(const Element& element) { - bool shiftKey, ctrlKey, altKey, metaKey; - shiftKey = ctrlKey = altKey = metaKey = false; - int keyState = dragData->modifierKeyState(); - shiftKey = static_cast<bool>(keyState & PlatformEvent::ShiftKey); - ctrlKey = static_cast<bool>(keyState & PlatformEvent::CtrlKey); - altKey = static_cast<bool>(keyState & PlatformEvent::AltKey); - metaKey = static_cast<bool>(keyState & PlatformEvent::MetaKey); + if (is<HTMLAnchorElement>(element)) + return downcast<HTMLAnchorElement>(element).isLiveLink(); + if (is<SVGAElement>(element)) + return element.isLink(); + return false; +} - return PlatformMouseEvent(dragData->clientPosition(), dragData->globalPosition(), +#if ENABLE(DRAG_SUPPORT) + +static PlatformMouseEvent createMouseEvent(DragData& dragData) +{ + int keyState = dragData.modifierKeyState(); + bool shiftKey = static_cast<bool>(keyState & PlatformEvent::ShiftKey); + bool ctrlKey = static_cast<bool>(keyState & PlatformEvent::CtrlKey); + bool altKey = static_cast<bool>(keyState & PlatformEvent::AltKey); + bool metaKey = static_cast<bool>(keyState & PlatformEvent::MetaKey); + + return PlatformMouseEvent(dragData.clientPosition(), dragData.globalPosition(), LeftButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, - metaKey, currentTime()); + metaKey, currentTime(), ForceAtClick); } -DragController::DragController(Page* page, DragClient* client) +DragController::DragController(Page& page, DragClient& client) : m_page(page) , m_client(client) - , m_documentUnderMouse(0) - , m_dragInitiator(0) - , m_fileInputElementUnderMouse(0) + , m_numberOfItemsToBeAccepted(0) , m_documentIsHandlingDrag(false) , m_dragDestinationAction(DragDestinationActionNone) , m_dragSourceAction(DragSourceActionNone) , m_didInitiateDrag(false) , m_sourceDragOperation(DragOperationNone) { - ASSERT(m_client); } DragController::~DragController() { - m_client->dragControllerDestroyed(); + m_client.dragControllerDestroyed(); } -PassOwnPtr<DragController> DragController::create(Page* page, DragClient* client) +static PassRefPtr<DocumentFragment> documentFragmentFromDragData(DragData& dragData, Frame& frame, Range& context, bool allowPlainText, bool& chosePlainText) { - return adoptPtr(new DragController(page, client)); -} - -static PassRefPtr<DocumentFragment> documentFragmentFromDragData(DragData* dragData, Frame* frame, RefPtr<Range> context, - bool allowPlainText, bool& chosePlainText) -{ - ASSERT(dragData); chosePlainText = false; - Document* document = context->ownerDocument(); - ASSERT(document); - if (document && dragData->containsCompatibleContent()) { - if (PassRefPtr<DocumentFragment> fragment = dragData->asFragment(frame, context, allowPlainText, chosePlainText)) + Document& document = context.ownerDocument(); + if (dragData.containsCompatibleContent()) { + if (PassRefPtr<DocumentFragment> fragment = frame.editor().webContentFromPasteboard(*Pasteboard::createForDragAndDrop(dragData), context, allowPlainText, chosePlainText)) return fragment; - if (dragData->containsURL(frame, DragData::DoNotConvertFilenames)) { + if (dragData.containsURL(DragData::DoNotConvertFilenames)) { String title; - String url = dragData->asURL(frame, DragData::DoNotConvertFilenames, &title); + String url = dragData.asURL(DragData::DoNotConvertFilenames, &title); if (!url.isEmpty()) { - RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::create(document); + Ref<HTMLAnchorElement> anchor = HTMLAnchorElement::create(document); anchor->setHref(url); if (title.isEmpty()) { // Try the plain text first because the url might be normalized or escaped. - if (dragData->containsPlainText()) - title = dragData->asPlainText(frame); + if (dragData.containsPlainText()) + title = dragData.asPlainText(); if (title.isEmpty()) title = url; } - RefPtr<Node> anchorText = document->createTextNode(title); - anchor->appendChild(anchorText, IGNORE_EXCEPTION); - RefPtr<DocumentFragment> fragment = document->createDocumentFragment(); - fragment->appendChild(anchor, IGNORE_EXCEPTION); - return fragment.get(); + anchor->appendChild(document.createTextNode(title), IGNORE_EXCEPTION); + Ref<DocumentFragment> fragment = document.createDocumentFragment(); + fragment->appendChild(WTFMove(anchor), IGNORE_EXCEPTION); + return fragment.ptr(); } } } - if (allowPlainText && dragData->containsPlainText()) { + if (allowPlainText && dragData.containsPlainText()) { chosePlainText = true; - return createFragmentFromText(context.get(), dragData->asPlainText(frame)).get(); + return createFragmentFromText(context, dragData.asPlainText()).ptr(); } - return 0; + return nullptr; } -bool DragController::dragIsMove(FrameSelection* selection, DragData* dragData) +bool DragController::dragIsMove(FrameSelection& selection, DragData& dragData) { - return m_documentUnderMouse == m_dragInitiator && selection->isContentEditable() && selection->isRange() && !isCopyKeyDown(dragData); + const VisibleSelection& visibleSelection = selection.selection(); + return m_documentUnderMouse == m_dragInitiator && visibleSelection.isContentEditable() && visibleSelection.isRange() && !isCopyKeyDown(dragData); } -// FIXME: This method is poorly named. We're just clearing the selection from the document this drag is exiting. -void DragController::cancelDrag() +void DragController::clearDragCaret() { - m_page->dragCaretController()->clear(); + m_page.dragCaretController().clear(); } void DragController::dragEnded() { - m_dragInitiator = 0; + m_dragInitiator = nullptr; m_didInitiateDrag = false; - m_page->dragCaretController()->clear(); + clearDragCaret(); - m_client->dragEnded(); + m_client.dragEnded(); } -DragSession DragController::dragEntered(DragData* dragData) +DragOperation DragController::dragEntered(DragData& dragData) { return dragEnteredOrUpdated(dragData); } -void DragController::dragExited(DragData* dragData) +void DragController::dragExited(DragData& dragData) { - ASSERT(dragData); - Frame* mainFrame = m_page->mainFrame(); - - if (RefPtr<FrameView> v = mainFrame->view()) { - ClipboardAccessPolicy policy = (!m_documentUnderMouse || m_documentUnderMouse->securityOrigin()->isLocal()) ? ClipboardReadable : ClipboardTypesReadable; - RefPtr<Clipboard> clipboard = Clipboard::create(policy, dragData, mainFrame); - clipboard->setSourceOperation(dragData->draggingSourceOperationMask()); - mainFrame->eventHandler()->cancelDragAndDrop(createMouseEvent(dragData), clipboard.get()); - clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security + if (RefPtr<FrameView> v = m_page.mainFrame().view()) { +#if ENABLE(DASHBOARD_SUPPORT) + DataTransferAccessPolicy policy = (m_page.mainFrame().settings().usesDashboardBackwardCompatibilityMode() && (!m_documentUnderMouse || m_documentUnderMouse->securityOrigin()->isLocal())) + ? DataTransferAccessPolicy::Readable : DataTransferAccessPolicy::TypesReadable; +#else + DataTransferAccessPolicy policy = DataTransferAccessPolicy::TypesReadable; +#endif + RefPtr<DataTransfer> dataTransfer = DataTransfer::createForDragAndDrop(policy, dragData); + dataTransfer->setSourceOperation(dragData.draggingSourceOperationMask()); + m_page.mainFrame().eventHandler().cancelDragAndDrop(createMouseEvent(dragData), dataTransfer.get()); + dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security. } - mouseMovedIntoDocument(0); + mouseMovedIntoDocument(nullptr); if (m_fileInputElementUnderMouse) m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false); - m_fileInputElementUnderMouse = 0; + m_fileInputElementUnderMouse = nullptr; } -DragSession DragController::dragUpdated(DragData* dragData) +DragOperation DragController::dragUpdated(DragData& dragData) { return dragEnteredOrUpdated(dragData); } -bool DragController::performDrag(DragData* dragData) +bool DragController::performDragOperation(DragData& dragData) { - ASSERT(dragData); - m_documentUnderMouse = m_page->mainFrame()->documentAtPoint(dragData->clientPosition()); + m_documentUnderMouse = m_page.mainFrame().documentAtPoint(dragData.clientPosition()); + + ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy = ShouldOpenExternalURLsPolicy::ShouldNotAllow; + if (m_documentUnderMouse) + shouldOpenExternalURLsPolicy = m_documentUnderMouse->shouldOpenExternalURLsPolicyToPropagate(); + if ((m_dragDestinationAction & DragDestinationActionDHTML) && m_documentIsHandlingDrag) { - m_client->willPerformDragDestinationAction(DragDestinationActionDHTML, dragData); - RefPtr<Frame> mainFrame = m_page->mainFrame(); + m_client.willPerformDragDestinationAction(DragDestinationActionDHTML, dragData); + Ref<MainFrame> mainFrame(m_page.mainFrame()); bool preventedDefault = false; if (mainFrame->view()) { // Sending an event can result in the destruction of the view and part. - RefPtr<Clipboard> clipboard = Clipboard::create(ClipboardReadable, dragData, mainFrame.get()); - clipboard->setSourceOperation(dragData->draggingSourceOperationMask()); - preventedDefault = mainFrame->eventHandler()->performDragAndDrop(createMouseEvent(dragData), clipboard.get()); - clipboard->setAccessPolicy(ClipboardNumb); // Invalidate clipboard here for security + RefPtr<DataTransfer> dataTransfer = DataTransfer::createForDragAndDrop(DataTransferAccessPolicy::Readable, dragData); + dataTransfer->setSourceOperation(dragData.draggingSourceOperationMask()); + preventedDefault = mainFrame->eventHandler().performDragAndDrop(createMouseEvent(dragData), dataTransfer.get()); + dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security. } if (preventedDefault) { - m_documentUnderMouse = 0; + clearDragCaret(); + m_documentUnderMouse = nullptr; return true; } } if ((m_dragDestinationAction & DragDestinationActionEdit) && concludeEditDrag(dragData)) { - m_documentUnderMouse = 0; + m_documentUnderMouse = nullptr; return true; } - m_documentUnderMouse = 0; + m_documentUnderMouse = nullptr; if (operationForLoad(dragData) == DragOperationNone) return false; - m_client->willPerformDragDestinationAction(DragDestinationActionLoad, dragData); - m_page->mainFrame()->loader()->load(FrameLoadRequest(m_page->mainFrame(), ResourceRequest(dragData->asURL(m_page->mainFrame())))); + m_client.willPerformDragDestinationAction(DragDestinationActionLoad, dragData); + m_page.mainFrame().loader().load(FrameLoadRequest(&m_page.mainFrame(), ResourceRequest(dragData.asURL()), shouldOpenExternalURLsPolicy)); return true; } @@ -254,40 +260,41 @@ void DragController::mouseMovedIntoDocument(Document* newDocument) // If we were over another document clear the selection if (m_documentUnderMouse) - cancelDrag(); + clearDragCaret(); m_documentUnderMouse = newDocument; } -DragSession DragController::dragEnteredOrUpdated(DragData* dragData) +DragOperation DragController::dragEnteredOrUpdated(DragData& dragData) { - ASSERT(dragData); - ASSERT(m_page->mainFrame()); - mouseMovedIntoDocument(m_page->mainFrame()->documentAtPoint(dragData->clientPosition())); + mouseMovedIntoDocument(m_page.mainFrame().documentAtPoint(dragData.clientPosition())); - m_dragDestinationAction = m_client->actionMaskForDrag(dragData); + m_dragDestinationAction = m_client.actionMaskForDrag(dragData); if (m_dragDestinationAction == DragDestinationActionNone) { - cancelDrag(); // FIXME: Why not call mouseMovedIntoDocument(0)? - return DragSession(); + clearDragCaret(); // FIXME: Why not call mouseMovedIntoDocument(nullptr)? + return DragOperationNone; } - DragSession dragSession; - m_documentIsHandlingDrag = tryDocumentDrag(dragData, m_dragDestinationAction, dragSession); + DragOperation dragOperation = DragOperationNone; + m_documentIsHandlingDrag = tryDocumentDrag(dragData, m_dragDestinationAction, dragOperation); if (!m_documentIsHandlingDrag && (m_dragDestinationAction & DragDestinationActionLoad)) - dragSession.operation = operationForLoad(dragData); - return dragSession; + dragOperation = operationForLoad(dragData); + return dragOperation; } -static HTMLInputElement* asFileInput(Node* node) +static HTMLInputElement* asFileInput(Node& node) { - ASSERT(node); + if (!is<HTMLInputElement>(node)) + return nullptr; - HTMLInputElement* inputElement = node->toInputElement(); + auto* inputElement = &downcast<HTMLInputElement>(node); // If this is a button inside of the a file input, move up to the file input. - if (inputElement && inputElement->isTextButton() && inputElement->treeScope()->rootNode()->isShadowRoot()) - inputElement = toShadowRoot(inputElement->treeScope()->rootNode())->host()->toInputElement(); + if (inputElement->isTextButton() && is<ShadowRoot>(inputElement->treeScope().rootNode())) { + auto& host = *downcast<ShadowRoot>(inputElement->treeScope().rootNode()).host(); + inputElement = is<HTMLInputElement>(host) ? &downcast<HTMLInputElement>(host) : nullptr; + } - return inputElement && inputElement->isFileUpload() ? inputElement : 0; + return inputElement && inputElement->isFileUpload() ? inputElement : nullptr; } // This can return null if an empty document is loaded. @@ -295,25 +302,22 @@ static Element* elementUnderMouse(Document* documentUnderMouse, const IntPoint& { Frame* frame = documentUnderMouse->frame(); float zoomFactor = frame ? frame->pageZoomFactor() : 1; - LayoutPoint point = roundedLayoutPoint(FloatPoint(p.x() * zoomFactor, p.y() * zoomFactor)); + LayoutPoint point(p.x() * zoomFactor, p.y() * zoomFactor); - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); HitTestResult result(point); - documentUnderMouse->renderView()->hitTest(request, result); + documentUnderMouse->renderView()->hitTest(HitTestRequest(), result); - Node* n = result.innerNode(); - while (n && !n->isElementNode()) - n = n->parentNode(); - if (n) - n = n->deprecatedShadowAncestorNode(); + Node* node = result.innerNode(); + while (node && !is<Element>(*node)) + node = node->parentNode(); + if (node) + node = node->deprecatedShadowAncestorNode(); - return toElement(n); + return downcast<Element>(node); } -bool DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction actionMask, DragSession& dragSession) +bool DragController::tryDocumentDrag(DragData& dragData, DragDestinationAction actionMask, DragOperation& dragOperation) { - ASSERT(dragData); - if (!m_documentUnderMouse) return false; @@ -322,7 +326,7 @@ bool DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction a bool isHandlingDrag = false; if (actionMask & DragDestinationActionDHTML) { - isHandlingDrag = tryDHTMLDrag(dragData, dragSession.operation); + isHandlingDrag = tryDHTMLDrag(dragData, dragOperation); // Do not continue if m_documentUnderMouse has been reset by tryDHTMLDrag. // tryDHTMLDrag fires dragenter event. The event listener that listens // to this event may create a nested message loop (open a modal dialog), @@ -339,22 +343,22 @@ bool DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction a return false; if (isHandlingDrag) { - m_page->dragCaretController()->clear(); + clearDragCaret(); return true; } if ((actionMask & DragDestinationActionEdit) && canProcessDrag(dragData)) { - if (dragData->containsColor()) { - dragSession.operation = DragOperationGeneric; + if (dragData.containsColor()) { + dragOperation = DragOperationGeneric; return true; } - IntPoint point = frameView->windowToContents(dragData->clientPosition()); + IntPoint point = frameView->windowToContents(dragData.clientPosition()); Element* element = elementUnderMouse(m_documentUnderMouse.get(), point); if (!element) return false; - HTMLInputElement* elementAsFileInput = asFileInput(element); + HTMLInputElement* elementAsFileInput = asFileInput(*element); if (m_fileInputElementUnderMouse != elementAsFileInput) { if (m_fileInputElementUnderMouse) m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false); @@ -362,127 +366,125 @@ bool DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction a } if (!m_fileInputElementUnderMouse) - m_page->dragCaretController()->setCaretPosition(m_documentUnderMouse->frame()->visiblePositionForPoint(point)); + m_page.dragCaretController().setCaretPosition(m_documentUnderMouse->frame()->visiblePositionForPoint(point)); + else + clearDragCaret(); - Frame* innerFrame = element->document()->frame(); - dragSession.operation = dragIsMove(innerFrame->selection(), dragData) ? DragOperationMove : DragOperationCopy; - dragSession.mouseIsOverFileInput = m_fileInputElementUnderMouse; - dragSession.numberOfItemsToBeAccepted = 0; + Frame* innerFrame = element->document().frame(); + dragOperation = dragIsMove(innerFrame->selection(), dragData) ? DragOperationMove : DragOperationCopy; + m_numberOfItemsToBeAccepted = 0; - unsigned numberOfFiles = dragData->numberOfFiles(); + unsigned numberOfFiles = dragData.numberOfFiles(); if (m_fileInputElementUnderMouse) { if (m_fileInputElementUnderMouse->isDisabledFormControl()) - dragSession.numberOfItemsToBeAccepted = 0; + m_numberOfItemsToBeAccepted = 0; else if (m_fileInputElementUnderMouse->multiple()) - dragSession.numberOfItemsToBeAccepted = numberOfFiles; + m_numberOfItemsToBeAccepted = numberOfFiles; else if (numberOfFiles > 1) - dragSession.numberOfItemsToBeAccepted = 0; + m_numberOfItemsToBeAccepted = 0; else - dragSession.numberOfItemsToBeAccepted = 1; + m_numberOfItemsToBeAccepted = 1; - if (!dragSession.numberOfItemsToBeAccepted) - dragSession.operation = DragOperationNone; - m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(dragSession.numberOfItemsToBeAccepted); + if (!m_numberOfItemsToBeAccepted) + dragOperation = DragOperationNone; + m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(m_numberOfItemsToBeAccepted); } else { // We are not over a file input element. The dragged item(s) will only // be loaded into the view the number of dragged items is 1. - dragSession.numberOfItemsToBeAccepted = numberOfFiles != 1 ? 0 : 1; + m_numberOfItemsToBeAccepted = numberOfFiles != 1 ? 0 : 1; } return true; } // We are not over an editable region. Make sure we're clearing any prior drag cursor. - m_page->dragCaretController()->clear(); + clearDragCaret(); if (m_fileInputElementUnderMouse) m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false); - m_fileInputElementUnderMouse = 0; + m_fileInputElementUnderMouse = nullptr; return false; } DragSourceAction DragController::delegateDragSourceAction(const IntPoint& rootViewPoint) { - m_dragSourceAction = m_client->dragSourceActionMaskForPoint(rootViewPoint); + m_dragSourceAction = m_client.dragSourceActionMaskForPoint(rootViewPoint); return m_dragSourceAction; } -DragOperation DragController::operationForLoad(DragData* dragData) +DragOperation DragController::operationForLoad(DragData& dragData) { - ASSERT(dragData); - Document* doc = m_page->mainFrame()->documentAtPoint(dragData->clientPosition()); + Document* document = m_page.mainFrame().documentAtPoint(dragData.clientPosition()); bool pluginDocumentAcceptsDrags = false; - if (doc && doc->isPluginDocument()) { - const Widget* widget = toPluginDocument(doc)->pluginWidget(); - const PluginViewBase* pluginView = (widget && widget->isPluginViewBase()) ? static_cast<const PluginViewBase*>(widget) : 0; + if (is<PluginDocument>(document)) { + const Widget* widget = downcast<PluginDocument>(*document).pluginWidget(); + const PluginViewBase* pluginView = is<PluginViewBase>(widget) ? downcast<PluginViewBase>(widget) : nullptr; if (pluginView) pluginDocumentAcceptsDrags = pluginView->shouldAllowNavigationFromDrags(); } - if (doc && (m_didInitiateDrag || (doc->isPluginDocument() && !pluginDocumentAcceptsDrags) || doc->rendererIsEditable())) + if (document && (m_didInitiateDrag || (is<PluginDocument>(*document) && !pluginDocumentAcceptsDrags) || document->hasEditableStyle())) return DragOperationNone; return dragOperation(dragData); } static bool setSelectionToDragCaret(Frame* frame, VisibleSelection& dragCaret, RefPtr<Range>& range, const IntPoint& point) { - frame->selection()->setSelection(dragCaret); - if (frame->selection()->isNone()) { + frame->selection().setSelection(dragCaret); + if (frame->selection().selection().isNone()) { dragCaret = frame->visiblePositionForPoint(point); - frame->selection()->setSelection(dragCaret); + frame->selection().setSelection(dragCaret); range = dragCaret.toNormalizedRange(); } - return !frame->selection()->isNone() && frame->selection()->isContentEditable(); + return !frame->selection().isNone() && frame->selection().selection().isContentEditable(); } -bool DragController::dispatchTextInputEventFor(Frame* innerFrame, DragData* dragData) +bool DragController::dispatchTextInputEventFor(Frame* innerFrame, DragData& dragData) { - ASSERT(m_page->dragCaretController()->hasCaret()); - String text = m_page->dragCaretController()->isContentRichlyEditable() ? "" : dragData->asPlainText(innerFrame); - Node* target = innerFrame->editor().findEventTargetFrom(m_page->dragCaretController()->caretPosition()); - return target->dispatchEvent(TextEvent::createForDrop(innerFrame->document()->domWindow(), text), IGNORE_EXCEPTION); + ASSERT(m_page.dragCaretController().hasCaret()); + String text = m_page.dragCaretController().isContentRichlyEditable() ? emptyString() : dragData.asPlainText(); + Node* target = innerFrame->editor().findEventTargetFrom(m_page.dragCaretController().caretPosition()); + return target->dispatchEvent(TextEvent::createForDrop(innerFrame->document()->domWindow(), text)); } -bool DragController::concludeEditDrag(DragData* dragData) +bool DragController::concludeEditDrag(DragData& dragData) { - ASSERT(dragData); - RefPtr<HTMLInputElement> fileInput = m_fileInputElementUnderMouse; if (m_fileInputElementUnderMouse) { m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false); - m_fileInputElementUnderMouse = 0; + m_fileInputElementUnderMouse = nullptr; } if (!m_documentUnderMouse) return false; - IntPoint point = m_documentUnderMouse->view()->windowToContents(dragData->clientPosition()); + IntPoint point = m_documentUnderMouse->view()->windowToContents(dragData.clientPosition()); Element* element = elementUnderMouse(m_documentUnderMouse.get(), point); if (!element) return false; - RefPtr<Frame> innerFrame = element->ownerDocument()->frame(); + RefPtr<Frame> innerFrame = element->document().frame(); ASSERT(innerFrame); - if (m_page->dragCaretController()->hasCaret() && !dispatchTextInputEventFor(innerFrame.get(), dragData)) + if (m_page.dragCaretController().hasCaret() && !dispatchTextInputEventFor(innerFrame.get(), dragData)) return true; - if (dragData->containsColor()) { - Color color = dragData->asColor(); + if (dragData.containsColor()) { + Color color = dragData.asColor(); if (!color.isValid()) return false; - RefPtr<Range> innerRange = innerFrame->selection()->toNormalizedRange(); - RefPtr<MutableStylePropertySet> style = MutableStylePropertySet::create(); + RefPtr<Range> innerRange = innerFrame->selection().toNormalizedRange(); + RefPtr<MutableStyleProperties> style = MutableStyleProperties::create(); style->setProperty(CSSPropertyColor, color.serialized(), false); if (!innerFrame->editor().shouldApplyStyle(style.get(), innerRange.get())) return false; - m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData); + m_client.willPerformDragDestinationAction(DragDestinationActionEdit, dragData); innerFrame->editor().applyStyle(style.get(), EditActionSetColor); return true; } - if (dragData->containsFiles() && fileInput) { + if (dragData.containsFiles() && fileInput) { // fileInput should be the element we hit tested for, unless it was made // display:none in a drop event handler. ASSERT(fileInput == element || !fileInput->renderer()); @@ -492,90 +494,87 @@ bool DragController::concludeEditDrag(DragData* dragData) return fileInput->receiveDroppedFiles(dragData); } - if (!m_page->dragController()->canProcessDrag(dragData)) { - m_page->dragCaretController()->clear(); + if (!m_page.dragController().canProcessDrag(dragData)) { + clearDragCaret(); return false; } - VisibleSelection dragCaret = m_page->dragCaretController()->caretPosition(); - m_page->dragCaretController()->clear(); + VisibleSelection dragCaret = m_page.dragCaretController().caretPosition(); + clearDragCaret(); RefPtr<Range> range = dragCaret.toNormalizedRange(); - RefPtr<Element> rootEditableElement = innerFrame->selection()->rootEditableElement(); + RefPtr<Element> rootEditableElement = innerFrame->selection().selection().rootEditableElement(); // For range to be null a WebKit client must have done something bad while // manually controlling drag behaviour if (!range) return false; - CachedResourceLoader* cachedResourceLoader = range->ownerDocument()->cachedResourceLoader(); - ResourceCacheValidationSuppressor validationSuppressor(cachedResourceLoader); + + ResourceCacheValidationSuppressor validationSuppressor(range->ownerDocument().cachedResourceLoader()); if (dragIsMove(innerFrame->selection(), dragData) || dragCaret.isContentRichlyEditable()) { bool chosePlainText = false; - RefPtr<DocumentFragment> fragment = documentFragmentFromDragData(dragData, innerFrame.get(), range, true, chosePlainText); + RefPtr<DocumentFragment> fragment = documentFragmentFromDragData(dragData, *innerFrame, *range, true, chosePlainText); if (!fragment || !innerFrame->editor().shouldInsertFragment(fragment, range, EditorInsertActionDropped)) { return false; } - m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData); + m_client.willPerformDragDestinationAction(DragDestinationActionEdit, dragData); if (dragIsMove(innerFrame->selection(), dragData)) { // NSTextView behavior is to always smart delete on moving a selection, // but only to smart insert if the selection granularity is word granularity. bool smartDelete = innerFrame->editor().smartInsertDeleteEnabled(); - bool smartInsert = smartDelete && innerFrame->selection()->granularity() == WordGranularity && dragData->canSmartReplace(); + bool smartInsert = smartDelete && innerFrame->selection().granularity() == WordGranularity && dragData.canSmartReplace(); applyCommand(MoveSelectionCommand::create(fragment, dragCaret.base(), smartInsert, smartDelete)); } else { if (setSelectionToDragCaret(innerFrame.get(), dragCaret, range, point)) { ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::PreventNesting; - if (dragData->canSmartReplace()) + if (dragData.canSmartReplace()) options |= ReplaceSelectionCommand::SmartReplace; if (chosePlainText) options |= ReplaceSelectionCommand::MatchStyle; - applyCommand(ReplaceSelectionCommand::create(m_documentUnderMouse.get(), fragment, options)); + applyCommand(ReplaceSelectionCommand::create(*m_documentUnderMouse, WTFMove(fragment), options)); } } } else { - String text = dragData->asPlainText(innerFrame.get()); + String text = dragData.asPlainText(); if (text.isEmpty() || !innerFrame->editor().shouldInsertText(text, range.get(), EditorInsertActionDropped)) { return false; } - m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData); + m_client.willPerformDragDestinationAction(DragDestinationActionEdit, dragData); if (setSelectionToDragCaret(innerFrame.get(), dragCaret, range, point)) - applyCommand(ReplaceSelectionCommand::create(m_documentUnderMouse.get(), createFragmentFromText(range.get(), text), ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting)); + applyCommand(ReplaceSelectionCommand::create(*m_documentUnderMouse, createFragmentFromText(*range, text), ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting)); } if (rootEditableElement) { - if (Frame* frame = rootEditableElement->document()->frame()) - frame->eventHandler()->updateDragStateAfterEditDragIfNeeded(rootEditableElement.get()); + if (Frame* frame = rootEditableElement->document().frame()) + frame->eventHandler().updateDragStateAfterEditDragIfNeeded(rootEditableElement.get()); } return true; } -bool DragController::canProcessDrag(DragData* dragData) +bool DragController::canProcessDrag(DragData& dragData) { - ASSERT(dragData); - - if (!dragData->containsCompatibleContent()) + if (!dragData.containsCompatibleContent()) return false; - IntPoint point = m_page->mainFrame()->view()->windowToContents(dragData->clientPosition()); + IntPoint point = m_page.mainFrame().view()->windowToContents(dragData.clientPosition()); HitTestResult result = HitTestResult(point); - if (!m_page->mainFrame()->contentRenderer()) + if (!m_page.mainFrame().contentRenderer()) return false; - result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, HitTestRequest::ReadOnly | HitTestRequest::Active); + result = m_page.mainFrame().eventHandler().hitTestResultAtPoint(point, HitTestRequest::ReadOnly | HitTestRequest::Active); if (!result.innerNonSharedNode()) return false; - if (dragData->containsFiles() && asFileInput(result.innerNonSharedNode())) + if (dragData.containsFiles() && asFileInput(*result.innerNonSharedNode())) return true; - if (result.innerNonSharedNode()->isPluginElement()) { - HTMLPlugInElement* plugin = static_cast<HTMLPlugInElement*>(result.innerNonSharedNode()); - if (!plugin->canProcessDrag() && !result.innerNonSharedNode()->rendererIsEditable()) + if (is<HTMLPlugInElement>(*result.innerNonSharedNode())) { + if (!downcast<HTMLPlugInElement>(result.innerNonSharedNode())->canProcessDrag() && !result.innerNonSharedNode()->hasEditableStyle()) return false; - } else if (!result.innerNonSharedNode()->rendererIsEditable()) + } else if (!result.innerNonSharedNode()->hasEditableStyle()) return false; if (m_didInitiateDrag && m_documentUnderMouse == m_dragInitiator && result.isSelected()) @@ -603,118 +602,124 @@ static DragOperation defaultOperationForDrag(DragOperation srcOpMask) return DragOperationGeneric; } -bool DragController::tryDHTMLDrag(DragData* dragData, DragOperation& operation) +bool DragController::tryDHTMLDrag(DragData& dragData, DragOperation& operation) { - ASSERT(dragData); ASSERT(m_documentUnderMouse); - RefPtr<Frame> mainFrame = m_page->mainFrame(); + Ref<MainFrame> mainFrame(m_page.mainFrame()); RefPtr<FrameView> viewProtector = mainFrame->view(); if (!viewProtector) return false; - ClipboardAccessPolicy policy = m_documentUnderMouse->securityOrigin()->isLocal() ? ClipboardReadable : ClipboardTypesReadable; - RefPtr<Clipboard> clipboard = Clipboard::create(policy, dragData, mainFrame.get()); - DragOperation srcOpMask = dragData->draggingSourceOperationMask(); - clipboard->setSourceOperation(srcOpMask); +#if ENABLE(DASHBOARD_SUPPORT) + DataTransferAccessPolicy policy = (mainFrame->settings().usesDashboardBackwardCompatibilityMode() && m_documentUnderMouse->securityOrigin()->isLocal()) ? + DataTransferAccessPolicy::Readable : DataTransferAccessPolicy::TypesReadable; +#else + DataTransferAccessPolicy policy = DataTransferAccessPolicy::TypesReadable; +#endif + RefPtr<DataTransfer> dataTransfer = DataTransfer::createForDragAndDrop(policy, dragData); + DragOperation srcOpMask = dragData.draggingSourceOperationMask(); + dataTransfer->setSourceOperation(srcOpMask); PlatformMouseEvent event = createMouseEvent(dragData); - if (!mainFrame->eventHandler()->updateDragAndDrop(event, clipboard.get())) { - clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security + if (!mainFrame->eventHandler().updateDragAndDrop(event, dataTransfer.get())) { + dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security. return false; } - operation = clipboard->destinationOperation(); - if (clipboard->dropEffectIsUninitialized()) + operation = dataTransfer->destinationOperation(); + if (dataTransfer->dropEffectIsUninitialized()) operation = defaultOperationForDrag(srcOpMask); else if (!(srcOpMask & operation)) { // The element picked an operation which is not supported by the source operation = DragOperationNone; } - clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security + dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security. return true; } Element* DragController::draggableElement(const Frame* sourceFrame, Element* startElement, const IntPoint& dragOrigin, DragState& state) const { - state.type = (sourceFrame->selection()->contains(dragOrigin)) ? DragSourceActionSelection : DragSourceActionNone; + state.type = (sourceFrame->selection().contains(dragOrigin)) ? DragSourceActionSelection : DragSourceActionNone; if (!startElement) - return 0; + return nullptr; +#if ENABLE(ATTACHMENT_ELEMENT) + // Unlike image elements, attachment elements are immediately selected upon mouse down, + // but for those elements we still want to use the single element drag behavior as long as + // the element is the only content of the selection. + const VisibleSelection& selection = sourceFrame->selection().selection(); + if (selection.isRange() && is<HTMLAttachmentElement>(selection.start().anchorNode()) && selection.start().anchorNode() == selection.end().anchorNode()) + state.type = DragSourceActionNone; +#endif - for (const RenderObject* renderer = startElement->renderer(); renderer; renderer = renderer->parent()) { - Node* node = renderer->nonPseudoNode(); - if (!node) - // Anonymous render blocks don't correspond to actual DOM nodes, so we skip over them - // for the purposes of finding a draggable node. + for (auto* renderer = startElement->renderer(); renderer; renderer = renderer->parent()) { + Element* element = renderer->nonPseudoElement(); + if (!element) { + // Anonymous render blocks don't correspond to actual DOM elements, so we skip over them + // for the purposes of finding a draggable element. continue; - if (!(state.type & DragSourceActionSelection) && node->isTextNode() && node->canStartSelection()) - // In this case we have a click in the unselected portion of text. If this text is - // selectable, we want to start the selection process instead of looking for a parent - // to try to drag. - return 0; - if (node->isElementNode()) { - EUserDrag dragMode = renderer->style()->userDrag(); - if ((m_dragSourceAction & DragSourceActionDHTML) && dragMode == DRAG_ELEMENT) { - state.type = static_cast<DragSourceAction>(state.type | DragSourceActionDHTML); - return toElement(node); + } + EUserDrag dragMode = renderer->style().userDrag(); + if ((m_dragSourceAction & DragSourceActionDHTML) && dragMode == DRAG_ELEMENT) { + state.type = static_cast<DragSourceAction>(state.type | DragSourceActionDHTML); + return element; + } + if (dragMode == DRAG_AUTO) { + if ((m_dragSourceAction & DragSourceActionImage) + && is<HTMLImageElement>(*element) + && sourceFrame->settings().loadsImagesAutomatically()) { + state.type = static_cast<DragSourceAction>(state.type | DragSourceActionImage); + return element; } - if (dragMode == DRAG_AUTO) { - if ((m_dragSourceAction & DragSourceActionImage) - && isHTMLImageElement(node) - && sourceFrame->settings() - && sourceFrame->settings()->loadsImagesAutomatically()) { - state.type = static_cast<DragSourceAction>(state.type | DragSourceActionImage); - return toElement(node); - } - if ((m_dragSourceAction & DragSourceActionLink) - && isHTMLAnchorElement(node) - && toHTMLAnchorElement(node)->isLiveLink()) { - state.type = static_cast<DragSourceAction>(state.type | DragSourceActionLink); - return toElement(node); - } + if ((m_dragSourceAction & DragSourceActionLink) && isDraggableLink(*element)) { + state.type = static_cast<DragSourceAction>(state.type | DragSourceActionLink); + return element; } +#if ENABLE(ATTACHMENT_ELEMENT) + if ((m_dragSourceAction & DragSourceActionAttachment) + && is<HTMLAttachmentElement>(*element) + && downcast<HTMLAttachmentElement>(*element).file()) { + state.type = static_cast<DragSourceAction>(state.type | DragSourceActionAttachment); + return element; + } +#endif } } // We either have nothing to drag or we have a selection and we're not over a draggable element. - return (state.type & DragSourceActionSelection) ? startElement : 0; + return (state.type & DragSourceActionSelection) ? startElement : nullptr; } -static CachedImage* getCachedImage(Element* element) +static CachedImage* getCachedImage(Element& element) { - ASSERT(element); - RenderObject* renderer = element->renderer(); - if (!renderer || !renderer->isImage()) - return 0; - RenderImage* image = toRenderImage(renderer); - return image->cachedImage(); + RenderObject* renderer = element.renderer(); + if (!is<RenderImage>(renderer)) + return nullptr; + auto& image = downcast<RenderImage>(*renderer); + return image.cachedImage(); } -static Image* getImage(Element* element) +static Image* getImage(Element& element) { - ASSERT(element); CachedImage* cachedImage = getCachedImage(element); // Don't use cachedImage->imageForRenderer() here as that may return BitmapImages for cached SVG Images. // Users of getImage() want access to the SVGImage, in order to figure out the filename extensions, // which would be empty when asking the cached BitmapImages. return (cachedImage && !cachedImage->errorOccurred()) ? - cachedImage->image() : 0; + cachedImage->image() : nullptr; } -static void prepareClipboardForImageDrag(Frame* source, Clipboard* clipboard, Element* node, const KURL& linkURL, const KURL& imageURL, const String& label) +static void selectElement(Element& element) { - if (node->isContentRichlyEditable()) { - RefPtr<Range> range = source->document()->createRange(); - range->selectNode(node, ASSERT_NO_EXCEPTION); - source->selection()->setSelection(VisibleSelection(range.get(), DOWNSTREAM)); - } - clipboard->declareAndWriteDragImage(node, !linkURL.isEmpty() ? linkURL : imageURL, label, source); + RefPtr<Range> range = element.document().createRange(); + range->selectNode(&element); + element.document().frame()->selection().setSelection(VisibleSelection(*range, DOWNSTREAM)); } static IntPoint dragLocForDHTMLDrag(const IntPoint& mouseDraggedPoint, const IntPoint& dragOrigin, const IntPoint& dragImageOffset, bool isLinkImage) { // dragImageOffset is the cursor position relative to the lower-left corner of the image. -#if PLATFORM(MAC) +#if PLATFORM(COCOA) // We add in the Y dimension because we are a flipped view, so adding moves the image down. const int yOffset = dragImageOffset.y(); #else @@ -727,13 +732,13 @@ static IntPoint dragLocForDHTMLDrag(const IntPoint& mouseDraggedPoint, const Int return IntPoint(dragOrigin.x() - dragImageOffset.x(), dragOrigin.y() + yOffset); } -static IntPoint dragLocForSelectionDrag(Frame* src) +static IntPoint dragLocForSelectionDrag(Frame& src) { - IntRect draggingRect = enclosingIntRect(src->selection()->bounds()); + IntRect draggingRect = enclosingIntRect(src.selection().selectionBounds()); int xpos = draggingRect.maxX(); xpos = draggingRect.x() < xpos ? draggingRect.x() : xpos; int ypos = draggingRect.maxY(); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) // Deal with flipped coordinates on Mac ypos = draggingRect.y() > ypos ? draggingRect.y() : ypos; #else @@ -742,34 +747,50 @@ static IntPoint dragLocForSelectionDrag(Frame* src) return IntPoint(xpos, ypos); } -bool DragController::startDrag(Frame* src, const DragState& state, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin) +bool DragController::startDrag(Frame& src, const DragState& state, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin) { - ASSERT(src); - - if (!src->view() || !src->contentRenderer()) + if (!src.view() || !src.contentRenderer() || !state.source) return false; - HitTestResult hitTestResult = src->eventHandler()->hitTestResultAtPoint(dragOrigin, HitTestRequest::ReadOnly | HitTestRequest::Active); - if (!state.source->contains(hitTestResult.innerNode())) + HitTestResult hitTestResult = src.eventHandler().hitTestResultAtPoint(dragOrigin, HitTestRequest::ReadOnly | HitTestRequest::Active); + + // FIXME(136836): Investigate whether all elements should use the containsIncludingShadowDOM() path here. + bool includeShadowDOM = false; +#if ENABLE(VIDEO) + includeShadowDOM = state.source->isMediaElement(); +#endif + bool sourceContainsHitNode; + if (!includeShadowDOM) + sourceContainsHitNode = state.source->contains(hitTestResult.innerNode()); + else + sourceContainsHitNode = state.source->containsIncludingShadowDOM(hitTestResult.innerNode()); + + if (!sourceContainsHitNode) // The original node being dragged isn't under the drag origin anymore... maybe it was // hidden or moved out from under the cursor. Regardless, we don't want to start a drag on // something that's not actually under the drag origin. return false; - KURL linkURL = hitTestResult.absoluteLinkURL(); - KURL imageURL = hitTestResult.absoluteImageURL(); + URL linkURL = hitTestResult.absoluteLinkURL(); + URL imageURL = hitTestResult.absoluteImageURL(); +#if ENABLE(ATTACHMENT_ELEMENT) + URL attachmentURL = hitTestResult.absoluteAttachmentURL(); + m_draggingAttachmentURL = URL(); +#endif - IntPoint mouseDraggedPoint = src->view()->windowToContents(dragEvent.position()); + IntPoint mouseDraggedPoint = src.view()->windowToContents(dragEvent.position()); - m_draggingImageURL = KURL(); + m_draggingImageURL = URL(); m_sourceDragOperation = srcOp; - DragImageRef dragImage = 0; + DragImageRef dragImage = nullptr; IntPoint dragLoc(0, 0); IntPoint dragImageOffset(0, 0); - Clipboard* clipboard = state.clipboard.get(); + ASSERT(state.dataTransfer); + + DataTransfer& dataTransfer = *state.dataTransfer; if (state.type == DragSourceActionDHTML) - dragImage = clipboard->createDragImage(dragImageOffset); + dragImage = dataTransfer.createDragImage(dragImageOffset); if (state.type == DragSourceActionSelection || !imageURL.isEmpty() || !linkURL.isEmpty()) // Selection, image, and link drags receive a default set of allowed drag operations that // follows from: @@ -785,79 +806,117 @@ bool DragController::startDrag(Frame* src, const DragState& state, DragOperation bool startedDrag = true; // optimism - we almost always manage to start the drag - Element* element = state.source.get(); + ASSERT(state.source); + Element& element = *state.source; Image* image = getImage(element); if (state.type == DragSourceActionSelection) { - if (!clipboard->hasData()) { - RefPtr<Range> selectionRange = src->selection()->toNormalizedRange(); + if (!dataTransfer.pasteboard().hasData()) { + // FIXME: This entire block is almost identical to the code in Editor::copy, and the code should be shared. + + RefPtr<Range> selectionRange = src.selection().toNormalizedRange(); ASSERT(selectionRange); - src->editor().willWriteSelectionToPasteboard(selectionRange.get()); + src.editor().willWriteSelectionToPasteboard(selectionRange.get()); - if (enclosingTextFormControl(src->selection()->start())) - clipboard->writePlainText(src->editor().selectedTextForClipboard()); - else - clipboard->writeRange(selectionRange.get(), src); + if (enclosingTextFormControl(src.selection().selection().start())) + dataTransfer.pasteboard().writePlainText(src.editor().selectedTextForDataTransfer(), Pasteboard::CannotSmartReplace); + else { +#if PLATFORM(COCOA) || PLATFORM(EFL) || PLATFORM(GTK) || PLATFORM(QT) + src.editor().writeSelectionToPasteboard(dataTransfer.pasteboard()); +#else + // FIXME: Convert all other platforms to match Mac and delete this. + dataTransfer.pasteboard().writeSelection(*selectionRange, src.editor().canSmartCopyOrDelete(), src, IncludeImageAltTextForDataTransfer); +#endif + } - src->editor().didWriteSelectionToPasteboard(); + src.editor().didWriteSelectionToPasteboard(); } - m_client->willPerformDragSourceAction(DragSourceActionSelection, dragOrigin, clipboard); + m_client.willPerformDragSourceAction(DragSourceActionSelection, dragOrigin, dataTransfer); if (!dragImage) { - dragImage = dissolveDragImageToFraction(src->dragImageForSelection(), DragImageAlpha); + dragImage = dissolveDragImageToFraction(createDragImageForSelection(src), DragImageAlpha); dragLoc = dragLocForSelectionDrag(src); m_dragOffset = IntPoint(dragOrigin.x() - dragLoc.x(), dragOrigin.y() - dragLoc.y()); } - doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false); - } else if (!imageURL.isEmpty() && element && image && !image->isNull() - && (m_dragSourceAction & DragSourceActionImage)) { + + if (!dragImage) + return false; + + doSystemDrag(dragImage, dragLoc, dragOrigin, dataTransfer, src, false); + } else if (!src.document()->securityOrigin()->canDisplay(linkURL)) { + src.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Not allowed to drag local resource: " + linkURL.stringCenterEllipsizedToLength()); + startedDrag = false; + } else if (!imageURL.isEmpty() && image && !image->isNull() && (m_dragSourceAction & DragSourceActionImage)) { // We shouldn't be starting a drag for an image that can't provide an extension. // This is an early detection for problems encountered later upon drop. ASSERT(!image->filenameExtension().isEmpty()); - if (!clipboard->hasData()) { + if (!dataTransfer.pasteboard().hasData()) { m_draggingImageURL = imageURL; - prepareClipboardForImageDrag(src, clipboard, element, linkURL, imageURL, hitTestResult.altDisplayString()); + if (element.isContentRichlyEditable()) + selectElement(element); + declareAndWriteDragImage(dataTransfer, element, !linkURL.isEmpty() ? linkURL : imageURL, hitTestResult.altDisplayString()); } - m_client->willPerformDragSourceAction(DragSourceActionImage, dragOrigin, clipboard); + m_client.willPerformDragSourceAction(DragSourceActionImage, dragOrigin, dataTransfer); if (!dragImage) { IntRect imageRect = hitTestResult.imageRect(); - imageRect.setLocation(m_page->mainFrame()->view()->rootViewToContents(src->view()->contentsToRootView(imageRect.location()))); - doImageDrag(element, dragOrigin, hitTestResult.imageRect(), clipboard, src, m_dragOffset); - } else + imageRect.setLocation(m_page.mainFrame().view()->rootViewToContents(src.view()->contentsToRootView(imageRect.location()))); + doImageDrag(element, dragOrigin, hitTestResult.imageRect(), dataTransfer, src, m_dragOffset); + } else { // DHTML defined drag image - doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false); - + doSystemDrag(dragImage, dragLoc, dragOrigin, dataTransfer, src, false); + } } else if (!linkURL.isEmpty() && (m_dragSourceAction & DragSourceActionLink)) { - if (!clipboard->hasData()) - // Simplify whitespace so the title put on the clipboard resembles what the user sees + if (!dataTransfer.pasteboard().hasData()) { + // Simplify whitespace so the title put on the dataTransfer resembles what the user sees // on the web page. This includes replacing newlines with spaces. - clipboard->writeURL(linkURL, hitTestResult.textContent().simplifyWhiteSpace(), src); + src.editor().copyURL(linkURL, hitTestResult.textContent().simplifyWhiteSpace(), dataTransfer.pasteboard()); + } - if (src->selection()->isCaret() && src->selection()->isContentEditable()) { + const VisibleSelection& sourceSelection = src.selection().selection(); + if (sourceSelection.isCaret() && sourceSelection.isContentEditable()) { // a user can initiate a drag on a link without having any text // selected. In this case, we should expand the selection to // the enclosing anchor element - Position pos = src->selection()->base(); + Position pos = sourceSelection.base(); Node* node = enclosingAnchorElement(pos); if (node) - src->selection()->setSelection(VisibleSelection::selectionFromContentsOfNode(node)); + src.selection().setSelection(VisibleSelection::selectionFromContentsOfNode(node)); } - m_client->willPerformDragSourceAction(DragSourceActionLink, dragOrigin, clipboard); + m_client.willPerformDragSourceAction(DragSourceActionLink, dragOrigin, dataTransfer); if (!dragImage) { - dragImage = createDragImageForLink(linkURL, hitTestResult.textContent(), src->settings() ? src->settings()->fontRenderingMode() : NormalRenderingMode); + dragImage = createDragImageForLink(linkURL, hitTestResult.textContent(), src.settings().fontRenderingMode()); IntSize size = dragImageSize(dragImage); m_dragOffset = IntPoint(-size.width() / 2, -LinkDragBorderInset); dragLoc = IntPoint(mouseDraggedPoint.x() + m_dragOffset.x(), mouseDraggedPoint.y() + m_dragOffset.y()); + // Later code expects the drag image to be scaled by device's scale factor. + dragImage = scaleDragImage(dragImage, FloatSize(m_page.deviceScaleFactor(), m_page.deviceScaleFactor())); + } + doSystemDrag(dragImage, dragLoc, mouseDraggedPoint, dataTransfer, src, true); +#if ENABLE(ATTACHMENT_ELEMENT) + } else if (!attachmentURL.isEmpty() && (m_dragSourceAction & DragSourceActionAttachment)) { + if (!dataTransfer.pasteboard().hasData()) { + m_draggingAttachmentURL = attachmentURL; + selectElement(element); + declareAndWriteAttachment(dataTransfer, element, attachmentURL); } - doSystemDrag(dragImage, dragLoc, mouseDraggedPoint, clipboard, src, true); + + m_client.willPerformDragSourceAction(DragSourceActionAttachment, dragOrigin, dataTransfer); + + if (!dragImage) { + dragImage = dissolveDragImageToFraction(createDragImageForSelection(src), DragImageAlpha); + dragLoc = dragLocForSelectionDrag(src); + m_dragOffset = IntPoint(dragOrigin.x() - dragLoc.x(), dragOrigin.y() - dragLoc.y()); + } + doSystemDrag(dragImage, dragLoc, dragOrigin, dataTransfer, src, false); +#endif } else if (state.type == DragSourceActionDHTML) { if (dragImage) { ASSERT(m_dragSourceAction & DragSourceActionDHTML); - m_client->willPerformDragSourceAction(DragSourceActionDHTML, dragOrigin, clipboard); - doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false); + m_client.willPerformDragSourceAction(DragSourceActionDHTML, dragOrigin, dataTransfer); + doSystemDrag(dragImage, dragLoc, dragOrigin, dataTransfer, src, false); } else startedDrag = false; } else { @@ -871,57 +930,60 @@ bool DragController::startDrag(Frame* src, const DragState& state, DragOperation return startedDrag; } -void DragController::doImageDrag(Element* element, const IntPoint& dragOrigin, const IntRect& rect, Clipboard* clipboard, Frame* frame, IntPoint& dragImageOffset) +void DragController::doImageDrag(Element& element, const IntPoint& dragOrigin, const IntRect& layoutRect, DataTransfer& dataTransfer, Frame& frame, IntPoint& dragImageOffset) { IntPoint mouseDownPoint = dragOrigin; - DragImageRef dragImage = 0; - IntPoint origin; + DragImageRef dragImage = nullptr; + IntPoint scaledOrigin; + + if (!element.renderer()) + return; + + ImageOrientationDescription orientationDescription(element.renderer()->shouldRespectImageOrientation(), element.renderer()->style().imageOrientation()); Image* image = getImage(element); if (image && image->size().height() * image->size().width() <= MaxOriginalImageArea - && (dragImage = createDragImageFromImage(image, element->renderer() ? element->renderer()->shouldRespectImageOrientation() : DoNotRespectImageOrientation))) { - IntSize originalSize = rect.size(); - origin = rect.location(); + && (dragImage = createDragImageFromImage(image, element.renderer() ? orientationDescription : ImageOrientationDescription()))) { - dragImage = fitDragImageToMaxSize(dragImage, rect.size(), maxDragImageSize()); + dragImage = fitDragImageToMaxSize(dragImage, layoutRect.size(), maxDragImageSize()); + IntSize fittedSize = dragImageSize(dragImage); + + dragImage = scaleDragImage(dragImage, FloatSize(m_page.deviceScaleFactor(), m_page.deviceScaleFactor())); dragImage = dissolveDragImageToFraction(dragImage, DragImageAlpha); - IntSize newSize = dragImageSize(dragImage); - - // Properly orient the drag image and orient it differently if it's smaller than the original - float scale = newSize.width() / (float)originalSize.width(); - float dx = origin.x() - mouseDownPoint.x(); - dx *= scale; - origin.setX((int)(dx + 0.5)); -#if PLATFORM(MAC) - //Compensate for accursed flipped coordinates in cocoa - origin.setY(origin.y() + originalSize.height()); + + // Properly orient the drag image and orient it differently if it's smaller than the original. + float scale = fittedSize.width() / (float)layoutRect.width(); + float dx = scale * (layoutRect.x() - mouseDownPoint.x()); + float originY = layoutRect.y(); +#if PLATFORM(COCOA) + // Compensate for accursed flipped coordinates in Cocoa. + originY += layoutRect.height(); #endif - float dy = origin.y() - mouseDownPoint.y(); - dy *= scale; - origin.setY((int)(dy + 0.5)); + float dy = scale * (originY - mouseDownPoint.y()); + scaledOrigin = IntPoint((int)(dx + 0.5), (int)(dy + 0.5)); } else { if (CachedImage* cachedImage = getCachedImage(element)) { dragImage = createDragImageIconForCachedImageFilename(cachedImage->response().suggestedFilename()); if (dragImage) - origin = IntPoint(DragIconRightInset - dragImageSize(dragImage).width(), DragIconBottomInset); + scaledOrigin = IntPoint(DragIconRightInset - dragImageSize(dragImage).width(), DragIconBottomInset); } } - dragImageOffset = mouseDownPoint + origin; - doSystemDrag(dragImage, dragImageOffset, dragOrigin, clipboard, frame, false); + dragImageOffset = mouseDownPoint + scaledOrigin; + doSystemDrag(dragImage, dragImageOffset, dragOrigin, dataTransfer, frame, false); deleteDragImage(dragImage); } -void DragController::doSystemDrag(DragImageRef image, const IntPoint& dragLoc, const IntPoint& eventPos, Clipboard* clipboard, Frame* frame, bool forLink) +void DragController::doSystemDrag(DragImageRef image, const IntPoint& dragLoc, const IntPoint& eventPos, DataTransfer& dataTransfer, Frame& frame, bool forLink) { m_didInitiateDrag = true; - m_dragInitiator = frame->document(); + m_dragInitiator = frame.document(); // Protect this frame and view, as a load may occur mid drag and attempt to unload this frame - RefPtr<Frame> frameProtector = m_page->mainFrame(); + Ref<MainFrame> frameProtector(m_page.mainFrame()); RefPtr<FrameView> viewProtector = frameProtector->view(); - m_client->startDrag(image, viewProtector->rootViewToContents(frame->view()->contentsToRootView(dragLoc)), - viewProtector->rootViewToContents(frame->view()->contentsToRootView(eventPos)), clipboard, frameProtector.get(), forLink); + m_client.startDrag(image, viewProtector->rootViewToContents(frame.view()->contentsToRootView(dragLoc)), + viewProtector->rootViewToContents(frame.view()->contentsToRootView(eventPos)), dataTransfer, frameProtector.get(), forLink); // DragClient::startDrag can cause our Page to dispear, deallocating |this|. if (!frameProtector->page()) return; @@ -932,7 +994,7 @@ void DragController::doSystemDrag(DragImageRef image, const IntPoint& dragLoc, c // Manual drag caret manipulation void DragController::placeDragCaret(const IntPoint& windowPoint) { - mouseMovedIntoDocument(m_page->mainFrame()->documentAtPoint(windowPoint)); + mouseMovedIntoDocument(m_page.mainFrame().documentAtPoint(windowPoint)); if (!m_documentUnderMouse) return; Frame* frame = m_documentUnderMouse->frame(); @@ -941,9 +1003,9 @@ void DragController::placeDragCaret(const IntPoint& windowPoint) return; IntPoint framePoint = frameView->windowToContents(windowPoint); - m_page->dragCaretController()->setCaretPosition(frame->visiblePositionForPoint(framePoint)); + m_page.dragCaretController().setCaretPosition(frame->visiblePositionForPoint(framePoint)); } -} // namespace WebCore - #endif // ENABLE(DRAG_SUPPORT) + +} // namespace WebCore |