Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsContentAreaDragDrop.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsReadableUtils.h"
8
9
// Local Includes
10
#include "nsContentAreaDragDrop.h"
11
12
// Helper Classes
13
#include "nsString.h"
14
15
// Interfaces needed to be included
16
#include "nsCopySupport.h"
17
#include "nsISelectionController.h"
18
#include "nsPIDOMWindow.h"
19
#include "nsIFormControl.h"
20
#include "nsITransferable.h"
21
#include "nsComponentManagerUtils.h"
22
#include "nsXPCOM.h"
23
#include "nsISupportsPrimitives.h"
24
#include "nsServiceManagerUtils.h"
25
#include "nsNetUtil.h"
26
#include "nsIFile.h"
27
#include "nsFrameLoader.h"
28
#include "nsIWebNavigation.h"
29
#include "nsIDocShell.h"
30
#include "nsIContent.h"
31
#include "nsIContentInlines.h"
32
#include "nsIImageLoadingContent.h"
33
#include "nsITextControlElement.h"
34
#include "nsUnicharUtils.h"
35
#include "nsIURL.h"
36
#include "nsIURIMutator.h"
37
#include "nsIDocument.h"
38
#include "nsIScriptSecurityManager.h"
39
#include "nsIPrincipal.h"
40
#include "nsIDocShellTreeItem.h"
41
#include "nsIWebBrowserPersist.h"
42
#include "nsEscape.h"
43
#include "nsContentUtils.h"
44
#include "nsIMIMEService.h"
45
#include "imgIContainer.h"
46
#include "imgIRequest.h"
47
#include "mozilla/dom/DataTransfer.h"
48
#include "nsIMIMEInfo.h"
49
#include "nsRange.h"
50
#include "TabParent.h"
51
#include "mozilla/dom/Element.h"
52
#include "mozilla/dom/HTMLAreaElement.h"
53
#include "mozilla/dom/HTMLAnchorElement.h"
54
#include "mozilla/dom/Selection.h"
55
#include "nsVariant.h"
56
57
using namespace mozilla::dom;
58
using mozilla::IgnoreErrors;
59
60
class MOZ_STACK_CLASS DragDataProducer
61
{
62
public:
63
  DragDataProducer(nsPIDOMWindowOuter* aWindow,
64
                   nsIContent* aTarget,
65
                   nsIContent* aSelectionTargetNode,
66
                   bool aIsAltKeyPressed);
67
  nsresult Produce(DataTransfer* aDataTransfer,
68
                   bool* aCanDrag,
69
                   Selection** aSelection,
70
                   nsIContent** aDragNode,
71
                   nsACString& aPrincipalURISpec);
72
73
private:
74
  void AddString(DataTransfer* aDataTransfer,
75
                 const nsAString& aFlavor,
76
                 const nsAString& aData,
77
                 nsIPrincipal* aPrincipal,
78
                 bool aHidden=false);
79
  nsresult AddStringsToDataTransfer(nsIContent* aDragNode,
80
                                    DataTransfer* aDataTransfer);
81
  nsresult GetImageData(imgIContainer* aImage, imgIRequest* aRequest);
82
  static nsresult GetDraggableSelectionData(Selection* inSelection,
83
                                            nsIContent* inRealTargetNode,
84
                                            nsIContent **outImageOrLinkNode,
85
                                            bool* outDragSelectedText);
86
  static already_AddRefed<nsIContent> FindParentLinkNode(nsIContent* inNode);
87
  static MOZ_MUST_USE nsresult
88
  GetAnchorURL(nsIContent* inNode, nsAString& outURL);
89
  static void GetNodeString(nsIContent* inNode, nsAString & outNodeString);
90
  static void CreateLinkText(const nsAString& inURL, const nsAString & inText,
91
                              nsAString& outLinkText);
92
93
  nsCOMPtr<nsPIDOMWindowOuter> mWindow;
94
  nsCOMPtr<nsIContent> mTarget;
95
  nsCOMPtr<nsIContent> mSelectionTargetNode;
96
  bool mIsAltKeyPressed;
97
98
  nsString mUrlString;
99
  nsString mImageSourceString;
100
  nsString mImageDestFileName;
101
#if defined (XP_MACOSX)
102
  nsString mImageRequestMime;
103
#endif
104
  nsString mTitleString;
105
  // will be filled automatically if you fill urlstring
106
  nsString mHtmlString;
107
  nsString mContextString;
108
  nsString mInfoString;
109
110
  bool mIsAnchor;
111
  nsCOMPtr<imgIContainer> mImage;
112
};
113
114
115
nsresult
116
nsContentAreaDragDrop::GetDragData(nsPIDOMWindowOuter* aWindow,
117
                                   nsIContent* aTarget,
118
                                   nsIContent* aSelectionTargetNode,
119
                                   bool aIsAltKeyPressed,
120
                                   DataTransfer* aDataTransfer,
121
                                   bool* aCanDrag,
122
                                   Selection** aSelection,
123
                                   nsIContent** aDragNode,
124
                                   nsACString& aPrincipalURISpec)
125
0
{
126
0
  NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG);
127
0
128
0
  *aCanDrag = true;
129
0
130
0
  DragDataProducer
131
0
    provider(aWindow, aTarget, aSelectionTargetNode, aIsAltKeyPressed);
132
0
  return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode,
133
0
                          aPrincipalURISpec);
