bool DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction actionMask, DragOperation& operation) { ASSERT(dragData); if (!m_documentUnderMouse) return false; m_isHandlingDrag = false; if (actionMask & DragDestinationActionDHTML) { m_isHandlingDrag = tryDHTMLDrag(dragData, operation); // 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), // which could process dragleave event and reset m_documentUnderMouse in // dragExited. if (!m_documentUnderMouse) return false; } // It's unclear why this check is after tryDHTMLDrag. // We send drag events in tryDHTMLDrag and that may be the reason. RefPtr<FrameView> frameView = m_documentUnderMouse->view(); if (!frameView) return false; if (m_isHandlingDrag) { m_page->dragCaretController()->clear(); return true; } else if ((actionMask & DragDestinationActionEdit) && canProcessDrag(dragData)) { if (dragData->containsColor()) { operation = DragOperationGeneric; return true; } IntPoint point = frameView->windowToContents(dragData->clientPosition()); Element* element = elementUnderMouse(m_documentUnderMouse, point); if (!asFileInput(element)) { VisibleSelection dragCaret = m_documentUnderMouse->frame()->visiblePositionForPoint(point); m_page->dragCaretController()->setSelection(dragCaret); } Frame* innerFrame = element->document()->frame(); operation = dragIsMove(innerFrame->selection()) ? DragOperationMove : DragOperationCopy; return true; } // If we're not over an editable region, make sure we're clearing any prior drag cursor. m_page->dragCaretController()->clear(); return false; }
DragOperation DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction actionMask) { ASSERT(dragData); if (!m_document) return DragOperationNone; DragOperation operation = DragOperationNone; if (actionMask & DragDestinationActionDHTML) operation = tryDHTMLDrag(dragData); m_isHandlingDrag = operation != DragOperationNone; RefPtr<FrameView> frameView = m_document->view(); if (!frameView) return operation; if ((actionMask & DragDestinationActionEdit) && !m_isHandlingDrag && canProcessDrag(dragData)) { if (dragData->containsColor()) return DragOperationGeneric; IntPoint dragPos = dragData->clientPosition(); IntPoint point = frameView->windowToContents(dragPos); Element* element = m_document->elementFromPoint(point.x(), point.y()); ASSERT(element); Frame* innerFrame = element->document()->frame(); ASSERT(innerFrame); if (!asFileInput(element)) { Selection dragCaret; if (Frame* frame = m_document->frame()) dragCaret = frame->visiblePositionForPoint(point); m_page->dragCaretController()->setSelection(dragCaret); } return dragIsMove(innerFrame->selection()) ? DragOperationMove : DragOperationCopy; } m_page->dragCaretController()->clear(); return operation; }
bool DragController::concludeEditDrag(DragData* dragData) { ASSERT(dragData); RefPtr<HTMLInputElement> fileInput = m_fileInputElementUnderMouse; if (m_fileInputElementUnderMouse) { m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false); m_fileInputElementUnderMouse = 0; } if (!m_documentUnderMouse) return false; IntPoint point = m_documentUnderMouse->view()->windowToContents(dragData->clientPosition()); Element* element = elementUnderMouse(m_documentUnderMouse.get(), point); if (!element) return false; Frame* innerFrame = element->ownerDocument()->frame(); ASSERT(innerFrame); if (m_page->dragCaretController()->hasCaret() && !dispatchTextInputEventFor(innerFrame, dragData)) return true; if (dragData->containsColor()) { Color color = dragData->asColor(); if (!color.isValid()) return false; RefPtr<Range> innerRange = innerFrame->selection()->toNormalizedRange(); RefPtr<StylePropertySet> style = StylePropertySet::create(); style->setProperty(CSSPropertyColor, color.serialized(), false); if (!innerFrame->editor()->shouldApplyStyle(style.get(), innerRange.get())) return false; m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData); innerFrame->editor()->applyStyle(style.get(), EditActionSetColor); return true; } 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()); if (fileInput->disabled()) return false; Vector<String> filenames; dragData->asFilenames(filenames); if (filenames.isEmpty()) return false; fileInput->receiveDroppedFiles(filenames); m_client->willPerformDragDestinationAction(DragDestinationActionUpload, dragData); return true; } if (!m_page->dragController()->canProcessDrag(dragData)) { m_page->dragCaretController()->clear(); return false; } VisibleSelection dragCaret = m_page->dragCaretController()->caretPosition(); m_page->dragCaretController()->clear(); RefPtr<Range> range = dragCaret.toNormalizedRange(); RefPtr<Element> rootEditableElement = innerFrame->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); if (dragIsMove(innerFrame->selection(), dragData) || dragCaret.isContentRichlyEditable()) { bool chosePlainText = false; RefPtr<DocumentFragment> fragment = documentFragmentFromDragData(dragData, innerFrame, range, true, chosePlainText); if (!fragment || !innerFrame->editor()->shouldInsertFragment(fragment, range, EditorInsertActionDropped)) { return false; } 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(); applyCommand(MoveSelectionCommand::create(fragment, dragCaret.base(), smartInsert, smartDelete)); } else { if (setSelectionToDragCaret(innerFrame, dragCaret, range, point)) { ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::PreventNesting; if (dragData->canSmartReplace()) options |= ReplaceSelectionCommand::SmartReplace; if (chosePlainText) options |= ReplaceSelectionCommand::MatchStyle; applyCommand(ReplaceSelectionCommand::create(m_documentUnderMouse.get(), fragment, options)); } } } else { String text = dragData->asPlainText(innerFrame); if (text.isEmpty() || !innerFrame->editor()->shouldInsertText(text, range.get(), EditorInsertActionDropped)) { return false; } m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData); if (setSelectionToDragCaret(innerFrame, dragCaret, range, point)) applyCommand(ReplaceSelectionCommand::create(m_documentUnderMouse.get(), createFragmentFromText(range.get(), text), ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting)); } if (rootEditableElement) { if (Frame* frame = rootEditableElement->document()->frame()) frame->eventHandler()->updateDragStateAfterEditDragIfNeeded(rootEditableElement.get()); } return true; }
bool DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction actionMask, DragSession& dragSession) { ASSERT(dragData); if (!m_documentUnderMouse) return false; if (m_dragInitiator && !m_documentUnderMouse->securityOrigin()->canReceiveDragData(m_dragInitiator->securityOrigin())) return false; m_isHandlingDrag = false; if (actionMask & DragDestinationActionDHTML) { m_isHandlingDrag = tryDHTMLDrag(dragData, dragSession.operation); // 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), // which could process dragleave event and reset m_documentUnderMouse in // dragExited. if (!m_documentUnderMouse) return false; } // It's unclear why this check is after tryDHTMLDrag. // We send drag events in tryDHTMLDrag and that may be the reason. RefPtr<FrameView> frameView = m_documentUnderMouse->view(); if (!frameView) return false; if (m_isHandlingDrag) { m_page->dragCaretController()->clear(); return true; } else if ((actionMask & DragDestinationActionEdit) && canProcessDrag(dragData)) { if (dragData->containsColor()) { dragSession.operation = DragOperationGeneric; return true; } IntPoint point = frameView->windowToContents(dragData->clientPosition()); Element* element = elementUnderMouse(m_documentUnderMouse.get(), point); if (!element) return false; HTMLInputElement* elementAsFileInput = asFileInput(element); if (m_fileInputElementUnderMouse != elementAsFileInput) { if (m_fileInputElementUnderMouse) m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false); m_fileInputElementUnderMouse = elementAsFileInput; } if (!m_fileInputElementUnderMouse) m_page->dragCaretController()->setCaretPosition(m_documentUnderMouse->frame()->visiblePositionForPoint(point)); Frame* innerFrame = element->document()->frame(); dragSession.operation = dragIsMove(innerFrame->selection(), dragData) ? DragOperationMove : DragOperationCopy; dragSession.mouseIsOverFileInput = m_fileInputElementUnderMouse; dragSession.numberOfItemsToBeAccepted = 0; unsigned numberOfFiles = dragData->numberOfFiles(); if (m_fileInputElementUnderMouse) { if (m_fileInputElementUnderMouse->disabled()) dragSession.numberOfItemsToBeAccepted = 0; else if (m_fileInputElementUnderMouse->multiple()) dragSession.numberOfItemsToBeAccepted = numberOfFiles; else if (numberOfFiles > 1) dragSession.numberOfItemsToBeAccepted = 0; else dragSession.numberOfItemsToBeAccepted = 1; if (!dragSession.numberOfItemsToBeAccepted) dragSession.operation = DragOperationNone; m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(dragSession.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; } return true; } // We are not over an editable region. Make sure we're clearing any prior drag cursor. m_page->dragCaretController()->clear(); if (m_fileInputElementUnderMouse) m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false); m_fileInputElementUnderMouse = 0; return false; }
bool DragController::concludeEditDrag(DragData* dragData) { ASSERT(dragData); ASSERT(!m_isHandlingDrag); if (!m_documentUnderMouse) return false; IntPoint point = m_documentUnderMouse->view()->windowToContents(dragData->clientPosition()); Element* element = elementUnderMouse(m_documentUnderMouse, point); Frame* innerFrame = element->ownerDocument()->frame(); ASSERT(innerFrame); if (dragData->containsColor()) { Color color = dragData->asColor(); if (!color.isValid()) return false; if (!innerFrame) return false; RefPtr<Range> innerRange = innerFrame->selection()->toNormalizedRange(); RefPtr<CSSStyleDeclaration> style = m_documentUnderMouse->createCSSStyleDeclaration(); ExceptionCode ec; style->setProperty("color", color.name(), ec); if (!innerFrame->editor()->shouldApplyStyle(style.get(), innerRange.get())) return false; m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData); innerFrame->editor()->applyStyle(style.get(), EditActionSetColor); return true; } if (!m_page->dragController()->canProcessDrag(dragData)) { m_page->dragCaretController()->clear(); return false; } if (HTMLInputElement* fileInput = asFileInput(element)) { if (!fileInput->isEnabledFormControl()) return false; if (!dragData->containsFiles()) return false; Vector<String> filenames; dragData->asFilenames(filenames); if (filenames.isEmpty()) return false; // Ugly. For security none of the APIs available to us can set the input value // on file inputs. Even forcing a change in HTMLInputElement doesn't work as // RenderFileUploadControl clears the file when doing updateFromElement(). RenderFileUploadControl* renderer = toRenderFileUploadControl(fileInput->renderer()); if (!renderer) return false; renderer->receiveDroppedFiles(filenames); return true; } VisibleSelection dragCaret(m_page->dragCaretController()->selection()); m_page->dragCaretController()->clear(); RefPtr<Range> range = dragCaret.toNormalizedRange(); // For range to be null a WebKit client must have done something bad while // manually controlling drag behaviour if (!range) return false; DocLoader* loader = range->ownerDocument()->docLoader(); loader->setAllowStaleResources(true); if (dragIsMove(innerFrame->selection()) || dragCaret.isContentRichlyEditable()) { bool chosePlainText = false; RefPtr<DocumentFragment> fragment = documentFragmentFromDragData(dragData, range, true, chosePlainText); if (!fragment || !innerFrame->editor()->shouldInsertFragment(fragment, range, EditorInsertActionDropped)) { loader->setAllowStaleResources(false); return false; } m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData); if (dragIsMove(innerFrame->selection())) { bool smartMove = innerFrame->selectionGranularity() == WordGranularity && innerFrame->editor()->smartInsertDeleteEnabled() && dragData->canSmartReplace(); applyCommand(MoveSelectionCommand::create(fragment, dragCaret.base(), smartMove)); } else { if (setSelectionToDragCaret(innerFrame, dragCaret, range, point)) applyCommand(ReplaceSelectionCommand::create(m_documentUnderMouse, fragment, true, dragData->canSmartReplace(), chosePlainText)); } } else { String text = dragData->asPlainText(); if (text.isEmpty() || !innerFrame->editor()->shouldInsertText(text, range.get(), EditorInsertActionDropped)) { loader->setAllowStaleResources(false); return false; } m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData); if (setSelectionToDragCaret(innerFrame, dragCaret, range, point)) applyCommand(ReplaceSelectionCommand::create(m_documentUnderMouse, createFragmentFromText(range.get(), text), true, false, true)); } loader->setAllowStaleResources(false); return true; }