Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/events/DataTransfer.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 "mozilla/ArrayUtils.h"
8
#include "mozilla/BasicEvents.h"
9
#include "mozilla/CheckedInt.h"
10
11
#include "DataTransfer.h"
12
13
#include "nsISupportsPrimitives.h"
14
#include "nsIScriptSecurityManager.h"
15
#include "mozilla/dom/DOMStringList.h"
16
#include "nsArray.h"
17
#include "nsError.h"
18
#include "nsIDragService.h"
19
#include "nsIClipboard.h"
20
#include "nsContentUtils.h"
21
#include "nsIContent.h"
22
#include "nsIObjectInputStream.h"
23
#include "nsIObjectOutputStream.h"
24
#include "nsIStorageStream.h"
25
#include "nsStringStream.h"
26
#include "nsCRT.h"
27
#include "nsIScriptObjectPrincipal.h"
28
#include "nsIScriptContext.h"
29
#include "nsIDocument.h"
30
#include "nsIScriptGlobalObject.h"
31
#include "nsVariant.h"
32
#include "mozilla/dom/ContentChild.h"
33
#include "mozilla/dom/DataTransferBinding.h"
34
#include "mozilla/dom/DataTransferItemList.h"
35
#include "mozilla/dom/Directory.h"
36
#include "mozilla/dom/Element.h"
37
#include "mozilla/dom/FileList.h"
38
#include "mozilla/dom/BindingUtils.h"
39
#include "mozilla/dom/OSFileSystem.h"
40
#include "mozilla/dom/Promise.h"
41
#include "nsNetUtil.h"
42
43
0
#define MOZ_CALLS_ENABLED_PREF "dom.datatransfer.mozAtAPIs"
44
45
namespace mozilla {
46
namespace dom {
47
48
NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
49
50
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
51
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
52
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems)
53
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
54
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
55
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
56
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
57
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer)
58
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
59
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems)
60
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
61
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
62
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
63
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DataTransfer)
64
65
NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer)
66
NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransfer)
67
68
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransfer)
69
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
70
0
  NS_INTERFACE_MAP_ENTRY(mozilla::dom::DataTransfer)
71
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
72
0
NS_INTERFACE_MAP_END
73
74
// the size of the array
75
const char DataTransfer::sEffects[8][9] = {
76
  "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"
77
};
78
79
// Used for custom clipboard types.
80
enum CustomClipboardTypeId {
81
  eCustomClipboardTypeId_None,
82
  eCustomClipboardTypeId_String
83
};
84
85
// The dom.events.dataTransfer.protected.enabled preference controls whether or
86
// not the `protected` dataTransfer state is enabled. If the `protected`
87
// dataTransfer stae is disabled, then the DataTransfer will be read-only
88
// whenever it should be protected, and will not be disconnected after a drag
89
// event is completed.
90
static bool
91
PrefProtected()
92
0
{
93
0
  static bool sInitialized = false;
94
0
  static bool sValue = false;
95
0
  if (!sInitialized) {
96
0
    sInitialized = true;
97
0
    Preferences::AddBoolVarCache(&sValue, "dom.events.dataTransfer.protected.enabled");
98
0
  }
99
0
  return sValue;
100
0
}
101
102
static DataTransfer::Mode
103
ModeForEvent(EventMessage aEventMessage)
104
0
{
105
0
  switch (aEventMessage) {
106
0
  case eCut:
107
0
  case eCopy:
108
0
  case eDragStart:
109
0
    // For these events, we want to be able to add data to the data transfer,
110
0
    // Otherwise, the data is already present.
111
0
    return DataTransfer::Mode::ReadWrite;
112
0
  case eDrop:
113
0
  case ePaste:
114
0
  case ePasteNoFormatting:
115
0
    // For these events we want to be able to read the data which is stored in
116
0
    // the DataTransfer, rather than just the type information.
117
0
    return DataTransfer::Mode::ReadOnly;
118
0
  default:
119
0
    return PrefProtected()
120
0
      ? DataTransfer::Mode::Protected
121
0
      : DataTransfer::Mode::ReadOnly;
122
0
  }
123
0
}
124
125
DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
126
                           bool aIsExternal, int32_t aClipboardType)
127
  : mParent(aParent)
128
  , mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE)
129
  , mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
130
  , mEventMessage(aEventMessage)
131
  , mCursorState(false)
132
  , mMode(ModeForEvent(aEventMessage))
133
  , mIsExternal(aIsExternal)
134
  , mUserCancelled(false)
135
  , mIsCrossDomainSubFrameDrop(false)
136
  , mClipboardType(aClipboardType)
137
  , mDragImageX(0)
138
  , mDragImageY(0)
139
0
{
140
0
  mItems = new DataTransferItemList(this, aIsExternal);
141
0
142
0
  // For external usage, cache the data from the native clipboard or drag.
143
0
  if (mIsExternal && mMode != Mode::ReadWrite) {
144
0
    if (aEventMessage == ePasteNoFormatting) {
145
0
      mEventMessage = ePaste;
146
0
      CacheExternalClipboardFormats(true);
147
0
    } else if (aEventMessage == ePaste) {
148
0
      CacheExternalClipboardFormats(false);
149
0
    } else if (aEventMessage >= eDragDropEventFirst &&
150
0
               aEventMessage <= eDragDropEventLast) {
151
0
      CacheExternalDragFormats();
152
0
    }
153
0
  }
154
0
}
155
156
DataTransfer::DataTransfer(nsISupports* aParent,
157
                           EventMessage aEventMessage,
158
                           const uint32_t aEffectAllowed,
159
                           bool aCursorState,
160
                           bool aIsExternal,
161
                           bool aUserCancelled,
162
                           bool aIsCrossDomainSubFrameDrop,
163
                           int32_t aClipboardType,
164
                           DataTransferItemList* aItems,
165
                           Element* aDragImage,
166
                           uint32_t aDragImageX,
167
                           uint32_t aDragImageY)
168
  : mParent(aParent)
169
  , mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE)
170
  , mEffectAllowed(aEffectAllowed)
171
  , mEventMessage(aEventMessage)
172
  , mCursorState(aCursorState)
173
  , mMode(ModeForEvent(aEventMessage))
174
  , mIsExternal(aIsExternal)
175
  , mUserCancelled(aUserCancelled)
176
  , mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop)
177
  , mClipboardType(aClipboardType)
178
  , mDragImage(aDragImage)
179
  , mDragImageX(aDragImageX)
180
  , mDragImageY(aDragImageY)
