nsBaseDragService.cpp 25 KB

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