Line data Source code
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/. */
5 :
6 : #include "nsBaseDragService.h"
7 : #include "nsITransferable.h"
8 :
9 : #include "nsIServiceManager.h"
10 : #include "nsITransferable.h"
11 : #include "nsSize.h"
12 : #include "nsXPCOM.h"
13 : #include "nsISupportsPrimitives.h"
14 : #include "nsCOMPtr.h"
15 : #include "nsIInterfaceRequestorUtils.h"
16 : #include "nsIFrame.h"
17 : #include "nsIDocument.h"
18 : #include "nsIContent.h"
19 : #include "nsIPresShell.h"
20 : #include "nsViewManager.h"
21 : #include "nsIDOMNode.h"
22 : #include "nsIDOMDragEvent.h"
23 : #include "nsISelection.h"
24 : #include "nsISelectionPrivate.h"
25 : #include "nsPresContext.h"
26 : #include "nsIDOMDataTransfer.h"
27 : #include "nsIImageLoadingContent.h"
28 : #include "imgIContainer.h"
29 : #include "imgIRequest.h"
30 : #include "ImageRegion.h"
31 : #include "nsRegion.h"
32 : #include "nsXULPopupManager.h"
33 : #include "nsMenuPopupFrame.h"
34 : #include "SVGImageContext.h"
35 : #include "mozilla/MouseEvents.h"
36 : #include "mozilla/Preferences.h"
37 : #include "mozilla/dom/DataTransferItemList.h"
38 : #include "mozilla/gfx/2D.h"
39 : #include "mozilla/Unused.h"
40 : #include "nsFrameLoader.h"
41 : #include "TabParent.h"
42 :
43 : #include "gfxContext.h"
44 : #include "gfxPlatform.h"
45 : #include <algorithm>
46 :
47 : using namespace mozilla;
48 : using namespace mozilla::dom;
49 : using namespace mozilla::gfx;
50 : using namespace mozilla::image;
51 :
52 : #define DRAGIMAGES_PREF "nglayout.enable_drag_images"
53 :
54 1 : nsBaseDragService::nsBaseDragService()
55 : : mCanDrop(false), mOnlyChromeDrop(false), mDoingDrag(false),
56 : mHasImage(false), mUserCancelled(false),
57 : mDragEventDispatchedToChildProcess(false),
58 : mDragAction(DRAGDROP_ACTION_NONE),
59 : mDragActionFromChildProcess(DRAGDROP_ACTION_UNINITIALIZED), mTargetSize(0,0),
60 : mContentPolicyType(nsIContentPolicy::TYPE_OTHER),
61 1 : mSuppressLevel(0), mInputSource(nsIDOMMouseEvent::MOZ_SOURCE_MOUSE)
62 : {
63 1 : }
64 :
65 : nsBaseDragService::~nsBaseDragService() = default;
66 :
67 23 : NS_IMPL_ISUPPORTS(nsBaseDragService, nsIDragService, nsIDragSession)
68 :
69 : //---------------------------------------------------------
70 : NS_IMETHODIMP
71 0 : nsBaseDragService::SetCanDrop(bool aCanDrop)
72 : {
73 0 : mCanDrop = aCanDrop;
74 0 : return NS_OK;
75 : }
76 :
77 : //---------------------------------------------------------
78 : NS_IMETHODIMP
79 0 : nsBaseDragService::GetCanDrop(bool * aCanDrop)
80 : {
81 0 : *aCanDrop = mCanDrop;
82 0 : return NS_OK;
83 : }
84 : //---------------------------------------------------------
85 : NS_IMETHODIMP
86 0 : nsBaseDragService::SetOnlyChromeDrop(bool aOnlyChrome)
87 : {
88 0 : mOnlyChromeDrop = aOnlyChrome;
89 0 : return NS_OK;
90 : }
91 :
92 : //---------------------------------------------------------
93 : NS_IMETHODIMP
94 0 : nsBaseDragService::GetOnlyChromeDrop(bool* aOnlyChrome)
95 : {
96 0 : *aOnlyChrome = mOnlyChromeDrop;
97 0 : return NS_OK;
98 : }
99 :
100 : //---------------------------------------------------------
101 : NS_IMETHODIMP
102 0 : nsBaseDragService::SetDragAction(uint32_t anAction)
103 : {
104 0 : mDragAction = anAction;
105 0 : return NS_OK;
106 : }
107 :
108 : //---------------------------------------------------------
109 : NS_IMETHODIMP
110 0 : nsBaseDragService::GetDragAction(uint32_t * anAction)
111 : {
112 0 : *anAction = mDragAction;
113 0 : return NS_OK;
114 : }
115 :
116 : //---------------------------------------------------------
117 : NS_IMETHODIMP
118 0 : nsBaseDragService::SetTargetSize(nsSize aDragTargetSize)
119 : {
120 0 : mTargetSize = aDragTargetSize;
121 0 : return NS_OK;
122 : }
123 :
124 : //---------------------------------------------------------
125 : NS_IMETHODIMP
126 0 : nsBaseDragService::GetTargetSize(nsSize * aDragTargetSize)
127 : {
128 0 : *aDragTargetSize = mTargetSize;
129 0 : return NS_OK;
130 : }
131 :
132 : //-------------------------------------------------------------------------
133 :
134 : NS_IMETHODIMP
135 0 : nsBaseDragService::GetNumDropItems(uint32_t * aNumItems)
136 : {
137 0 : *aNumItems = 0;
138 0 : return NS_ERROR_FAILURE;
139 : }
140 :
141 :
142 : //
143 : // GetSourceDocument
144 : //
145 : // Returns the DOM document where the drag was initiated. This will be
146 : // nullptr if the drag began outside of our application.
147 : //
148 : NS_IMETHODIMP
149 0 : nsBaseDragService::GetSourceDocument(nsIDOMDocument** aSourceDocument)
150 : {
151 0 : *aSourceDocument = mSourceDocument.get();
152 0 : NS_IF_ADDREF(*aSourceDocument);
153 :
154 0 : return NS_OK;
155 : }
156 :
157 : //
158 : // GetSourceNode
159 : //
160 : // Returns the DOM node where the drag was initiated. This will be
161 : // nullptr if the drag began outside of our application.
162 : //
163 : NS_IMETHODIMP
164 0 : nsBaseDragService::GetSourceNode(nsIDOMNode** aSourceNode)
165 : {
166 0 : *aSourceNode = mSourceNode.get();
167 0 : NS_IF_ADDREF(*aSourceNode);
168 :
169 0 : return NS_OK;
170 : }
171 :
172 :
173 : //-------------------------------------------------------------------------
174 :
175 : NS_IMETHODIMP
176 0 : nsBaseDragService::GetData(nsITransferable * aTransferable,
177 : uint32_t aItemIndex)
178 : {
179 0 : return NS_ERROR_FAILURE;
180 : }
181 :
182 : //-------------------------------------------------------------------------
183 : NS_IMETHODIMP
184 0 : nsBaseDragService::IsDataFlavorSupported(const char *aDataFlavor,
185 : bool *_retval)
186 : {
187 0 : return NS_ERROR_FAILURE;
188 : }
189 :
190 : NS_IMETHODIMP
191 0 : nsBaseDragService::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer)
192 : {
193 0 : *aDataTransfer = mDataTransfer;
194 0 : NS_IF_ADDREF(*aDataTransfer);
195 0 : return NS_OK;
196 : }
197 :
198 : NS_IMETHODIMP
199 0 : nsBaseDragService::SetDataTransfer(nsIDOMDataTransfer* aDataTransfer)
200 : {
201 0 : mDataTransfer = aDataTransfer;
202 0 : return NS_OK;
203 : }
204 :
205 : //-------------------------------------------------------------------------
206 : NS_IMETHODIMP
207 0 : nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
208 : nsIArray* aTransferableArray,
209 : nsIScriptableRegion* aDragRgn,
210 : uint32_t aActionType,
211 : nsContentPolicyType aContentPolicyType =
212 : nsIContentPolicy::TYPE_OTHER)
213 : {
214 0 : AUTO_PROFILER_LABEL("nsBaseDragService::InvokeDragSession", OTHER);
215 :
216 0 : NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG);
217 0 : NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
218 :
219 : // stash the document of the dom node
220 0 : aDOMNode->GetOwnerDocument(getter_AddRefs(mSourceDocument));
221 0 : mSourceNode = aDOMNode;
222 0 : mContentPolicyType = aContentPolicyType;
223 0 : mEndDragPoint = LayoutDeviceIntPoint(0, 0);
224 :
225 : // When the mouse goes down, the selection code starts a mouse
226 : // capture. However, this gets in the way of determining drag
227 : // feedback for things like trees because the event coordinates
228 : // are in the wrong coord system, so turn off mouse capture.
229 0 : nsIPresShell::ClearMouseCapture(nullptr);
230 :
231 : nsresult rv = InvokeDragSessionImpl(aTransferableArray,
232 0 : aDragRgn, aActionType);
233 :
234 0 : if (NS_FAILED(rv)) {
235 0 : mSourceNode = nullptr;
236 0 : mSourceDocument = nullptr;
237 : }
238 :
239 0 : return rv;
240 : }
241 :
242 : NS_IMETHODIMP
243 0 : nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode,
244 : nsIArray* aTransferableArray,
245 : nsIScriptableRegion* aRegion,
246 : uint32_t aActionType,
247 : nsIDOMNode* aImage,
248 : int32_t aImageX, int32_t aImageY,
249 : nsIDOMDragEvent* aDragEvent,
250 : nsIDOMDataTransfer* aDataTransfer)
251 : {
252 0 : NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
253 0 : NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER);
254 0 : NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
255 :
256 0 : mDataTransfer = aDataTransfer;
257 0 : mSelection = nullptr;
258 0 : mHasImage = true;
259 0 : mDragPopup = nullptr;
260 0 : mImage = aImage;
261 0 : mImageOffset = CSSIntPoint(aImageX, aImageY);
262 :
263 0 : aDragEvent->GetScreenX(&mScreenPosition.x);
264 0 : aDragEvent->GetScreenY(&mScreenPosition.y);
265 0 : aDragEvent->GetMozInputSource(&mInputSource);
266 :
267 : nsresult rv = InvokeDragSession(aDOMNode, aTransferableArray,
268 : aRegion, aActionType,
269 0 : nsIContentPolicy::TYPE_INTERNAL_IMAGE);
270 :
271 0 : if (NS_FAILED(rv)) {
272 0 : mImage = nullptr;
273 0 : mHasImage = false;
274 0 : mDataTransfer = nullptr;
275 : }
276 :
277 0 : return rv;
278 : }
279 :
280 : NS_IMETHODIMP
281 0 : nsBaseDragService::InvokeDragSessionWithSelection(nsISelection* aSelection,
282 : nsIArray* aTransferableArray,
283 : uint32_t aActionType,
284 : nsIDOMDragEvent* aDragEvent,
285 : nsIDOMDataTransfer* aDataTransfer)
286 : {
287 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
288 0 : NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
289 0 : NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
290 :
291 0 : mDataTransfer = aDataTransfer;
292 0 : mSelection = aSelection;
293 0 : mHasImage = true;
294 0 : mDragPopup = nullptr;
295 0 : mImage = nullptr;
296 0 : mImageOffset = CSSIntPoint();
297 :
298 0 : aDragEvent->GetScreenX(&mScreenPosition.x);
299 0 : aDragEvent->GetScreenY(&mScreenPosition.y);
300 0 : aDragEvent->GetMozInputSource(&mInputSource);
301 :
302 : // just get the focused node from the selection
303 : // XXXndeakin this should actually be the deepest node that contains both
304 : // endpoints of the selection
305 0 : nsCOMPtr<nsIDOMNode> node;
306 0 : aSelection->GetFocusNode(getter_AddRefs(node));
307 :
308 0 : nsresult rv = InvokeDragSession(node, aTransferableArray,
309 : nullptr, aActionType,
310 0 : nsIContentPolicy::TYPE_OTHER);
311 :
312 0 : if (NS_FAILED(rv)) {
313 0 : mHasImage = false;
314 0 : mSelection = nullptr;
315 0 : mDataTransfer = nullptr;
316 : }
317 :
318 0 : return rv;
319 : }
320 :
321 : //-------------------------------------------------------------------------
322 : NS_IMETHODIMP
323 6 : nsBaseDragService::GetCurrentSession(nsIDragSession ** aSession)
324 : {
325 6 : if (!aSession)
326 0 : return NS_ERROR_INVALID_ARG;
327 :
328 : // "this" also implements a drag session, so say we are one but only
329 : // if there is currently a drag going on.
330 6 : if (!mSuppressLevel && mDoingDrag) {
331 0 : *aSession = this;
332 0 : NS_ADDREF(*aSession); // addRef because we're a "getter"
333 : }
334 : else
335 6 : *aSession = nullptr;
336 :
337 6 : return NS_OK;
338 : }
339 :
340 : //-------------------------------------------------------------------------
341 : NS_IMETHODIMP
342 0 : nsBaseDragService::StartDragSession()
343 : {
344 0 : if (mDoingDrag) {
345 0 : return NS_ERROR_FAILURE;
346 : }
347 0 : mDoingDrag = true;
348 : // By default dispatch drop also to content.
349 0 : mOnlyChromeDrop = false;
350 :
351 0 : return NS_OK;
352 : }
353 :
354 : void
355 0 : nsBaseDragService::OpenDragPopup()
356 : {
357 0 : if (mDragPopup) {
358 0 : nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
359 0 : if (pm) {
360 0 : pm->ShowPopupAtScreen(mDragPopup, mScreenPosition.x - mImageOffset.x,
361 0 : mScreenPosition.y - mImageOffset.y, false, nullptr);
362 : }
363 : }
364 0 : }
365 :
366 : int32_t
367 0 : nsBaseDragService::TakeChildProcessDragAction()
368 : {
369 : // If the last event was dispatched to the child process, use the drag action
370 : // assigned from it instead and return it. DRAGDROP_ACTION_UNINITIALIZED is
371 : // returned otherwise.
372 0 : int32_t retval = DRAGDROP_ACTION_UNINITIALIZED;
373 0 : if (TakeDragEventDispatchedToChildProcess() &&
374 0 : mDragActionFromChildProcess != DRAGDROP_ACTION_UNINITIALIZED) {
375 0 : retval = mDragActionFromChildProcess;
376 : }
377 :
378 0 : return retval;
379 : }
380 :
381 : //-------------------------------------------------------------------------
382 : NS_IMETHODIMP
383 0 : nsBaseDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers)
384 : {
385 0 : if (!mDoingDrag) {
386 0 : return NS_ERROR_FAILURE;
387 : }
388 :
389 0 : if (aDoneDrag && !mSuppressLevel) {
390 0 : FireDragEventAtSource(eDragEnd, aKeyModifiers);
391 : }
392 :
393 0 : if (mDragPopup) {
394 0 : nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
395 0 : if (pm) {
396 0 : pm->HidePopup(mDragPopup, false, true, false, false);
397 : }
398 : }
399 :
400 0 : for (uint32_t i = 0; i < mChildProcesses.Length(); ++i) {
401 0 : mozilla::Unused << mChildProcesses[i]->SendEndDragSession(aDoneDrag,
402 : mUserCancelled,
403 : mEndDragPoint,
404 : aKeyModifiers);
405 : }
406 0 : mChildProcesses.Clear();
407 :
408 : // mDataTransfer and the items it owns are going to die anyway, but we
409 : // explicitly deref the contained data here so that we don't have to wait for
410 : // CC to reclaim the memory.
411 0 : if (XRE_IsParentProcess()) {
412 0 : DiscardInternalTransferData();
413 : }
414 :
415 0 : mDoingDrag = false;
416 0 : mCanDrop = false;
417 :
418 : // release the source we've been holding on to.
419 0 : mSourceDocument = nullptr;
420 0 : mSourceNode = nullptr;
421 0 : mSelection = nullptr;
422 0 : mDataTransfer = nullptr;
423 0 : mHasImage = false;
424 0 : mUserCancelled = false;
425 0 : mDragPopup = nullptr;
426 0 : mImage = nullptr;
427 0 : mImageOffset = CSSIntPoint();
428 0 : mScreenPosition = CSSIntPoint();
429 0 : mEndDragPoint = LayoutDeviceIntPoint(0, 0);
430 0 : mInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
431 :
432 0 : return NS_OK;
433 : }
434 :
435 : void
436 0 : nsBaseDragService::DiscardInternalTransferData()
437 : {
438 0 : if (mDataTransfer && mSourceNode) {
439 0 : MOZ_ASSERT(!!DataTransfer::Cast(mDataTransfer));
440 :
441 0 : DataTransferItemList* items = DataTransfer::Cast(mDataTransfer)->Items();
442 0 : for (size_t i = 0; i < items->Length(); i++) {
443 : bool found;
444 0 : DataTransferItem* item = items->IndexedGetter(i, found);
445 :
446 : // Non-OTHER items may still be needed by JS. Skip them.
447 0 : if (!found || item->Kind() != DataTransferItem::KIND_OTHER) {
448 0 : continue;
449 : }
450 :
451 0 : nsCOMPtr<nsIVariant> variant = item->DataNoSecurityCheck();
452 0 : nsCOMPtr<nsIWritableVariant> writable = do_QueryInterface(variant);
453 :
454 0 : if (writable) {
455 0 : writable->SetAsEmpty();
456 : }
457 : }
458 : }
459 0 : }
460 :
461 : NS_IMETHODIMP
462 0 : nsBaseDragService::FireDragEventAtSource(EventMessage aEventMessage,
463 : uint32_t aKeyModifiers)
464 : {
465 0 : if (mSourceNode && !mSuppressLevel) {
466 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(mSourceDocument);
467 0 : if (doc) {
468 0 : nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
469 0 : if (presShell) {
470 0 : nsEventStatus status = nsEventStatus_eIgnore;
471 0 : WidgetDragEvent event(true, aEventMessage, nullptr);
472 0 : event.inputSource = mInputSource;
473 0 : if (aEventMessage == eDragEnd) {
474 0 : event.mRefPoint = mEndDragPoint;
475 0 : event.mUserCancelled = mUserCancelled;
476 : }
477 0 : event.mModifiers = aKeyModifiers;
478 : // Send the drag event to APZ, which needs to know about them to be
479 : // able to accurately detect the end of a drag gesture.
480 0 : if (nsPresContext* presContext = presShell->GetPresContext()) {
481 0 : if (nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget()) {
482 0 : widget->DispatchEventToAPZOnly(&event);
483 : }
484 : }
485 :
486 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mSourceNode);
487 0 : return presShell->HandleDOMEventWithTarget(content, &event, &status);
488 : }
489 : }
490 : }
491 :
492 0 : return NS_OK;
493 : }
494 :
495 : /* This is used by Windows and Mac to update the position of a popup being
496 : * used as a drag image during the drag. This isn't used on GTK as it manages
497 : * the drag popup itself.
498 : */
499 : NS_IMETHODIMP
500 0 : nsBaseDragService::DragMoved(int32_t aX, int32_t aY)
501 : {
502 0 : if (mDragPopup) {
503 0 : nsIFrame* frame = mDragPopup->GetPrimaryFrame();
504 0 : if (frame && frame->IsMenuPopupFrame()) {
505 0 : CSSIntPoint cssPos = RoundedToInt(LayoutDeviceIntPoint(aX, aY) /
506 0 : frame->PresContext()->CSSToDevPixelScale()) - mImageOffset;
507 0 : (static_cast<nsMenuPopupFrame *>(frame))->MoveTo(cssPos, true);
508 : }
509 : }
510 :
511 0 : return NS_OK;
512 : }
513 :
514 : static nsIPresShell*
515 0 : GetPresShellForContent(nsIDOMNode* aDOMNode)
516 : {
517 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMNode);
518 0 : if (!content)
519 0 : return nullptr;
520 :
521 0 : nsCOMPtr<nsIDocument> document = content->GetUncomposedDoc();
522 0 : if (document) {
523 0 : document->FlushPendingNotifications(FlushType::Display);
524 :
525 0 : return document->GetShell();
526 : }
527 :
528 0 : return nullptr;
529 : }
530 :
531 : nsresult
532 0 : nsBaseDragService::DrawDrag(nsIDOMNode* aDOMNode,
533 : nsIScriptableRegion* aRegion,
534 : CSSIntPoint aScreenPosition,
535 : LayoutDeviceIntRect* aScreenDragRect,
536 : RefPtr<SourceSurface>* aSurface,
537 : nsPresContext** aPresContext)
538 : {
539 0 : *aSurface = nullptr;
540 0 : *aPresContext = nullptr;
541 :
542 : // use a default size, in case of an error.
543 0 : aScreenDragRect->MoveTo(aScreenPosition.x - mImageOffset.x,
544 0 : aScreenPosition.y - mImageOffset.y);
545 0 : aScreenDragRect->SizeTo(1, 1);
546 :
547 : // if a drag image was specified, use that, otherwise, use the source node
548 0 : nsCOMPtr<nsIDOMNode> dragNode = mImage ? mImage.get() : aDOMNode;
549 :
550 : // get the presshell for the node being dragged. If the drag image is not in
551 : // a document or has no frame, get the presshell from the source drag node
552 0 : nsIPresShell* presShell = GetPresShellForContent(dragNode);
553 0 : if (!presShell && mImage)
554 0 : presShell = GetPresShellForContent(aDOMNode);
555 0 : if (!presShell)
556 0 : return NS_ERROR_FAILURE;
557 :
558 0 : *aPresContext = presShell->GetPresContext();
559 :
560 0 : nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(dragNode);
561 0 : if (flo) {
562 0 : RefPtr<nsFrameLoader> fl = flo->GetFrameLoader();
563 0 : if (fl) {
564 0 : auto* tp = static_cast<mozilla::dom::TabParent*>(fl->GetRemoteBrowser());
565 0 : if (tp && tp->TakeDragVisualization(*aSurface, aScreenDragRect)) {
566 0 : if (mImage) {
567 : // Just clear the surface if chrome has overridden it with an image.
568 0 : *aSurface = nullptr;
569 : }
570 :
571 0 : return NS_OK;
572 : }
573 : }
574 : }
575 :
576 : // convert mouse position to dev pixels of the prescontext
577 0 : CSSIntPoint screenPosition(aScreenPosition);
578 0 : screenPosition.x -= mImageOffset.x;
579 0 : screenPosition.y -= mImageOffset.y;
580 0 : LayoutDeviceIntPoint screenPoint = ConvertToUnscaledDevPixels(*aPresContext, screenPosition);
581 0 : aScreenDragRect->x = screenPoint.x;
582 0 : aScreenDragRect->y = screenPoint.y;
583 :
584 : // check if drag images are disabled
585 0 : bool enableDragImages = Preferences::GetBool(DRAGIMAGES_PREF, true);
586 :
587 : // didn't want an image, so just set the screen rectangle to the frame size
588 0 : if (!enableDragImages || !mHasImage) {
589 : // if a region was specified, set the screen rectangle to the area that
590 : // the region occupies
591 0 : CSSIntRect dragRect;
592 0 : if (aRegion) {
593 : // the region's coordinates are relative to the root frame
594 0 : aRegion->GetBoundingBox(&dragRect.x, &dragRect.y, &dragRect.width, &dragRect.height);
595 :
596 0 : nsIFrame* rootFrame = presShell->GetRootFrame();
597 0 : CSSIntRect screenRect = rootFrame->GetScreenRect();
598 0 : dragRect.MoveBy(screenRect.TopLeft());
599 : }
600 : else {
601 : // otherwise, there was no region so just set the rectangle to
602 : // the size of the primary frame of the content.
603 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
604 0 : nsIFrame* frame = content->GetPrimaryFrame();
605 0 : if (frame) {
606 0 : dragRect = frame->GetScreenRect();
607 : }
608 : }
609 :
610 : nsIntRect dragRectDev =
611 0 : ToAppUnits(dragRect, nsPresContext::AppUnitsPerCSSPixel()).
612 0 : ToOutsidePixels((*aPresContext)->AppUnitsPerDevPixel());
613 0 : aScreenDragRect->SizeTo(dragRectDev.width, dragRectDev.height);
614 0 : return NS_OK;
615 : }
616 :
617 : // draw the image for selections
618 0 : if (mSelection) {
619 0 : LayoutDeviceIntPoint pnt(aScreenDragRect->TopLeft());
620 0 : *aSurface = presShell->RenderSelection(mSelection, pnt, aScreenDragRect,
621 0 : mImage ? 0 : nsIPresShell::RENDER_AUTO_SCALE);
622 0 : return NS_OK;
623 : }
624 :
625 : // if a custom image was specified, check if it is an image node and draw
626 : // using the source rather than the displayed image. But if mImage isn't
627 : // an image or canvas, fall through to RenderNode below.
628 0 : if (mImage) {
629 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
630 0 : HTMLCanvasElement *canvas = HTMLCanvasElement::FromContentOrNull(content);
631 0 : if (canvas) {
632 0 : return DrawDragForImage(*aPresContext, nullptr, canvas, aScreenDragRect, aSurface);
633 : }
634 :
635 0 : nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(dragNode);
636 : // for image nodes, create the drag image from the actual image data
637 0 : if (imageLoader) {
638 0 : return DrawDragForImage(*aPresContext, imageLoader, nullptr, aScreenDragRect, aSurface);
639 : }
640 :
641 : // If the image is a popup, use that as the image. This allows custom drag
642 : // images that can change during the drag, but means that any platform
643 : // default image handling won't occur.
644 : // XXXndeakin this should be chrome-only
645 :
646 0 : nsIFrame* frame = content->GetPrimaryFrame();
647 0 : if (frame && frame->IsMenuPopupFrame()) {
648 0 : mDragPopup = content;
649 : }
650 : }
651 :
652 0 : if (!mDragPopup) {
653 : // otherwise, just draw the node
654 0 : nsIntRegion clipRegion;
655 0 : uint32_t renderFlags = mImage ? 0 : nsIPresShell::RENDER_AUTO_SCALE;
656 0 : if (aRegion) {
657 0 : aRegion->GetRegion(&clipRegion);
658 : }
659 :
660 0 : if (renderFlags) {
661 0 : nsCOMPtr<nsIDOMNode> child;
662 0 : nsCOMPtr<nsIDOMNodeList> childList;
663 : uint32_t length;
664 0 : uint32_t count = 0;
665 0 : nsAutoString childNodeName;
666 :
667 0 : if (NS_SUCCEEDED(dragNode->GetChildNodes(getter_AddRefs(childList))) &&
668 0 : NS_SUCCEEDED(childList->GetLength(&length))) {
669 : // check every childnode for being a img-tag
670 0 : while (count < length) {
671 0 : if (NS_FAILED(childList->Item(count, getter_AddRefs(child))) ||
672 0 : NS_FAILED(child->GetNodeName(childNodeName))) {
673 0 : break;
674 : }
675 : // here the node is checked for being a img-tag
676 0 : if (childNodeName.LowerCaseEqualsLiteral("img")) {
677 : // if the dragnnode contains a image, set RENDER_IS_IMAGE flag
678 0 : renderFlags = renderFlags | nsIPresShell::RENDER_IS_IMAGE;
679 0 : break;
680 : }
681 0 : count++;
682 : }
683 : }
684 : }
685 0 : LayoutDeviceIntPoint pnt(aScreenDragRect->TopLeft());
686 0 : *aSurface = presShell->RenderNode(dragNode, aRegion ? &clipRegion : nullptr,
687 : pnt, aScreenDragRect,
688 0 : renderFlags);
689 : }
690 :
691 : // If an image was specified, reset the position from the offset that was supplied.
692 0 : if (mImage) {
693 0 : aScreenDragRect->x = screenPoint.x;
694 0 : aScreenDragRect->y = screenPoint.y;
695 : }
696 :
697 0 : return NS_OK;
698 : }
699 :
700 : nsresult
701 0 : nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext,
702 : nsIImageLoadingContent* aImageLoader,
703 : HTMLCanvasElement* aCanvas,
704 : LayoutDeviceIntRect* aScreenDragRect,
705 : RefPtr<SourceSurface>* aSurface)
706 : {
707 0 : nsCOMPtr<imgIContainer> imgContainer;
708 0 : if (aImageLoader) {
709 0 : nsCOMPtr<imgIRequest> imgRequest;
710 0 : nsresult rv = aImageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
711 0 : getter_AddRefs(imgRequest));
712 0 : NS_ENSURE_SUCCESS(rv, rv);
713 0 : if (!imgRequest)
714 0 : return NS_ERROR_NOT_AVAILABLE;
715 :
716 0 : rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
717 0 : NS_ENSURE_SUCCESS(rv, rv);
718 0 : if (!imgContainer)
719 0 : return NS_ERROR_NOT_AVAILABLE;
720 :
721 : // use the size of the image as the size of the drag image
722 : int32_t imageWidth, imageHeight;
723 0 : rv = imgContainer->GetWidth(&imageWidth);
724 0 : NS_ENSURE_SUCCESS(rv, rv);
725 :
726 0 : rv = imgContainer->GetHeight(&imageHeight);
727 0 : NS_ENSURE_SUCCESS(rv, rv);
728 :
729 0 : aScreenDragRect->width = aPresContext->CSSPixelsToDevPixels(imageWidth);
730 0 : aScreenDragRect->height = aPresContext->CSSPixelsToDevPixels(imageHeight);
731 : }
732 : else {
733 : // XXX The canvas size should be converted to dev pixels.
734 0 : NS_ASSERTION(aCanvas, "both image and canvas are null");
735 0 : nsIntSize sz = aCanvas->GetSize();
736 0 : aScreenDragRect->width = sz.width;
737 0 : aScreenDragRect->height = sz.height;
738 : }
739 :
740 0 : nsIntSize destSize;
741 0 : destSize.width = aScreenDragRect->width;
742 0 : destSize.height = aScreenDragRect->height;
743 0 : if (destSize.width == 0 || destSize.height == 0)
744 0 : return NS_ERROR_FAILURE;
745 :
746 0 : nsresult result = NS_OK;
747 0 : if (aImageLoader) {
748 : RefPtr<DrawTarget> dt =
749 : gfxPlatform::GetPlatform()->
750 0 : CreateOffscreenContentDrawTarget(destSize,
751 0 : SurfaceFormat::B8G8R8A8);
752 0 : if (!dt || !dt->IsValid())
753 0 : return NS_ERROR_FAILURE;
754 :
755 0 : RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
756 0 : if (!ctx)
757 0 : return NS_ERROR_FAILURE;
758 :
759 : DrawResult res =
760 0 : imgContainer->Draw(ctx, destSize, ImageRegion::Create(destSize),
761 : imgIContainer::FRAME_CURRENT,
762 0 : SamplingFilter::GOOD, /* no SVGImageContext */ Nothing(),
763 0 : imgIContainer::FLAG_SYNC_DECODE, 1.0);
764 0 : if (res == DrawResult::BAD_IMAGE || res == DrawResult::BAD_ARGS) {
765 0 : return NS_ERROR_FAILURE;
766 : }
767 0 : *aSurface = dt->Snapshot();
768 : } else {
769 0 : *aSurface = aCanvas->GetSurfaceSnapshot();
770 : }
771 :
772 0 : return result;
773 : }
774 :
775 : LayoutDeviceIntPoint
776 0 : nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext,
777 : CSSIntPoint aScreenPosition)
778 : {
779 0 : int32_t adj = aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
780 0 : return LayoutDeviceIntPoint(nsPresContext::CSSPixelsToAppUnits(aScreenPosition.x) / adj,
781 0 : nsPresContext::CSSPixelsToAppUnits(aScreenPosition.y) / adj);
782 : }
783 :
784 : NS_IMETHODIMP
785 0 : nsBaseDragService::Suppress()
786 : {
787 0 : EndDragSession(false, 0);
788 0 : ++mSuppressLevel;
789 0 : return NS_OK;
790 : }
791 :
792 : NS_IMETHODIMP
793 0 : nsBaseDragService::Unsuppress()
794 : {
795 0 : --mSuppressLevel;
796 0 : return NS_OK;
797 : }
798 :
799 : NS_IMETHODIMP
800 0 : nsBaseDragService::UserCancelled()
801 : {
802 0 : mUserCancelled = true;
803 0 : return NS_OK;
804 : }
805 :
806 : NS_IMETHODIMP
807 0 : nsBaseDragService::UpdateDragEffect()
808 : {
809 0 : mDragActionFromChildProcess = mDragAction;
810 0 : return NS_OK;
811 : }
812 :
813 : NS_IMETHODIMP
814 0 : nsBaseDragService::UpdateDragImage(nsIDOMNode* aImage, int32_t aImageX, int32_t aImageY)
815 : {
816 : // Don't change the image if this is a drag from another source or if there
817 : // is a drag popup.
818 0 : if (!mSourceNode || mDragPopup)
819 0 : return NS_OK;
820 :
821 0 : mImage = aImage;
822 0 : mImageOffset = CSSIntPoint(aImageX, aImageY);
823 0 : return NS_OK;
824 : }
825 :
826 : NS_IMETHODIMP
827 0 : nsBaseDragService::DragEventDispatchedToChildProcess()
828 : {
829 0 : mDragEventDispatchedToChildProcess = true;
830 0 : return NS_OK;
831 : }
832 :
833 : bool
834 0 : nsBaseDragService::MaybeAddChildProcess(mozilla::dom::ContentParent* aChild)
835 : {
836 0 : if (!mChildProcesses.Contains(aChild)) {
837 0 : mChildProcesses.AppendElement(aChild);
838 0 : return true;
839 : }
840 0 : return false;
841 : }
|