OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007 Apple Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions | |
6 * are met: | |
7 * 1. Redistributions of source code must retain the above copyright | |
8 * notice, this list of conditions and the following disclaimer. | |
9 * 2. Redistributions in binary form must reproduce the above copyright | |
10 * notice, this list of conditions and the following disclaimer in the | |
11 * documentation and/or other materials provided with the distribution. | |
12 * | |
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 */ | |
25 | |
26 #include "config.h" | |
27 #include "DragController.h" | |
28 | |
29 #include "CSSStyleDeclaration.h" | |
30 #include "Clipboard.h" | |
31 #include "ClipboardAccessPolicy.h" | |
32 #include "DocLoader.h" | |
33 #include "Document.h" | |
34 #include "DocumentFragment.h" | |
35 #include "DragActions.h" | |
36 #include "DragClient.h" | |
37 #include "DragData.h" | |
38 #include "Editor.h" | |
39 #include "EditorClient.h" | |
40 #include "Element.h" | |
41 #include "EventHandler.h" | |
42 #include "FloatRect.h" | |
43 #include "Frame.h" | |
44 #include "FrameLoader.h" | |
45 #include "FrameView.h" | |
46 #include "HTMLAnchorElement.h" | |
47 #include "HTMLInputElement.h" | |
48 #include "HTMLNames.h" | |
49 #include "HitTestResult.h" | |
50 #include "Image.h" | |
51 #include "MoveSelectionCommand.h" | |
52 #include "Node.h" | |
53 #include "Page.h" | |
54 #include "RenderFileUploadControl.h" | |
55 #include "RenderImage.h" | |
56 #include "ReplaceSelectionCommand.h" | |
57 #include "ResourceRequest.h" | |
58 #include "SelectionController.h" | |
59 #include "Settings.h" | |
60 #include "SystemTime.h" | |
61 #include "Text.h" | |
62 #include "markup.h" | |
63 #include <wtf/RefPtr.h> | |
64 | |
65 namespace WebCore { | |
66 | |
67 static PlatformMouseEvent createMouseEvent(DragData* dragData) | |
68 { | |
69 // FIXME: We should fake modifier keys here. | |
70 return PlatformMouseEvent(dragData->clientPosition(), dragData->globalPositi
on(), | |
71 LeftButton, MouseEventMoved, 0, false, false, fals
e, false, currentTime()); | |
72 | |
73 } | |
74 | |
75 DragController::DragController(Page* page, DragClient* client) | |
76 : m_page(page) | |
77 , m_client(client) | |
78 , m_document(0) | |
79 , m_dragInitiator(0) | |
80 , m_dragDestinationAction(DragDestinationActionNone) | |
81 , m_dragSourceAction(DragSourceActionNone) | |
82 , m_didInitiateDrag(false) | |
83 , m_isHandlingDrag(false) | |
84 , m_dragOperation(DragOperationNone) | |
85 { | |
86 } | |
87 | |
88 DragController::~DragController() | |
89 { | |
90 m_client->dragControllerDestroyed(); | |
91 } | |
92 | |
93 static PassRefPtr<DocumentFragment> documentFragmentFromDragData(DragData* dragD
ata, RefPtr<Range> context, | |
94 bool allowPlainText, bool& chosePlainT
ext) | |
95 { | |
96 ASSERT(dragData); | |
97 chosePlainText = false; | |
98 | |
99 Document* document = context->ownerDocument(); | |
100 ASSERT(document); | |
101 if (document && dragData->containsCompatibleContent()) { | |
102 if (PassRefPtr<DocumentFragment> fragment = dragData->asFragment(documen
t)) | |
103 return fragment; | |
104 | |
105 if (dragData->containsURL()) { | |
106 String title; | |
107 String url = dragData->asURL(&title); | |
108 if (!url.isEmpty()) { | |
109 ExceptionCode ec; | |
110 RefPtr<HTMLAnchorElement> anchor = static_cast<HTMLAnchorElement
*>(document->createElement("a", ec).get()); | |
111 anchor->setHref(url); | |
112 RefPtr<Node> anchorText = document->createTextNode(title); | |
113 anchor->appendChild(anchorText, ec); | |
114 RefPtr<DocumentFragment> fragment = document->createDocumentFrag
ment(); | |
115 fragment->appendChild(anchor, ec); | |
116 return fragment.get(); | |
117 } | |
118 } | |
119 } | |
120 if (allowPlainText && dragData->containsPlainText()) { | |
121 chosePlainText = true; | |
122 return createFragmentFromText(context.get(), dragData->asPlainText()).ge
t(); | |
123 } | |
124 | |
125 return 0; | |
126 } | |
127 | |
128 bool DragController::dragIsMove(SelectionController* selection, DragData* dragDa
ta) | |
129 { | |
130 return m_document == m_dragInitiator | |
131 && selection->isContentEditable() | |
132 && !isCopyKeyDown(); | |
133 } | |
134 | |
135 void DragController::cancelDrag() | |
136 { | |
137 m_page->dragCaretController()->clear(); | |
138 } | |
139 | |
140 void DragController::dragEnded() | |
141 { | |
142 m_dragInitiator = 0; | |
143 m_didInitiateDrag = false; | |
144 m_page->dragCaretController()->clear(); | |
145 } | |
146 | |
147 DragOperation DragController::dragEntered(DragData* dragData) | |
148 { | |
149 return dragEnteredOrUpdated(dragData); | |
150 } | |
151 | |
152 void DragController::dragExited(DragData* dragData) | |
153 { | |
154 ASSERT(dragData); | |
155 Frame* mainFrame = m_page->mainFrame(); | |
156 | |
157 if (RefPtr<FrameView> v = mainFrame->view()) { | |
158 ClipboardAccessPolicy policy = mainFrame->loader()->baseURL().isLocalFil
e() ? ClipboardReadable : ClipboardTypesReadable; | |
159 RefPtr<Clipboard> clipboard = dragData->createClipboard(policy); | |
160 clipboard->setSourceOperation(dragData->draggingSourceOperationMask()); | |
161 mainFrame->eventHandler()->cancelDragAndDrop(createMouseEvent(dragData),
clipboard.get()); | |
162 clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard he
re for security | |
163 } | |
164 | |
165 cancelDrag(); | |
166 m_document = 0; | |
167 } | |
168 | |
169 DragOperation DragController::dragUpdated(DragData* dragData) | |
170 { | |
171 return dragEnteredOrUpdated(dragData); | |
172 } | |
173 | |
174 bool DragController::performDrag(DragData* dragData) | |
175 { | |
176 ASSERT(dragData); | |
177 m_document = m_page->mainFrame()->documentAtPoint(dragData->clientPosition()
); | |
178 if (m_isHandlingDrag) { | |
179 ASSERT(m_dragDestinationAction & DragDestinationActionDHTML); | |
180 m_client->willPerformDragDestinationAction(DragDestinationActionDHTML, d
ragData); | |
181 RefPtr<Frame> mainFrame = m_page->mainFrame(); | |
182 if (mainFrame->view()) { | |
183 // Sending an event can result in the destruction of the view and pa
rt. | |
184 RefPtr<Clipboard> clipboard = dragData->createClipboard(ClipboardRea
dable); | |
185 clipboard->setSourceOperation(dragData->draggingSourceOperationMask(
)); | |
186 mainFrame->eventHandler()->performDragAndDrop(createMouseEvent(dragD
ata), clipboard.get()); | |
187 clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboar
d here for security | |
188 } | |
189 m_document = 0; | |
190 return true; | |
191 } | |
192 | |
193 if ((m_dragDestinationAction & DragDestinationActionEdit) && concludeDrag(dr
agData, m_dragDestinationAction)) { | |
194 m_document = 0; | |
195 return true; | |
196 } | |
197 | |
198 m_document = 0; | |
199 | |
200 if (operationForLoad(dragData) == DragOperationNone) | |
201 return false; | |
202 | |
203 m_client->willPerformDragDestinationAction(DragDestinationActionLoad, dragDa
ta); | |
204 m_page->mainFrame()->loader()->load(ResourceRequest(dragData->asURL())); | |
205 return true; | |
206 } | |
207 | |
208 DragOperation DragController::dragEnteredOrUpdated(DragData* dragData) | |
209 { | |
210 ASSERT(dragData); | |
211 IntPoint windowPoint = dragData->clientPosition(); | |
212 | |
213 Document* newDraggingDoc = 0; | |
214 if (Frame* frame = m_page->mainFrame()) | |
215 newDraggingDoc = frame->documentAtPoint(windowPoint); | |
216 if (m_document != newDraggingDoc) { | |
217 if (m_document) | |
218 cancelDrag(); | |
219 m_document = newDraggingDoc; | |
220 } | |
221 | |
222 m_dragDestinationAction = m_client->actionMaskForDrag(dragData); | |
223 | |
224 DragOperation operation = DragOperationNone; | |
225 | |
226 if (m_dragDestinationAction == DragDestinationActionNone) | |
227 cancelDrag(); | |
228 else { | |
229 operation = tryDocumentDrag(dragData, m_dragDestinationAction); | |
230 if (operation == DragOperationNone && (m_dragDestinationAction & DragDes
tinationActionLoad)) | |
231 return operationForLoad(dragData); | |
232 } | |
233 | |
234 return operation; | |
235 } | |
236 | |
237 static HTMLInputElement* asFileInput(Node* node) | |
238 { | |
239 ASSERT(node); | |
240 | |
241 // The button for a FILE input is a sub element with no set input type | |
242 // In order to get around this problem we assume any non-FILE input element | |
243 // is this internal button, and try querying the shadow parent node. | |
244 if (node->hasTagName(HTMLNames::inputTag) && node->isShadowNode() && static_
cast<HTMLInputElement*>(node)->inputType() != HTMLInputElement::FILE) | |
245 node = node->shadowParentNode(); | |
246 | |
247 if (!node || !node->hasTagName(HTMLNames::inputTag)) | |
248 return 0; | |
249 | |
250 HTMLInputElement* inputElem = static_cast<HTMLInputElement*>(node); | |
251 if (inputElem->inputType() == HTMLInputElement::FILE) | |
252 return inputElem; | |
253 | |
254 return 0; | |
255 } | |
256 | |
257 DragOperation DragController::tryDocumentDrag(DragData* dragData, DragDestinatio
nAction actionMask) | |
258 { | |
259 ASSERT(dragData); | |
260 | |
261 if (!m_document) | |
262 return DragOperationNone; | |
263 | |
264 DragOperation operation = DragOperationNone; | |
265 if (actionMask & DragDestinationActionDHTML) | |
266 operation = tryDHTMLDrag(dragData); | |
267 m_isHandlingDrag = operation != DragOperationNone; | |
268 | |
269 RefPtr<FrameView> frameView = m_document->view(); | |
270 if (!frameView) | |
271 return operation; | |
272 | |
273 if ((actionMask & DragDestinationActionEdit) && !m_isHandlingDrag && canProc
essDrag(dragData)) { | |
274 if (dragData->containsColor()) | |
275 return DragOperationGeneric; | |
276 | |
277 IntPoint dragPos = dragData->clientPosition(); | |
278 IntPoint point = frameView->windowToContents(dragPos); | |
279 Element* element = m_document->elementFromPoint(point.x(), point.y()); | |
280 ASSERT(element); | |
281 Frame* innerFrame = element->document()->frame(); | |
282 ASSERT(innerFrame); | |
283 if (!asFileInput(element)) { | |
284 Selection dragCaret; | |
285 if (Frame* frame = m_document->frame()) | |
286 dragCaret = frame->visiblePositionForPoint(point); | |
287 m_page->dragCaretController()->setSelection(dragCaret); | |
288 } | |
289 | |
290 return dragIsMove(innerFrame->selection(), dragData) ? DragOperationMove
: DragOperationCopy; | |
291 } | |
292 | |
293 m_page->dragCaretController()->clear(); | |
294 return operation; | |
295 } | |
296 | |
297 DragSourceAction DragController::delegateDragSourceAction(const IntPoint& window
Point) | |
298 { | |
299 m_dragSourceAction = m_client->dragSourceActionMaskForPoint(windowPoint); | |
300 return m_dragSourceAction; | |
301 } | |
302 | |
303 DragOperation DragController::operationForLoad(DragData* dragData) | |
304 { | |
305 ASSERT(dragData); | |
306 Document* doc = 0; | |
307 doc = m_page->mainFrame()->documentAtPoint(dragData->clientPosition()); | |
308 if (doc && (m_didInitiateDrag || doc->isPluginDocument() || (doc->frame() &&
doc->frame()->editor()->clientIsEditable()))) | |
309 return DragOperationNone; | |
310 return dragOperation(dragData); | |
311 } | |
312 | |
313 static bool setSelectionToDragCaret(Frame* frame, Selection& dragCaret, RefPtr<R
ange>& range, const IntPoint& point) | |
314 { | |
315 frame->selection()->setSelection(dragCaret); | |
316 if (frame->selection()->isNone()) { | |
317 dragCaret = frame->visiblePositionForPoint(point); | |
318 frame->selection()->setSelection(dragCaret); | |
319 range = dragCaret.toRange(); | |
320 } | |
321 return !frame->selection()->isNone() && frame->selection()->isContentEditabl
e(); | |
322 } | |
323 | |
324 bool DragController::concludeDrag(DragData* dragData, DragDestinationAction acti
onMask) | |
325 { | |
326 ASSERT(dragData); | |
327 ASSERT(!m_isHandlingDrag); | |
328 ASSERT(actionMask & DragDestinationActionEdit); | |
329 | |
330 if (!m_document) | |
331 return false; | |
332 | |
333 IntPoint point = m_document->view()->windowToContents(dragData->clientPositi
on()); | |
334 Element* element = m_document->elementFromPoint(point.x(), point.y()); | |
335 ASSERT(element); | |
336 Frame* innerFrame = element->ownerDocument()->frame(); | |
337 ASSERT(innerFrame); | |
338 | |
339 if (dragData->containsColor()) { | |
340 Color color = dragData->asColor(); | |
341 if (!color.isValid()) | |
342 return false; | |
343 if (!innerFrame) | |
344 return false; | |
345 RefPtr<Range> innerRange = innerFrame->selection()->toRange(); | |
346 RefPtr<CSSStyleDeclaration> style = m_document->createCSSStyleDeclaratio
n(); | |
347 ExceptionCode ec; | |
348 style->setProperty("color", color.name(), ec); | |
349 if (!innerFrame->editor()->shouldApplyStyle(style.get(), innerRange.get(
))) | |
350 return false; | |
351 m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dr
agData); | |
352 innerFrame->editor()->applyStyle(style.get(), EditActionSetColor); | |
353 return true; | |
354 } | |
355 | |
356 if (!m_page->dragController()->canProcessDrag(dragData)) { | |
357 m_page->dragCaretController()->clear(); | |
358 return false; | |
359 } | |
360 | |
361 if (HTMLInputElement* fileInput = asFileInput(element)) { | |
362 | |
363 if (!fileInput->isEnabled()) | |
364 return false; | |
365 | |
366 if (!dragData->containsFiles()) | |
367 return false; | |
368 | |
369 Vector<String> filenames; | |
370 dragData->asFilenames(filenames); | |
371 if (filenames.isEmpty()) | |
372 return false; | |
373 | |
374 // Ugly. For security none of the API's available to us to set the inpu
t value | |
375 // on file inputs. Even forcing a change in HTMLInputElement doesn't wo
rk as | |
376 // RenderFileUploadControl clears the file when doing updateFromElement(
) | |
377 RenderFileUploadControl* renderer = static_cast<RenderFileUploadControl*
>(fileInput->renderer()); | |
378 | |
379 if (!renderer) | |
380 return false; | |
381 | |
382 // Only take the first filename as <input type="file" /> can only accept
one | |
383 renderer->receiveDroppedFile(filenames[0]); | |
384 return true; | |
385 } | |
386 | |
387 Selection dragCaret(m_page->dragCaretController()->selection()); | |
388 m_page->dragCaretController()->clear(); | |
389 RefPtr<Range> range = dragCaret.toRange(); | |
390 | |
391 // For range to be null a WebKit client must have done something bad while | |
392 // manually controlling drag behaviour | |
393 if (!range) | |
394 return false; | |
395 DocLoader* loader = range->ownerDocument()->docLoader(); | |
396 loader->setAllowStaleResources(true); | |
397 if (dragIsMove(innerFrame->selection(), dragData) || dragCaret.isContentRich
lyEditable()) { | |
398 bool chosePlainText = false; | |
399 RefPtr<DocumentFragment> fragment = documentFragmentFromDragData(dragDat
a, range, true, chosePlainText); | |
400 if (!fragment || !innerFrame->editor()->shouldInsertFragment(fragment, r
ange, EditorInsertActionDropped)) { | |
401 loader->setAllowStaleResources(false); | |
402 return false; | |
403 } | |
404 | |
405 m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dr
agData); | |
406 if (dragIsMove(innerFrame->selection(), dragData)) { | |
407 bool smartMove = innerFrame->selectionGranularity() == WordGranulari
ty | |
408 && innerFrame->editor()->smartInsertDeleteEnabled() | |
409 && dragData->canSmartReplace(); | |
410 applyCommand(MoveSelectionCommand::create(fragment, dragCaret.base()
, smartMove)); | |
411 } else { | |
412 if (setSelectionToDragCaret(innerFrame, dragCaret, range, point)) | |
413 applyCommand(ReplaceSelectionCommand::create(m_document, fragmen
t, true, dragData->canSmartReplace(), chosePlainText)); | |
414 } | |
415 } else { | |
416 String text = dragData->asPlainText(); | |
417 if (text.isEmpty() || !innerFrame->editor()->shouldInsertText(text, rang
e.get(), EditorInsertActionDropped)) { | |
418 loader->setAllowStaleResources(false); | |
419 return false; | |
420 } | |
421 | |
422 m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dr
agData); | |
423 if (setSelectionToDragCaret(innerFrame, dragCaret, range, point)) | |
424 applyCommand(ReplaceSelectionCommand::create(m_document, createFragm
entFromText(range.get(), text), true, false, true)); | |
425 } | |
426 loader->setAllowStaleResources(false); | |
427 | |
428 return true; | |
429 } | |
430 | |
431 | |
432 bool DragController::canProcessDrag(DragData* dragData) | |
433 { | |
434 ASSERT(dragData); | |
435 | |
436 if (!dragData->containsCompatibleContent()) | |
437 return false; | |
438 | |
439 IntPoint point = m_page->mainFrame()->view()->windowToContents(dragData->cli
entPosition()); | |
440 HitTestResult result = HitTestResult(point); | |
441 if (!m_page->mainFrame()->contentRenderer()) | |
442 return false; | |
443 | |
444 result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, tr
ue); | |
445 | |
446 if (!result.innerNonSharedNode()) | |
447 return false; | |
448 | |
449 if (dragData->containsFiles() && asFileInput(result.innerNonSharedNode())) | |
450 return true; | |
451 | |
452 if (!result.innerNonSharedNode()->isContentEditable()) | |
453 return false; | |
454 | |
455 if (m_didInitiateDrag && m_document == m_dragInitiator && result.isSelected(
)) | |
456 return false; | |
457 | |
458 return true; | |
459 } | |
460 | |
461 DragOperation DragController::tryDHTMLDrag(DragData* dragData) | |
462 { | |
463 ASSERT(dragData); | |
464 ASSERT(m_document); | |
465 DragOperation op = DragOperationNone; | |
466 RefPtr<Frame> frame = m_page->mainFrame(); | |
467 RefPtr<FrameView> viewProtector = frame->view(); | |
468 if (!viewProtector) | |
469 return DragOperationNone; | |
470 | |
471 ClipboardAccessPolicy policy = frame->loader()->baseURL().isLocalFile() ? Cl
ipboardReadable : ClipboardTypesReadable; | |
472 RefPtr<Clipboard> clipboard = dragData->createClipboard(policy); | |
473 DragOperation srcOp = dragData->draggingSourceOperationMask(); | |
474 clipboard->setSourceOperation(srcOp); | |
475 | |
476 PlatformMouseEvent event = createMouseEvent(dragData); | |
477 if (frame->eventHandler()->updateDragAndDrop(event, clipboard.get())) { | |
478 // *op unchanged if no source op was set | |
479 if (!clipboard->destinationOperation(op)) { | |
480 // The element accepted but they didn't pick an operation, so we pic
k one for them | |
481 // (as does WinIE). | |
482 if (srcOp & DragOperationCopy) | |
483 op = DragOperationCopy; | |
484 else if (srcOp & DragOperationMove || srcOp & DragOperationGeneric) | |
485 op = DragOperationMove; | |
486 else if (srcOp & DragOperationLink) | |
487 op = DragOperationLink; | |
488 else | |
489 op = DragOperationGeneric; | |
490 } else if (!(op & srcOp)) { | |
491 op = DragOperationNone; | |
492 } | |
493 | |
494 clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard he
re for security | |
495 return op; | |
496 } | |
497 return op; | |
498 } | |
499 | |
500 bool DragController::mayStartDragAtEventLocation(const Frame* frame, const IntPo
int& framePos) | |
501 { | |
502 ASSERT(frame); | |
503 ASSERT(frame->settings()); | |
504 | |
505 if (!frame->view() || !frame->contentRenderer()) | |
506 return false; | |
507 | |
508 HitTestResult mouseDownTarget = HitTestResult(framePos); | |
509 | |
510 mouseDownTarget = frame->eventHandler()->hitTestResultAtPoint(framePos, true
); | |
511 | |
512 if (mouseDownTarget.image() | |
513 && !mouseDownTarget.absoluteImageURL().isEmpty() | |
514 && frame->settings()->loadsImagesAutomatically() | |
515 && m_dragSourceAction & DragSourceActionImage) | |
516 return true; | |
517 | |
518 if (!mouseDownTarget.absoluteLinkURL().isEmpty() | |
519 && m_dragSourceAction & DragSourceActionLink | |
520 && mouseDownTarget.isLiveLink()) | |
521 return true; | |
522 | |
523 if (mouseDownTarget.isSelected() | |
524 && m_dragSourceAction & DragSourceActionSelection) | |
525 return true; | |
526 | |
527 return false; | |
528 | |
529 } | |
530 | |
531 static CachedImage* getCachedImage(Element* element) | |
532 { | |
533 ASSERT(element); | |
534 RenderObject* renderer = element->renderer(); | |
535 if (!renderer || !renderer->isImage()) | |
536 return 0; | |
537 RenderImage* image = static_cast<RenderImage*>(renderer); | |
538 return image->cachedImage(); | |
539 } | |
540 | |
541 static Image* getImage(Element* element) | |
542 { | |
543 ASSERT(element); | |
544 RenderObject* renderer = element->renderer(); | |
545 if (!renderer || !renderer->isImage()) | |
546 return 0; | |
547 | |
548 RenderImage* image = static_cast<RenderImage*>(renderer); | |
549 if (image->cachedImage() && !image->cachedImage()->errorOccurred()) | |
550 return image->cachedImage()->image(); | |
551 return 0; | |
552 } | |
553 | |
554 static void prepareClipboardForImageDrag(Frame* src, Clipboard* clipboard, Eleme
nt* node, const KURL& linkURL, const KURL& imageURL, const String& label) | |
555 { | |
556 RefPtr<Range> range = src->document()->createRange(); | |
557 ExceptionCode ec = 0; | |
558 range->selectNode(node, ec); | |
559 ASSERT(ec == 0); | |
560 src->selection()->setSelection(Selection(range.get(), DOWNSTREAM));
| |
561 clipboard->declareAndWriteDragImage(node, !linkURL.isEmpty() ? linkURL : ima
geURL, label, src); | |
562 } | |
563 | |
564 static IntPoint dragLocForDHTMLDrag(const IntPoint& mouseDraggedPoint, const Int
Point& dragOrigin, const IntPoint& dragImageOffset, bool isLinkImage) | |
565 { | |
566 // dragImageOffset is the cursor position relative to the lower-left corner
of the image. | |
567 #if PLATFORM(MAC) | |
568 // We add in the Y dimension because we are a flipped view, so adding moves
the image down. | |
569 const int yOffset = dragImageOffset.y(); | |
570 #else | |
571 const int yOffset = -dragImageOffset.y(); | |
572 #endif | |
573 | |
574 if (isLinkImage) | |
575 return IntPoint(mouseDraggedPoint.x() - dragImageOffset.x(), mouseDragge
dPoint.y() + yOffset); | |
576 | |
577 return IntPoint(dragOrigin.x() - dragImageOffset.x(), dragOrigin.y() + yOffs
et); | |
578 } | |
579 | |
580 static IntPoint dragLocForSelectionDrag(Frame* src) | |
581 { | |
582 IntRect draggingRect = enclosingIntRect(src->selectionRect()); | |
583 int xpos = draggingRect.right(); | |
584 xpos = draggingRect.x() < xpos ? draggingRect.x() : xpos; | |
585 int ypos = draggingRect.bottom(); | |
586 #if PLATFORM(MAC) | |
587 // Deal with flipped coordinates on Mac | |
588 ypos = draggingRect.y() > ypos ? draggingRect.y() : ypos; | |
589 #else | |
590 ypos = draggingRect.y() < ypos ? draggingRect.y() : ypos; | |
591 #endif | |
592 return IntPoint(xpos, ypos); | |
593 } | |
594 | |
595 bool DragController::startDrag(Frame* src, Clipboard* clipboard, DragOperation s
rcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin, bool isDH
TMLDrag) | |
596 { | |
597 ASSERT(src); | |
598 ASSERT(clipboard); | |
599 | |
600 if (!src->view() || !src->contentRenderer()) | |
601 return false; | |
602 | |
603 HitTestResult dragSource = HitTestResult(dragOrigin); | |
604 dragSource = src->eventHandler()->hitTestResultAtPoint(dragOrigin, true); | |
605 KURL linkURL = dragSource.absoluteLinkURL(); | |
606 KURL imageURL = dragSource.absoluteImageURL(); | |
607 bool isSelected = dragSource.isSelected(); | |
608 | |
609 IntPoint mouseDraggedPoint = src->view()->windowToContents(dragEvent.pos()); | |
610 | |
611 m_draggingImageURL = KURL(); | |
612 m_dragOperation = srcOp; | |
613 | |
614 DragImageRef dragImage = 0; | |
615 IntPoint dragLoc(0, 0); | |
616 IntPoint dragImageOffset(0, 0); | |
617 | |
618 if (isDHTMLDrag) | |
619 dragImage = clipboard->createDragImage(dragImageOffset); | |
620 | |
621 // We allow DHTML/JS to set the drag image, even if its a link, image or tex
t we're dragging. | |
622 // This is in the spirit of the IE API, which allows overriding of pasteboar
d data and DragOp. | |
623 if (dragImage) { | |
624 dragLoc = dragLocForDHTMLDrag(mouseDraggedPoint, dragOrigin, dragImageOf
fset, !linkURL.isEmpty()); | |
625 m_dragOffset = dragImageOffset; | |
626 } | |
627 | |
628 bool startedDrag = true; // optimism - we almost always manage to start the
drag | |
629 | |
630 Node* node = dragSource.innerNonSharedNode(); | |
631 | |
632 if (!imageURL.isEmpty() && node && node->isElementNode() | |
633 && getImage(static_cast<Element*>(node)) | |
634 && (m_dragSourceAction & DragSourceActionImage)) { | |
635 Element* element = static_cast<Element*>(node); | |
636 if (!clipboard->hasData()) { | |
637 m_draggingImageURL = imageURL; | |
638 prepareClipboardForImageDrag(src, clipboard, element, linkURL, image
URL, dragSource.altDisplayString()); | |
639 } | |
640 | |
641 m_client->willPerformDragSourceAction(DragSourceActionImage, dragOrigin,
clipboard); | |
642 | |
643 if (!dragImage) { | |
644 IntRect imageRect = dragSource.imageRect(); | |
645 imageRect.setLocation(m_page->mainFrame()->view()->windowToContents(
src->view()->contentsToWindow(imageRect.location()))); | |
646 doImageDrag(element, dragOrigin, dragSource.imageRect(), clipboard,
src, m_dragOffset); | |
647 } else | |
648 // DHTML defined drag image | |
649 doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false); | |
650 | |
651 } else if (!linkURL.isEmpty() && (m_dragSourceAction & DragSourceActionLink)
) { | |
652 if (!clipboard->hasData()) | |
653 // Simplify whitespace so the title put on the clipboard resembles w
hat the user sees | |
654 // on the web page. This includes replacing newlines with spaces. | |
655 clipboard->writeURL(linkURL, dragSource.textContent().simplifyWhiteS
pace(), src); | |
656 | |
657 m_client->willPerformDragSourceAction(DragSourceActionLink, dragOrigin,
clipboard); | |
658 if (!dragImage) { | |
659 dragImage = m_client->createDragImageForLink(linkURL, dragSource.tex
tContent(), src); | |
660 IntSize size = dragImageSize(dragImage); | |
661 m_dragOffset = IntPoint(-size.width() / 2, -LinkDragBorderInset); | |
662 dragLoc = IntPoint(mouseDraggedPoint.x() + m_dragOffset.x(), mouseDr
aggedPoint.y() + m_dragOffset.y()); | |
663 } | |
664 doSystemDrag(dragImage, dragLoc, mouseDraggedPoint, clipboard, src, true
); | |
665 } else if (isSelected && (m_dragSourceAction & DragSourceActionSelection)) { | |
666 RefPtr<Range> selectionRange = src->selection()->toRange(); | |
667 ASSERT(selectionRange); | |
668 if (!clipboard->hasData()) | |
669 clipboard->writeRange(selectionRange.get(), src); | |
670 m_client->willPerformDragSourceAction(DragSourceActionSelection, dragOri
gin, clipboard); | |
671 if (!dragImage) { | |
672 dragImage = createDragImageForSelection(src); | |
673 dragLoc = dragLocForSelectionDrag(src); | |
674 m_dragOffset = IntPoint((int)(dragOrigin.x() - dragLoc.x()), (int)(d
ragOrigin.y() - dragLoc.y())); | |
675 } | |
676 doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false); | |
677 } else if (isDHTMLDrag) { | |
678 ASSERT(m_dragSourceAction & DragSourceActionDHTML); | |
679 m_client->willPerformDragSourceAction(DragSourceActionDHTML, dragOrigin,
clipboard); | |
680 doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false); | |
681 } else { | |
682 // Only way I know to get here is if to get here is if the original elem
ent clicked on in the mousedown is no longer | |
683 // under the mousedown point, so linkURL, imageURL and isSelected are al
l false/empty. | |
684 startedDrag = false; | |
685 } | |
686 | |
687 if (dragImage) | |
688 deleteDragImage(dragImage); | |
689 return startedDrag; | |
690 } | |
691 | |
692 void DragController::doImageDrag(Element* element, const IntPoint& dragOrigin, c
onst IntRect& rect, Clipboard* clipboard, Frame* frame, IntPoint& dragImageOffse
t) | |
693 { | |
694 IntPoint mouseDownPoint = dragOrigin; | |
695 DragImageRef dragImage; | |
696 IntPoint origin; | |
697 | |
698 Image* image = getImage(element); | |
699 if (image && image->size().height() * image->size().width() <= MaxOriginalIm
ageArea | |
700 && (dragImage = createDragImageFromImage(image))) { | |
701 IntSize originalSize = rect.size(); | |
702 origin = rect.location(); | |
703 | |
704 dragImage = fitDragImageToMaxSize(dragImage, rect.size(), maxDragImageSi
ze()); | |
705 dragImage = dissolveDragImageToFraction(dragImage, DragImageAlpha); | |
706 IntSize newSize = dragImageSize(dragImage); | |
707 | |
708 // Properly orient the drag image and orient it differently if it's smal
ler than the original | |
709 float scale = newSize.width() / (float)originalSize.width(); | |
710 float dx = origin.x() - mouseDownPoint.x(); | |
711 dx *= scale; | |
712 origin.setX((int)(dx + 0.5)); | |
713 #if PLATFORM(MAC) | |
714 //Compensate for accursed flipped coordinates in cocoa | |
715 origin.setY(origin.y() + originalSize.height()); | |
716 #endif | |
717 float dy = origin.y() - mouseDownPoint.y(); | |
718 dy *= scale; | |
719 origin.setY((int)(dy + 0.5)); | |
720 } else { | |
721 dragImage = createDragImageIconForCachedImage(getCachedImage(element)); | |
722 if (dragImage) | |
723 origin = IntPoint(DragIconRightInset - dragImageSize(dragImage).widt
h(), DragIconBottomInset); | |
724 } | |
725 | |
726 dragImageOffset.setX(mouseDownPoint.x() + origin.x()); | |
727 dragImageOffset.setY(mouseDownPoint.y() + origin.y()); | |
728 doSystemDrag(dragImage, dragImageOffset, dragOrigin, clipboard, frame, false
); | |
729 | |
730 deleteDragImage(dragImage); | |
731 } | |
732 | |
733 void DragController::doSystemDrag(DragImageRef image, const IntPoint& dragLoc, c
onst IntPoint& eventPos, Clipboard* clipboard, Frame* frame, bool forLink) | |
734 { | |
735 m_didInitiateDrag = true; | |
736 m_dragInitiator = frame->document(); | |
737 // Protect this frame and view, as a load may occur mid drag and attempt to
unload this frame | |
738 RefPtr<Frame> frameProtector = m_page->mainFrame(); | |
739 RefPtr<FrameView> viewProtector = frameProtector->view(); | |
740 m_client->startDrag(image, viewProtector->windowToContents(frame->view()->co
ntentsToWindow(dragLoc)), | |
741 viewProtector->windowToContents(frame->view()->contentsToWindow(eventPos
)), clipboard, frameProtector.get(), forLink); | |
742 | |
743 // Drag has ended, dragEnded *should* have been called, however it is possib
le | |
744 // for the UIDelegate to take over the drag, and fail to send the appropriat
e | |
745 // drag termination event. As dragEnded just resets drag variables, we just
| |
746 // call it anyway to be on the safe side | |
747 // Except if drag and drop happens asynchronously, in which case dragging | |
748 // has not ended. | |
749 //dragEnded(); | |
750 } | |
751 | |
752 // Manual drag caret manipulation | |
753 void DragController::placeDragCaret(const IntPoint& windowPoint) | |
754 { | |
755 Frame* mainFrame = m_page->mainFrame(); | |
756 Document* newDraggingDoc = mainFrame->documentAtPoint(windowPoint); | |
757 if (m_document != newDraggingDoc) { | |
758 if (m_document) | |
759 cancelDrag(); | |
760 m_document = newDraggingDoc; | |
761 } | |
762 if (!m_document) | |
763 return; | |
764 Frame* frame = m_document->frame(); | |
765 ASSERT(frame); | |
766 FrameView* frameView = frame->view(); | |
767 if (!frameView) | |
768 return; | |
769 IntPoint framePoint = frameView->windowToContents(windowPoint); | |
770 Selection dragCaret(frame->visiblePositionForPoint(framePoint)); | |
771 m_page->dragCaretController()->setSelection(dragCaret); | |
772 } | |
773 | |
774 } | |
OLD | NEW |