Bug 1527661 [wpt PR 15356] - Update wpt metadata, a=testonly
[gecko.git] / widget / nsBaseDragService.cpp
blob62c695174a65e295427e45b8dd1f46f2fc26448d
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsBaseDragService.h"
7 #include "nsITransferable.h"
9 #include "nsArrayUtils.h"
10 #include "nsIServiceManager.h"
11 #include "nsITransferable.h"
12 #include "nsSize.h"
13 #include "nsXPCOM.h"
14 #include "nsISupportsPrimitives.h"
15 #include "nsCOMPtr.h"
16 #include "nsIInterfaceRequestorUtils.h"
17 #include "nsIFrame.h"
18 #include "nsFrameLoaderOwner.h"
19 #include "mozilla/dom/Document.h"
20 #include "nsIContent.h"
21 #include "nsIPresShell.h"
22 #include "nsViewManager.h"
23 #include "nsINode.h"
24 #include "nsPresContext.h"
25 #include "nsIImageLoadingContent.h"
26 #include "imgIContainer.h"
27 #include "imgIRequest.h"
28 #include "ImageRegion.h"
29 #include "nsQueryObject.h"
30 #include "nsRegion.h"
31 #include "nsXULPopupManager.h"
32 #include "nsMenuPopupFrame.h"
33 #include "SVGImageContext.h"
34 #ifdef MOZ_XUL
35 # include "nsTreeBodyFrame.h"
36 #endif
37 #include "mozilla/MouseEvents.h"
38 #include "mozilla/Preferences.h"
39 #include "mozilla/dom/BindingDeclarations.h"
40 #include "mozilla/dom/DataTransferItemList.h"
41 #include "mozilla/dom/DataTransfer.h"
42 #include "mozilla/dom/DragEvent.h"
43 #include "mozilla/dom/MouseEventBinding.h"
44 #include "mozilla/dom/Selection.h"
45 #include "mozilla/gfx/2D.h"
46 #include "mozilla/Unused.h"
47 #include "nsFrameLoader.h"
48 #include "TabParent.h"
50 #include "gfxContext.h"
51 #include "gfxPlatform.h"
52 #include <algorithm>
54 using namespace mozilla;
55 using namespace mozilla::dom;
56 using namespace mozilla::gfx;
57 using namespace mozilla::image;
59 #define DRAGIMAGES_PREF "nglayout.enable_drag_images"
61 nsBaseDragService::nsBaseDragService()
62 : mCanDrop(false),
63 mOnlyChromeDrop(false),
64 mDoingDrag(false),
65 mHasImage(false),
66 mUserCancelled(false),
67 mDragEventDispatchedToChildProcess(false),
68 mDragAction(DRAGDROP_ACTION_NONE),
69 mDragActionFromChildProcess(DRAGDROP_ACTION_UNINITIALIZED),
70 mContentPolicyType(nsIContentPolicy::TYPE_OTHER),
71 mSuppressLevel(0),
72 mInputSource(MouseEvent_Binding::MOZ_SOURCE_MOUSE) {}
74 nsBaseDragService::~nsBaseDragService() = default;
76 NS_IMPL_ISUPPORTS(nsBaseDragService, nsIDragService, nsIDragSession)
78 //---------------------------------------------------------
79 NS_IMETHODIMP
80 nsBaseDragService::SetCanDrop(bool aCanDrop) {
81 mCanDrop = aCanDrop;
82 return NS_OK;
85 //---------------------------------------------------------
86 NS_IMETHODIMP
87 nsBaseDragService::GetCanDrop(bool* aCanDrop) {
88 *aCanDrop = mCanDrop;
89 return NS_OK;
91 //---------------------------------------------------------
92 NS_IMETHODIMP
93 nsBaseDragService::SetOnlyChromeDrop(bool aOnlyChrome) {
94 mOnlyChromeDrop = aOnlyChrome;
95 return NS_OK;
98 //---------------------------------------------------------
99 NS_IMETHODIMP
100 nsBaseDragService::GetOnlyChromeDrop(bool* aOnlyChrome) {
101 *aOnlyChrome = mOnlyChromeDrop;
102 return NS_OK;
105 //---------------------------------------------------------
106 NS_IMETHODIMP
107 nsBaseDragService::SetDragAction(uint32_t anAction) {
108 mDragAction = anAction;
109 return NS_OK;
112 //---------------------------------------------------------
113 NS_IMETHODIMP
114 nsBaseDragService::GetDragAction(uint32_t* anAction) {
115 *anAction = mDragAction;
116 return NS_OK;
119 //-------------------------------------------------------------------------
121 NS_IMETHODIMP
122 nsBaseDragService::GetNumDropItems(uint32_t* aNumItems) {
123 *aNumItems = 0;
124 return NS_ERROR_FAILURE;
128 // GetSourceDocument
130 // Returns the DOM document where the drag was initiated. This will be
131 // nullptr if the drag began outside of our application.
133 NS_IMETHODIMP
134 nsBaseDragService::GetSourceDocument(Document** aSourceDocument) {
135 *aSourceDocument = mSourceDocument.get();
136 NS_IF_ADDREF(*aSourceDocument);
138 return NS_OK;
142 // GetSourceNode
144 // Returns the DOM node where the drag was initiated. This will be
145 // nullptr if the drag began outside of our application.
147 NS_IMETHODIMP
148 nsBaseDragService::GetSourceNode(nsINode** aSourceNode) {
149 *aSourceNode = do_AddRef(mSourceNode).take();
150 return NS_OK;
153 NS_IMETHODIMP
154 nsBaseDragService::GetTriggeringPrincipal(nsIPrincipal** aPrincipal) {
155 NS_IF_ADDREF(*aPrincipal = mTriggeringPrincipal);
156 return NS_OK;
159 NS_IMETHODIMP
160 nsBaseDragService::SetTriggeringPrincipal(nsIPrincipal* aPrincipal) {
161 mTriggeringPrincipal = aPrincipal;
162 return NS_OK;
165 //-------------------------------------------------------------------------
167 NS_IMETHODIMP
168 nsBaseDragService::GetData(nsITransferable* aTransferable,
169 uint32_t aItemIndex) {
170 return NS_ERROR_FAILURE;
173 //-------------------------------------------------------------------------
174 NS_IMETHODIMP
175 nsBaseDragService::IsDataFlavorSupported(const char* aDataFlavor,
176 bool* _retval) {
177 return NS_ERROR_FAILURE;
180 NS_IMETHODIMP
181 nsBaseDragService::GetDataTransferXPCOM(DataTransfer** aDataTransfer) {
182 *aDataTransfer = mDataTransfer;
183 NS_IF_ADDREF(*aDataTransfer);
184 return NS_OK;
187 NS_IMETHODIMP
188 nsBaseDragService::SetDataTransferXPCOM(DataTransfer* aDataTransfer) {
189 NS_ENSURE_STATE(aDataTransfer);
190 mDataTransfer = aDataTransfer;
191 return NS_OK;
194 DataTransfer* nsBaseDragService::GetDataTransfer() { return mDataTransfer; }
196 void nsBaseDragService::SetDataTransfer(DataTransfer* aDataTransfer) {
197 mDataTransfer = aDataTransfer;
200 //-------------------------------------------------------------------------
201 NS_IMETHODIMP
202 nsBaseDragService::InvokeDragSession(
203 nsINode* aDOMNode, nsIPrincipal* aPrincipal, nsIArray* aTransferableArray,
204 uint32_t aActionType,
205 nsContentPolicyType aContentPolicyType = nsIContentPolicy::TYPE_OTHER) {
206 AUTO_PROFILER_LABEL("nsBaseDragService::InvokeDragSession", OTHER);
208 NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG);
209 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
211 // stash the document of the dom node
212 mSourceDocument = aDOMNode->OwnerDoc();
213 mTriggeringPrincipal = aPrincipal;
214 mSourceNode = aDOMNode;
215 mContentPolicyType = aContentPolicyType;
216 mEndDragPoint = LayoutDeviceIntPoint(0, 0);
218 // When the mouse goes down, the selection code starts a mouse
219 // capture. However, this gets in the way of determining drag
220 // feedback for things like trees because the event coordinates
221 // are in the wrong coord system, so turn off mouse capture.
222 nsIPresShell::ClearMouseCapture(nullptr);
224 uint32_t length = 0;
225 mozilla::Unused << aTransferableArray->GetLength(&length);
226 for (uint32_t i = 0; i < length; ++i) {
227 nsCOMPtr<nsITransferable> trans = do_QueryElementAt(aTransferableArray, i);
228 if (trans) {
229 // Set the requestingPrincipal on the transferable.
230 trans->SetRequestingPrincipal(mSourceNode->NodePrincipal());
231 trans->SetContentPolicyType(mContentPolicyType);
235 nsresult rv = InvokeDragSessionImpl(aTransferableArray, mRegion, aActionType);
237 if (NS_FAILED(rv)) {
238 // Set mDoingDrag so that EndDragSession cleans up and sends the dragend
239 // event after the aborted drag.
240 mDoingDrag = true;
241 EndDragSession(true, 0);
244 return rv;
247 NS_IMETHODIMP
248 nsBaseDragService::InvokeDragSessionWithImage(
249 nsINode* aDOMNode, nsIPrincipal* aPrincipal, nsIArray* aTransferableArray,
250 uint32_t aActionType, nsINode* aImage, int32_t aImageX, int32_t aImageY,
251 DragEvent* aDragEvent, DataTransfer* aDataTransfer) {
252 NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
253 NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER);
254 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
256 mDataTransfer = aDataTransfer;
257 mSelection = nullptr;
258 mHasImage = true;
259 mDragPopup = nullptr;
260 mImage = aImage;
261 mImageOffset = CSSIntPoint(aImageX, aImageY);
263 mScreenPosition.x = aDragEvent->ScreenX(CallerType::System);
264 mScreenPosition.y = aDragEvent->ScreenY(CallerType::System);
265 mInputSource = aDragEvent->MozInputSource();
267 // If dragging within a XUL tree and no custom drag image was
268 // set, the region argument to InvokeDragSessionWithImage needs
269 // to be set to the area encompassing the selected rows of the
270 // tree to ensure that the drag feedback gets clipped to those
271 // rows. For other content, region should be null.
272 mRegion = Nothing();
273 #ifdef MOZ_XUL
274 if (aDOMNode && aDOMNode->IsContent() && !aImage) {
275 if (aDOMNode->NodeInfo()->Equals(nsGkAtoms::treechildren,
276 kNameSpaceID_XUL)) {
277 nsTreeBodyFrame* treeBody =
278 do_QueryFrame(aDOMNode->AsContent()->GetPrimaryFrame());
279 if (treeBody) {
280 mRegion = treeBody->GetSelectionRegion();
284 #endif
286 nsresult rv =
287 InvokeDragSession(aDOMNode, aPrincipal, aTransferableArray, aActionType,
288 nsIContentPolicy::TYPE_INTERNAL_IMAGE);
289 mRegion = Nothing();
290 return rv;
293 NS_IMETHODIMP
294 nsBaseDragService::InvokeDragSessionWithSelection(Selection* aSelection,
295 nsIPrincipal* aPrincipal,
296 nsIArray* aTransferableArray,
297 uint32_t aActionType,
298 DragEvent* aDragEvent,
299 DataTransfer* aDataTransfer) {
300 NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
301 NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
302 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
304 mDataTransfer = aDataTransfer;
305 mSelection = aSelection;
306 mHasImage = true;
307 mDragPopup = nullptr;
308 mImage = nullptr;
309 mImageOffset = CSSIntPoint();
310 mRegion = Nothing();
312 mScreenPosition.x = aDragEvent->ScreenX(CallerType::System);
313 mScreenPosition.y = aDragEvent->ScreenY(CallerType::System);
314 mInputSource = aDragEvent->MozInputSource();
316 // just get the focused node from the selection
317 // XXXndeakin this should actually be the deepest node that contains both
318 // endpoints of the selection
319 nsCOMPtr<nsINode> node = aSelection->GetFocusNode();
321 return InvokeDragSession(node, aPrincipal, aTransferableArray, aActionType,
322 nsIContentPolicy::TYPE_OTHER);
325 //-------------------------------------------------------------------------
326 NS_IMETHODIMP
327 nsBaseDragService::GetCurrentSession(nsIDragSession** aSession) {
328 if (!aSession) return NS_ERROR_INVALID_ARG;
330 // "this" also implements a drag session, so say we are one but only
331 // if there is currently a drag going on.
332 if (!mSuppressLevel && mDoingDrag) {
333 *aSession = this;
334 NS_ADDREF(*aSession); // addRef because we're a "getter"
335 } else
336 *aSession = nullptr;
338 return NS_OK;
341 //-------------------------------------------------------------------------
342 NS_IMETHODIMP
343 nsBaseDragService::StartDragSession() {
344 if (mDoingDrag) {
345 return NS_ERROR_FAILURE;
347 mDoingDrag = true;
348 // By default dispatch drop also to content.
349 mOnlyChromeDrop = false;
351 return NS_OK;
354 void nsBaseDragService::OpenDragPopup() {
355 if (mDragPopup) {
356 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
357 if (pm) {
358 pm->ShowPopupAtScreen(mDragPopup, mScreenPosition.x - mImageOffset.x,
359 mScreenPosition.y - mImageOffset.y, false, nullptr);
364 int32_t nsBaseDragService::TakeChildProcessDragAction() {
365 // If the last event was dispatched to the child process, use the drag action
366 // assigned from it instead and return it. DRAGDROP_ACTION_UNINITIALIZED is
367 // returned otherwise.
368 int32_t retval = DRAGDROP_ACTION_UNINITIALIZED;
369 if (TakeDragEventDispatchedToChildProcess() &&
370 mDragActionFromChildProcess != DRAGDROP_ACTION_UNINITIALIZED) {
371 retval = mDragActionFromChildProcess;
374 return retval;
377 //-------------------------------------------------------------------------
378 NS_IMETHODIMP
379 nsBaseDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers) {
380 if (!mDoingDrag) {
381 return NS_ERROR_FAILURE;
384 if (aDoneDrag && !mSuppressLevel) {
385 FireDragEventAtSource(eDragEnd, aKeyModifiers);
388 if (mDragPopup) {
389 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
390 if (pm) {
391 pm->HidePopup(mDragPopup, false, true, false, false);
395 for (uint32_t i = 0; i < mChildProcesses.Length(); ++i) {
396 mozilla::Unused << mChildProcesses[i]->SendEndDragSession(
397 aDoneDrag, mUserCancelled, mEndDragPoint, aKeyModifiers);
398 // Continue sending input events with input priority when stopping the dnd
399 // session.
400 mChildProcesses[i]->SetInputPriorityEventEnabled(true);
402 mChildProcesses.Clear();
404 // mDataTransfer and the items it owns are going to die anyway, but we
405 // explicitly deref the contained data here so that we don't have to wait for
406 // CC to reclaim the memory.
407 if (XRE_IsParentProcess()) {
408 DiscardInternalTransferData();
411 mDoingDrag = false;
412 mCanDrop = false;
414 // release the source we've been holding on to.
415 mSourceDocument = nullptr;
416 mSourceNode = nullptr;
417 mTriggeringPrincipal = nullptr;
418 mSelection = nullptr;
419 mDataTransfer = nullptr;
420 mHasImage = false;
421 mUserCancelled = false;
422 mDragPopup = nullptr;
423 mImage = nullptr;
424 mImageOffset = CSSIntPoint();
425 mScreenPosition = CSSIntPoint();
426 mEndDragPoint = LayoutDeviceIntPoint(0, 0);
427 mInputSource = MouseEvent_Binding::MOZ_SOURCE_MOUSE;
428 mRegion = Nothing();
430 return NS_OK;
433 void nsBaseDragService::DiscardInternalTransferData() {
434 if (mDataTransfer && mSourceNode) {
435 MOZ_ASSERT(mDataTransfer);
437 DataTransferItemList* items = mDataTransfer->Items();
438 for (size_t i = 0; i < items->Length(); i++) {
439 bool found;
440 DataTransferItem* item = items->IndexedGetter(i, found);
442 // Non-OTHER items may still be needed by JS. Skip them.
443 if (!found || item->Kind() != DataTransferItem::KIND_OTHER) {
444 continue;
447 nsCOMPtr<nsIVariant> variant = item->DataNoSecurityCheck();
448 nsCOMPtr<nsIWritableVariant> writable = do_QueryInterface(variant);
450 if (writable) {
451 writable->SetAsEmpty();
457 NS_IMETHODIMP
458 nsBaseDragService::FireDragEventAtSource(EventMessage aEventMessage,
459 uint32_t aKeyModifiers) {
460 if (mSourceNode && mSourceDocument && !mSuppressLevel) {
461 nsCOMPtr<nsIPresShell> presShell = mSourceDocument->GetShell();
462 if (presShell) {
463 nsEventStatus status = nsEventStatus_eIgnore;
464 WidgetDragEvent event(true, aEventMessage, nullptr);
465 event.inputSource = mInputSource;
466 if (aEventMessage == eDragEnd) {
467 event.mRefPoint = mEndDragPoint;
468 event.mUserCancelled = mUserCancelled;
470 event.mModifiers = aKeyModifiers;
471 // Send the drag event to APZ, which needs to know about them to be
472 // able to accurately detect the end of a drag gesture.
473 if (nsPresContext* presContext = presShell->GetPresContext()) {
474 if (nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget()) {
475 widget->DispatchEventToAPZOnly(&event);
479 nsCOMPtr<nsIContent> content = do_QueryInterface(mSourceNode);
480 return presShell->HandleDOMEventWithTarget(content, &event, &status);
484 return NS_OK;
487 /* This is used by Windows and Mac to update the position of a popup being
488 * used as a drag image during the drag. This isn't used on GTK as it manages
489 * the drag popup itself.
491 NS_IMETHODIMP
492 nsBaseDragService::DragMoved(int32_t aX, int32_t aY) {
493 if (mDragPopup) {
494 nsIFrame* frame = mDragPopup->GetPrimaryFrame();
495 if (frame && frame->IsMenuPopupFrame()) {
496 CSSIntPoint cssPos =
497 RoundedToInt(LayoutDeviceIntPoint(aX, aY) /
498 frame->PresContext()->CSSToDevPixelScale()) -
499 mImageOffset;
500 (static_cast<nsMenuPopupFrame*>(frame))->MoveTo(cssPos, true);
504 return NS_OK;
507 static nsIPresShell* GetPresShellForContent(nsINode* aDOMNode) {
508 nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMNode);
509 if (!content) return nullptr;
511 RefPtr<Document> document = content->GetComposedDoc();
512 if (document) {
513 document->FlushPendingNotifications(FlushType::Display);
514 return document->GetShell();
517 return nullptr;
520 nsresult nsBaseDragService::DrawDrag(nsINode* aDOMNode,
521 const Maybe<CSSIntRegion>& aRegion,
522 CSSIntPoint aScreenPosition,
523 LayoutDeviceIntRect* aScreenDragRect,
524 RefPtr<SourceSurface>* aSurface,
525 nsPresContext** aPresContext) {
526 *aSurface = nullptr;
527 *aPresContext = nullptr;
529 // use a default size, in case of an error.
530 aScreenDragRect->SetRect(aScreenPosition.x - mImageOffset.x,
531 aScreenPosition.y - mImageOffset.y, 1, 1);
533 // if a drag image was specified, use that, otherwise, use the source node
534 nsCOMPtr<nsINode> dragNode = mImage ? mImage.get() : aDOMNode;
536 // get the presshell for the node being dragged. If the drag image is not in
537 // a document or has no frame, get the presshell from the source drag node
538 nsIPresShell* presShell = GetPresShellForContent(dragNode);
539 if (!presShell && mImage) presShell = GetPresShellForContent(aDOMNode);
540 if (!presShell) return NS_ERROR_FAILURE;
542 *aPresContext = presShell->GetPresContext();
544 RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(dragNode);
545 if (flo) {
546 RefPtr<nsFrameLoader> fl = flo->GetFrameLoader();
547 if (fl) {
548 auto* tp = static_cast<mozilla::dom::TabParent*>(fl->GetRemoteBrowser());
549 if (tp && tp->TakeDragVisualization(*aSurface, aScreenDragRect)) {
550 if (mImage) {
551 // Just clear the surface if chrome has overridden it with an image.
552 *aSurface = nullptr;
555 return NS_OK;
560 // convert mouse position to dev pixels of the prescontext
561 CSSIntPoint screenPosition(aScreenPosition);
562 screenPosition.x -= mImageOffset.x;
563 screenPosition.y -= mImageOffset.y;
564 LayoutDeviceIntPoint screenPoint =
565 ConvertToUnscaledDevPixels(*aPresContext, screenPosition);
566 aScreenDragRect->MoveTo(screenPoint.x, screenPoint.y);
568 // check if drag images are disabled
569 bool enableDragImages = Preferences::GetBool(DRAGIMAGES_PREF, true);
571 // didn't want an image, so just set the screen rectangle to the frame size
572 if (!enableDragImages || !mHasImage) {
573 // if a region was specified, set the screen rectangle to the area that
574 // the region occupies
575 CSSIntRect dragRect;
576 if (aRegion) {
577 // the region's coordinates are relative to the root frame
578 dragRect = aRegion->GetBounds();
580 nsIFrame* rootFrame = presShell->GetRootFrame();
581 CSSIntRect screenRect = rootFrame->GetScreenRect();
582 dragRect.MoveBy(screenRect.TopLeft());
583 } else {
584 // otherwise, there was no region so just set the rectangle to
585 // the size of the primary frame of the content.
586 nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
587 nsIFrame* frame = content->GetPrimaryFrame();
588 if (frame) {
589 dragRect = frame->GetScreenRect();
593 nsIntRect dragRectDev =
594 ToAppUnits(dragRect, AppUnitsPerCSSPixel())
595 .ToOutsidePixels((*aPresContext)->AppUnitsPerDevPixel());
596 aScreenDragRect->SizeTo(dragRectDev.Width(), dragRectDev.Height());
597 return NS_OK;
600 // draw the image for selections
601 if (mSelection) {
602 LayoutDeviceIntPoint pnt(aScreenDragRect->TopLeft());
603 *aSurface = presShell->RenderSelection(
604 mSelection, pnt, aScreenDragRect,
605 mImage ? 0 : nsIPresShell::RENDER_AUTO_SCALE);
606 return NS_OK;
609 // if a custom image was specified, check if it is an image node and draw
610 // using the source rather than the displayed image. But if mImage isn't
611 // an image or canvas, fall through to RenderNode below.
612 if (mImage) {
613 nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
614 HTMLCanvasElement* canvas = HTMLCanvasElement::FromNodeOrNull(content);
615 if (canvas) {
616 return DrawDragForImage(*aPresContext, nullptr, canvas, aScreenDragRect,
617 aSurface);
620 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(dragNode);
621 // for image nodes, create the drag image from the actual image data
622 if (imageLoader) {
623 return DrawDragForImage(*aPresContext, imageLoader, nullptr,
624 aScreenDragRect, aSurface);
627 // If the image is a popup, use that as the image. This allows custom drag
628 // images that can change during the drag, but means that any platform
629 // default image handling won't occur.
630 // XXXndeakin this should be chrome-only
632 nsIFrame* frame = content->GetPrimaryFrame();
633 if (frame && frame->IsMenuPopupFrame()) {
634 mDragPopup = content;
638 if (!mDragPopup) {
639 // otherwise, just draw the node
640 uint32_t renderFlags = mImage ? 0 : nsIPresShell::RENDER_AUTO_SCALE;
641 if (renderFlags) {
642 // check if the dragged node itself is an img element
643 if (dragNode->NodeName().LowerCaseEqualsLiteral("img")) {
644 renderFlags = renderFlags | nsIPresShell::RENDER_IS_IMAGE;
645 } else {
646 nsINodeList* childList = dragNode->ChildNodes();
647 uint32_t length = childList->Length();
648 // check every childnode for being an img element
649 // XXXbz why don't we need to check descendants recursively?
650 for (uint32_t count = 0; count < length; ++count) {
651 if (childList->Item(count)->NodeName().LowerCaseEqualsLiteral(
652 "img")) {
653 // if the dragnode contains an image, set RENDER_IS_IMAGE flag
654 renderFlags = renderFlags | nsIPresShell::RENDER_IS_IMAGE;
655 break;
660 LayoutDeviceIntPoint pnt(aScreenDragRect->TopLeft());
661 *aSurface = presShell->RenderNode(dragNode, aRegion, pnt, aScreenDragRect,
662 renderFlags);
665 // If an image was specified, reset the position from the offset that was
666 // supplied.
667 if (mImage) {
668 aScreenDragRect->MoveTo(screenPoint.x, screenPoint.y);
671 return NS_OK;
674 nsresult nsBaseDragService::DrawDragForImage(
675 nsPresContext* aPresContext, nsIImageLoadingContent* aImageLoader,
676 HTMLCanvasElement* aCanvas, LayoutDeviceIntRect* aScreenDragRect,
677 RefPtr<SourceSurface>* aSurface) {
678 nsCOMPtr<imgIContainer> imgContainer;
679 if (aImageLoader) {
680 nsCOMPtr<imgIRequest> imgRequest;
681 nsresult rv = aImageLoader->GetRequest(
682 nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(imgRequest));
683 NS_ENSURE_SUCCESS(rv, rv);
684 if (!imgRequest) return NS_ERROR_NOT_AVAILABLE;
686 rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
687 NS_ENSURE_SUCCESS(rv, rv);
688 if (!imgContainer) return NS_ERROR_NOT_AVAILABLE;
690 // use the size of the image as the size of the drag image
691 int32_t imageWidth, imageHeight;
692 rv = imgContainer->GetWidth(&imageWidth);
693 NS_ENSURE_SUCCESS(rv, rv);
695 rv = imgContainer->GetHeight(&imageHeight);
696 NS_ENSURE_SUCCESS(rv, rv);
698 aScreenDragRect->SizeTo(aPresContext->CSSPixelsToDevPixels(imageWidth),
699 aPresContext->CSSPixelsToDevPixels(imageHeight));
700 } else {
701 // XXX The canvas size should be converted to dev pixels.
702 NS_ASSERTION(aCanvas, "both image and canvas are null");
703 nsIntSize sz = aCanvas->GetSize();
704 aScreenDragRect->SizeTo(sz.width, sz.height);
707 nsIntSize destSize;
708 destSize.width = aScreenDragRect->Width();
709 destSize.height = aScreenDragRect->Height();
710 if (destSize.width == 0 || destSize.height == 0) return NS_ERROR_FAILURE;
712 nsresult result = NS_OK;
713 if (aImageLoader) {
714 RefPtr<DrawTarget> dt =
715 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
716 destSize, SurfaceFormat::B8G8R8A8);
717 if (!dt || !dt->IsValid()) return NS_ERROR_FAILURE;
719 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
720 if (!ctx) return NS_ERROR_FAILURE;
722 ImgDrawResult res =
723 imgContainer->Draw(ctx, destSize, ImageRegion::Create(destSize),
724 imgIContainer::FRAME_CURRENT, SamplingFilter::GOOD,
725 /* no SVGImageContext */ Nothing(),
726 imgIContainer::FLAG_SYNC_DECODE, 1.0);
727 if (res == ImgDrawResult::BAD_IMAGE || res == ImgDrawResult::BAD_ARGS ||
728 res == ImgDrawResult::NOT_SUPPORTED) {
729 return NS_ERROR_FAILURE;
731 *aSurface = dt->Snapshot();
732 } else {
733 *aSurface = aCanvas->GetSurfaceSnapshot();
736 return result;
739 LayoutDeviceIntPoint nsBaseDragService::ConvertToUnscaledDevPixels(
740 nsPresContext* aPresContext, CSSIntPoint aScreenPosition) {
741 int32_t adj =
742 aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
743 return LayoutDeviceIntPoint(
744 nsPresContext::CSSPixelsToAppUnits(aScreenPosition.x) / adj,
745 nsPresContext::CSSPixelsToAppUnits(aScreenPosition.y) / adj);
748 NS_IMETHODIMP
749 nsBaseDragService::Suppress() {
750 EndDragSession(false, 0);
751 ++mSuppressLevel;
752 return NS_OK;
755 NS_IMETHODIMP
756 nsBaseDragService::Unsuppress() {
757 --mSuppressLevel;
758 return NS_OK;
761 NS_IMETHODIMP
762 nsBaseDragService::UserCancelled() {
763 mUserCancelled = true;
764 return NS_OK;
767 NS_IMETHODIMP
768 nsBaseDragService::UpdateDragEffect() {
769 mDragActionFromChildProcess = mDragAction;
770 return NS_OK;
773 NS_IMETHODIMP
774 nsBaseDragService::UpdateDragImage(nsINode* aImage, int32_t aImageX,
775 int32_t aImageY) {
776 // Don't change the image if this is a drag from another source or if there
777 // is a drag popup.
778 if (!mSourceNode || mDragPopup) return NS_OK;
780 mImage = aImage;
781 mImageOffset = CSSIntPoint(aImageX, aImageY);
782 return NS_OK;
785 NS_IMETHODIMP
786 nsBaseDragService::DragEventDispatchedToChildProcess() {
787 mDragEventDispatchedToChildProcess = true;
788 return NS_OK;
791 bool nsBaseDragService::MaybeAddChildProcess(
792 mozilla::dom::ContentParent* aChild) {
793 if (!mChildProcesses.Contains(aChild)) {
794 mChildProcesses.AppendElement(aChild);
795 return true;
797 return false;