Coverage Report

Created: 2018-09-25 14:53

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