Skip to content

Commit

Permalink
Drag-and-drop across OOPIFs.
Browse files Browse the repository at this point in the history
This patch enables drag-and-drop to work across out-of-process iframes.

BUG=647249, 655063

Review-Url: https://codereview.chromium.org/2508013002
Cr-Commit-Position: refs/heads/master@{#433068}
  • Loading branch information
paulmeyer90 authored and Commit bot committed Nov 18, 2016
1 parent 9112036 commit 90572ae
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 88 deletions.
77 changes: 38 additions & 39 deletions third_party/WebKit/Source/core/page/DragController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,10 @@ void DragController::dragEnded() {
m_page->dragCaretController().clear();
}

void DragController::dragExited(DragData* dragData) {
void DragController::dragExited(DragData* dragData, LocalFrame& localRoot) {
DCHECK(dragData);
LocalFrame* mainFrame = m_page->deprecatedLocalMainFrame();

FrameView* frameView(mainFrame->view());
FrameView* frameView(localRoot.view());
if (frameView) {
DataTransferAccessPolicy policy =
(!m_documentUnderMouse ||
Expand All @@ -221,8 +220,8 @@ void DragController::dragExited(DragData* dragData) {
: DataTransferTypesReadable;
DataTransfer* dataTransfer = createDraggingDataTransfer(policy, dragData);
dataTransfer->setSourceOperation(dragData->draggingSourceOperationMask());
mainFrame->eventHandler().cancelDragAndDrop(createMouseEvent(dragData),
dataTransfer);
localRoot.eventHandler().cancelDragAndDrop(createMouseEvent(dragData),
dataTransfer);
dataTransfer->setAccessPolicy(
DataTransferNumb); // invalidate clipboard here for security
}
Expand All @@ -232,30 +231,28 @@ void DragController::dragExited(DragData* dragData) {
m_fileInputElementUnderMouse = nullptr;
}

bool DragController::performDrag(DragData* dragData) {
bool DragController::performDrag(DragData* dragData, LocalFrame& localRoot) {
DCHECK(dragData);
m_documentUnderMouse = m_page->deprecatedLocalMainFrame()->documentAtPoint(
dragData->clientPosition());
m_documentUnderMouse = localRoot.documentAtPoint(dragData->clientPosition());
UserGestureIndicator gesture(DocumentUserGestureToken::create(
m_documentUnderMouse, UserGestureToken::NewGesture));
if ((m_dragDestinationAction & DragDestinationActionDHTML) &&
m_documentIsHandlingDrag) {
LocalFrame* mainFrame = m_page->deprecatedLocalMainFrame();
bool preventedDefault = false;
if (mainFrame->view()) {
if (localRoot.view()) {
// Sending an event can result in the destruction of the view and part.
DataTransfer* dataTransfer =
createDraggingDataTransfer(DataTransferReadable, dragData);
dataTransfer->setSourceOperation(dragData->draggingSourceOperationMask());
EventHandler& eventHandler = mainFrame->eventHandler();
EventHandler& eventHandler = localRoot.eventHandler();
preventedDefault = eventHandler.performDragAndDrop(
createMouseEvent(dragData), dataTransfer) !=
WebInputEventResult::NotHandled;
if (!preventedDefault) {
// When drop target is plugin element and it can process drag, we
// should prevent default behavior.
const IntPoint point =
mainFrame->view()->rootFrameToContents(dragData->clientPosition());
localRoot.view()->rootFrameToContents(dragData->clientPosition());
const HitTestResult result = eventHandler.hitTestResultAtPoint(point);
preventedDefault |=
isHTMLPlugInElement(*result.innerNode()) &&
Expand All @@ -280,7 +277,7 @@ bool DragController::performDrag(DragData* dragData) {

m_documentUnderMouse = nullptr;

if (operationForLoad(dragData) == DragOperationNone)
if (operationForLoad(dragData, localRoot) == DragOperationNone)
return false;

if (m_page->settings().navigateOnDragDrop()) {
Expand All @@ -300,11 +297,11 @@ void DragController::mouseMovedIntoDocument(Document* newDocument) {
m_documentUnderMouse = newDocument;
}

DragSession DragController::dragEnteredOrUpdated(DragData* dragData) {
DragSession DragController::dragEnteredOrUpdated(DragData* dragData,
LocalFrame& localRoot) {
DCHECK(dragData);
DCHECK(m_page->mainFrame());
mouseMovedIntoDocument(m_page->deprecatedLocalMainFrame()->documentAtPoint(
dragData->clientPosition()));

mouseMovedIntoDocument(localRoot.documentAtPoint(dragData->clientPosition()));

// TODO(esprehn): Replace acceptsLoadDrops with a Setting used in core.
m_dragDestinationAction =
Expand All @@ -314,11 +311,11 @@ DragSession DragController::dragEnteredOrUpdated(DragData* dragData) {
DragDestinationActionEdit);

DragSession dragSession;
m_documentIsHandlingDrag =
tryDocumentDrag(dragData, m_dragDestinationAction, dragSession);
m_documentIsHandlingDrag = tryDocumentDrag(dragData, m_dragDestinationAction,
dragSession, localRoot);
if (!m_documentIsHandlingDrag &&
(m_dragDestinationAction & DragDestinationActionLoad))
dragSession.operation = operationForLoad(dragData);
dragSession.operation = operationForLoad(dragData, localRoot);
return dragSession;
}

Expand Down Expand Up @@ -350,7 +347,8 @@ static Element* elementUnderMouse(Document* documentUnderMouse,

bool DragController::tryDocumentDrag(DragData* dragData,
DragDestinationAction actionMask,
DragSession& dragSession) {
DragSession& dragSession,
LocalFrame& localRoot) {
DCHECK(dragData);

if (!m_documentUnderMouse)
Expand All @@ -363,7 +361,7 @@ bool DragController::tryDocumentDrag(DragData* dragData,

bool isHandlingDrag = false;
if (actionMask & DragDestinationActionDHTML) {
isHandlingDrag = tryDHTMLDrag(dragData, dragSession.operation);
isHandlingDrag = tryDHTMLDrag(dragData, dragSession.operation, localRoot);
// 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),
Expand All @@ -384,7 +382,8 @@ bool DragController::tryDocumentDrag(DragData* dragData,
return true;
}

if ((actionMask & DragDestinationActionEdit) && canProcessDrag(dragData)) {
if ((actionMask & DragDestinationActionEdit) &&
canProcessDrag(dragData, localRoot)) {
IntPoint point = frameView->rootFrameToContents(dragData->clientPosition());
Element* element = elementUnderMouse(m_documentUnderMouse.get(), point);
if (!element)
Expand Down Expand Up @@ -442,10 +441,10 @@ bool DragController::tryDocumentDrag(DragData* dragData,
return false;
}

DragOperation DragController::operationForLoad(DragData* dragData) {
DragOperation DragController::operationForLoad(DragData* dragData,
LocalFrame& localRoot) {
DCHECK(dragData);
Document* doc = m_page->deprecatedLocalMainFrame()->documentAtPoint(
dragData->clientPosition());
Document* doc = localRoot.documentAtPoint(dragData->clientPosition());

if (doc &&
(m_didInitiateDrag || doc->isPluginDocument() || hasEditableStyle(*doc)))
Expand Down Expand Up @@ -534,7 +533,9 @@ bool DragController::concludeEditDrag(DragData* dragData) {
return fileInput->receiveDroppedFiles(dragData);
}

if (!m_page->dragController().canProcessDrag(dragData)) {
// TODO(paulmeyer): Isn't |m_page->dragController()| the same as |this|?
if (!m_page->dragController().canProcessDrag(dragData,
*innerFrame->localFrameRoot())) {
m_page->dragCaretController().clear();
return false;
}
Expand Down Expand Up @@ -655,21 +656,19 @@ bool DragController::concludeEditDrag(DragData* dragData) {
return true;
}

bool DragController::canProcessDrag(DragData* dragData) {
bool DragController::canProcessDrag(DragData* dragData, LocalFrame& localRoot) {
DCHECK(dragData);

if (!dragData->containsCompatibleContent())
return false;

IntPoint point =
m_page->deprecatedLocalMainFrame()->view()->rootFrameToContents(
dragData->clientPosition());
if (m_page->deprecatedLocalMainFrame()->contentLayoutItem().isNull())
if (localRoot.contentLayoutItem().isNull())
return false;

HitTestResult result =
m_page->deprecatedLocalMainFrame()->eventHandler().hitTestResultAtPoint(
point);
IntPoint point =
localRoot.view()->rootFrameToContents(dragData->clientPosition());

HitTestResult result = localRoot.eventHandler().hitTestResultAtPoint(point);

if (!result.innerNode())
return false;
Expand Down Expand Up @@ -712,11 +711,11 @@ static DragOperation defaultOperationForDrag(DragOperation srcOpMask) {
}

bool DragController::tryDHTMLDrag(DragData* dragData,
DragOperation& operation) {
DragOperation& operation,
LocalFrame& localRoot) {
DCHECK(dragData);
DCHECK(m_documentUnderMouse);
LocalFrame* mainFrame = m_page->deprecatedLocalMainFrame();
if (!mainFrame->view())
if (!localRoot.view())
return false;

DataTransferAccessPolicy policy =
Expand All @@ -728,7 +727,7 @@ bool DragController::tryDHTMLDrag(DragData* dragData,
dataTransfer->setSourceOperation(srcOpMask);

PlatformMouseEvent event = createMouseEvent(dragData);
if (mainFrame->eventHandler().updateDragAndDrop(event, dataTransfer) ==
if (localRoot.eventHandler().updateDragAndDrop(event, dataTransfer) ==
WebInputEventResult::NotHandled) {
dataTransfer->setAccessPolicy(
DataTransferNumb); // invalidate clipboard here for security
Expand Down
17 changes: 10 additions & 7 deletions third_party/WebKit/Source/core/page/DragController.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ class CORE_EXPORT DragController final
public:
static DragController* create(Page*);

DragSession dragEnteredOrUpdated(DragData*);
void dragExited(DragData*);
bool performDrag(DragData*);
DragSession dragEnteredOrUpdated(DragData*, LocalFrame& localRoot);
void dragExited(DragData*, LocalFrame& localRoot);
bool performDrag(DragData*, LocalFrame& localRoot);

enum SelectionDragPolicy {
ImmediateSelectionDragResolution,
Expand All @@ -85,11 +85,14 @@ class CORE_EXPORT DragController final
DragController(Page*);

DispatchEventResult dispatchTextInputEventFor(LocalFrame*, DragData*);
bool canProcessDrag(DragData*);
bool canProcessDrag(DragData*, LocalFrame& localRoot);
bool concludeEditDrag(DragData*);
DragOperation operationForLoad(DragData*);
bool tryDocumentDrag(DragData*, DragDestinationAction, DragSession&);
bool tryDHTMLDrag(DragData*, DragOperation&);
DragOperation operationForLoad(DragData*, LocalFrame& localRoot);
bool tryDocumentDrag(DragData*,
DragDestinationAction,
DragSession&,
LocalFrame& localRoot);
bool tryDHTMLDrag(DragData*, DragOperation&, LocalFrame& localRoot);
DragOperation dragOperation(DragData*);
void cancelDrag();
bool dragIsMove(FrameSelection&, DragData*);
Expand Down
3 changes: 1 addition & 2 deletions third_party/WebKit/Source/web/ChromeClientImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,7 @@ void ChromeClientImpl::startDragging(LocalFrame* frame,
const WebPoint& dragImageOffset) {
WebLocalFrameImpl* webFrame = WebLocalFrameImpl::fromFrame(frame);
WebReferrerPolicy policy = webFrame->document().referrerPolicy();
m_webView->setDoingDragAndDrop(true);
webFrame->localRoot()->frameWidget()->client()->startDragging(
webFrame->localRoot()->frameWidget()->startDragging(
policy, dragData, mask, dragImage, dragImageOffset);
}

Expand Down
52 changes: 44 additions & 8 deletions third_party/WebKit/Source/web/WebFrameWidgetBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,36 @@
#include "core/page/Page.h"
#include "public/web/WebAutofillClient.h"
#include "public/web/WebDocument.h"
#include "public/web/WebWidgetClient.h"
#include "web/WebLocalFrameImpl.h"
#include "web/WebViewImpl.h"

namespace blink {

namespace {

// Helper to get LocalFrame* from WebLocalFrame*.
// TODO(dcheng): This should be moved into WebLocalFrame.
LocalFrame* toCoreFrame(WebLocalFrame* frame) {
return toWebLocalFrameImpl(frame)->frame();
}

} // namespace

// Ensure that the WebDragOperation enum values stay in sync with the original
// DragOperation constants.
#define STATIC_ASSERT_ENUM(a, b) \
static_assert(static_cast<int>(a) == static_cast<int>(b), \
"mismatching enum : " #a)
STATIC_ASSERT_ENUM(DragOperationNone, WebDragOperationNone);
STATIC_ASSERT_ENUM(DragOperationCopy, WebDragOperationCopy);
STATIC_ASSERT_ENUM(DragOperationLink, WebDragOperationLink);
STATIC_ASSERT_ENUM(DragOperationGeneric, WebDragOperationGeneric);
STATIC_ASSERT_ENUM(DragOperationPrivate, WebDragOperationPrivate);
STATIC_ASSERT_ENUM(DragOperationMove, WebDragOperationMove);
STATIC_ASSERT_ENUM(DragOperationDelete, WebDragOperationDelete);
STATIC_ASSERT_ENUM(DragOperationEvery, WebDragOperationEvery);

WebDragOperation WebFrameWidgetBase::dragTargetDragEnter(
const WebDragData& webDragData,
const WebPoint& pointInViewport,
Expand Down Expand Up @@ -51,7 +76,7 @@ void WebFrameWidgetBase::dragTargetDragLeave() {
DragData dragData(m_currentDragData.get(), IntPoint(), IntPoint(),
static_cast<DragOperation>(m_operationsAllowed));

page()->dragController().dragExited(&dragData);
page()->dragController().dragExited(&dragData, *toCoreFrame(localRoot()));

// FIXME: why is the drag scroll timer not stopped here?

Expand Down Expand Up @@ -86,7 +111,7 @@ void WebFrameWidgetBase::dragTargetDrop(const WebDragData& webDragData,
DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint,
static_cast<DragOperation>(m_operationsAllowed));

page()->dragController().performDrag(&dragData);
page()->dragController().performDrag(&dragData, *toCoreFrame(localRoot()));

m_dragOperation = WebDragOperationNone;
m_currentDragData = nullptr;
Expand All @@ -103,19 +128,29 @@ void WebFrameWidgetBase::dragSourceEndedAt(const WebPoint& pointInViewport,
PlatformEvent::MouseMoved, 0, PlatformEvent::NoModifiers,
PlatformMouseEvent::RealOrIndistinguishable,
WTF::monotonicallyIncreasingTime());
page()->deprecatedLocalMainFrame()->eventHandler().dragSourceEndedAt(
pme, static_cast<DragOperation>(operation));
toCoreFrame(localRoot())
->eventHandler()
.dragSourceEndedAt(pme, static_cast<DragOperation>(operation));
}

void WebFrameWidgetBase::dragSourceSystemDragEnded() {
// It's possible for us to get this callback while not doing a drag if it's
// from a previous page that got unloaded.
if (view()->doingDragAndDrop()) {
if (m_doingDragAndDrop) {
page()->dragController().dragEnded();
view()->setDoingDragAndDrop(false);
m_doingDragAndDrop = false;
}
}

void WebFrameWidgetBase::startDragging(WebReferrerPolicy policy,
const WebDragData& data,
WebDragOperationsMask mask,
const WebImage& dragImage,
const WebPoint& dragImageOffset) {
m_doingDragAndDrop = true;
client()->startDragging(policy, data, mask, dragImage, dragImageOffset);
}

WebDragOperation WebFrameWidgetBase::dragTargetDragEnterOrOver(
const WebPoint& pointInViewport,
const WebPoint& screenPoint,
Expand All @@ -130,7 +165,8 @@ WebDragOperation WebFrameWidgetBase::dragTargetDragEnterOrOver(
static_cast<DragOperation>(m_operationsAllowed));

DragSession dragSession;
dragSession = page()->dragController().dragEnteredOrUpdated(&dragData);
dragSession = page()->dragController().dragEnteredOrUpdated(
&dragData, *toCoreFrame(localRoot()));

DragOperation dropEffect = dragSession.operation;

Expand All @@ -151,7 +187,7 @@ WebPoint WebFrameWidgetBase::viewportToRootFrame(
}

WebViewImpl* WebFrameWidgetBase::view() const {
return static_cast<WebLocalFrameImpl*>(localRoot())->viewImpl();
return toWebLocalFrameImpl(localRoot())->viewImpl();
}

Page* WebFrameWidgetBase::page() const {
Expand Down
10 changes: 10 additions & 0 deletions third_party/WebKit/Source/web/WebFrameWidgetBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class CompositorAnimationTimeline;
class CompositorProxyClient;
class DragController;
class GraphicsLayer;
class WebImage;
class WebLayer;
class WebLocalFrameImpl;
class WebViewImpl;
Expand Down Expand Up @@ -64,6 +65,15 @@ class WebFrameWidgetBase : public WebFrameWidget {
WebDragOperation) override;
void dragSourceSystemDragEnded() override;

// Called when a drag-n-drop operation should begin.
void startDragging(WebReferrerPolicy,
const WebDragData&,
WebDragOperationsMask,
const WebImage& dragImage,
const WebPoint& dragImageOffset);

bool doingDragAndDrop() { return m_doingDragAndDrop; }

protected:
enum DragAction { DragEnter, DragOver };

Expand Down
4 changes: 4 additions & 0 deletions third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,10 @@ WebInputEventResult WebFrameWidgetImpl::handleInputEvent(
TRACE_EVENT1("input", "WebFrameWidgetImpl::handleInputEvent", "type",
WebInputEvent::GetName(inputEvent.type));

// If a drag-and-drop operation is in progress, ignore input events.
if (m_doingDragAndDrop)
return WebInputEventResult::HandledSuppressed;

// Don't handle events once we've started shutting down.
if (!page())
return WebInputEventResult::NotHandled;
Expand Down
Loading

0 comments on commit 90572ae

Please sign in to comment.