134
0
}
135
136
137
NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
138
139
// SaveURIToFile
140
// used on platforms where it's possible to drag items (e.g. images)
141
// into the file system
142
nsresult
143
nsContentAreaDragDropDataProvider::SaveURIToFile(nsIURI* inSourceURI,
144
                                                 nsIPrincipal* inTriggeringPrincipal,
145
                                                 nsIFile* inDestFile,
146
                                                 bool isPrivate)
147
0
{
148
0
  nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(inSourceURI);
149
0
  if (!sourceURL) {
150
0
    return NS_ERROR_NO_INTERFACE;
151
0
  }
152
0
153
0
  nsresult rv = inDestFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
154
0
  NS_ENSURE_SUCCESS(rv, rv);
155
0
156
0
  // we rely on the fact that the WPB is refcounted by the channel etc,
157
0
  // so we don't keep a ref to it. It will die when finished.
158
0
  nsCOMPtr<nsIWebBrowserPersist> persist =
159
0
    do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1",
160
0
                      &rv);
161
0
  NS_ENSURE_SUCCESS(rv, rv);
162
0
163
0
  persist->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
164
0
165
0
  // referrer policy can be anything since the referrer is nullptr
166
0
  return persist->SavePrivacyAwareURI(inSourceURI,
167
0
                                      inTriggeringPrincipal, 0, nullptr,
168
0
                                      mozilla::net::RP_Unset,
169
0
                                      nullptr, nullptr,
170
0
                                      inDestFile, isPrivate);
171
0
}
172
173
/*
174
 * Check if the provided filename extension is valid for the MIME type and
175
 * return the MIME type's primary extension.
176
 *
177
 * @param aExtension           [in]  the extension to check
178
 * @param aMimeType            [in]  the MIME type to check the extension with
179
 * @param aIsValidExtension    [out] true if |aExtension| is valid for
180
 *                                   |aMimeType|
181
 * @param aPrimaryExtension    [out] the primary extension for the MIME type
182
 *                                   to potentially be used as a replacement
183
 *                                   for |aExtension|
184
 */
185
nsresult
186
CheckAndGetExtensionForMime(const nsCString& aExtension,
187
                            const nsCString& aMimeType,
188
                            bool* aIsValidExtension,
189
                            nsACString* aPrimaryExtension)
190
0
{
191
0
  nsresult rv;
192
0
193
0
  nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
194
0
  if (NS_WARN_IF(!mimeService)) {
195
0
    return NS_ERROR_FAILURE;
196
0
  }
197
0
198
0
  nsCOMPtr<nsIMIMEInfo> mimeInfo;
199
0
  rv = mimeService->GetFromTypeAndExtension(aMimeType, EmptyCString(),
200
0
                                            getter_AddRefs(mimeInfo));
201
0
  NS_ENSURE_SUCCESS(rv, rv);
202
0
203
0
  rv = mimeInfo->GetPrimaryExtension(*aPrimaryExtension);
204
0
  NS_ENSURE_SUCCESS(rv, rv);
205
0
206
0
  if (aExtension.IsEmpty()) {
207
0
    *aIsValidExtension = false;
208
0
    return NS_OK;
209
0
  }
210
0
211
0
  rv = mimeInfo->ExtensionExists(aExtension, aIsValidExtension);
212
0
  NS_ENSURE_SUCCESS(rv, rv);
213
0
214
0
  return NS_OK;
215
0
}
216
217
// This is our nsIFlavorDataProvider callback. There are several
218
// assumptions here that make this work:
219
//
220
// 1. Someone put a kFilePromiseURLMime flavor into the transferable
221
//    with the source URI of the file to save (as a string). We did
222
//    that in AddStringsToDataTransfer.
223
//
224
// 2. Someone put a kFilePromiseDirectoryMime flavor into the
225
//    transferable with an nsIFile for the directory we are to
226
//    save in. That has to be done by platform-specific code (in
227
//    widget), which gets the destination directory from
228
//    OS-specific drag information.
229
//
230
NS_IMETHODIMP
231
nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable *aTransferable,
232
                                                 const char *aFlavor,
233
                                                 nsISupports **aData,
234
                                                 uint32_t *aDataLen)
