nsresult nsBaseDragService::DrawDrag(nsIDOMNode* aDOMNode, nsIScriptableRegion* aRegion, CSSIntPoint aScreenPosition, LayoutDeviceIntRect* aScreenDragRect, RefPtr<SourceSurface>* aSurface, nsPresContext** aPresContext) { *aSurface = nullptr; *aPresContext = nullptr; // use a default size, in case of an error. aScreenDragRect->SetRect(aScreenPosition.x - mImageOffset.x, aScreenPosition.y - mImageOffset.y, 1, 1); // if a drag image was specified, use that, otherwise, use the source node nsCOMPtr<nsIDOMNode> dragNode = mImage ? mImage.get() : aDOMNode; // get the presshell for the node being dragged. If the drag image is not in // a document or has no frame, get the presshell from the source drag node nsIPresShell* presShell = GetPresShellForContent(dragNode); if (!presShell && mImage) presShell = GetPresShellForContent(aDOMNode); if (!presShell) return NS_ERROR_FAILURE; *aPresContext = presShell->GetPresContext(); nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(dragNode); if (flo) { RefPtr<nsFrameLoader> fl = flo->GetFrameLoader(); if (fl) { auto* tp = static_cast<mozilla::dom::TabParent*>(fl->GetRemoteBrowser()); if (tp && tp->TakeDragVisualization(*aSurface, aScreenDragRect)) { if (mImage) { // Just clear the surface if chrome has overridden it with an image. *aSurface = nullptr; } return NS_OK; } } } // convert mouse position to dev pixels of the prescontext CSSIntPoint screenPosition(aScreenPosition); screenPosition.x -= mImageOffset.x; screenPosition.y -= mImageOffset.y; LayoutDeviceIntPoint screenPoint = ConvertToUnscaledDevPixels(*aPresContext, screenPosition); aScreenDragRect->MoveTo(screenPoint.x, screenPoint.y); // check if drag images are disabled bool enableDragImages = Preferences::GetBool(DRAGIMAGES_PREF, true); // didn't want an image, so just set the screen rectangle to the frame size if (!enableDragImages || !mHasImage) { // if a region was specified, set the screen rectangle to the area that // the region occupies CSSIntRect dragRect; if (aRegion) { // the region's coordinates are relative to the root frame int32_t dragRectX, dragRectY, dragRectW, dragRectH; aRegion->GetBoundingBox(&dragRectX, &dragRectY, &dragRectW, &dragRectH); dragRect.SetRect(dragRectX, dragRectY, dragRectW, dragRectH); nsIFrame* rootFrame = presShell->GetRootFrame(); CSSIntRect screenRect = rootFrame->GetScreenRect(); dragRect.MoveBy(screenRect.TopLeft()); } else { // otherwise, there was no region so just set the rectangle to // the size of the primary frame of the content. nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode); nsIFrame* frame = content->GetPrimaryFrame(); if (frame) { dragRect = frame->GetScreenRect(); } } nsIntRect dragRectDev = ToAppUnits(dragRect, nsPresContext::AppUnitsPerCSSPixel()). ToOutsidePixels((*aPresContext)->AppUnitsPerDevPixel()); aScreenDragRect->SizeTo(dragRectDev.Width(), dragRectDev.Height()); return NS_OK; } // draw the image for selections if (mSelection) { LayoutDeviceIntPoint pnt(aScreenDragRect->TopLeft()); *aSurface = presShell->RenderSelection(mSelection, pnt, aScreenDragRect, mImage ? 0 : nsIPresShell::RENDER_AUTO_SCALE); return NS_OK; } // if a custom image was specified, check if it is an image node and draw // using the source rather than the displayed image. But if mImage isn't // an image or canvas, fall through to RenderNode below. if (mImage) { nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode); HTMLCanvasElement *canvas = HTMLCanvasElement::FromContentOrNull(content); if (canvas) { return DrawDragForImage(*aPresContext, nullptr, canvas, aScreenDragRect, aSurface); } nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(dragNode); // for image nodes, create the drag image from the actual image data if (imageLoader) { return DrawDragForImage(*aPresContext, imageLoader, nullptr, aScreenDragRect, aSurface); } // If the image is a popup, use that as the image. This allows custom drag // images that can change during the drag, but means that any platform // default image handling won't occur. // XXXndeakin this should be chrome-only nsIFrame* frame = content->GetPrimaryFrame(); if (frame && frame->IsMenuPopupFrame()) { mDragPopup = content; } } if (!mDragPopup) { // otherwise, just draw the node nsIntRegion clipRegion; uint32_t renderFlags = mImage ? 0 : nsIPresShell::RENDER_AUTO_SCALE; if (aRegion) { aRegion->GetRegion(&clipRegion); } if (renderFlags) { nsCOMPtr<nsINode> dragINode = do_QueryInterface(dragNode); // check if the dragged node itself is an img element if (dragINode->NodeName().LowerCaseEqualsLiteral("img")) { renderFlags = renderFlags | nsIPresShell::RENDER_IS_IMAGE; } else { nsINodeList* childList = dragINode->ChildNodes(); uint32_t length = childList->Length(); // check every childnode for being an img element // XXXbz why don't we need to check descendants recursively? for (uint32_t count = 0; count < length; ++count) { if (childList->Item(count)->NodeName().LowerCaseEqualsLiteral("img")) { // if the dragnode contains an image, set RENDER_IS_IMAGE flag renderFlags = renderFlags | nsIPresShell::RENDER_IS_IMAGE; break; } } } } LayoutDeviceIntPoint pnt(aScreenDragRect->TopLeft()); *aSurface = presShell->RenderNode(dragNode, aRegion ? &clipRegion : nullptr, pnt, aScreenDragRect, renderFlags); } // If an image was specified, reset the position from the offset that was supplied. if (mImage) { aScreenDragRect->MoveTo(screenPoint.x, screenPoint.y); } return NS_OK; }