Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / widget / src / xpwidgets / nsBaseDragService.cpp
blob1066c58c5794cdeac079f2e8c5d09e0819f25784
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Mats Palmgren <mats.palmgren@bredband.net>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsBaseDragService.h"
40 #include "nsITransferable.h"
42 #include "nsIServiceManager.h"
43 #include "nsITransferable.h"
44 #include "nsISupportsArray.h"
45 #include "nsSize.h"
46 #include "nsIRegion.h"
47 #include "nsXPCOM.h"
48 #include "nsISupportsPrimitives.h"
49 #include "nsCOMPtr.h"
50 #include "nsIInterfaceRequestorUtils.h"
51 #include "nsIFrame.h"
52 #include "nsIDocument.h"
53 #include "nsIContent.h"
54 #include "nsIPresShell.h"
55 #include "nsIViewManager.h"
56 #include "nsIScrollableView.h"
57 #include "nsIDOMNode.h"
58 #include "nsIDOMDragEvent.h"
59 #include "nsISelection.h"
60 #include "nsISelectionPrivate.h"
61 #include "nsPresContext.h"
62 #include "nsIDOMDataTransfer.h"
63 #include "nsIEventStateManager.h"
64 #include "nsICanvasElement.h"
65 #include "nsIImage.h"
66 #include "nsIImageLoadingContent.h"
67 #include "gfxIImageFrame.h"
68 #include "imgIContainer.h"
69 #include "imgIRequest.h"
70 #include "nsIViewObserver.h"
71 #include "nsRegion.h"
72 #include "nsGUIEvent.h"
73 #include "nsIPrefService.h"
75 #include "gfxContext.h"
76 #include "gfxImageSurface.h"
78 #define DRAGIMAGES_PREF "nglayout.enable_drag_images"
80 nsBaseDragService::nsBaseDragService()
81 : mCanDrop(PR_FALSE), mDoingDrag(PR_FALSE), mHasImage(PR_FALSE),
82 mDragAction(DRAGDROP_ACTION_NONE), mTargetSize(0,0),
83 mImageX(0), mImageY(0), mScreenX(-1), mScreenY(-1), mSuppressLevel(0)
87 nsBaseDragService::~nsBaseDragService()
91 NS_IMPL_ISUPPORTS2(nsBaseDragService, nsIDragService, nsIDragSession)
93 //---------------------------------------------------------
94 NS_IMETHODIMP
95 nsBaseDragService::SetCanDrop(PRBool aCanDrop)
97 mCanDrop = aCanDrop;
98 return NS_OK;
101 //---------------------------------------------------------
102 NS_IMETHODIMP
103 nsBaseDragService::GetCanDrop(PRBool * aCanDrop)
105 *aCanDrop = mCanDrop;
106 return NS_OK;
109 //---------------------------------------------------------
110 NS_IMETHODIMP
111 nsBaseDragService::SetDragAction(PRUint32 anAction)
113 mDragAction = anAction;
114 return NS_OK;
117 //---------------------------------------------------------
118 NS_IMETHODIMP
119 nsBaseDragService::GetDragAction(PRUint32 * anAction)
121 *anAction = mDragAction;
122 return NS_OK;
125 //---------------------------------------------------------
126 NS_IMETHODIMP
127 nsBaseDragService::SetTargetSize(nsSize aDragTargetSize)
129 mTargetSize = aDragTargetSize;
130 return NS_OK;
133 //---------------------------------------------------------
134 NS_IMETHODIMP
135 nsBaseDragService::GetTargetSize(nsSize * aDragTargetSize)
137 *aDragTargetSize = mTargetSize;
138 return NS_OK;
141 //-------------------------------------------------------------------------
143 NS_IMETHODIMP
144 nsBaseDragService::GetNumDropItems(PRUint32 * aNumItems)
146 *aNumItems = 0;
147 return NS_ERROR_FAILURE;
152 // GetSourceDocument
154 // Returns the DOM document where the drag was initiated. This will be
155 // nsnull if the drag began outside of our application.
157 NS_IMETHODIMP
158 nsBaseDragService::GetSourceDocument(nsIDOMDocument** aSourceDocument)
160 *aSourceDocument = mSourceDocument.get();
161 NS_IF_ADDREF(*aSourceDocument);
163 return NS_OK;
167 // GetSourceNode
169 // Returns the DOM node where the drag was initiated. This will be
170 // nsnull if the drag began outside of our application.
172 NS_IMETHODIMP
173 nsBaseDragService::GetSourceNode(nsIDOMNode** aSourceNode)
175 *aSourceNode = mSourceNode.get();
176 NS_IF_ADDREF(*aSourceNode);
178 return NS_OK;
182 //-------------------------------------------------------------------------
184 NS_IMETHODIMP
185 nsBaseDragService::GetData(nsITransferable * aTransferable,
186 PRUint32 aItemIndex)
188 return NS_ERROR_FAILURE;
191 //-------------------------------------------------------------------------
192 NS_IMETHODIMP
193 nsBaseDragService::IsDataFlavorSupported(const char *aDataFlavor,
194 PRBool *_retval)
196 return NS_ERROR_FAILURE;
199 NS_IMETHODIMP
200 nsBaseDragService::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer)
202 *aDataTransfer = mDataTransfer;
203 NS_IF_ADDREF(*aDataTransfer);
204 return NS_OK;
207 NS_IMETHODIMP
208 nsBaseDragService::SetDataTransfer(nsIDOMDataTransfer* aDataTransfer)
210 mDataTransfer = aDataTransfer;
211 return NS_OK;
214 //-------------------------------------------------------------------------
215 NS_IMETHODIMP
216 nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
217 nsISupportsArray* aTransferableArray,
218 nsIScriptableRegion* aDragRgn,
219 PRUint32 aActionType)
221 NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG);
222 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
224 // stash the document of the dom node
225 aDOMNode->GetOwnerDocument(getter_AddRefs(mSourceDocument));
226 mSourceNode = aDOMNode;
228 // When the mouse goes down, the selection code starts a mouse
229 // capture. However, this gets in the way of determining drag
230 // feedback for things like trees because the event coordinates
231 // are in the wrong coord system. Turn off mouse capture in
232 // the associated view manager.
233 nsCOMPtr<nsIContent> contentNode = do_QueryInterface(aDOMNode);
234 if (contentNode) {
235 nsIDocument* doc = contentNode->GetCurrentDoc();
236 if (doc) {
237 nsIPresShell* presShell = doc->GetPrimaryShell();
238 if (presShell) {
239 nsIViewManager* vm = presShell->GetViewManager();
240 if (vm) {
241 PRBool notUsed;
242 vm->GrabMouseEvents(nsnull, notUsed);
248 return NS_OK;
251 NS_IMETHODIMP
252 nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode,
253 nsISupportsArray* aTransferableArray,
254 nsIScriptableRegion* aRegion,
255 PRUint32 aActionType,
256 nsIDOMNode* aImage,
257 PRInt32 aImageX, PRInt32 aImageY,
258 nsIDOMDragEvent* aDragEvent,
259 nsIDOMDataTransfer* aDataTransfer)
261 NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
262 NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER);
263 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
265 mDataTransfer = aDataTransfer;
266 mSelection = nsnull;
267 mHasImage = PR_TRUE;
268 mImage = aImage;
269 mImageX = aImageX;
270 mImageY = aImageY;
272 aDragEvent->GetScreenX(&mScreenX);
273 aDragEvent->GetScreenY(&mScreenY);
275 return InvokeDragSession(aDOMNode, aTransferableArray, aRegion, aActionType);
278 NS_IMETHODIMP
279 nsBaseDragService::InvokeDragSessionWithSelection(nsISelection* aSelection,
280 nsISupportsArray* aTransferableArray,
281 PRUint32 aActionType,
282 nsIDOMDragEvent* aDragEvent,
283 nsIDOMDataTransfer* aDataTransfer)
285 NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
286 NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
287 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
289 mDataTransfer = aDataTransfer;
290 mSelection = aSelection;
291 mHasImage = PR_TRUE;
292 mImage = nsnull;
293 mImageX = 0;
294 mImageY = 0;
296 aDragEvent->GetScreenX(&mScreenX);
297 aDragEvent->GetScreenY(&mScreenY);
299 // just get the focused node from the selection
300 // XXXndeakin this should actually be the deepest node that contains both
301 // endpoints of the selection
302 nsCOMPtr<nsIDOMNode> node;
303 aSelection->GetFocusNode(getter_AddRefs(node));
305 return InvokeDragSession(node, aTransferableArray, nsnull, aActionType);
308 //-------------------------------------------------------------------------
309 NS_IMETHODIMP
310 nsBaseDragService::GetCurrentSession(nsIDragSession ** aSession)
312 if (!aSession)
313 return NS_ERROR_INVALID_ARG;
315 // "this" also implements a drag session, so say we are one but only
316 // if there is currently a drag going on.
317 if (!mSuppressLevel && mDoingDrag) {
318 *aSession = this;
319 NS_ADDREF(*aSession); // addRef because we're a "getter"
321 else
322 *aSession = nsnull;
324 return NS_OK;
327 //-------------------------------------------------------------------------
328 NS_IMETHODIMP
329 nsBaseDragService::StartDragSession()
331 if (mDoingDrag) {
332 return NS_ERROR_FAILURE;
334 mDoingDrag = PR_TRUE;
335 return NS_OK;
338 //-------------------------------------------------------------------------
339 NS_IMETHODIMP
340 nsBaseDragService::EndDragSession(PRBool aDoneDrag)
342 if (!mDoingDrag) {
343 return NS_ERROR_FAILURE;
346 if (aDoneDrag && !mSuppressLevel)
347 FireDragEventAtSource(NS_DRAGDROP_END);
349 mDoingDrag = PR_FALSE;
351 // release the source we've been holding on to.
352 mSourceDocument = nsnull;
353 mSourceNode = nsnull;
354 mSelection = nsnull;
355 mDataTransfer = nsnull;
356 mHasImage = PR_FALSE;
357 mImage = nsnull;
358 mImageX = 0;
359 mImageY = 0;
360 mScreenX = -1;
361 mScreenY = -1;
363 return NS_OK;
366 NS_IMETHODIMP
367 nsBaseDragService::FireDragEventAtSource(PRUint32 aMsg)
369 if (mSourceNode && !mSuppressLevel) {
370 nsCOMPtr<nsIDocument> doc = do_QueryInterface(mSourceDocument);
371 if (doc) {
372 nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
373 if (presShell) {
374 nsEventStatus status = nsEventStatus_eIgnore;
375 nsDragEvent event(PR_TRUE, aMsg, nsnull);
377 nsCOMPtr<nsIContent> content = do_QueryInterface(mSourceNode);
378 return presShell->HandleDOMEventWithTarget(content, &event, &status);
383 return NS_OK;
386 static nsIPresShell*
387 GetPresShellForContent(nsIDOMNode* aDOMNode)
389 nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMNode);
390 nsCOMPtr<nsIDocument> document = content->GetCurrentDoc();
391 if (document) {
392 document->FlushPendingNotifications(Flush_Display);
394 return document->GetPrimaryShell();
397 return nsnull;
400 nsresult
401 nsBaseDragService::DrawDrag(nsIDOMNode* aDOMNode,
402 nsIScriptableRegion* aRegion,
403 PRInt32 aScreenX, PRInt32 aScreenY,
404 nsRect* aScreenDragRect,
405 gfxASurface** aSurface,
406 nsPresContext** aPresContext)
408 *aSurface = nsnull;
409 *aPresContext = nsnull;
411 // use a default size, in case of an error.
412 aScreenDragRect->x = aScreenX - mImageX;
413 aScreenDragRect->y = aScreenY - mImageY;
414 aScreenDragRect->width = 20;
415 aScreenDragRect->height = 20;
417 // if a drag image was specified, use that, otherwise, use the source node
418 nsCOMPtr<nsIDOMNode> dragNode = mImage ? mImage.get() : aDOMNode;
420 // get the presshell for the node being dragged. If the drag image is not in
421 // a document or has no frame, get the presshell from the source drag node
422 nsIPresShell* presShell = GetPresShellForContent(dragNode);
423 if (!presShell && mImage)
424 presShell = GetPresShellForContent(aDOMNode);
425 if (!presShell)
426 return NS_ERROR_FAILURE;
428 *aPresContext = presShell->GetPresContext();
430 // check if drag images are disabled
431 PRBool enableDragImages = PR_TRUE;
432 nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
433 if (prefs)
434 prefs->GetBoolPref(DRAGIMAGES_PREF, &enableDragImages);
436 // didn't want an image, so just set the screen rectangle to the frame size
437 if (!enableDragImages || !mHasImage) {
438 // if a region was specified, set the screen rectangle to the area that
439 // the region occupies
440 if (aRegion) {
441 // the region's coordinates are relative to the root frame
442 nsIFrame* rootFrame = presShell->GetRootFrame();
443 if (rootFrame && *aPresContext) {
444 nsRect dragRect;
445 aRegion->GetBoundingBox(&dragRect.x, &dragRect.y, &dragRect.width, &dragRect.height);
446 dragRect.ScaleRoundOut(nsPresContext::AppUnitsPerCSSPixel());
447 dragRect.ScaleRoundOut(1.0 / (*aPresContext)->AppUnitsPerDevPixel());
449 nsIntRect screenRect = rootFrame->GetScreenRectExternal();
450 aScreenDragRect->SetRect(screenRect.x + dragRect.x, screenRect.y + dragRect.y,
451 dragRect.width, dragRect.height);
454 else {
455 // otherwise, there was no region so just set the rectangle to
456 // the size of the primary frame of the content.
457 nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
458 nsIFrame* frame = presShell->GetPrimaryFrameFor(content);
459 if (frame) {
460 nsIntRect screenRect = frame->GetScreenRectExternal();
461 aScreenDragRect->SetRect(screenRect.x, screenRect.y,
462 screenRect.width, screenRect.height);
466 return NS_OK;
469 // draw the image for selections
470 if (mSelection) {
471 nsPoint pnt(aScreenDragRect->x, aScreenDragRect->y);
472 nsRefPtr<gfxASurface> surface = presShell->RenderSelection(mSelection, pnt, aScreenDragRect);
473 *aSurface = surface;
474 NS_IF_ADDREF(*aSurface);
475 return NS_OK;
478 // if an custom image was specified, check if it is an image node and draw
479 // using the source rather than the displayed image. But if mImage isn't
480 // an image, fall through to RenderNode below.
481 if (mImage) {
482 nsCOMPtr<nsICanvasElement> canvas = do_QueryInterface(dragNode);
483 if (canvas) {
484 return DrawDragForImage(*aPresContext, nsnull, canvas, aScreenX,
485 aScreenY, aScreenDragRect, aSurface);
488 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(dragNode);
489 // for image nodes, create the drag image from the actual image data
490 if (imageLoader) {
491 return DrawDragForImage(*aPresContext, imageLoader, nsnull, aScreenX,
492 aScreenY, aScreenDragRect, aSurface);
496 // otherwise, just draw the node
497 nsCOMPtr<nsIRegion> clipRegion;
498 if (aRegion)
499 aRegion->GetRegion(getter_AddRefs(clipRegion));
501 nsPoint pnt(aScreenDragRect->x, aScreenDragRect->y);
502 nsRefPtr<gfxASurface> surface = presShell->RenderNode(dragNode, clipRegion,
503 pnt, aScreenDragRect);
505 // if an image was specified, reposition the drag rectangle to
506 // the supplied offset in mImageX and mImageY.
507 if (mImage) {
508 aScreenDragRect->x = aScreenX - mImageX;
509 aScreenDragRect->y = aScreenY - mImageY;
512 *aSurface = surface;
513 NS_IF_ADDREF(*aSurface);
515 return NS_OK;
518 nsresult
519 nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext,
520 nsIImageLoadingContent* aImageLoader,
521 nsICanvasElement* aCanvas,
522 PRInt32 aScreenX, PRInt32 aScreenY,
523 nsRect* aScreenDragRect,
524 gfxASurface** aSurface)
526 nsCOMPtr<nsIImage> img;
527 if (aImageLoader) {
528 nsCOMPtr<imgIRequest> imgRequest;
529 nsresult rv = aImageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
530 getter_AddRefs(imgRequest));
531 NS_ENSURE_SUCCESS(rv, rv);
532 if (!imgRequest)
533 return NS_ERROR_NOT_AVAILABLE;
535 nsCOMPtr<imgIContainer> imgContainer;
536 rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
537 NS_ENSURE_SUCCESS(rv, rv);
538 if (!imgContainer)
539 return NS_ERROR_NOT_AVAILABLE;
541 nsCOMPtr<gfxIImageFrame> iframe;
542 imgContainer->GetCurrentFrame(getter_AddRefs(iframe));
543 if (!iframe)
544 return NS_ERROR_FAILURE;
546 img = do_GetInterface(iframe);
547 if (!img)
548 return NS_ERROR_FAILURE;
550 // use the size of the image as the size of the drag image
551 imgContainer->GetWidth(&aScreenDragRect->width);
552 imgContainer->GetHeight(&aScreenDragRect->height);
554 else {
555 NS_ASSERTION(aCanvas, "both image and canvas are null");
556 PRUint32 width, height;
557 aCanvas->GetSize(&width, &height);
558 aScreenDragRect->width = width;
559 aScreenDragRect->height = height;
562 nsSize srcSize = aScreenDragRect->Size();
563 nsSize destSize = srcSize;
565 if (destSize.width == 0 || destSize.height == 0)
566 return NS_ERROR_FAILURE;
568 // if the image is larger than half the screen size, scale it down. This
569 // scaling algorithm is the same as is used in nsPresShell::PaintRangePaintInfo
570 nsIDeviceContext* deviceContext = aPresContext->DeviceContext();
571 nsRect maxSize;
572 deviceContext->GetClientRect(maxSize);
573 nscoord maxWidth = aPresContext->AppUnitsToDevPixels(maxSize.width >> 1);
574 nscoord maxHeight = aPresContext->AppUnitsToDevPixels(maxSize.height >> 1);
575 if (destSize.width > maxWidth || destSize.height > maxHeight) {
576 float scale = 1.0;
577 if (destSize.width > maxWidth)
578 scale = PR_MIN(scale, float(maxWidth) / destSize.width);
579 if (destSize.height > maxHeight)
580 scale = PR_MIN(scale, float(maxHeight) / destSize.height);
582 destSize.width = NSToIntFloor(float(destSize.width) * scale);
583 destSize.height = NSToIntFloor(float(destSize.height) * scale);
585 aScreenDragRect->x = NSToIntFloor(aScreenX - float(mImageX) * scale);
586 aScreenDragRect->y = NSToIntFloor(aScreenY - float(mImageY) * scale);
587 aScreenDragRect->width = destSize.width;
588 aScreenDragRect->height = destSize.height;
591 nsRefPtr<gfxImageSurface> surface =
592 new gfxImageSurface(gfxIntSize(destSize.width, destSize.height),
593 gfxImageSurface::ImageFormatARGB32);
594 if (!surface)
595 return NS_ERROR_FAILURE;
597 nsRefPtr<gfxContext> ctx = new gfxContext(surface);
598 if (!ctx)
599 return NS_ERROR_FAILURE;
601 *aSurface = surface;
602 NS_ADDREF(*aSurface);
604 if (aImageLoader) {
605 gfxRect outRect(0, 0, destSize.width, destSize.height);
606 gfxMatrix scale =
607 gfxMatrix().Scale(srcSize.width/outRect.Width(), srcSize.height/outRect.Height());
608 img->Draw(ctx, scale, outRect, nsIntMargin(0,0,0,0),
609 nsIntRect(0, 0, srcSize.width, srcSize.height));
610 return NS_OK;
611 } else {
612 return aCanvas->RenderContexts(ctx);
616 void
617 nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext,
618 PRInt32* aScreenX, PRInt32* aScreenY)
620 PRInt32 adj = aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel();
621 *aScreenX = nsPresContext::CSSPixelsToAppUnits(*aScreenX) / adj;
622 *aScreenY = nsPresContext::CSSPixelsToAppUnits(*aScreenY) / adj;
625 NS_IMETHODIMP
626 nsBaseDragService::Suppress()
628 EndDragSession(PR_FALSE);
629 ++mSuppressLevel;
630 return NS_OK;
633 NS_IMETHODIMP
634 nsBaseDragService::Unsuppress()
636 --mSuppressLevel;
637 return NS_OK;