235
0
{
236
0
  NS_ENSURE_ARG_POINTER(aData && aDataLen);
237
0
  *aData = nullptr;
238
0
  *aDataLen = 0;
239
0
240
0
  nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
241
0
242
0
  if (strcmp(aFlavor, kFilePromiseMime) == 0) {
243
0
    // get the URI from the kFilePromiseURLMime flavor
244
0
    NS_ENSURE_ARG(aTransferable);
245
0
    nsCOMPtr<nsISupports> tmp;
246
0
    uint32_t dataSize = 0;
247
0
    aTransferable->GetTransferData(kFilePromiseURLMime,
248
0
                                   getter_AddRefs(tmp), &dataSize);
249
0
    nsCOMPtr<nsISupportsString> supportsString =
250
0
      do_QueryInterface(tmp);
251
0
    if (!supportsString)
252
0
      return NS_ERROR_FAILURE;
253
0
254
0
    nsAutoString sourceURLString;
255
0
    supportsString->GetData(sourceURLString);
256
0
    if (sourceURLString.IsEmpty())
257
0
      return NS_ERROR_FAILURE;
258
0
259
0
    nsCOMPtr<nsIURI> sourceURI;
260
0
    rv = NS_NewURI(getter_AddRefs(sourceURI), sourceURLString);
261
0
    NS_ENSURE_SUCCESS(rv, rv);
262
0
263
0
    aTransferable->GetTransferData(kFilePromiseDestFilename,
264
0
                                   getter_AddRefs(tmp), &dataSize);
265
0
    supportsString = do_QueryInterface(tmp);
266
0
    if (!supportsString)
267
0
      return NS_ERROR_FAILURE;
268
0
269
0
    nsAutoString targetFilename;
270
0
    supportsString->GetData(targetFilename);
271
0
    if (targetFilename.IsEmpty())
272
0
      return NS_ERROR_FAILURE;
273
0
274
#if defined(XP_MACOSX)
275
    // Use the image request's MIME type to ensure the filename's
276
    // extension is compatible with the OS's handler for this type.
277
    // If it isn't, or is missing, replace the extension with the
278
    // primary extension. On Mac, do this in the parent process
279
    // because sandboxing blocks access to MIME-handler info from
280
    // content processes.
281
    if (XRE_IsParentProcess()) {
282
      aTransferable->GetTransferData(kImageRequestMime,
283
                                     getter_AddRefs(tmp), &dataSize);
284
      supportsString = do_QueryInterface(tmp);
285
      if (!supportsString)
286
        return NS_ERROR_FAILURE;
287
288
      nsAutoString imageRequestMime;
289
      supportsString->GetData(imageRequestMime);
290
291
      // If we have a MIME type, check the extension is compatible
292
      if (!imageRequestMime.IsEmpty()) {
293
        // Build a URL to get the filename extension
294
        nsCOMPtr<nsIURL> imageURL = do_QueryInterface(sourceURI, &rv);
295
        NS_ENSURE_SUCCESS(rv, rv);
296
297
        nsAutoCString extension;
298
        rv = imageURL->GetFileExtension(extension);
299
        NS_ENSURE_SUCCESS(rv, rv);
300
301
        NS_ConvertUTF16toUTF8 mimeCString(imageRequestMime);
302
        bool isValidExtension;
303
        nsAutoCString primaryExtension;
304
        rv = CheckAndGetExtensionForMime(extension,
305
                                         mimeCString,
306
                                         &isValidExtension,
307
                                         &primaryExtension);
308
        NS_ENSURE_SUCCESS(rv, rv);
309
310
        if (!isValidExtension) {
311
          // The filename extension is missing or incompatible
312
          // with the MIME type, replace it with the primary
313
          // extension.
314
          nsAutoCString newFileName;
315
          rv = imageURL->GetFileBaseName(newFileName);
316
          NS_ENSURE_SUCCESS(rv, rv);
317
          newFileName.Append(".");
318
          newFileName.Append(primaryExtension);
319
          targetFilename = NS_ConvertUTF8toUTF16(newFileName);
320
        }
321
      }
322
    }
323
    // make the filename safe for the filesystem
324
    targetFilename.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS,
325
                               '-');
326
#endif /* defined(XP_MACOSX) */
327
328
0
    // get the target directory from the kFilePromiseDirectoryMime
329
0
    // flavor
330
0
    nsCOMPtr<nsISupports> dirPrimitive;
331
0
    dataSize = 0;
332
0
    aTransferable->GetTransferData(kFilePromiseDirectoryMime,
333
0
                                   getter_AddRefs(dirPrimitive), &dataSize);
334
0
    nsCOMPtr<nsIFile> destDirectory = do_QueryInterface(dirPrimitive);
335
0
    if (!destDirectory)
336
0
      return NS_ERROR_FAILURE;
337
0
338
0
    nsCOMPtr<nsIFile> file;
339
0
    rv = destDirectory->Clone(getter_AddRefs(file));
340
0
    NS_ENSURE_SUCCESS(rv, rv);
341
0
342
0
    file->Append(targetFilename);
343
0
344
0
    bool isPrivate;
345
0
    aTransferable->GetIsPrivateData(&isPrivate);
346
0
347
0
    nsCOMPtr<nsIPrincipal> principal;
348
0
    aTransferable->GetRequestingPrincipal(getter_AddRefs(principal));
349
0
    rv = SaveURIToFile(sourceURI, principal, file, isPrivate);
350
0
    // send back an nsIFile
351
0
    if (NS_SUCCEEDED(rv)) {
352
0
      CallQueryInterface(file, aData);
353
0
      *aDataLen = sizeof(nsIFile*);
354
0
    }
355
0
  }
356
0
357
0
  return rv;
358
0
}
359
360
DragDataProducer::DragDataProducer(nsPIDOMWindowOuter* aWindow,
361
                                   nsIContent* aTarget,
362
                                   nsIContent* aSelectionTargetNode,
363
                                   bool aIsAltKeyPressed)
364
  : mWindow(aWindow),
365
    mTarget(aTarget),
366
    mSelectionTargetNode(aSelectionTargetNode),
367
    mIsAltKeyPressed(aIsAltKeyPressed),
368
    mIsAnchor(false)