181
0
{
182
0
  MOZ_ASSERT(mParent);
183
0
  MOZ_ASSERT(aItems);
184
0
185
0
  // We clone the items array after everything else, so that it has a valid
186
0
  // mParent value
187
0
  mItems = aItems->Clone(this);
188
0
  // The items are copied from aItems into mItems. There is no need to copy
189
0
  // the actual data in the items as the data transfer will be read only. The
190
0
  // dragstart event is the only time when items are
191
0
  // modifiable, but those events should have been using the first constructor
192
0
  // above.
193
0
  NS_ASSERTION(aEventMessage != eDragStart,
194
0
               "invalid event type for DataTransfer constructor");
195
0
}
196
197
DataTransfer::~DataTransfer()
198
0
{}
199
200
// static
201
already_AddRefed<DataTransfer>
202
DataTransfer::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
203
0
{
204
0
205
0
  RefPtr<DataTransfer> transfer = new DataTransfer(aGlobal.GetAsSupports(),
206
0
                                                   eCopy, /* is external */ false, /* clipboard type */ -1);
207
0
  transfer->mEffectAllowed = nsIDragService::DRAGDROP_ACTION_NONE;
208
0
  return transfer.forget();
209
0
}
210
211
JSObject*
212
DataTransfer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
213
0
{
214
0
  return DataTransfer_Binding::Wrap(aCx, this, aGivenProto);
215
0
}
216
217
void
218
DataTransfer::SetDropEffect(const nsAString& aDropEffect)
219
0
{
220
0
  // the drop effect can only be 'none', 'copy', 'move' or 'link'.
221
0
  for (uint32_t e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) {
222
0
    if (aDropEffect.EqualsASCII(sEffects[e])) {
223
0
      // don't allow copyMove
224
0
      if (e != (nsIDragService::DRAGDROP_ACTION_COPY |
225
0
                nsIDragService::DRAGDROP_ACTION_MOVE)) {
226
0
        mDropEffect = e;
227
0
      }
228
0
      break;
229
0
    }
230
0
  }
231
0
}
232
233
void
234
DataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed)
235
0
{
236
0
  if (aEffectAllowed.EqualsLiteral("uninitialized")) {
237
0
    mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
238
0
    return;
239
0
  }
240
0
241
0
  static_assert(nsIDragService::DRAGDROP_ACTION_NONE == 0,
242
0
                "DRAGDROP_ACTION_NONE constant is wrong");
243
0
  static_assert(nsIDragService::DRAGDROP_ACTION_COPY == 1,
244
0
                "DRAGDROP_ACTION_COPY constant is wrong");
245
0
  static_assert(nsIDragService::DRAGDROP_ACTION_MOVE == 2,
246
0
                "DRAGDROP_ACTION_MOVE constant is wrong");
247
0
  static_assert(nsIDragService::DRAGDROP_ACTION_LINK == 4,
248
0
                "DRAGDROP_ACTION_LINK constant is wrong");
249
0
250
0
  for (uint32_t e = 0; e < ArrayLength(sEffects); e++) {
251
0
    if (aEffectAllowed.EqualsASCII(sEffects[e])) {
252
0
      mEffectAllowed = e;
253
0
      break;
254
0
    }
255
0
  }
256
0
}
257
258
void
259
DataTransfer::GetMozTriggeringPrincipalURISpec(nsAString& aPrincipalURISpec)
260
0
{
261
0
  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
262
0
  if (!dragSession) {
263
0
    aPrincipalURISpec.Truncate(0);
264
0
    return;
265
0
  }
266
0
267
0
  nsCString principalURISpec;
268
0
  dragSession->GetTriggeringPrincipalURISpec(principalURISpec);
269
0
  CopyUTF8toUTF16(principalURISpec, aPrincipalURISpec);
270
0
}
271
272
already_AddRefed<FileList>
273
DataTransfer::GetFiles(nsIPrincipal& aSubjectPrincipal)
274
0
{
275
0
  return mItems->Files(&aSubjectPrincipal);
276
0
}
277
278
void
279
DataTransfer::GetTypes(nsTArray<nsString>& aTypes, CallerType aCallerType) const
280
0
{
281
0
  // When called from bindings, aTypes will be empty, but since we might have
282
0
  // Gecko-internal callers too, clear it to be safe.
283
0
  aTypes.Clear();
284
0
285
0
  const nsTArray<RefPtr<DataTransferItem>>* items = mItems->MozItemsAt(0);
286
0
  if (NS_WARN_IF(!items)) {
287
0
    return;
288
0
  }
289
0
290
0
  for (uint32_t i = 0; i < items->Length(); i++) {
291
0
    DataTransferItem* item = items->ElementAt(i);
292
0
    MOZ_ASSERT(item);
293
0
294
0
    if (item->ChromeOnly() && aCallerType != CallerType::System) {
295
0
      continue;
296
0
    }
297
0
298
0
    // NOTE: The reason why we get the internal type here is because we want
299
0
    // kFileMime to appear in the types list for backwards compatibility
300
0
    // reasons.
301
0
    nsAutoString type;
302
0
    item->GetInternalType(type);
303
0
    if (item->Kind() != DataTransferItem::KIND_FILE || type.EqualsASCII(kFileMime)) {
304
0
      // If the entry has kind KIND_STRING or KIND_OTHER we want to add it to the list.
305
0
      aTypes.AppendElement(type);
306
0
    }
307
0
  }
308
0
309
0
  for (uint32_t i = 0; i < mItems->Length(); ++i) {
310
0
    bool found = false;
311
0
    DataTransferItem* item = mItems->IndexedGetter(i, found);
312
0
    MOZ_ASSERT(found);
313
0
    if (item->Kind() != DataTransferItem::KIND_FILE) {
314
0
      continue;
315
0
    }
316
0
    aTypes.AppendElement(NS_LITERAL_STRING("Files"));
317
0
    break;
318
0
  }
319
0
}
320
321
void
322
DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
323
                      nsIPrincipal& aSubjectPrincipal,
324
                      ErrorResult& aRv)
325
0
{
326
0
  // return an empty string if data for the format was not found
327
0
  aData.Truncate();
328
0
329
0
  nsCOMPtr<nsIVariant> data;
330
0
  nsresult rv =
331
0
    GetDataAtInternal(aFormat, 0, &aSubjectPrincipal,
332
0
                      getter_AddRefs(data));
333
0
  if (NS_FAILED(rv)) {
334
0
    if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
335
0
      aRv.Throw(rv);
336
0
    }
337
0
    return;
338
0
  }
339
0
340
0
  if (data) {
341
0
    nsAutoString stringdata;
342
0
    data->GetAsAString(stringdata);
343
0
344
0
    // for the URL type, parse out the first URI from the list. The URIs are
345
0
    // separated by newlines
346
0
    nsAutoString lowercaseFormat;
347
0
    nsContentUtils::ASCIIToLower(aFormat, lowercaseFormat);
348
0
349
0
    if (lowercaseFormat.EqualsLiteral("url")) {
350
0
      int32_t lastidx = 0, idx;
351
0
      int32_t length = stringdata.Length();
352
0
      while (lastidx < length) {
353
0
        idx = stringdata.FindChar('\n', lastidx);
354
0
        // lines beginning with # are comments
355
0
        if (stringdata[lastidx] == '#') {
356
0
          if (idx == -1) {
357
0
            break;
358
0
          }
359
0
        }
360
0
        else {
361
0
          if (idx == -1) {
362
0
            aData.Assign(Substring(stringdata, lastidx));
363
0
          } else {
364
0
            aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
365
0
          }
366
0
          aData = nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(aData,
367
0
                                                                      true);
368
0
          return;
369
0
        }
370
0
        lastidx = idx + 1;
371
0
      }
372
0
    }
373
0
    else {
374
0
      aData = stringdata;
375
0
    }
376
0
  }
377
0
}
378
379
void
380
DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
381
                      nsIPrincipal& aSubjectPrincipal,
382
                      ErrorResult& aRv)
383
0
{
384
0
  RefPtr<nsVariantCC> variant = new nsVariantCC();
385
0
  variant->SetAsAString(aData);
386
0
387
0
  aRv = SetDataAtInternal(aFormat, variant, 0, &aSubjectPrincipal);
388
0
}
389
390
void
391
DataTransfer::ClearData(const Optional<nsAString>& aFormat,
392
                        nsIPrincipal& aSubjectPrincipal,
393
                        ErrorResult& aRv)
394
0
{
395
0
  if (IsReadOnly()) {
396
0
    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
397
0
    return;
398
0
  }
399
0
400
0
  if (MozItemCount() == 0) {
401
0
    return;
402
0
  }
403
0
404
0
  if (aFormat.WasPassed()) {
405
0
    MozClearDataAtHelper(aFormat.Value(), 0, aSubjectPrincipal, aRv);
406
0
  } else {
407
0
    MozClearDataAtHelper(EmptyString(), 0, aSubjectPrincipal, aRv);
408
0
  }
409
0
}
410
411
void
412
DataTransfer::SetMozCursor(const nsAString& aCursorState)
413
0
{
414
0
  // Lock the cursor to an arrow during the drag.
415
0
  mCursorState = aCursorState.EqualsLiteral("default");
416
0
}
417
418
already_AddRefed<nsINode>
419
DataTransfer::GetMozSourceNode()
420
0
{
421
0
  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
422
0
  if (!dragSession) {
423
0
    return nullptr;
424
0
  }
425
0
426
0
  nsCOMPtr<nsINode> sourceNode;
427
0
  dragSession->GetSourceNode(getter_AddRefs(sourceNode));
428
0
  if (sourceNode && !nsContentUtils::LegacyIsCallerNativeCode()
429
0
      && !nsContentUtils::CanCallerAccess(sourceNode)) {
430
0
    return nullptr;
431
0
  }
432
0
433
0
  return sourceNode.forget();
434
0
}
435
436
already_AddRefed<DOMStringList>
437
DataTransfer::MozTypesAt(uint32_t aIndex, CallerType aCallerType,
438
                         ErrorResult& aRv) const
