142
143
void DragController::dragEnded()
144
145
m_dragInitiator = 0;
145
m_didInitiateDrag = false;
146
m_page->dragCaretController()->clear();
146
m_didInitiateDrag = false;
147
m_page->dragCaretController()->clear();
149
DragOperation DragController::dragEntered(DragData* dragData)
150
DragOperation DragController::dragEntered(DragData* dragData)
151
152
return dragEnteredOrUpdated(dragData);
154
void DragController::dragExited(DragData* dragData)
155
void DragController::dragExited(DragData* dragData)
156
157
ASSERT(dragData);
157
158
Frame* mainFrame = m_page->mainFrame();
159
160
if (RefPtr<FrameView> v = mainFrame->view()) {
160
ClipboardAccessPolicy policy = m_document->securityOrigin()->isLocal() ? ClipboardReadable : ClipboardTypesReadable;
161
ClipboardAccessPolicy policy = (!m_documentUnderMouse || m_documentUnderMouse->securityOrigin()->isLocal()) ? ClipboardReadable : ClipboardTypesReadable;
161
162
RefPtr<Clipboard> clipboard = dragData->createClipboard(policy);
162
163
clipboard->setSourceOperation(dragData->draggingSourceOperationMask());
163
164
mainFrame->eventHandler()->cancelDragAndDrop(createMouseEvent(dragData), clipboard.get());
164
165
clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security
167
mouseMovedIntoDocument(0);
172
DragOperation DragController::dragUpdated(DragData* dragData)
170
DragOperation DragController::dragUpdated(DragData* dragData)
174
172
return dragEnteredOrUpdated(dragData);
177
175
bool DragController::performDrag(DragData* dragData)
179
177
ASSERT(dragData);
180
m_document = m_page->mainFrame()->documentAtPoint(dragData->clientPosition());
178
m_documentUnderMouse = m_page->mainFrame()->documentAtPoint(dragData->clientPosition());
181
179
if (m_isHandlingDrag) {
182
180
ASSERT(m_dragDestinationAction & DragDestinationActionDHTML);
183
181
m_client->willPerformDragDestinationAction(DragDestinationActionDHTML, dragData);
189
187
mainFrame->eventHandler()->performDragAndDrop(createMouseEvent(dragData), clipboard.get());
190
188
clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security
196
if ((m_dragDestinationAction & DragDestinationActionEdit) && concludeDrag(dragData, m_dragDestinationAction)) {
190
m_documentUnderMouse = 0;
194
if ((m_dragDestinationAction & DragDestinationActionEdit) && concludeEditDrag(dragData)) {
195
m_documentUnderMouse = 0;
199
m_documentUnderMouse = 0;
203
201
if (operationForLoad(dragData) == DragOperationNone)
206
204
m_client->willPerformDragDestinationAction(DragDestinationActionLoad, dragData);
207
m_page->mainFrame()->loader()->load(ResourceRequest(dragData->asURL()));
205
m_page->mainFrame()->loader()->load(ResourceRequest(dragData->asURL()), false);
209
void DragController::mouseMovedIntoDocument(Document* newDocument)
211
if (m_documentUnderMouse == newDocument)
214
// If we were over another document clear the selection
215
if (m_documentUnderMouse)
217
m_documentUnderMouse = newDocument;
211
220
DragOperation DragController::dragEnteredOrUpdated(DragData* dragData)
213
222
ASSERT(dragData);
214
IntPoint windowPoint = dragData->clientPosition();
216
Document* newDraggingDoc = 0;
217
if (Frame* frame = m_page->mainFrame())
218
newDraggingDoc = frame->documentAtPoint(windowPoint);
219
if (m_document != newDraggingDoc) {
222
m_document = newDraggingDoc;
223
ASSERT(m_page->mainFrame()); // It is not possible in Mac WebKit to have a Page without a mainFrame()
224
mouseMovedIntoDocument(m_page->mainFrame()->documentAtPoint(dragData->clientPosition()));
225
226
m_dragDestinationAction = m_client->actionMaskForDrag(dragData);
227
if (m_dragDestinationAction == DragDestinationActionNone) {
228
cancelDrag(); // FIXME: Why not call mouseMovedIntoDocument(0)?
229
return DragOperationNone;
227
232
DragOperation operation = DragOperationNone;
229
if (m_dragDestinationAction == DragDestinationActionNone)
232
operation = tryDocumentDrag(dragData, m_dragDestinationAction);
233
if (operation == DragOperationNone && (m_dragDestinationAction & DragDestinationActionLoad))
234
return operationForLoad(dragData);
233
bool handledByDocument = tryDocumentDrag(dragData, m_dragDestinationAction, operation);
234
if (!handledByDocument && (m_dragDestinationAction & DragDestinationActionLoad))
235
return operationForLoad(dragData);
237
236
return operation;
240
239
static HTMLInputElement* asFileInput(Node* node)
244
243
// The button for a FILE input is a sub element with no set input type
245
244
// In order to get around this problem we assume any non-FILE input element
246
245
// is this internal button, and try querying the shadow parent node.
247
246
if (node->hasTagName(HTMLNames::inputTag) && node->isShadowNode() && static_cast<HTMLInputElement*>(node)->inputType() != HTMLInputElement::FILE)
248
247
node = node->shadowParentNode();
250
249
if (!node || !node->hasTagName(HTMLNames::inputTag))
253
252
HTMLInputElement* inputElem = static_cast<HTMLInputElement*>(node);
254
253
if (inputElem->inputType() == HTMLInputElement::FILE)
255
254
return inputElem;
260
DragOperation DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction actionMask)
259
static Element* elementUnderMouse(Document* documentUnderMouse, const IntPoint& p)
261
float zoomFactor = documentUnderMouse->frame()->pageZoomFactor();
262
IntPoint point = roundedIntPoint(FloatPoint(p.x() * zoomFactor, p.y() * zoomFactor));
264
HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);
265
HitTestResult result(point);
266
documentUnderMouse->renderView()->layer()->hitTest(request, result);
268
Node* n = result.innerNode();
269
while (n && !n->isElementNode())
272
n = n->shadowAncestorNode();
275
return static_cast<Element*>(n);
278
bool DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction actionMask, DragOperation& operation)
262
280
ASSERT(dragData);
265
return DragOperationNone;
267
DragOperation operation = DragOperationNone;
268
if (actionMask & DragDestinationActionDHTML)
269
operation = tryDHTMLDrag(dragData);
270
m_isHandlingDrag = operation != DragOperationNone;
272
RefPtr<FrameView> frameView = m_document->view();
282
if (!m_documentUnderMouse)
285
m_isHandlingDrag = false;
286
if (actionMask & DragDestinationActionDHTML) {
287
m_isHandlingDrag = tryDHTMLDrag(dragData, operation);
288
// Do not continue if m_documentUnderMouse has been reset by tryDHTMLDrag.
289
// tryDHTMLDrag fires dragenter event. The event listener that listens
290
// to this event may create a nested message loop (open a modal dialog),
291
// which could process dragleave event and reset m_documentUnderMouse in
293
if (!m_documentUnderMouse)
297
// It's unclear why this check is after tryDHTMLDrag.
298
// We send drag events in tryDHTMLDrag and that may be the reason.
299
RefPtr<FrameView> frameView = m_documentUnderMouse->view();
276
if ((actionMask & DragDestinationActionEdit) && !m_isHandlingDrag && canProcessDrag(dragData)) {
277
if (dragData->containsColor())
278
return DragOperationGeneric;
280
IntPoint dragPos = dragData->clientPosition();
281
IntPoint point = frameView->windowToContents(dragPos);
282
Element* element = m_document->elementFromPoint(point.x(), point.y());
284
Frame* innerFrame = element->document()->frame();
303
if (m_isHandlingDrag) {
304
m_page->dragCaretController()->clear();
306
} else if ((actionMask & DragDestinationActionEdit) && canProcessDrag(dragData)) {
307
if (dragData->containsColor()) {
308
operation = DragOperationGeneric;
312
IntPoint point = frameView->windowToContents(dragData->clientPosition());
313
Element* element = elementUnderMouse(m_documentUnderMouse, point);
286
314
if (!asFileInput(element)) {
288
if (Frame* frame = m_document->frame())
289
dragCaret = frame->visiblePositionForPoint(point);
315
VisibleSelection dragCaret = m_documentUnderMouse->frame()->visiblePositionForPoint(point);
290
316
m_page->dragCaretController()->setSelection(dragCaret);
293
return dragIsMove(innerFrame->selection(), dragData) ? DragOperationMove : DragOperationCopy;
319
Frame* innerFrame = element->document()->frame();
320
operation = dragIsMove(innerFrame->selection()) ? DragOperationMove : DragOperationCopy;
323
// If we're not over an editable region, make sure we're clearing any prior drag cursor.
296
324
m_page->dragCaretController()->clear();
300
328
DragSourceAction DragController::delegateDragSourceAction(const IntPoint& windowPoint)
302
330
m_dragSourceAction = m_client->dragSourceActionMaskForPoint(windowPoint);
303
331
return m_dragSourceAction;
306
334
DragOperation DragController::operationForLoad(DragData* dragData)
308
336
ASSERT(dragData);
310
doc = m_page->mainFrame()->documentAtPoint(dragData->clientPosition());
337
Document* doc = m_page->mainFrame()->documentAtPoint(dragData->clientPosition());
311
338
if (doc && (m_didInitiateDrag || doc->isPluginDocument() || (doc->frame() && doc->frame()->editor()->clientIsEditable())))
312
339
return DragOperationNone;
313
340
return dragOperation(dragData);
316
static bool setSelectionToDragCaret(Frame* frame, Selection& dragCaret, RefPtr<Range>& range, const IntPoint& point)
343
static bool setSelectionToDragCaret(Frame* frame, VisibleSelection& dragCaret, RefPtr<Range>& range, const IntPoint& point)
318
345
frame->selection()->setSelection(dragCaret);
319
346
if (frame->selection()->isNone()) {
320
347
dragCaret = frame->visiblePositionForPoint(point);
321
348
frame->selection()->setSelection(dragCaret);
322
range = dragCaret.toRange();
349
range = dragCaret.toNormalizedRange();
324
351
return !frame->selection()->isNone() && frame->selection()->isContentEditable();
327
bool DragController::concludeDrag(DragData* dragData, DragDestinationAction actionMask)
354
bool DragController::concludeEditDrag(DragData* dragData)
329
356
ASSERT(dragData);
330
357
ASSERT(!m_isHandlingDrag);
331
ASSERT(actionMask & DragDestinationActionEdit);
359
if (!m_documentUnderMouse)
336
IntPoint point = m_document->view()->windowToContents(dragData->clientPosition());
337
Element* element = m_document->elementFromPoint(point.x(), point.y());
362
IntPoint point = m_documentUnderMouse->view()->windowToContents(dragData->clientPosition());
363
Element* element = elementUnderMouse(m_documentUnderMouse, point);
339
364
Frame* innerFrame = element->ownerDocument()->frame();
342
367
if (dragData->containsColor()) {
343
368
Color color = dragData->asColor();
355
380
innerFrame->editor()->applyStyle(style.get(), EditActionSetColor);
359
384
if (!m_page->dragController()->canProcessDrag(dragData)) {
360
385
m_page->dragCaretController()->clear();
364
389
if (HTMLInputElement* fileInput = asFileInput(element)) {
366
if (!fileInput->isEnabled())
390
if (!fileInput->isEnabledFormControl())
369
393
if (!dragData->containsFiles())
372
396
Vector<String> filenames;
373
397
dragData->asFilenames(filenames);
374
398
if (filenames.isEmpty())
377
// Ugly. For security none of the API's available to us to set the input value
378
// on file inputs. Even forcing a change in HTMLInputElement doesn't work as
379
// RenderFileUploadControl clears the file when doing updateFromElement()
380
RenderFileUploadControl* renderer = static_cast<RenderFileUploadControl*>(fileInput->renderer());
401
// Ugly. For security none of the APIs available to us can set the input value
402
// on file inputs. Even forcing a change in HTMLInputElement doesn't work as
403
// RenderFileUploadControl clears the file when doing updateFromElement().
404
RenderFileUploadControl* renderer = toRenderFileUploadControl(fileInput->renderer());
385
408
renderer->receiveDroppedFiles(filenames);
389
Selection dragCaret(m_page->dragCaretController()->selection());
412
VisibleSelection dragCaret(m_page->dragCaretController()->selection());
390
413
m_page->dragCaretController()->clear();
391
RefPtr<Range> range = dragCaret.toRange();
414
RefPtr<Range> range = dragCaret.toNormalizedRange();
393
416
// For range to be null a WebKit client must have done something bad while
394
417
// manually controlling drag behaviour
397
420
DocLoader* loader = range->ownerDocument()->docLoader();
398
421
loader->setAllowStaleResources(true);
399
if (dragIsMove(innerFrame->selection(), dragData) || dragCaret.isContentRichlyEditable()) {
422
if (dragIsMove(innerFrame->selection()) || dragCaret.isContentRichlyEditable()) {
400
423
bool chosePlainText = false;
401
424
RefPtr<DocumentFragment> fragment = documentFragmentFromDragData(dragData, range, true, chosePlainText);
402
425
if (!fragment || !innerFrame->editor()->shouldInsertFragment(fragment, range, EditorInsertActionDropped)) {
403
426
loader->setAllowStaleResources(false);
407
430
m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
408
if (dragIsMove(innerFrame->selection(), dragData)) {
409
bool smartMove = innerFrame->selectionGranularity() == WordGranularity
410
&& innerFrame->editor()->smartInsertDeleteEnabled()
431
if (dragIsMove(innerFrame->selection())) {
432
bool smartMove = innerFrame->selectionGranularity() == WordGranularity
433
&& innerFrame->editor()->smartInsertDeleteEnabled()
411
434
&& dragData->canSmartReplace();
412
435
applyCommand(MoveSelectionCommand::create(fragment, dragCaret.base(), smartMove));
414
437
if (setSelectionToDragCaret(innerFrame, dragCaret, range, point))
415
applyCommand(ReplaceSelectionCommand::create(m_document, fragment, true, dragData->canSmartReplace(), chosePlainText));
438
applyCommand(ReplaceSelectionCommand::create(m_documentUnderMouse, fragment, true, dragData->canSmartReplace(), chosePlainText));
418
441
String text = dragData->asPlainText();
419
442
if (text.isEmpty() || !innerFrame->editor()->shouldInsertText(text, range.get(), EditorInsertActionDropped)) {
420
443
loader->setAllowStaleResources(false);
424
447
m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
425
448
if (setSelectionToDragCaret(innerFrame, dragCaret, range, point))
426
applyCommand(ReplaceSelectionCommand::create(m_document, createFragmentFromText(range.get(), text), true, false, true));
449
applyCommand(ReplaceSelectionCommand::create(m_documentUnderMouse, createFragmentFromText(range.get(), text), true, false, true));
428
451
loader->setAllowStaleResources(false);
434
bool DragController::canProcessDrag(DragData* dragData)
456
bool DragController::canProcessDrag(DragData* dragData)
436
458
ASSERT(dragData);
438
460
if (!dragData->containsCompatibleContent())
441
463
IntPoint point = m_page->mainFrame()->view()->windowToContents(dragData->clientPosition());
442
464
HitTestResult result = HitTestResult(point);
443
465
if (!m_page->mainFrame()->contentRenderer())
446
468
result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, true);
448
if (!result.innerNonSharedNode())
470
if (!result.innerNonSharedNode())
451
473
if (dragData->containsFiles() && asFileInput(result.innerNonSharedNode()))
454
476
if (!result.innerNonSharedNode()->isContentEditable())
457
if (m_didInitiateDrag && m_document == m_dragInitiator && result.isSelected())
479
if (m_didInitiateDrag && m_documentUnderMouse == m_dragInitiator && result.isSelected())
463
DragOperation DragController::tryDHTMLDrag(DragData* dragData)
485
static DragOperation defaultOperationForDrag(DragOperation srcOpMask)
487
// This is designed to match IE's operation fallback for the case where
488
// the page calls preventDefault() in a drag event but doesn't set dropEffect.
489
if (srcOpMask & DragOperationCopy)
490
return DragOperationCopy;
491
if (srcOpMask & DragOperationMove || srcOpMask & DragOperationGeneric)
492
return DragOperationMove;
493
if (srcOpMask & DragOperationLink)
494
return DragOperationLink;
496
// FIXME: Does IE really return "generic" even if no operations were allowed by the source?
497
return DragOperationGeneric;
500
bool DragController::tryDHTMLDrag(DragData* dragData, DragOperation& operation)
465
502
ASSERT(dragData);
467
DragOperation op = DragOperationNone;
503
ASSERT(m_documentUnderMouse);
468
504
RefPtr<Frame> mainFrame = m_page->mainFrame();
469
505
RefPtr<FrameView> viewProtector = mainFrame->view();
470
506
if (!viewProtector)
471
return DragOperationNone;
473
ClipboardAccessPolicy policy = m_document->securityOrigin()->isLocal() ? ClipboardReadable : ClipboardTypesReadable;
509
ClipboardAccessPolicy policy = m_documentUnderMouse->securityOrigin()->isLocal() ? ClipboardReadable : ClipboardTypesReadable;
474
510
RefPtr<Clipboard> clipboard = dragData->createClipboard(policy);
475
DragOperation srcOp = dragData->draggingSourceOperationMask();
476
clipboard->setSourceOperation(srcOp);
511
DragOperation srcOpMask = dragData->draggingSourceOperationMask();
512
clipboard->setSourceOperation(srcOpMask);
478
514
PlatformMouseEvent event = createMouseEvent(dragData);
479
if (mainFrame->eventHandler()->updateDragAndDrop(event, clipboard.get())) {
480
// *op unchanged if no source op was set
481
if (!clipboard->destinationOperation(op)) {
482
// The element accepted but they didn't pick an operation, so we pick one for them
484
if (srcOp & DragOperationCopy)
485
op = DragOperationCopy;
486
else if (srcOp & DragOperationMove || srcOp & DragOperationGeneric)
487
op = DragOperationMove;
488
else if (srcOp & DragOperationLink)
489
op = DragOperationLink;
491
op = DragOperationGeneric;
492
} else if (!(op & srcOp)) {
493
op = DragOperationNone;
515
if (!mainFrame->eventHandler()->updateDragAndDrop(event, clipboard.get())) {
496
516
clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security
520
if (!clipboard->destinationOperation(operation)) {
521
// The element accepted but they didn't pick an operation, so we pick one (to match IE).
522
operation = defaultOperationForDrag(srcOpMask);
523
} else if (!(srcOpMask & operation)) {
524
// The element picked an operation which is not supported by the source
525
operation = DragOperationNone;
528
clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security
502
532
bool DragController::mayStartDragAtEventLocation(const Frame* frame, const IntPoint& framePos)
533
563
static CachedImage* getCachedImage(Element* element)
536
566
RenderObject* renderer = element->renderer();
537
if (!renderer || !renderer->isImage())
567
if (!renderer || !renderer->isImage())
539
RenderImage* image = static_cast<RenderImage*>(renderer);
569
RenderImage* image = toRenderImage(renderer);
540
570
return image->cachedImage();
543
573
static Image* getImage(Element* element)
546
576
RenderObject* renderer = element->renderer();
547
if (!renderer || !renderer->isImage())
577
if (!renderer || !renderer->isImage())
550
RenderImage* image = static_cast<RenderImage*>(renderer);
580
RenderImage* image = toRenderImage(renderer);
551
581
if (image->cachedImage() && !image->cachedImage()->errorOccurred())
552
582
return image->cachedImage()->image();
556
586
static void prepareClipboardForImageDrag(Frame* src, Clipboard* clipboard, Element* node, const KURL& linkURL, const KURL& imageURL, const String& label)
558
588
RefPtr<Range> range = src->document()->createRange();
559
589
ExceptionCode ec = 0;
560
590
range->selectNode(node, ec);
562
src->selection()->setSelection(Selection(range.get(), DOWNSTREAM));
592
src->selection()->setSelection(VisibleSelection(range.get(), DOWNSTREAM));
563
593
clipboard->declareAndWriteDragImage(node, !linkURL.isEmpty() ? linkURL : imageURL, label, src);
566
596
static IntPoint dragLocForDHTMLDrag(const IntPoint& mouseDraggedPoint, const IntPoint& dragOrigin, const IntPoint& dragImageOffset, bool isLinkImage)
568
598
// dragImageOffset is the cursor position relative to the lower-left corner of the image.
570
// We add in the Y dimension because we are a flipped view, so adding moves the image down.
600
// We add in the Y dimension because we are a flipped view, so adding moves the image down.
571
601
const int yOffset = dragImageOffset.y();
573
603
const int yOffset = -dragImageOffset.y();
577
607
return IntPoint(mouseDraggedPoint.x() - dragImageOffset.x(), mouseDraggedPoint.y() + yOffset);
579
609
return IntPoint(dragOrigin.x() - dragImageOffset.x(), dragOrigin.y() + yOffset);
582
612
static IntPoint dragLocForSelectionDrag(Frame* src)
584
614
IntRect draggingRect = enclosingIntRect(src->selectionBounds());
594
624
return IntPoint(xpos, ypos);
597
627
bool DragController::startDrag(Frame* src, Clipboard* clipboard, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin, bool isDHTMLDrag)
600
630
ASSERT(clipboard);
602
632
if (!src->view() || !src->contentRenderer())
605
635
HitTestResult dragSource = HitTestResult(dragOrigin);
606
636
dragSource = src->eventHandler()->hitTestResultAtPoint(dragOrigin, true);
607
637
KURL linkURL = dragSource.absoluteLinkURL();
608
638
KURL imageURL = dragSource.absoluteImageURL();
609
639
bool isSelected = dragSource.isSelected();
611
641
IntPoint mouseDraggedPoint = src->view()->windowToContents(dragEvent.pos());
613
643
m_draggingImageURL = KURL();
614
m_dragOperation = srcOp;
644
m_sourceDragOperation = srcOp;
616
646
DragImageRef dragImage = 0;
617
647
IntPoint dragLoc(0, 0);
618
648
IntPoint dragImageOffset(0, 0);
621
651
dragImage = clipboard->createDragImage(dragImageOffset);
623
653
// We allow DHTML/JS to set the drag image, even if its a link, image or text we're dragging.
624
654
// This is in the spirit of the IE API, which allows overriding of pasteboard data and DragOp.
626
656
dragLoc = dragLocForDHTMLDrag(mouseDraggedPoint, dragOrigin, dragImageOffset, !linkURL.isEmpty());
627
657
m_dragOffset = dragImageOffset;
630
660
bool startedDrag = true; // optimism - we almost always manage to start the drag
632
662
Node* node = dragSource.innerNonSharedNode();
634
664
Image* image = getImage(static_cast<Element*>(node));
635
665
if (!imageURL.isEmpty() && node && node->isElementNode() && image
636
666
&& (m_dragSourceAction & DragSourceActionImage)) {
637
// We shouldn't be starting a drag for an image that can't provide an extension.
667
// We shouldn't be starting a drag for an image that can't provide an extension.
638
668
// This is an early detection for problems encountered later upon drop.
639
669
ASSERT(!image->filenameExtension().isEmpty());
640
670
Element* element = static_cast<Element*>(node);
641
671
if (!clipboard->hasData()) {
642
m_draggingImageURL = imageURL;
672
m_draggingImageURL = imageURL;
643
673
prepareClipboardForImageDrag(src, clipboard, element, linkURL, imageURL, dragSource.altDisplayString());
646
676
m_client->willPerformDragSourceAction(DragSourceActionImage, dragOrigin, clipboard);
648
678
if (!dragImage) {
649
679
IntRect imageRect = dragSource.imageRect();
650
680
imageRect.setLocation(m_page->mainFrame()->view()->windowToContents(src->view()->contentsToWindow(imageRect.location())));
651
681
doImageDrag(element, dragOrigin, dragSource.imageRect(), clipboard, src, m_dragOffset);
653
683
// DHTML defined drag image
654
684
doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false);