369
0
{
370
0
}
371
372
373
//
374
// FindParentLinkNode
375
//
376
// Finds the parent with the given link tag starting at |inNode|. If
377
// it gets up to the root without finding it, we stop looking and
378
// return null.
379
//
380
already_AddRefed<nsIContent>
381
DragDataProducer::FindParentLinkNode(nsIContent* inNode)
382
0
{
383
0
  nsIContent* content = inNode;
384
0
  if (!content) {
385
0
    // That must have been the document node; nothing else to do here;
386
0
    return nullptr;
387
0
  }
388
0
389
0
  for (; content; content = content->GetParent()) {
390
0
    if (nsContentUtils::IsDraggableLink(content)) {
391
0
      nsCOMPtr<nsIContent> ret = content;
392
0
      return ret.forget();
393
0
    }
394
0
  }
395
0
396
0
  return nullptr;
397
0
}
398
399
400
//
401
// GetAnchorURL
402
//
403
nsresult
404
DragDataProducer::GetAnchorURL(nsIContent* inNode, nsAString& outURL)
405
0
{
406
0
  nsCOMPtr<nsIURI> linkURI;
407
0
  if (!inNode || !inNode->IsLink(getter_AddRefs(linkURI))) {
408
0
    // Not a link
409
0
    outURL.Truncate();
410
0
    return NS_OK;
411
0
  }
412
0
413
0
  nsAutoCString spec;
414
0
  nsresult rv = linkURI->GetSpec(spec);
415
0
  NS_ENSURE_SUCCESS(rv, rv);
416
0
  CopyUTF8toUTF16(spec, outURL);
417
0
  return NS_OK;
418
0
}
419
420
421
//
422
// CreateLinkText
423
//
424
// Creates the html for an anchor in the form
425
//  <a href="inURL">inText</a>
426
//
427
void
428
DragDataProducer::CreateLinkText(const nsAString& inURL,
429
                                 const nsAString & inText,
430
                                 nsAString& outLinkText)
431
0
{
432
0
  // use a temp var in case |inText| is the same string as
433
0
  // |outLinkText| to avoid overwriting it while building up the
434
0
  // string in pieces.
435
0
  nsAutoString linkText(NS_LITERAL_STRING("<a href=\"") +
436
0
                        inURL +
437
0
                        NS_LITERAL_STRING("\">") +
438
0
                        inText +
439
0
                        NS_LITERAL_STRING("</a>") );
440
0
441
0
  outLinkText = linkText;
442
0
}
443
444
445
//
446
// GetNodeString
447
//
448
// Gets the text associated with a node
449
//
450
void
451
DragDataProducer::GetNodeString(nsIContent* inNode,
452
                                nsAString & outNodeString)
453
0
{
454
0
  nsCOMPtr<nsINode> node = inNode;
455
0
456
0
  outNodeString.Truncate();
457
0
458
0
  // use a range to get the text-equivalent of the node
459
0
  nsCOMPtr<nsIDocument> doc = node->OwnerDoc();
460
0
  RefPtr<nsRange> range = doc->CreateRange(IgnoreErrors());
461
0
  if (range) {
462
0
    range->SelectNode(*node, IgnoreErrors());
463
0
    range->ToString(outNodeString, IgnoreErrors());
464
0
  }
465
0
}
466
467
nsresult
468
DragDataProducer::GetImageData(imgIContainer* aImage, imgIRequest* aRequest)
469
0
{
470
0
  nsCOMPtr<nsIURI> imgUri;
471
0
  aRequest->GetURI(getter_AddRefs(imgUri));
472
0
473
0
  nsCOMPtr<nsIURL> imgUrl(do_QueryInterface(imgUri));
474
0
  if (imgUrl) {
475
0
    nsAutoCString spec;
476
0
    nsresult rv = imgUrl->GetSpec(spec);
477
0
    NS_ENSURE_SUCCESS(rv, rv);
478
0
479
0
    // pass out the image source string
480
0
    CopyUTF8toUTF16(spec, mImageSourceString);
481
0
482
0
    nsCString mimeType;
483
0
    aRequest->GetMimeType(getter_Copies(mimeType));
484
0
485
#if defined(XP_MACOSX)
486
    // Save the MIME type so we can make sure the extension
487
    // is compatible (and replace it if it isn't) when the
488
    // image is dropped. On Mac, we need to get the OS MIME
489
    // handler information in the parent due to sandboxing.
490
    CopyUTF8toUTF16(mimeType, mImageRequestMime);
491
#else
492
    nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
493
0
    if (NS_WARN_IF(!mimeService)) {
494
0
      return NS_ERROR_FAILURE;
495
0
    }
496
0
497
0
    nsCOMPtr<nsIMIMEInfo> mimeInfo;
498
0
    mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(),
499
0
           getter_AddRefs(mimeInfo));
500
0
    if (mimeInfo) {
501
0
      nsAutoCString extension;
502
0
      imgUrl->GetFileExtension(extension);
503
0
504
0
      bool validExtension;
505
0
      if (extension.IsEmpty() ||
506
0
          NS_FAILED(mimeInfo->ExtensionExists(extension,
507
0
                                              &validExtension)) ||
508
0
          !validExtension) {
509
0
        // Fix the file extension in the URL
510
0
        nsAutoCString primaryExtension;
511
0
        mimeInfo->GetPrimaryExtension(primaryExtension);
512
0
513
0
        rv = NS_MutateURI(imgUrl)
514
0
               .Apply(NS_MutatorMethod(&nsIURLMutator::SetFileExtension,
515
0
                                       primaryExtension, nullptr))
516
0
               .Finalize(imgUrl);
517
0
        NS_ENSURE_SUCCESS(rv, rv);
518
0
      }
519
0
    }
520
0
#endif /* defined(XP_MACOSX) */
521
0
522
0
    nsAutoCString fileName;
523
0
    imgUrl->GetFileName(fileName);
524
0
525
0
    NS_UnescapeURL(fileName);
526
0
527
0
#if !defined(XP_MACOSX)
528
0
    // make the filename safe for the filesystem
529
0
    fileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '-');