439
0
{
440
0
  // Only the first item is valid for clipboard events
441
0
  if (aIndex > 0 &&
442
0
      (mEventMessage == eCut || mEventMessage == eCopy ||
443
0
       mEventMessage == ePaste)) {
444
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
445
0
    return nullptr;
446
0
  }
447
0
448
0
  RefPtr<DOMStringList> types = new DOMStringList();
449
0
  if (aIndex < MozItemCount()) {
450
0
    // note that you can retrieve the types regardless of their principal
451
0
    const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(aIndex);
452
0
453
0
    bool addFile = false;
454
0
    for (uint32_t i = 0; i < items.Length(); i++) {
455
0
      if (items[i]->ChromeOnly() && aCallerType != CallerType::System) {
456
0
        continue;
457
0
      }
458
0
459
0
      // NOTE: The reason why we get the internal type here is because we want
460
0
      // kFileMime to appear in the types list for backwards compatibility
461
0
      // reasons.
462
0
      nsAutoString type;
463
0
      items[i]->GetInternalType(type);
464
0
      if (NS_WARN_IF(!types->Add(type))) {
465
0
        aRv.Throw(NS_ERROR_FAILURE);
466
0
        return nullptr;
467
0
      }
468
0
469
0
      if (items[i]->Kind() == DataTransferItem::KIND_FILE) {
470
0
        addFile = true;
471
0
      }
472
0
    }
473
0
474
0
    if (addFile) {
475
0
      types->Add(NS_LITERAL_STRING("Files"));
476
0
    }
477
0
  }
478
0
479
0
  return types.forget();
480
0
}
481
482
nsresult
483
DataTransfer::GetDataAtNoSecurityCheck(const nsAString& aFormat,
484
                                       uint32_t aIndex,
485
                                       nsIVariant** aData)
486
0
{
487
0
  return GetDataAtInternal(aFormat, aIndex,
488
0
                           nsContentUtils::GetSystemPrincipal(), aData);
489
0
}
490
491
nsresult
492
DataTransfer::GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
493
                                nsIPrincipal* aSubjectPrincipal,
494
                                nsIVariant** aData)
495
0
{
496
0
  *aData = nullptr;
497
0
498
0
  if (aFormat.IsEmpty()) {
499
0
    return NS_OK;
500
0
  }
501
0
502
0
  if (aIndex >= MozItemCount()) {
503
0
    return NS_ERROR_DOM_INDEX_SIZE_ERR;
504
0
  }
505
0
506
0
  // Only the first item is valid for clipboard events
507
0
  if (aIndex > 0 &&
508
0
      (mEventMessage == eCut || mEventMessage == eCopy ||
509
0
       mEventMessage == ePaste)) {
510
0
    return NS_ERROR_DOM_INDEX_SIZE_ERR;
511
0
  }
512
0
513
0
  nsAutoString format;
514
0
  GetRealFormat(aFormat, format);
515
0
516
0
  MOZ_ASSERT(aSubjectPrincipal);
517
0
518
0
  RefPtr<DataTransferItem> item = mItems->MozItemByTypeAt(format, aIndex);
519
0
  if (!item) {
520
0
    // The index exists but there's no data for the specified format, in this
521
0
    // case we just return undefined
522
0
    return NS_OK;
523
0
  }
524
0
525
0
  // If we have chrome only content, and we aren't chrome, don't allow access
526
0
  if (!nsContentUtils::IsSystemPrincipal(aSubjectPrincipal) && item->ChromeOnly()) {
527
0
    return NS_OK;
528
0
  }
529
0
530
0
  // DataTransferItem::Data() handles the principal checks
531
0
  ErrorResult result;
532
0
  nsCOMPtr<nsIVariant> data = item->Data(aSubjectPrincipal, result);
533
0
  if (NS_WARN_IF(!data || result.Failed())) {
534
0
    return result.StealNSResult();
535
0
  }
536
0
537
0
  data.forget(aData);
538
0
  return NS_OK;
539
0
}
540
541
void
542
DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
543
                           uint32_t aIndex,
544
                           JS::MutableHandle<JS::Value> aRetval,
545
                           nsIPrincipal& aSubjectPrincipal,
546
                           mozilla::ErrorResult& aRv)
547
0
{
548
0
  nsCOMPtr<nsIVariant> data;
549
0
  aRv = GetDataAtInternal(aFormat, aIndex, &aSubjectPrincipal,
550
0
                          getter_AddRefs(data));
551
0
  if (aRv.Failed()) {
552
0
    return;
553
0
  }
554
0
555
0
  if (!data) {
556
0
    aRetval.setNull();
557
0
    return;
558
0
  }
559
0
560
0
  JS::Rooted<JS::Value> result(aCx);
561
0
  if (!VariantToJsval(aCx, data, aRetval)) {
562
0
    aRv = NS_ERROR_FAILURE;
563
0
    return;
564
0
  }
565
0
}
566
567
/* static */ bool
568
DataTransfer::PrincipalMaySetData(const nsAString& aType,
569
                                  nsIVariant* aData,
570
                                  nsIPrincipal* aPrincipal)
571
0
{
572
0
  if (!nsContentUtils::IsSystemPrincipal(aPrincipal)) {
573
0
    DataTransferItem::eKind kind = DataTransferItem::KindFromData(aData);
574
0
    if (kind == DataTransferItem::KIND_OTHER) {
575
0
      NS_WARNING("Disallowing adding non string/file types to DataTransfer");
576
0
      return false;
577
0
    }
578
0
579
0
    if (aType.EqualsASCII(kFileMime) ||
580
0
        aType.EqualsASCII(kFilePromiseMime)) {
581
0
      NS_WARNING("Disallowing adding x-moz-file or x-moz-file-promize types to DataTransfer");
582
0
      return false;
583
0
    }
584
0
  }
585
0
  return true;
586
0
}
587
588
void
589
DataTransfer::TypesListMayHaveChanged()
590
0
{
591
0
  DataTransfer_Binding::ClearCachedTypesValue(this);
592
0
}
593
594
already_AddRefed<DataTransfer>
595
DataTransfer::MozCloneForEvent(const nsAString& aEvent, ErrorResult& aRv)
596
0
{
597
0
  RefPtr<nsAtom> atomEvt = NS_Atomize(aEvent);
598
0
  if (!atomEvt) {
599
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
600
0
    return nullptr;
601
0
  }
602
0
  EventMessage eventMessage = nsContentUtils::GetEventMessage(atomEvt);
603
0
604
0
  RefPtr<DataTransfer> dt;
605
0
  nsresult rv = Clone(mParent, eventMessage, false, false, getter_AddRefs(dt));
606
0
  if (NS_FAILED(rv)) {
607
0
    aRv.Throw(rv);
608
0
    return nullptr;
609
0
  }
610
0
  return dt.forget();
611
0
}
612
613
/* static */
614
void
615
DataTransfer::GetExternalClipboardFormats(const int32_t& aWhichClipboard,
616
                                         const bool& aPlainTextOnly,
617
                                         nsTArray<nsCString>* aResult)