530
0
#endif
531
0
532
0
    CopyUTF8toUTF16(fileName, mImageDestFileName);
533
0
534
0
    // and the image object
535
0
    mImage = aImage;
536
0
  }
537
0
538
0
  return NS_OK;
539
0
}
540
541
nsresult
542
DragDataProducer::Produce(DataTransfer* aDataTransfer,
543
                          bool* aCanDrag,
544
                          Selection** aSelection,
545
                          nsIContent** aDragNode,
546
                          nsACString& aPrincipalURISpec)
547
0
{
548
0
  MOZ_ASSERT(aCanDrag && aSelection && aDataTransfer && aDragNode,
549
0
             "null pointer passed to Produce");
550
0
  NS_ASSERTION(mWindow, "window not set");
551
0
  NS_ASSERTION(mSelectionTargetNode, "selection target node should have been set");
552
0
553
0
  *aDragNode = nullptr;
554
0
555
0
  nsresult rv;
556
0
  nsIContent* dragNode = nullptr;
557
0
  *aSelection = nullptr;
558
0
559
0
  // Find the selection to see what we could be dragging and if what we're
560
0
  // dragging is in what is selected. If this is an editable textbox, use
561
0
  // the textbox's selection, otherwise use the window's selection.
562
0
  RefPtr<Selection> selection;
563
0
  nsIContent* editingElement = mSelectionTargetNode->IsEditable() ?
564
0
                               mSelectionTargetNode->GetEditingHost() : nullptr;
565
0
  nsCOMPtr<nsITextControlElement> textControl =
566
0
    nsITextControlElement::GetTextControlElementFromEditingHost(editingElement);
567
0
  if (textControl) {
568
0
    nsISelectionController* selcon = textControl->GetSelectionController();
569
0
    if (selcon) {
570
0
      selection = selcon->GetSelection(nsISelectionController::SELECTION_NORMAL);
571
0
    }
572
0
573
0
    if (!selection)
574
0
      return NS_OK;
575
0
  }
576
0
  else {
577
0
    selection = mWindow->GetSelection();
578
0
    if (!selection)
579
0
      return NS_OK;
580
0
581
0
    // Check if the node is inside a form control. Don't set aCanDrag to false
582
0
    //however, as we still want to allow the drag.
583
0
    nsCOMPtr<nsIContent> findFormNode = mSelectionTargetNode;
584
0
    nsIContent* findFormParent = findFormNode->GetParent();
585
0
    while (findFormParent) {
586
0
      nsCOMPtr<nsIFormControl> form(do_QueryInterface(findFormParent));
587
0
      if (form && !form->AllowDraggableChildren()) {
588
0
        return NS_OK;
589
0
      }
590
0
      findFormParent = findFormParent->GetParent();
591
0
    }
592
0
  }
593
0
594
0
  // if set, serialize the content under this node
595
0
  nsCOMPtr<nsIContent> nodeToSerialize;
596
0
597
0
  nsCOMPtr<nsIDocShellTreeItem> dsti = mWindow->GetDocShell();
598
0
  const bool isChromeShell =
599
0
    dsti && dsti->ItemType() == nsIDocShellTreeItem::typeChrome;
600
0
601
0
  // In chrome shells, only allow dragging inside editable areas.
602
0
  if (isChromeShell && !editingElement) {
603
0
    nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(mTarget);
604
0
    if (flo) {
605
0
      RefPtr<nsFrameLoader> fl = flo->GetFrameLoader();
606
0
      if (fl) {
607
0
        TabParent* tp = static_cast<TabParent*>(fl->GetRemoteBrowser());
608
0
        if (tp) {
609
0
          // We have a TabParent, so it may have data for dnd in case the child
610
0
          // process started a dnd session.
611
0
          tp->AddInitialDnDDataTo(aDataTransfer, aPrincipalURISpec);
612
0
        }
613
0
      }
614
0
    }
615
0
    return NS_OK;
616
0
  }
617
0
618
0
  if (isChromeShell && textControl) {
619
0
    // Only use the selection if the target node is in the selection.
620
0
    if (!selection->ContainsNode(*mSelectionTargetNode, false, IgnoreErrors()))
621
0
      return NS_OK;
622
0
623
0
    selection.swap(*aSelection);
624
0
  }
625
0
  else {
626
0
    // In content shells, a number of checks are made below to determine
627
0
    // whether an image or a link is being dragged. If so, add additional
628
0
    // data to the data transfer. This is also done for chrome shells, but
629
0
    // only when in a non-textbox editor.
630
0
631
0
    bool haveSelectedContent = false;
632
0
633
0
    // possible parent link node
634
0
    nsCOMPtr<nsIContent> parentLink;
635
0
    nsCOMPtr<nsIContent> draggedNode;
636
0
637
0
    {
638
0
      // only drag form elements by using the alt key,
639
0
      // otherwise buttons and select widgets are hard to use
640
0
641
0
      // Note that while <object> elements implement nsIFormControl, we should
642
0
      // really allow dragging them if they happen to be images.
643
0
      nsCOMPtr<nsIFormControl> form(do_QueryInterface(mTarget));
644
0
      if (form && !mIsAltKeyPressed && form->ControlType() != NS_FORM_OBJECT) {
645
0
        *aCanDrag = false;
646
0
        return NS_OK;
647
0
      }
648
0
649
0
      draggedNode = mTarget;
650
0
    }
651
0
652
0
    nsCOMPtr<nsIImageLoadingContent>  image;
653
0
654
0
    nsCOMPtr<nsIContent> selectedImageOrLinkNode;
655
0
    GetDraggableSelectionData(selection, mSelectionTargetNode,
656
0
                              getter_AddRefs(selectedImageOrLinkNode),
657
0
                              &haveSelectedContent);
658
0
659
0
    // either plain text or anchor text is selected
660
0
    if (haveSelectedContent) {
661
0
      selection.swap(*aSelection);
662
0
    } else if (selectedImageOrLinkNode) {
663
0
      // an image is selected
664
0
      image = do_QueryInterface(selectedImageOrLinkNode);
665
0
    } else {
666
0
      // nothing is selected -
667
0
      //
668
0
      // look for draggable elements under the mouse
669
0
      //
670
0
      // if the alt key is down, don't start a drag if we're in an
671
0
      // anchor because we want to do selection.
672
0
      parentLink = FindParentLinkNode(draggedNode);
673
0
      if (parentLink && mIsAltKeyPressed) {
674
0
        *aCanDrag = false;
675
0
        return NS_OK;
676
0
      }
677
0
      image = do_QueryInterface(draggedNode);
678
0
    }
679
0
680
0
    {
681
0
      // set for linked images, and links
682
0
      nsCOMPtr<nsIContent> linkNode;
683
0
684
0
      RefPtr<HTMLAreaElement> areaElem = HTMLAreaElement::FromNodeOrNull(draggedNode);
685
0
      if (areaElem) {
686
0
        // use the alt text (or, if missing, the href) as the title
687
0
        areaElem->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString);
688
0
        if (mTitleString.IsEmpty()) {
689
0
          // this can be a relative link
690
0
          areaElem->GetAttribute(NS_LITERAL_STRING("href"), mTitleString);
691
0
        }
692
0
693
0
        // we'll generate HTML like <a href="absurl">alt text</a>
694
0
        mIsAnchor = true;
695
0
696
0
        // gives an absolute link
697
0
        nsresult rv = GetAnchorURL(draggedNode, mUrlString);
698
0
        NS_ENSURE_SUCCESS(rv, rv);
699
0
700
0
        mHtmlString.AssignLiteral("<a href=\"");
701
0
        mHtmlString.Append(mUrlString);
702
0
        mHtmlString.AppendLiteral("\">");
703
0
        mHtmlString.Append(mTitleString);
704
0
        mHtmlString.AppendLiteral("</a>");
705
0
706
0
        dragNode = draggedNode;
707
0
      } else if (image) {
708
0
        mIsAnchor = true;
709
0
        // grab the href as the url, use alt text as the title of the
710
0
        // area if it's there.  the drag data is the image tag and src
711
0
        // attribute.
712
0
        nsCOMPtr<nsIURI> imageURI;
713
0
        image->GetCurrentURI(getter_AddRefs(imageURI));
714
0
        if (imageURI) {
715
0
          nsAutoCString spec;
716
0
          rv = imageURI->GetSpec(spec);
717
0
          NS_ENSURE_SUCCESS(rv, rv);
718
0
          CopyUTF8toUTF16(spec, mUrlString);
719
0
        }
720
0
721
0
        nsCOMPtr<Element> imageElement(do_QueryInterface(image));
722
0
        // XXXbz Shouldn't we use the "title" attr for title?  Using
723
0
        // "alt" seems very wrong....
724
0
        // XXXbz Also, what if this is an nsIImageLoadingContent
725
0
        // that's not an <html:img>?
726
0
        if (imageElement) {
727
0
          imageElement->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString);
728
0
        }
729
0
730
0
        if (mTitleString.IsEmpty()) {
731
0
          mTitleString = mUrlString;
732
0
        }
733
0
734
0
        nsCOMPtr<imgIRequest> imgRequest;
735
0
736
0
        // grab the image data, and its request.
737
0
        nsCOMPtr<imgIContainer> img =
738
0
          nsContentUtils::GetImageFromContent(image,
739
0
                                              getter_AddRefs(imgRequest));
740
0
        if (imgRequest) {
741
0
          rv = GetImageData(img, imgRequest);
742
0
          NS_ENSURE_SUCCESS(rv, rv);
743
0
        }
744
0
745
0
        if (parentLink) {
746
0
          // If we are dragging around an image in an anchor, then we
747
0
          // are dragging the entire anchor
748
0
          linkNode = parentLink;
749
0
          nodeToSerialize = linkNode;
750
0
        } else {
751
0
          nodeToSerialize = draggedNode;
752
0
        }
753
0
        dragNode = nodeToSerialize;
754
0
      } else if (draggedNode && draggedNode->IsHTMLElement(nsGkAtoms::a)) {
755
0
        // set linkNode. The code below will handle this
756
0
        linkNode = draggedNode;    // XXX test this
757
0
        GetNodeString(draggedNode, mTitleString);
758
0
      } else if (parentLink) {
759
0
        // parentLink will always be null if there's selected content
760
0
        linkNode = parentLink;
761
0
        nodeToSerialize = linkNode;
762
0
      } else if (!haveSelectedContent) {
763
0
        // nothing draggable
764
0
        return NS_OK;
765
0
      }
766
0
767
0
      if (linkNode) {
768
0
        mIsAnchor = true;
769
0
        rv = GetAnchorURL(linkNode, mUrlString);
770
0
        NS_ENSURE_SUCCESS(rv, rv);
771
0
        dragNode = linkNode;
772
0
      }
773
0
    }
774
0
  }
775
0
776
0
  if (nodeToSerialize || *aSelection) {
777
0
    mHtmlString.Truncate();
778
0
    mContextString.Truncate();
779
0
    mInfoString.Truncate();
780
0
    mTitleString.Truncate();
781
0
782
0
    nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
783
0
    NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
784
0
785
0
    // if we have selected text, use it in preference to the node
786
0
    nsCOMPtr<nsITransferable> transferable;
787
0
    if (*aSelection) {
788
0
      rv = nsCopySupport::GetTransferableForSelection(*aSelection, doc,
789
0
                                                      getter_AddRefs(transferable));
790
0
    }
791
0
    else {
792
0
      rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc,
793
0
                                                 getter_AddRefs(transferable));
794
0
    }
795
0
    NS_ENSURE_SUCCESS(rv, rv);
796
0
797
0
    nsCOMPtr<nsISupports> supports;
798
0
    nsCOMPtr<nsISupportsString> data;
799
0
    uint32_t dataSize;
800
0
    rv = transferable->GetTransferData(kHTMLMime, getter_AddRefs(supports),
801
0
                                       &dataSize);
802
0
    data = do_QueryInterface(supports);
803
0
    if (NS_SUCCEEDED(rv)) {
804
0
      data->GetData(mHtmlString);
805
0
    }