618
0
{
619
0
  MOZ_ASSERT(aResult);
620
0
  nsCOMPtr<nsIClipboard> clipboard =
621
0
    do_GetService("@mozilla.org/widget/clipboard;1");
622
0
  if (!clipboard || aWhichClipboard < 0) {
623
0
    return;
624
0
  }
625
0
626
0
  if (aPlainTextOnly) {
627
0
    bool hasType;
628
0
    static const char * unicodeMime[] = { kUnicodeMime };
629
0
    nsresult rv = clipboard->HasDataMatchingFlavors(unicodeMime,
630
0
                                                    /* number of flavors to check */ 1,
631
0
                                                    aWhichClipboard, &hasType);
632
0
    NS_SUCCEEDED(rv);
633
0
    if (hasType) {
634
0
      aResult->AppendElement(kUnicodeMime);
635
0
    }
636
0
    return;
637
0
  }
638
0
639
0
  // If not plain text only, then instead check all the other types
640
0
  static const char * formats[] = { kCustomTypesMime, kFileMime, kHTMLMime, kRTFMime,
641
0
                                  kURLMime, kURLDataMime, kUnicodeMime, kPNGImageMime };
642
0
643
0
  for (uint32_t f = 0; f < mozilla::ArrayLength(formats); ++f) {
644
0
    bool hasType;
645
0
    nsresult rv = clipboard->HasDataMatchingFlavors(&(formats[f]),
646
0
                                                    /* number of flavors to check */ 1,
647
0
                                                    aWhichClipboard, &hasType);
648
0
    NS_SUCCEEDED(rv);
649
0
    if (hasType) {
650
0
      aResult->AppendElement(formats[f]);
651
0
    }
652
0
  }
653
0
}
654
655
nsresult
656
DataTransfer::SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData,
657
                                uint32_t aIndex,
658
                                nsIPrincipal* aSubjectPrincipal)
659
0
{
660
0
  if (aFormat.IsEmpty()) {
661
0
    return NS_OK;
662
0
  }
663
0
664
0
  if (IsReadOnly()) {
665
0
    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
666
0
  }
667
0
668
0
  // Specifying an index less than the current length will replace an existing
669
0
  // item. Specifying an index equal to the current length will add a new item.
670
0
  if (aIndex > MozItemCount()) {
671
0
    return NS_ERROR_DOM_INDEX_SIZE_ERR;
672
0
  }
673
0
674
0
  // Only the first item is valid for clipboard events
675
0
  if (aIndex > 0 &&
676
0
      (mEventMessage == eCut || mEventMessage == eCopy ||
677
0
       mEventMessage == ePaste)) {
678
0
    return NS_ERROR_DOM_INDEX_SIZE_ERR;
679
0
  }
680
0
681
0
  // Don't allow the custom type to be assigned.
682
0
  if (aFormat.EqualsLiteral(kCustomTypesMime)) {
683
0
    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
684
0
  }
685
0
686
0
  if (!PrincipalMaySetData(aFormat, aData, aSubjectPrincipal)) {
687
0
    return NS_ERROR_DOM_SECURITY_ERR;
688
0
  }
689
0
690
0
  return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
691
0
}
692
693
void
694
DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
695
                           JS::Handle<JS::Value> aData, uint32_t aIndex,
696
                           nsIPrincipal& aSubjectPrincipal,
697
                           ErrorResult& aRv)
698
0
{
699
0
  nsCOMPtr<nsIVariant> data;
700
0
  aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
701
0
                                                    getter_AddRefs(data));
702
0
  if (!aRv.Failed()) {
703
0
    aRv = SetDataAtInternal(aFormat, data, aIndex, &aSubjectPrincipal);
704
0
  }
705
0
}
706
707
void
708
DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
709
                             nsIPrincipal& aSubjectPrincipal,
710
                             ErrorResult& aRv)
711
0
{
712
0
  if (IsReadOnly()) {
713
0
    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
714
0
    return;
715
0
  }
716
0
717
0
  if (aIndex >= MozItemCount()) {
718
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
719
0
    return;
720
0
  }
721
0
722
0
  // Only the first item is valid for clipboard events
723
0
  if (aIndex > 0 &&
724
0
      (mEventMessage == eCut || mEventMessage == eCopy ||
725
0
       mEventMessage == ePaste)) {
726
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
727
0
    return;
728
0
  }
729
0
730
0
  MozClearDataAtHelper(aFormat, aIndex, aSubjectPrincipal, aRv);
731
0
732
0
  // If we just cleared the 0-th index, and there are still more than 1 indexes
733
0
  // remaining, MozClearDataAt should cause the 1st index to become the 0th
734
0
  // index. This should _only_ happen when the MozClearDataAt function is
735
0
  // explicitly called by script, as this behavior is inconsistent with spec.
736
0
  // (however, so is the MozClearDataAt API)
737
0
738
0
  if (aIndex == 0 && mItems->MozItemCount() > 1 &&
739
0
      mItems->MozItemsAt(0)->Length() == 0) {
740
0
    mItems->PopIndexZero();
741
0
  }
742
0
}
743
744
void
745
DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
746
                                   nsIPrincipal& aSubjectPrincipal,
747
                                   ErrorResult& aRv)
748
0
{
749
0
  MOZ_ASSERT(!IsReadOnly());
750
0
  MOZ_ASSERT(aIndex < MozItemCount());
751
0
  MOZ_ASSERT(aIndex == 0 ||
752
0
             (mEventMessage != eCut && mEventMessage != eCopy &&
753
0
              mEventMessage != ePaste));
754
0
755
0
  nsAutoString format;
756
0
  GetRealFormat(aFormat, format);
757
0
758
0
  mItems->MozRemoveByTypeAt(format, aIndex, aSubjectPrincipal, aRv);
759
0
}
760
761
void
762
DataTransfer::SetDragImage(Element& aImage, int32_t aX, int32_t aY)
763
0
{
764
0
  if (!IsReadOnly()) {
765
0
    mDragImage = &aImage;
766
0
    mDragImageX = aX;
767
0
    mDragImageY = aY;
768
0
  }
769
0
}
770
771
void
772
DataTransfer::UpdateDragImage(Element& aImage, int32_t aX, int32_t aY)
773
0
{
774
0
  if (mEventMessage < eDragDropEventFirst || mEventMessage > eDragDropEventLast) {
775
0
    return;
776
0
  }
777
0
778
0
  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
779
0
  if (dragSession) {
780
0
    dragSession->UpdateDragImage(&aImage, aX, aY);
781
0
  }
782
0
}
783
784
already_AddRefed<Promise>
785
DataTransfer::GetFilesAndDirectories(nsIPrincipal& aSubjectPrincipal,
786
                                     ErrorResult& aRv)
787
0
{
788
0
  nsCOMPtr<nsINode> parentNode = do_QueryInterface(mParent);
789
0
  if (!parentNode) {
790
0
    aRv.Throw(NS_ERROR_FAILURE);
791
0
    return nullptr;
792
0
  }
793
0
794
0
  nsCOMPtr<nsIGlobalObject> global = parentNode->OwnerDoc()->GetScopeObject();
795
0
  MOZ_ASSERT(global);
796
0
  if (!global) {
797
0
    aRv.Throw(NS_ERROR_FAILURE);
798
0
    return nullptr;
799
0
  }
800
0
801
0
  RefPtr<Promise> p = Promise::Create(global, aRv);
802
0
  if (NS_WARN_IF(aRv.Failed())) {
803
0
    return nullptr;
804
0
  }
805
0
806
0
  RefPtr<FileList> files = mItems->Files(&aSubjectPrincipal);
807
0
  if (NS_WARN_IF(!files)) {
808
0
    return nullptr;
809
0
  }
810
0
811
0
  Sequence<RefPtr<File>> filesSeq;
812
0
  files->ToSequence(filesSeq, aRv);
813
0
  if (NS_WARN_IF(aRv.Failed())) {
814
0
    return nullptr;
815
0
  }
816
0
817
0
  p->MaybeResolve(filesSeq);
818
0
819
0
  return p.forget();
820
0
}
821
822
already_AddRefed<Promise>
823
DataTransfer::GetFiles(bool aRecursiveFlag,
824
                       nsIPrincipal& aSubjectPrincipal,
825
                       ErrorResult& aRv)