806
0
    rv = transferable->GetTransferData(kHTMLContext, getter_AddRefs(supports),
807
0
                                       &dataSize);
808
0
    data = do_QueryInterface(supports);
809
0
    if (NS_SUCCEEDED(rv)) {
810
0
      data->GetData(mContextString);
811
0
    }
812
0
    rv = transferable->GetTransferData(kHTMLInfo, getter_AddRefs(supports),
813
0
                                       &dataSize);
814
0
    data = do_QueryInterface(supports);
815
0
    if (NS_SUCCEEDED(rv)) {
816
0
      data->GetData(mInfoString);
817
0
    }
818
0
    rv = transferable->GetTransferData(kUnicodeMime, getter_AddRefs(supports),
819
0
                                       &dataSize);
820
0
    data = do_QueryInterface(supports);
821
0
    NS_ENSURE_SUCCESS(rv, rv); // require plain text at a minimum
822
0
    data->GetData(mTitleString);
823
0
  }
824
0
825
0
  // default text value is the URL
826
0
  if (mTitleString.IsEmpty()) {
827
0
    mTitleString = mUrlString;
828
0
  }
829
0
830
0
  // if we haven't constructed a html version, make one now
831
0
  if (mHtmlString.IsEmpty() && !mUrlString.IsEmpty())
832
0
    CreateLinkText(mUrlString, mTitleString, mHtmlString);
833
0
834
0
  // if there is no drag node, which will be the case for a selection, just
835
0
  // use the selection target node.
836
0
  rv = AddStringsToDataTransfer(
837
0
         dragNode ? dragNode : mSelectionTargetNode.get(), aDataTransfer);
838
0
  NS_ENSURE_SUCCESS(rv, rv);
839
0
840
0
  NS_IF_ADDREF(*aDragNode = dragNode);
841
0
  return NS_OK;
842
0
}
843
844
void
845
DragDataProducer::AddString(DataTransfer* aDataTransfer,
846
                            const nsAString& aFlavor,
847
                            const nsAString& aData,
848
                            nsIPrincipal* aPrincipal,
849
                            bool aHidden)
850
0
{
851
0
  RefPtr<nsVariantCC> variant = new nsVariantCC();
852
0
  variant->SetAsAString(aData);
853
0
  aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal, aHidden);
854
0
}
855
856
nsresult
857
DragDataProducer::AddStringsToDataTransfer(nsIContent* aDragNode,
858
                                           DataTransfer* aDataTransfer)
859
0
{
860
0
  NS_ASSERTION(aDragNode, "adding strings for null node");
861
0
862
0
  // set all of the data to have the principal of the node where the data came from
863
0
  nsIPrincipal* principal = aDragNode->NodePrincipal();
864
0
865
0
  // add a special flavor if we're an anchor to indicate that we have
866
0
  // a URL in the drag data
867
0
  if (!mUrlString.IsEmpty() && mIsAnchor) {
868
0
    nsAutoString dragData(mUrlString);
869
0
    dragData.Append('\n');
870
0
    // Remove leading and trailing newlines in the title and replace them with
871
0
    // space in remaining positions - they confuse PlacesUtils::unwrapNodes
872
0
    // that expects url\ntitle formatted data for x-moz-url.
873
0
    nsAutoString title(mTitleString);
874
0
    title.Trim("\r\n");
875
0
    title.ReplaceChar("\r\n", ' ');
876
0
    dragData += title;
877
0
878
0
    AddString(aDataTransfer, NS_LITERAL_STRING(kURLMime), dragData, principal);
879
0
    AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal);
880
0
    AddString(aDataTransfer, NS_LITERAL_STRING(kURLDescriptionMime), mTitleString, principal);
881
0
    AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal);
882
0
  }
883
0
884
0
  // add a special flavor for the html context data
885
0
  if (!mContextString.IsEmpty())
886
0
    AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal);
887
0
888
0
  // add a special flavor if we have html info data
889
0
  if (!mInfoString.IsEmpty())
890
0
    AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), mInfoString, principal);
891
0
892
0
  // add the full html
893
0
  if (!mHtmlString.IsEmpty())
894
0
    AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal);
895
0
896
0
  // add the plain text. we use the url for text/plain data if an anchor is
897
0
  // being dragged, rather than the title text of the link or the alt text for
898
0
  // an anchor image.
899
0
  AddString(aDataTransfer, NS_LITERAL_STRING(kTextMime),
900
0
            mIsAnchor ? mUrlString : mTitleString, principal);
901
0
902
0
  // add image data, if present. For now, all we're going to do with
903
0
  // this is turn it into a native data flavor, so indicate that with
904
0
  // a new flavor so as not to confuse anyone who is really registered
905
0
  // for image/gif or image/jpg.
906
0
  if (mImage) {
907
0
    RefPtr<nsVariantCC> variant = new nsVariantCC();
908
0
    variant->SetAsISupports(mImage);
909
0
    aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kNativeImageMime),
910
0
                                        variant, 0, principal);
911
0
912
0
    // assume the image comes from a file, and add a file promise. We
913
0
    // register ourselves as a nsIFlavorDataProvider, and will use the
914
0
    // GetFlavorData callback to save the image to disk.
915
0
916
0
    nsCOMPtr<nsIFlavorDataProvider> dataProvider =
917
0
      new nsContentAreaDragDropDataProvider();
918
0
    if (dataProvider) {
919
0
      RefPtr<nsVariantCC> variant = new nsVariantCC();
920
0
      variant->SetAsISupports(dataProvider);
921
0
      aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kFilePromiseMime),
922
0
                                          variant, 0, principal);
923
0
    }
924
0
925
0
    AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseURLMime),
926
0
              mImageSourceString, principal);
927
0
    AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseDestFilename),
928
0
              mImageDestFileName, principal);
929
#if defined(XP_MACOSX)
930
    AddString(aDataTransfer, NS_LITERAL_STRING(kImageRequestMime),
931
              mImageRequestMime, principal, /* aHidden= */ true);
932
#endif
933
934
0
    // if not an anchor, add the image url
935
0
    if (!mIsAnchor) {
936
0
      AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal);
937
0
      AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal);
938
0
    }
939
0
  }
940
0
941
0
  return NS_OK;
942
0
}
943
944
// note that this can return NS_OK, but a null out param (by design)
945
// static
946
nsresult
947
DragDataProducer::GetDraggableSelectionData(Selection* inSelection,
948
                                            nsIContent* inRealTargetNode,
949
                                            nsIContent **outImageOrLinkNode,
950
                                            bool* outDragSelectedText)
951
0
{
952
0
  NS_ENSURE_ARG(inSelection);
953
0
  NS_ENSURE_ARG(inRealTargetNode);
954
0
  NS_ENSURE_ARG_POINTER(outImageOrLinkNode);
955
0
956
0
  *outImageOrLinkNode = nullptr;
957
0
  *outDragSelectedText = false;
958
0
959
0
  if (!inSelection->IsCollapsed()) {
960
0
    if (inSelection->ContainsNode(*inRealTargetNode, false, IgnoreErrors())) {
961
0
      // track down the anchor node, if any, for the url
962
0
      nsINode* selectionStart = inSelection->GetAnchorNode();
963
0
      nsINode* selectionEnd = inSelection->GetFocusNode();
964
0
965
0
      // look for a selection around a single node, like an image.
966
0
      // in this case, drag the image, rather than a serialization of the HTML
967
0
      // XXX generalize this to other draggable element types?
968
0
      if (selectionStart == selectionEnd) {
969
0
        nsCOMPtr<nsIContent> selStartContent = nsIContent::FromNodeOrNull(selectionStart);
970
0
        if (selStartContent && selStartContent->HasChildNodes()) {
971
0
          // see if just one node is selected
972
0
          uint32_t anchorOffset = inSelection->AnchorOffset();
973
0
          uint32_t focusOffset = inSelection->FocusOffset();
974
0
          if (anchorOffset == focusOffset + 1 ||
975
0
              focusOffset == anchorOffset + 1) {
976
0
            uint32_t childOffset = std::min(anchorOffset, focusOffset);
977
0
            nsIContent *childContent =
978
0
              selStartContent->GetChildAt_Deprecated(childOffset);
979
0
            // if we find an image, we'll fall into the node-dragging code,
980
0
            // rather the the selection-dragging code
981
0
            if (nsContentUtils::IsDraggableImage(childContent)) {
982
0
              NS_ADDREF(*outImageOrLinkNode = childContent);
983
0
              return NS_OK;
984
0
            }
985
0
          }
986
0
        }
987
0
      }
988
0
989
0
      // indicate that a link or text is selected
990
0
      *outDragSelectedText = true;
991
0
    }
992
0
  }
993
0
994
0
  return NS_OK;
995
0
}