826
0
{
827
0
  // Currently we don't support directories.
828
0
  return GetFilesAndDirectories(aSubjectPrincipal, aRv);
829
0
}
830
831
void
832
DataTransfer::AddElement(Element& aElement, ErrorResult& aRv)
833
0
{
834
0
  if (IsReadOnly()) {
835
0
    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
836
0
    return;
837
0
  }
838
0
839
0
  mDragTarget = &aElement;
840
0
}
841
842
nsresult
843
DataTransfer::Clone(nsISupports* aParent, EventMessage aEventMessage,
844
                    bool aUserCancelled, bool aIsCrossDomainSubFrameDrop,
845
                    DataTransfer** aNewDataTransfer)
846
0
{
847
0
  RefPtr<DataTransfer> newDataTransfer =
848
0
    new DataTransfer(aParent, aEventMessage, mEffectAllowed, mCursorState,
849
0
                     mIsExternal, aUserCancelled, aIsCrossDomainSubFrameDrop,
850
0
                     mClipboardType, mItems, mDragImage, mDragImageX,
851
0
                     mDragImageY);
852
0
853
0
  newDataTransfer.forget(aNewDataTransfer);
854
0
  return NS_OK;
855
0
}
856
857
already_AddRefed<nsIArray>
858
DataTransfer::GetTransferables(nsINode* aDragTarget)
859
0
{
860
0
  MOZ_ASSERT(aDragTarget);
861
0
862
0
  nsIDocument* doc = aDragTarget->GetComposedDoc();
863
0
  if (!doc) {
864
0
    return nullptr;
865
0
  }
866
0
867
0
  return GetTransferables(doc->GetLoadContext());
868
0
}
869
870
already_AddRefed<nsIArray>
871
DataTransfer::GetTransferables(nsILoadContext* aLoadContext)
872
0
{
873
0
  nsCOMPtr<nsIMutableArray> transArray = nsArray::Create();
874
0
  if (!transArray) {
875
0
    return nullptr;
876
0
  }
877
0
878
0
  uint32_t count = MozItemCount();
879
0
  for (uint32_t i = 0; i < count; i++) {
880
0
    nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext);
881
0
    if (transferable) {
882
0
      transArray->AppendElement(transferable);
883
0
    }
884
0
  }
885
0
886
0
  return transArray.forget();
887
0
}
888
889
already_AddRefed<nsITransferable>
890
DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
891
0
{
892
0
  if (aIndex >= MozItemCount()) {
893
0
    return nullptr;
894
0
  }
895
0
896
0
  const nsTArray<RefPtr<DataTransferItem>>& item = *mItems->MozItemsAt(aIndex);
897
0
  uint32_t count = item.Length();
898
0
  if (!count) {
899
0
    return nullptr;
900
0
  }
901
0
902
0
  nsCOMPtr<nsITransferable> transferable =
903
0
    do_CreateInstance("@mozilla.org/widget/transferable;1");
904
0
  if (!transferable) {
905
0
    return nullptr;
906
0
  }
907
0
  transferable->Init(aLoadContext);
908
0
909
0
  nsCOMPtr<nsIStorageStream> storageStream;
910
0
  nsCOMPtr<nsIObjectOutputStream> stream;
911
0
912
0
  bool added = false;
913
0
  bool handlingCustomFormats = true;
914
0
915
0
  // When writing the custom data, we need to ensure that there is sufficient
916
0
  // space for a (uint32_t) data ending type, and the null byte character at
917
0
  // the end of the nsCString. We claim that space upfront and store it in
918
0
  // baseLength. This value will be set to zero if a write error occurs
919
0
  // indicating that the data and length are no longer valid.
920
0
  const uint32_t baseLength = sizeof(uint32_t) + 1;
921
0
  uint32_t totalCustomLength = baseLength;
922
0
923
0
  const char* knownFormats[] = {
924
0
    kTextMime, kHTMLMime, kNativeHTMLMime, kRTFMime,
925
0
    kURLMime, kURLDataMime, kURLDescriptionMime, kURLPrivateMime,
926
0
    kPNGImageMime, kJPEGImageMime, kGIFImageMime, kNativeImageMime,
927
0
    kFileMime, kFilePromiseMime, kFilePromiseURLMime,
928
0
    kFilePromiseDestFilename, kFilePromiseDirectoryMime,
929
0
    kMozTextInternal, kHTMLContext, kHTMLInfo, kImageRequestMime };
930
0
931
0
  /*
932
0
   * Two passes are made here to iterate over all of the types. First, look for
933
0
   * any types that are not in the list of known types. For this pass,
934
0
   * handlingCustomFormats will be true. Data that corresponds to unknown types
935
0
   * will be pulled out and inserted into a single type (kCustomTypesMime) by
936
0
   * writing the data into a stream.
937
0
   *
938
0
   * The second pass will iterate over the formats looking for known types.
939
0
   * These are added as is. The unknown types are all then inserted as a single
940
0
   * type (kCustomTypesMime) in the same position of the first custom type. This
941
0
   * model is used to maintain the format order as best as possible.
942
0
   *
943
0
   * The format of the kCustomTypesMime type is one or more of the following
944
0
   * stored sequentially:
945
0
   *   <32-bit> type (only none or string is supported)
946
0
   *   <32-bit> length of format
947
0
   *   <wide string> format
948
0
   *   <32-bit> length of data
949
0
   *   <wide string> data
950
0
   * A type of eCustomClipboardTypeId_None ends the list, without any following
951
0
   * data.
952
0
   */
953
0
  do {
954
0
    for (uint32_t f = 0; f < count; f++) {
955
0
      RefPtr<DataTransferItem> formatitem = item[f];
956
0
      nsCOMPtr<nsIVariant> variant = formatitem->DataNoSecurityCheck();
957
0
      if (!variant) { // skip empty items
958
0
        continue;
959
0
      }
960
0
961
0
      nsAutoString type;
962
0
      formatitem->GetInternalType(type);
963
0
964
0
      // If the data is of one of the well-known formats, use it directly.
965
0
      bool isCustomFormat = true;
966
0
      for (uint32_t f = 0; f < ArrayLength(knownFormats); f++) {
967
0
        if (type.EqualsASCII(knownFormats[f])) {
968
0
          isCustomFormat = false;
969
0
          break;
970
0
        }
971
0
      }
972
0
973
0
      uint32_t lengthInBytes;
974
0
      nsCOMPtr<nsISupports> convertedData;
975
0
976
0
      if (handlingCustomFormats) {
977
0
        if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
978
0
                                &lengthInBytes)) {
979
0
          continue;
980
0
        }
981
0
982
0
        // When handling custom types, add the data to the stream if this is a
983
0
        // custom type. If totalCustomLength is 0, then a write error occurred
984
0
        // on a previous item, so ignore any others.
985
0
        if (isCustomFormat && totalCustomLength > 0) {
986
0
          // If it isn't a string, just ignore it. The dataTransfer is cached in
987
0
          // the drag sesion during drag-and-drop, so non-strings will be
988
0
          // available when dragging locally.
989
0
          nsCOMPtr<nsISupportsString> str(do_QueryInterface(convertedData));
990
0
          if (str) {
991
0
            nsAutoString data;
992
0
            str->GetData(data);
993
0
994
0
            if (!stream) {
995
0
              // Create a storage stream to write to.
996
0
              NS_NewStorageStream(1024, UINT32_MAX, getter_AddRefs(storageStream));
997
0
998
0
              nsCOMPtr<nsIOutputStream> outputStream;
999
0
              storageStream->GetOutputStream(0, getter_AddRefs(outputStream));
1000
0
1001
0
              stream = NS_NewObjectOutputStream(outputStream);
1002
0
            }
1003
0
1004
0
            CheckedInt<uint32_t> formatLength =
1005
0
              CheckedInt<uint32_t>(type.Length()) * sizeof(nsString::char_type);
1006
0
1007
0
            // The total size of the stream is the format length, the data
1008
0
            // length, two integers to hold the lengths and one integer for
1009
0
            // the string flag. Guard against large data by ignoring any that
1010
0
            // don't fit.
1011
0
            CheckedInt<uint32_t> newSize = formatLength + totalCustomLength +
1012
0
                                           lengthInBytes + (sizeof(uint32_t) * 3);
1013
0
            if (newSize.isValid()) {
1014
0
              // If a write error occurs, set totalCustomLength to 0 so that
1015
0
              // further processing gets ignored.
1016
0
              nsresult rv = stream->Write32(eCustomClipboardTypeId_String);
1017
0
              if (NS_WARN_IF(NS_FAILED(rv))) {
1018
0
                totalCustomLength = 0;
1019
0
                continue;
1020
0
              }
1021
0
              rv = stream->Write32(formatLength.value());
1022
0
              if (NS_WARN_IF(NS_FAILED(rv))) {
1023
0
                totalCustomLength = 0;
1024
0
                continue;
1025
0
              }
1026
0
              rv = stream->WriteBytes((const char *)type.get(), formatLength.value());
1027
0
              if (NS_WARN_IF(NS_FAILED(rv))) {
1028
0
                totalCustomLength = 0;
1029
0
                continue;
1030
0
              }
1031
0
              rv = stream->Write32(lengthInBytes);
1032
0
              if (NS_WARN_IF(NS_FAILED(rv))) {
1033
0
                totalCustomLength = 0;
1034
0
                continue;
1035
0
              }
1036
0
              rv = stream->WriteBytes((const char *)data.get(), lengthInBytes);
1037
0
              if (NS_WARN_IF(NS_FAILED(rv))) {
1038
0
                totalCustomLength = 0;
1039
0
                continue;
1040
0
              }
1041
0
1042
0
              totalCustomLength = newSize.value();
1043
0
            }
1044
0
          }
1045
0
        }
1046
0
      } else if (isCustomFormat && stream) {
1047
0
        // This is the second pass of the loop (handlingCustomFormats is false).
1048
0
        // When encountering the first custom format, append all of the stream
1049
0
        // at this position. If totalCustomLength is 0 indicating a write error
1050
0
        // occurred, or no data has been added to it, don't output anything,
1051
0
        if (totalCustomLength > baseLength) {
1052
0
          // Write out an end of data terminator.
1053
0
          nsresult rv = stream->Write32(eCustomClipboardTypeId_None);
1054
0
          if (NS_SUCCEEDED(rv)) {
1055
0
            nsCOMPtr<nsIInputStream> inputStream;
1056
0
            storageStream->NewInputStream(0, getter_AddRefs(inputStream));
1057
0
1058
0
            RefPtr<nsStringBuffer> stringBuffer =
1059
0
              nsStringBuffer::Alloc(totalCustomLength);
1060
0
1061
0
            // Subtract off the null terminator when reading.
1062
0
            totalCustomLength--;
1063
0
1064
0
            // Read the data from the stream and add a null-terminator as
1065
0
            // ToString needs it.
1066
0
            uint32_t amountRead;
1067
0
            rv = inputStream->Read(static_cast<char*>(stringBuffer->Data()),
1068
0
                              totalCustomLength, &amountRead);
1069
0
            if (NS_SUCCEEDED(rv)) {
1070
0
              static_cast<char*>(stringBuffer->Data())[amountRead] = 0;
1071
0
1072
0
              nsCString str;
1073
0
              stringBuffer->ToString(totalCustomLength, str);
1074
0
              nsCOMPtr<nsISupportsCString>
1075
0
                strSupports(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
1076
0
              strSupports->SetData(str);
1077
0
1078
0
              nsresult rv = transferable->SetTransferData(kCustomTypesMime,
1079
0
                                                          strSupports,
1080
0
                                                          totalCustomLength);
1081
0
              if (NS_FAILED(rv)) {
1082
0
                return nullptr;
1083
0
              }
1084
0
1085
0
              added = true;
1086
0
            }
1087
0
          }
1088
0
        }
1089
0
1090
0
        // Clear the stream so it doesn't get used again.
1091
0
        stream = nullptr;
1092
0
      } else {
1093
0
        // This is the second pass of the loop and a known type is encountered.
1094
0
        // Add it as is.
1095
0
        if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
1096
0
                                &lengthInBytes)) {
1097
0
          continue;
1098
0
        }
1099
0
1100
0
        // The underlying drag code uses text/unicode, so use that instead of
1101
0
        // text/plain
1102
0
        const char* format;
1103
0
        NS_ConvertUTF16toUTF8 utf8format(type);
1104
0
        if (utf8format.EqualsLiteral(kTextMime)) {
1105
0
          format = kUnicodeMime;
1106
0
        } else {
1107
0
          format = utf8format.get();
1108
0
        }
1109
0
1110
0
        // If a converter is set for a format, set the converter for the
1111
0
        // transferable and don't add the item
1112
0
        nsCOMPtr<nsIFormatConverter> converter =
1113
0
          do_QueryInterface(convertedData);
1114
0
        if (converter) {
1115
0
          transferable->AddDataFlavor(format);
1116
0
          transferable->SetConverter(converter);
1117
0
          continue;
1118
0
        }
1119
0
1120
0
        nsresult rv = transferable->SetTransferData(format, convertedData,
1121
0
                                                    lengthInBytes);
1122
0
        if (NS_FAILED(rv)) {
1123
0
          return nullptr;
1124
0
        }
1125
0
1126
0
        added = true;
1127
0
      }
1128
0
    }
1129
0
1130
0
    handlingCustomFormats = !handlingCustomFormats;
1131
0
  } while (!handlingCustomFormats);
1132
0
1133
0
  // only return the transferable if data was successfully added to it
1134
0
  if (added) {
1135
0
    return transferable.forget();
1136
0
  }
1137
0
1138
0
  return nullptr;
1139
0
}
1140
1141
bool
1142
DataTransfer::ConvertFromVariant(nsIVariant* aVariant,
1143
                                 nsISupports** aSupports,
1144
                                 uint32_t* aLength) const
1145
0
{
1146
0
  *aSupports = nullptr;
1147
0
  *aLength = 0;
1148
0
1149
0
  uint16_t type;
1150
0
  aVariant->GetDataType(&type);
1151
0
  if (type == nsIDataType::VTYPE_INTERFACE ||
1152
0
      type == nsIDataType::VTYPE_INTERFACE_IS) {
1153
0
    nsCOMPtr<nsISupports> data;
1154
0
    if (NS_FAILED(aVariant->GetAsISupports(getter_AddRefs(data)))) {
1155
0
      return false;
1156
0
    }
1157
0
1158
0
    nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(data);
1159
0
    if (fdp) {
1160
0
      // for flavour data providers, use kFlavorHasDataProvider (which has the
1161
0
      // value 0) as the length.
1162
0
      fdp.forget(aSupports);
1163
0
      *aLength = nsITransferable::kFlavorHasDataProvider;
1164
0
    }
1165
0
    else {
1166
0
      // wrap the item in an nsISupportsInterfacePointer
1167
0
      nsCOMPtr<nsISupportsInterfacePointer> ptrSupports =
1168
0
        do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
1169
0
      if (!ptrSupports) {
1170
0
        return false;
1171
0
      }
1172
0
1173
0
      ptrSupports->SetData(data);
1174
0
      ptrSupports.forget(aSupports);
1175
0
1176
0
      *aLength = sizeof(nsISupportsInterfacePointer *);
1177
0
    }
1178
0
1179
0
    return true;
1180
0
  }
1181
0
1182
0
  nsAutoString str;
1183
0
  nsresult rv = aVariant->GetAsAString(str);
1184
0
  if (NS_FAILED(rv)) {
1185
0
    return false;
1186
0
  }
1187
0
1188
0
  nsCOMPtr<nsISupportsString>
1189
0
    strSupports(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
1190
0
  if (!strSupports) {
1191
0
    return false;
1192
0
  }
1193
0
1194
0
  strSupports->SetData(str);
1195
0
1196
0
  strSupports.forget(aSupports);
1197
0
1198
0
  // each character is two bytes
1199
0
  *aLength = str.Length() * 2;
1200
0
1201
0
  return true;
1202
0
}
1203
1204
void
1205
DataTransfer::Disconnect()
1206
0
{
1207
0
  SetMode(Mode::Protected);
1208
0
  if (PrefProtected()) {
1209
0
    ClearAll();
1210
0
  }
1211
0
}
1212
1213
void
1214
DataTransfer::ClearAll()
1215
0
{
1216
0
  mItems->ClearAllItems();
1217
0
}
1218
1219
uint32_t
1220
DataTransfer::MozItemCount() const
1221
0
{
1222
0
  return mItems->MozItemCount();
1223
0
}
1224
1225
nsresult
1226
DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
1227
                                   nsIVariant* aData,
1228
                                   uint32_t aIndex,
1229
                                   nsIPrincipal* aPrincipal,
1230
                                   bool aHidden)
1231
0
{
1232
0
  nsAutoString format;
1233
0
  GetRealFormat(aFormat, format);
1234
0
1235
0
  ErrorResult rv;
1236
0
  RefPtr<DataTransferItem> item =
1237
0
    mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
1238
0
                                 /* aInsertOnly = */ false,
1239
0
                                 aHidden,
1240
0
                                 rv);
1241
0
  return rv.StealNSResult();
1242
0
}
1243
1244
void
1245
DataTransfer::SetDataWithPrincipalFromOtherProcess(const nsAString& aFormat,
1246
                                                   nsIVariant* aData,
1247
                                                   uint32_t aIndex,
1248
                                                   nsIPrincipal* aPrincipal,
1249
                                                   bool aHidden)
1250
0
{
1251
0
  if (aFormat.EqualsLiteral(kCustomTypesMime)) {
1252
0
    FillInExternalCustomTypes(aData, aIndex, aPrincipal);
1253
0
  } else {
1254
0
    nsAutoString format;
1255
0
    GetRealFormat(aFormat, format);
1256
0
1257
0
    ErrorResult rv;
1258
0
    RefPtr<DataTransferItem> item =
1259
0
      mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
1260
0
                                   /* aInsertOnly = */ false, aHidden, rv);
1261
0
    if (NS_WARN_IF(rv.Failed())) {
1262
0
      rv.SuppressException();
1263
0
    }
1264
0
  }
1265
0
}
1266
1267
void
1268
DataTransfer::GetRealFormat(const nsAString& aInFormat,
1269
                            nsAString& aOutFormat) const
1270
0
{
1271
0
  // treat text/unicode as equivalent to text/plain
1272
0
  nsAutoString lowercaseFormat;
1273
0
  nsContentUtils::ASCIIToLower(aInFormat, lowercaseFormat);
1274
0
  if (lowercaseFormat.EqualsLiteral("text") ||
1275
0
      lowercaseFormat.EqualsLiteral("text/unicode")) {
1276
0
    aOutFormat.AssignLiteral("text/plain");
1277
0
    return;
1278
0
  }
1279
0
1280
0
  if (lowercaseFormat.EqualsLiteral("url")) {
1281
0
    aOutFormat.AssignLiteral("text/uri-list");
1282
0
    return;
1283
0
  }
1284
0
1285
0
  aOutFormat.Assign(lowercaseFormat);
1286
0
}
1287
1288
nsresult
1289
DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex,
1290
                                nsIPrincipal* aPrincipal, bool aHidden)
1291
0
{
1292
0
  ErrorResult rv;
1293
0
  RefPtr<DataTransferItem> item;
1294
0
1295
0
  if (strcmp(aFormat, kUnicodeMime) == 0) {
1296
0
    item = mItems->SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nullptr,
1297
0
                                        aIndex, aPrincipal, false, aHidden, rv);
1298
0
    if (NS_WARN_IF(rv.Failed())) {
1299
0
      return rv.StealNSResult();
1300
0
    }
1301
0
    return NS_OK;
1302
0
  }
1303
0
1304
0
  if (strcmp(aFormat, kURLDataMime) == 0) {
1305
0
    item = mItems->SetDataWithPrincipal(NS_LITERAL_STRING("text/uri-list"), nullptr,
1306
0
                                        aIndex, aPrincipal, false, aHidden, rv);
1307
0
    if (NS_WARN_IF(rv.Failed())) {
1308
0
      return rv.StealNSResult();
1309
0
    }
1310
0
    return NS_OK;
1311
0
  }
1312
0
1313
0
  nsAutoString format;
1314
0
  GetRealFormat(NS_ConvertUTF8toUTF16(aFormat), format);
1315
0
  item = mItems->SetDataWithPrincipal(format, nullptr, aIndex,
1316
0
                                      aPrincipal, false, aHidden, rv);
1317
0
  if (NS_WARN_IF(rv.Failed())) {
1318
0
    return rv.StealNSResult();
1319
0
  }
1320
0
  return NS_OK;
1321
0
}
1322
1323
void
1324
DataTransfer::CacheExternalDragFormats()
1325
0
{
1326
0
  // Called during the constructor to cache the formats available from an
1327
0
  // external drag. The data associated with each format will be set to null.
1328
0
  // This data will instead only be retrieved in FillInExternalDragData when
1329
0
  // asked for, as it may be time consuming for the source application to
1330
0
  // generate it.
1331
0
1332
0
  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
1333
0
  if (!dragSession) {
1334
0
    return;
1335
0
  }
1336
0
1337
0
  // make sure that the system principal is used for external drags
1338
0
  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1339
0
  nsCOMPtr<nsIPrincipal> sysPrincipal;
1340
0
  ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
1341
0
1342
0
  // there isn't a way to get a list of the formats that might be available on
1343
0
  // all platforms, so just check for the types that can actually be imported
1344
0
  // XXXndeakin there are some other formats but those are platform specific.
1345
0
  // NOTE: kFileMime must have index 0
1346
0
  const char* formats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime,
1347
0
                            kUnicodeMime, kPNGImageMime };
1348
0
1349
0
  uint32_t count;
1350
0
  dragSession->GetNumDropItems(&count);
1351
0
  for (uint32_t c = 0; c < count; c++) {
1352
0
    bool hasFileData = false;
1353
0
    dragSession->IsDataFlavorSupported(kFileMime, &hasFileData);
1354
0
1355
0
    // First, check for the special format that holds custom types.
1356
0
    bool supported;
1357
0
    dragSession->IsDataFlavorSupported(kCustomTypesMime, &supported);
1358
0
    if (supported) {
1359
0
      FillInExternalCustomTypes(c, sysPrincipal);
1360
0
    }
1361
0
1362
0
    for (uint32_t f = 0; f < ArrayLength(formats); f++) {
1363
0
      // IsDataFlavorSupported doesn't take an index as an argument and just
1364
0
      // checks if any of the items support a particular flavor, even though
1365
0
      // the GetData method does take an index. Here, we just assume that
1366
0
      // every item being dragged has the same set of flavors.
1367
0
      bool supported;
1368
0
      dragSession->IsDataFlavorSupported(formats[f], &supported);
1369
0
      // if the format is supported, add an item to the array with null as
1370
0
      // the data. When retrieved, GetRealData will read the data.
1371
0
      if (supported) {
1372
0
        CacheExternalData(formats[f], c, sysPrincipal, /* hidden = */ f && hasFileData);
1373
0
      }
1374
0
    }
1375
0
  }
1376
0
}
1377
1378
void
1379
DataTransfer::CacheExternalClipboardFormats(bool aPlainTextOnly)
1380
0
{
1381
0
  // Called during the constructor for paste events to cache the formats
1382
0
  // available on the clipboard. As with CacheExternalDragFormats, the
1383
0
  // data will only be retrieved when needed.
1384
0
  NS_ASSERTION(mEventMessage == ePaste,
1385
0
               "caching clipboard data for invalid event");
1386
0
1387
0
  nsCOMPtr<nsIPrincipal> sysPrincipal = nsContentUtils::GetSystemPrincipal();
1388
0
1389
0
  nsTArray<nsCString> typesArray;
1390
0
1391
0
  if (XRE_IsContentProcess()) {
1392
0
    ContentChild::GetSingleton()->SendGetExternalClipboardFormats(mClipboardType, aPlainTextOnly, &typesArray);
1393
0
  } else {
1394
0
    GetExternalClipboardFormats(mClipboardType, aPlainTextOnly, &typesArray);
1395
0
  }
1396
0
1397
0
  if (aPlainTextOnly) {
1398
0
    // The only thing that will be in types is kUnicodeMime
1399
0
    MOZ_ASSERT(typesArray.IsEmpty() || typesArray.Length() == 1);
1400
0
    if (typesArray.Length() == 1) {
1401
0
      CacheExternalData(kUnicodeMime, 0, sysPrincipal, false);
1402
0
    }
1403
0
    return;
1404
0
  }
1405
0
1406
0
  bool hasFileData = false;
1407
0
  for (const nsCString& type: typesArray) {
1408
0
    if (type.EqualsLiteral(kCustomTypesMime)) {
1409
0
      FillInExternalCustomTypes(0, sysPrincipal);
1410
0
    } else if (type.EqualsLiteral(kFileMime) && XRE_IsContentProcess()) {
1411
0
      // We will be ignoring any application/x-moz-file files found in the paste
1412
0
      // datatransfer within e10s, as they will fail top be sent over IPC. Because of
1413
0
      // that, we will unset hasFileData, whether or not it would have been set.
1414
0
      // (bug 1308007)
1415
0
      hasFileData = false;
1416
0
      continue;
1417
0
    } else {
1418
0
      // We expect that if kFileMime is supported, then it will be the either at
1419
0
      // index 0 or at index 1 in the typesArray returned by GetExternalClipboardFormats
1420
0
      if (type.EqualsLiteral(kFileMime) && !XRE_IsContentProcess()) {
1421
0
        hasFileData = true;
1422
0
      }
1423
0
      // If we aren't the file data, and we have file data, we want to be hidden
1424
0
      CacheExternalData(type.get(), 0, sysPrincipal, /* hidden = */ !type.EqualsLiteral(kFileMime) && hasFileData);
1425
0
    }
1426
0
  }
1427
0
}
1428
1429
void
1430
DataTransfer::FillAllExternalData()
1431
0
{
1432
0
  if (mIsExternal) {
1433
0
    for (uint32_t i = 0; i < MozItemCount(); ++i) {
1434
0
      const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(i);
1435
0
      for (uint32_t j = 0; j < items.Length(); ++j) {
1436
0
        MOZ_ASSERT(items[j]->Index() == i);
1437
0
1438
0
        items[j]->FillInExternalData();
1439
0
      }
1440
0
    }
1441
0
  }
1442
0
}
1443
1444
void
1445
DataTransfer::FillInExternalCustomTypes(uint32_t aIndex,
1446
                                        nsIPrincipal* aPrincipal)
1447
0
{
1448
0
  RefPtr<DataTransferItem> item = new DataTransferItem(this,
1449
0
                                                       NS_LITERAL_STRING(kCustomTypesMime),
1450
0
                                                       DataTransferItem::KIND_STRING);
1451
0
  item->SetIndex(aIndex);
1452
0
1453
0
  nsCOMPtr<nsIVariant> variant = item->DataNoSecurityCheck();
1454
0
  if (!variant) {
1455
0
    return;
1456
0
  }
1457
0
1458
0
  FillInExternalCustomTypes(variant, aIndex, aPrincipal);
1459
0
}
1460
1461
void
1462
DataTransfer::FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
1463
                                        nsIPrincipal* aPrincipal)
1464
0
{
1465
0
  char* chrs;
1466
0
  uint32_t len = 0;
1467
0
  nsresult rv = aData->GetAsStringWithSize(&len, &chrs);
1468
0
  if (NS_FAILED(rv)) {
1469
0
    return;
1470
0
  }
1471
0
1472
0
  CheckedInt<int32_t> checkedLen(len);
1473
0
  if (!checkedLen.isValid()) {
1474
0
    return;
1475
0
  }
1476
0
1477
0
  nsCOMPtr<nsIInputStream> stringStream;
1478
0
  NS_NewByteInputStream(getter_AddRefs(stringStream), chrs, checkedLen.value(),
1479
0
                        NS_ASSIGNMENT_ADOPT);
1480
0
1481
0
  nsCOMPtr<nsIObjectInputStream> stream =
1482
0
    NS_NewObjectInputStream(stringStream);
1483
0
1484
0
  uint32_t type;
1485
0
  do {
1486
0
    rv = stream->Read32(&type);
1487
0
    NS_ENSURE_SUCCESS_VOID(rv);
1488
0
    if (type == eCustomClipboardTypeId_String) {
1489
0
      uint32_t formatLength;
1490
0
      rv = stream->Read32(&formatLength);
1491
0
      NS_ENSURE_SUCCESS_VOID(rv);
1492
0
      char* formatBytes;
1493
0
      rv = stream->ReadBytes(formatLength, &formatBytes);
1494
0
      NS_ENSURE_SUCCESS_VOID(rv);
1495
0
      nsAutoString format;
1496
0
      format.Adopt(reinterpret_cast<char16_t*>(formatBytes),
1497
0
                   formatLength / sizeof(char16_t));
1498
0
1499
0
      uint32_t dataLength;
1500
0
      rv = stream->Read32(&dataLength);
1501
0
      NS_ENSURE_SUCCESS_VOID(rv);
1502
0
      char* dataBytes;
1503
0
      rv = stream->ReadBytes(dataLength, &dataBytes);
1504
0
      NS_ENSURE_SUCCESS_VOID(rv);
1505
0
      nsAutoString data;
1506
0
      data.Adopt(reinterpret_cast<char16_t*>(dataBytes),
1507
0
                 dataLength / sizeof(char16_t));
1508
0
1509
0
      RefPtr<nsVariantCC> variant = new nsVariantCC();
1510
0
      rv = variant->SetAsAString(data);
1511
0
      NS_ENSURE_SUCCESS_VOID(rv);
1512
0
1513
0
      SetDataWithPrincipal(format, variant, aIndex, aPrincipal);
1514
0
    }
1515
0
  } while (type != eCustomClipboardTypeId_None);
1516
0
}
1517
1518
void
1519
DataTransfer::SetMode(DataTransfer::Mode aMode)
1520
0
{
1521
0
  if (!PrefProtected() && aMode == Mode::Protected) {
1522
0
    mMode = Mode::ReadOnly;
1523
0
  } else {
1524
0
    mMode = aMode;
1525
0
  }
1526
0
}
1527
1528
/* static */
1529
bool
1530
DataTransfer::MozAtAPIsEnabled(JSContext* aCx, JSObject* aObj /*unused*/)
1531
0
{
1532
0
  // Read the pref
1533
0
  static bool sPrefCached = false;
1534
0
  static bool sPrefCacheValue = false;
1535
0
1536
0
  if (!sPrefCached) {
1537
0
    sPrefCached = true;
1538
0
    Preferences::AddBoolVarCache(&sPrefCacheValue, MOZ_CALLS_ENABLED_PREF);
1539
0
  }
1540
0
1541
0
  // We can expose moz* APIs if we are chrome code or if pref is enabled
1542
0
  return nsContentUtils::IsSystemCaller(aCx) || sPrefCacheValue;
1543
0
}
1544
1545
} // namespace dom
1546
} // namespace mozilla