Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/nsTransferable.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
/*
7
Notes to self:
8
9
- at some point, strings will be accessible from JS, so we won't have to wrap
10
   flavors in an nsISupportsCString. Until then, we're kinda stuck with
11
   this crappy API of nsIArrays.
12
13
*/
14
15
16
#include "nsTransferable.h"
17
#include "nsAnonymousTemporaryFile.h"
18
#include "nsArray.h"
19
#include "nsArrayUtils.h"
20
#include "nsString.h"
21
#include "nsReadableUtils.h"
22
#include "nsTArray.h"
23
#include "nsIFormatConverter.h"
24
#include "nsIContentPolicy.h"
25
#include "nsIComponentManager.h"
26
#include "nsCOMPtr.h"
27
#include "nsXPCOM.h"
28
#include "nsISupportsPrimitives.h"
29
#include "nsMemory.h"
30
#include "nsPrimitiveHelpers.h"
31
#include "nsDirectoryServiceDefs.h"
32
#include "nsDirectoryService.h"
33
#include "nsCRT.h"
34
#include "nsNetUtil.h"
35
#include "nsIOutputStream.h"
36
#include "nsIInputStream.h"
37
#include "nsIWeakReferenceUtils.h"
38
#include "nsILoadContext.h"
39
#include "mozilla/UniquePtr.h"
40
41
NS_IMPL_ISUPPORTS(nsTransferable, nsITransferable)
42
43
size_t GetDataForFlavor (const nsTArray<DataStruct>& aArray,
44
                           const char* aDataFlavor)
45
0
{
46
0
  for (size_t i = 0 ; i < aArray.Length () ; ++i) {
47
0
    if (aArray[i].GetFlavor().Equals (aDataFlavor))
48
0
      return i;
49
0
  }
50
0
51
0
  return aArray.NoIndex;
52
0
}
53
54
DataStruct::DataStruct(DataStruct&& aRHS)
55
  : mData(aRHS.mData.forget()),
56
    mDataLen(aRHS.mDataLen),
57
    mCacheFD(aRHS.mCacheFD),
58
    mFlavor(aRHS.mFlavor)
59
0
{
60
0
  aRHS.mCacheFD = nullptr;
61
0
}
62
63
//-------------------------------------------------------------------------
64
DataStruct::~DataStruct()
65
0
{
66
0
  if (mCacheFD) {
67
0
    PR_Close(mCacheFD);
68
0
  }
69
0
}
70
71
//-------------------------------------------------------------------------
72
void
73
DataStruct::SetData ( nsISupports* aData, uint32_t aDataLen, bool aIsPrivateData )
74
0
{
75
0
  // Now, check to see if we consider the data to be "too large"
76
0
  // as well as ensuring that private browsing mode is disabled
77
0
  if (aDataLen > kLargeDatasetSize && !aIsPrivateData) {
78
0
    // if so, cache it to disk instead of memory
79
0
    if (NS_SUCCEEDED(WriteCache(aData, aDataLen))) {
80
0
      // Clear previously set small data.
81
0
      mData = nullptr;
82
0
      mDataLen = 0;
83
0
      return;
84
0
    }
85
0
    NS_WARNING("Oh no, couldn't write data to the cache file");
86
0
  }
87
0
88
0
  if (mCacheFD) {
89
0
    // Clear previously set big data.
90
0
    PR_Close(mCacheFD);
91
0
    mCacheFD = nullptr;
92
0
  }
93
0
94
0
  mData    = aData;
95
0
  mDataLen = aDataLen;
96
0
}
97
98
99
//-------------------------------------------------------------------------
100
void
101
DataStruct::GetData ( nsISupports** aData, uint32_t *aDataLen )
102
0
{
103
0
  // check here to see if the data is cached on disk
104
0
  if (mCacheFD) {
105
0
    // if so, read it in and pass it back
106
0
    // ReadCache creates memory and copies the data into it.
107
0
    if ( NS_SUCCEEDED(ReadCache(aData, aDataLen)) )
108
0
      return;
109
0
    else {
110
0
      // oh shit, something went horribly wrong here.
111
0
      NS_WARNING("Oh no, couldn't read data in from the cache file");
112
0
      *aData = nullptr;
113
0
      *aDataLen = 0;
114
0
      PR_Close(mCacheFD);
115
0
      mCacheFD = nullptr;
116
0
      return;
117
0
    }
118
0
  }
119
0
120
0
  *aData = mData;
121
0
  if ( mData )
122
0
    NS_ADDREF(*aData);
123
0
  *aDataLen = mDataLen;
124
0
}
125
126
127
//-------------------------------------------------------------------------
128
nsresult
129
DataStruct::WriteCache(nsISupports* aData, uint32_t aDataLen)
130
0
{
131
0
  nsresult rv;
132
0
  if (!mCacheFD) {
133
0
    rv = NS_OpenAnonymousTemporaryFile(&mCacheFD);
134
0
    if (NS_FAILED(rv)) {
135
0
      return NS_ERROR_FAILURE;
136
0
    }
137
0
  } else if (PR_Seek64(mCacheFD, 0, PR_SEEK_SET) == -1) {
138
0
    return NS_ERROR_FAILURE;
139
0
  }
140
0
141
0
  // write out the contents of the clipboard to the file
142
0
  void* buff = nullptr;
143
0
  nsPrimitiveHelpers::CreateDataFromPrimitive(mFlavor, aData, &buff, aDataLen);
144
0
  if (buff) {
145
0
    int32_t written = PR_Write(mCacheFD, buff, aDataLen);
146
0
    free(buff);
147
0
    if (written) {
148
0
      return NS_OK;
149
0
    }
150
0
  }
151
0
  PR_Close(mCacheFD);
152
0
  mCacheFD = nullptr;
153
0
  return NS_ERROR_FAILURE;
154
0
}
155
156
157
//-------------------------------------------------------------------------
158
nsresult
159
DataStruct::ReadCache(nsISupports** aData, uint32_t* aDataLen)
160
0
{
161
0
  if (!mCacheFD) {
162
0
    return NS_ERROR_FAILURE;
163
0
  }
164
0
165
0
  PRFileInfo fileInfo;
166
0
  if (PR_GetOpenFileInfo(mCacheFD, &fileInfo) != PR_SUCCESS) {
167
0
    return NS_ERROR_FAILURE;
168
0
  }
169
0
  if (PR_Seek64(mCacheFD, 0, PR_SEEK_SET) == -1) {
170
0
    return NS_ERROR_FAILURE;
171
0
  }
172
0
  uint32_t fileSize = fileInfo.size;
173
0
174
0
  auto data = mozilla::MakeUnique<char[]>(fileSize);
175
0
  if (!data) {
176
0
    return NS_ERROR_OUT_OF_MEMORY;
177
0
  }
178
0
179
0
  uint32_t actual = PR_Read(mCacheFD, data.get(), fileSize);
180
0
  if (actual != fileSize) {
181
0
    return NS_ERROR_FAILURE;
182
0
  }
183
0
184
0
  nsPrimitiveHelpers::CreatePrimitiveForData(mFlavor, data.get(), fileSize, aData);
185
0
  *aDataLen = fileSize;
186
0
  return NS_OK;
187
0
}
188
189
190
//-------------------------------------------------------------------------
191
//
192
// Transferable constructor
193
//
194
//-------------------------------------------------------------------------
195
nsTransferable::nsTransferable()
196
  : mPrivateData(false)
197
  , mContentPolicyType(nsIContentPolicy::TYPE_OTHER)
198
#ifdef DEBUG
199
  , mInitialized(false)
200
#endif
201
0
{
202
0
}
203
204
//-------------------------------------------------------------------------
205
//
206
// Transferable destructor
207
//
208
//-------------------------------------------------------------------------
209
nsTransferable::~nsTransferable()
210
0
{
211
0
}
212
213
214
NS_IMETHODIMP
215
nsTransferable::Init(nsILoadContext* aContext)
216
0
{
217
0
  MOZ_ASSERT(!mInitialized);
218
0
219
0
  if (aContext) {
220
0
    mPrivateData = aContext->UsePrivateBrowsing();
221
0
  }
222
#ifdef DEBUG
223
  mInitialized = true;
224
#endif
225
  return NS_OK;
226
0
}
227
228
//
229
// GetTransferDataFlavors
230
//
231
// Returns a copy of the internal list of flavors. This does NOT take into
232
// account any converter that may be registered. This list consists of
233
// nsISupportsCString objects so that the flavor list can be accessed from JS.
234
//
235
already_AddRefed<nsIMutableArray>
236
nsTransferable::GetTransferDataFlavors()
237
0
{
238
0
  MOZ_ASSERT(mInitialized);
239
0
240
0
  nsCOMPtr<nsIMutableArray> array = nsArray::Create();
241
0
242
0
  for (size_t i = 0; i < mDataArray.Length(); ++i) {
243
0
    DataStruct& data = mDataArray.ElementAt(i);
244
0
    nsCOMPtr<nsISupportsCString> flavorWrapper = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
245
0
    if ( flavorWrapper ) {
246
0
      flavorWrapper->SetData ( data.GetFlavor() );
247
0
      nsCOMPtr<nsISupports> genericWrapper ( do_QueryInterface(flavorWrapper) );
248
0
      array->AppendElement( genericWrapper );
249
0
    }
250
0
  }
251
0
252
0
  return array.forget();
253
0
}
254
255
256
//
257
// GetTransferData
258
//
259
// Returns the data of the requested flavor, obtained from either having the data on hand or
260
// using a converter to get it. The data is wrapped in a nsISupports primitive so that it is
261
// accessible from JS.
262
//
263
NS_IMETHODIMP
264
nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, uint32_t *aDataLen)
265
0
{
266
0
  MOZ_ASSERT(mInitialized);
267
0
268
0
  NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
269
0
270
0
  nsresult rv = NS_OK;
271
0
  nsCOMPtr<nsISupports> savedData;
272
0
273
0
  // first look and see if the data is present in one of the intrinsic flavors
274
0
  for (size_t i = 0; i < mDataArray.Length(); ++i) {
275
0
    DataStruct& data = mDataArray.ElementAt(i);
276
0
    if ( data.GetFlavor().Equals(aFlavor) ) {
277
0
      nsCOMPtr<nsISupports> dataBytes;
278
0
      uint32_t len;
279
0
      data.GetData(getter_AddRefs(dataBytes), &len);
280
0
      if (len == kFlavorHasDataProvider && dataBytes) {
281
0
        // do we have a data provider?
282
0
        nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
283
0
        if (dataProvider) {
284
0
          rv = dataProvider->GetFlavorData(this, aFlavor,
285
0
                                           getter_AddRefs(dataBytes), &len);
286
0
          if (NS_FAILED(rv))
287
0
            break;    // the provider failed. fall into the converter code below.
288
0
        }
289
0
      }
290
0
      if (dataBytes && len > 0) { // XXXmats why is zero length not ok?
291
0
        *aDataLen = len;
292
0
        dataBytes.forget(aData);
293
0
        return NS_OK;
294
0
      }
295
0
      savedData = dataBytes;  // return this if format converter fails
296
0
      break;
297
0
    }
298
0
  }
299
0
300
0
  bool found = false;
301
0
302
0
  // if not, try using a format converter to get the requested flavor
303
0
  if ( mFormatConv ) {
304
0
    for (size_t i = 0; i < mDataArray.Length(); ++i) {
305
0
      DataStruct& data = mDataArray.ElementAt(i);
306
0
      bool canConvert = false;
307
0
      mFormatConv->CanConvert(data.GetFlavor().get(), aFlavor, &canConvert);
308
0
      if ( canConvert ) {
309
0
        nsCOMPtr<nsISupports> dataBytes;
310
0
        uint32_t len;
311
0
        data.GetData(getter_AddRefs(dataBytes), &len);
312
0
        if (len == kFlavorHasDataProvider && dataBytes) {
313
0
          // do we have a data provider?
314
0
          nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
315
0
          if (dataProvider) {
316
0
            rv = dataProvider->GetFlavorData(this, aFlavor,
317
0
                                             getter_AddRefs(dataBytes), &len);
318
0
            if (NS_FAILED(rv))
319
0
              break;  // give up
320
0
          }
321
0
        }
322
0
        mFormatConv->Convert(data.GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
323
0
        found = true;
324
0
        break;
325
0
      }
326
0
    }
327
0
  }
328
0
329
0
  // for backward compatibility
330
0
  if (!found) {
331
0
    savedData.forget(aData);
332
0
    *aDataLen = 0;
333
0
  }
334
0
335
0
  return found ? NS_OK : NS_ERROR_FAILURE;
336
0
}
337
338
339
//
340
// GetAnyTransferData
341
//
342
// Returns the data of the first flavor found. Caller is responsible for deleting the
343
// flavor string.
344
//
345
NS_IMETHODIMP
346
nsTransferable::GetAnyTransferData(nsACString& aFlavor, nsISupports **aData,
347
                                   uint32_t *aDataLen)
348
0
{
349
0
  MOZ_ASSERT(mInitialized);
350
0
351
0
  NS_ENSURE_ARG_POINTER(aData && aDataLen);
352
0
353
0
  for (size_t i = 0; i < mDataArray.Length(); ++i) {
354
0
    DataStruct& data = mDataArray.ElementAt(i);
355
0
    if (data.IsDataAvailable()) {
356
0
      aFlavor.Assign(data.GetFlavor());
357
0
      data.GetData(aData, aDataLen);
358
0
      return NS_OK;
359
0
    }
360
0
  }
361
0
362
0
  return NS_ERROR_FAILURE;
363
0
}
364
365
366
//
367
// SetTransferData
368
//
369
//
370
//
371
NS_IMETHODIMP
372
nsTransferable::SetTransferData(const char *aFlavor, nsISupports *aData, uint32_t aDataLen)
373
0
{
374
0
  MOZ_ASSERT(mInitialized);
375
0
376
0
  NS_ENSURE_ARG(aFlavor);
377
0
378
0
  // first check our intrinsic flavors to see if one has been registered.
379
0
  for (size_t i = 0; i < mDataArray.Length(); ++i) {
380
0
    DataStruct& data = mDataArray.ElementAt(i);
381
0
    if ( data.GetFlavor().Equals(aFlavor) ) {
382
0
      data.SetData ( aData, aDataLen, mPrivateData );
383
0
      return NS_OK;
384
0
    }
385
0
  }
386
0
387
0
  // if not, try using a format converter to find a flavor to put the data in
388
0
  if ( mFormatConv ) {
389
0
    for (size_t i = 0; i < mDataArray.Length(); ++i) {
390
0
      DataStruct& data = mDataArray.ElementAt(i);
391
0
      bool canConvert = false;
392
0
      mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
393
0
394
0
      if ( canConvert ) {
395
0
        nsCOMPtr<nsISupports> ConvertedData;
396
0
        uint32_t ConvertedLen;
397
0
        mFormatConv->Convert(aFlavor, aData, aDataLen, data.GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
398
0
        data.SetData(ConvertedData, ConvertedLen, mPrivateData);
399
0
        return NS_OK;
400
0
      }
401
0
    }
402
0
  }
403
0
404
0
  // Can't set data neither directly nor through converter. Just add this flavor and try again
405
0
  nsresult result = NS_ERROR_FAILURE;
406
0
  if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
407
0
    result = SetTransferData (aFlavor, aData, aDataLen);
408
0
409
0
  return result;
410
0
}
411
412
413
//
414
// AddDataFlavor
415
//
416
// Adds a data flavor to our list with no data. Error if it already exists.
417
//
418
NS_IMETHODIMP
419
nsTransferable::AddDataFlavor(const char *aDataFlavor)
420
0
{
421
0
  MOZ_ASSERT(mInitialized);
422
0
423
0
  if (GetDataForFlavor (mDataArray, aDataFlavor) != mDataArray.NoIndex)
424
0
    return NS_ERROR_FAILURE;
425
0
426
0
  // Create a new "slot" for the data
427
0
  mDataArray.AppendElement(DataStruct ( aDataFlavor ));
428
0
429
0
  return NS_OK;
430
0
}
431
432
433
//
434
// RemoveDataFlavor
435
//
436
// Removes a data flavor (and causes the data to be destroyed). Error if
437
// the requested flavor is not present.
438
//
439
NS_IMETHODIMP
440
nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
441
0
{
442
0
  MOZ_ASSERT(mInitialized);
443
0
444
0
  size_t idx = GetDataForFlavor(mDataArray, aDataFlavor);
445
0
  if (idx != mDataArray.NoIndex) {
446
0
    mDataArray.RemoveElementAt (idx);
447
0
    return NS_OK;
448
0
  }
449
0
  return NS_ERROR_FAILURE;
450
0
}
451
452
453
/**
454
  *
455
  *
456
  */
457
NS_IMETHODIMP
458
nsTransferable::IsLargeDataSet(bool *_retval)
459
0
{
460
0
  MOZ_ASSERT(mInitialized);
461
0
462
0
  NS_ENSURE_ARG_POINTER(_retval);
463
0
  *_retval = false;
464
0
  return NS_OK;
465
0
}
466
467
468
/**
469
  *
470
  *
471
  */
472
NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
473
0
{
474
0
  MOZ_ASSERT(mInitialized);
475
0
476
0
  mFormatConv = aConverter;
477
0
  return NS_OK;
478
0
}
479
480
481
/**
482
  *
483
  *
484
  */
485
NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
486
0
{
487
0
  MOZ_ASSERT(mInitialized);
488
0
489
0
  NS_ENSURE_ARG_POINTER(aConverter);
490
0
  *aConverter = mFormatConv;
491
0
  NS_IF_ADDREF(*aConverter);
492
0
  return NS_OK;
493
0
}
494
495
496
//
497
// FlavorsTransferableCanImport
498
//
499
// Computes a list of flavors that the transferable can accept into it, either through
500
// intrinsic knowledge or input data converters.
501
//
502
NS_IMETHODIMP
503
nsTransferable::FlavorsTransferableCanImport(nsIArray **_retval)
504
0
{
505
0
  MOZ_ASSERT(mInitialized);
506
0
507
0
  NS_ENSURE_ARG_POINTER(_retval);
508
0
509
0
  // Get the flavor list, and on to the end of it, append the list of flavors we
510
0
  // can also get to through a converter. This is so that we can just walk the list
511
0
  // in one go, looking for the desired flavor.
512
0
  nsCOMPtr<nsIMutableArray> array = GetTransferDataFlavors();
513
0
  nsCOMPtr<nsIFormatConverter> converter;
514
0
  GetConverter(getter_AddRefs(converter));
515
0
  if ( converter ) {
516
0
    nsCOMPtr<nsIArray> convertedList;
517
0
    converter->GetInputDataFlavors(getter_AddRefs(convertedList));
518
0
519
0
    if ( convertedList ) {
520
0
      uint32_t importListLen;
521
0
      convertedList->GetLength(&importListLen);
522
0
523
0
      for (uint32_t i = 0; i < importListLen; ++i ) {
524
0
        nsCOMPtr<nsISupportsCString> flavorWrapper =
525
0
            do_QueryElementAt(convertedList, i);
526
0
        nsAutoCString flavorStr;
527
0
        flavorWrapper->GetData( flavorStr );
528
0
529
0
        if (GetDataForFlavor (mDataArray, flavorStr.get())
530
0
            == mDataArray.NoIndex) // Don't append if already in intrinsic list
531
0
          array->AppendElement (flavorWrapper);
532
0
      } // foreach flavor that can be converted to
533
0
    }
534
0
  } // if a converter exists
535
0
536
0
  array.forget(_retval);
537
0
  return NS_OK;
538
0
} // FlavorsTransferableCanImport
539
540
541
//
542
// FlavorsTransferableCanExport
543
//
544
// Computes a list of flavors that the transferable can export, either through
545
// intrinsic knowledge or output data converters.
546
//
547
NS_IMETHODIMP
548
nsTransferable::FlavorsTransferableCanExport(nsIArray **_retval)
549
0
{
550
0
  MOZ_ASSERT(mInitialized);
551
0
552
0
  NS_ENSURE_ARG_POINTER(_retval);
553
0
554
0
  // Get the flavor list, and on to the end of it, append the list of flavors we
555
0
  // can also get to through a converter. This is so that we can just walk the list
556
0
  // in one go, looking for the desired flavor.
557
0
  nsCOMPtr<nsIMutableArray> array = GetTransferDataFlavors();
558
0
  nsCOMPtr<nsIFormatConverter> converter;
559
0
  GetConverter(getter_AddRefs(converter));
560
0
  if ( converter ) {
561
0
    nsCOMPtr<nsIArray> convertedList;
562
0
    converter->GetOutputDataFlavors(getter_AddRefs(convertedList));
563
0
564
0
    if ( convertedList ) {
565
0
      uint32_t importListLen;
566
0
      convertedList->GetLength(&importListLen);
567
0
568
0
      for ( uint32_t i=0; i < importListLen; ++i ) {
569
0
        nsCOMPtr<nsISupportsCString> flavorWrapper =
570
0
            do_QueryElementAt(convertedList, i);
571
0
        nsAutoCString flavorStr;
572
0
        flavorWrapper->GetData( flavorStr );
573
0
574
0
        if (GetDataForFlavor (mDataArray, flavorStr.get())
575
0
            == mDataArray.NoIndex) // Don't append if already in intrinsic list
576
0
          array->AppendElement (flavorWrapper);
577
0
      } // foreach flavor that can be converted to
578
0
    }
579
0
  } // if a converter exists
580
0
581
0
  array.forget(_retval);
582
0
  return NS_OK;
583
0
} // FlavorsTransferableCanExport
584
585
NS_IMETHODIMP
586
nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
587
0
{
588
0
  MOZ_ASSERT(mInitialized);
589
0
590
0
  NS_ENSURE_ARG_POINTER(aIsPrivateData);
591
0
592
0
  *aIsPrivateData = mPrivateData;
593
0
594
0
  return NS_OK;
595
0
}
596
597
NS_IMETHODIMP
598
nsTransferable::SetIsPrivateData(bool aIsPrivateData)
599
0
{
600
0
  MOZ_ASSERT(mInitialized);
601
0
602
0
  mPrivateData = aIsPrivateData;
603
0
604
0
  return NS_OK;
605
0
}
606
607
NS_IMETHODIMP
608
nsTransferable::GetRequestingPrincipal(nsIPrincipal** outRequestingPrincipal)
609
0
{
610
0
  NS_IF_ADDREF(*outRequestingPrincipal = mRequestingPrincipal);
611
0
  return NS_OK;
612
0
}
613
614
NS_IMETHODIMP
615
nsTransferable::SetRequestingPrincipal(nsIPrincipal* aRequestingPrincipal)
616
0
{
617
0
  mRequestingPrincipal = aRequestingPrincipal;
618
0
  return NS_OK;
619
0
}
620
621
NS_IMETHODIMP
622
nsTransferable::GetContentPolicyType(nsContentPolicyType* outContentPolicyType)
623
0
{
624
0
  NS_ENSURE_ARG_POINTER(outContentPolicyType);
625
0
  *outContentPolicyType = mContentPolicyType;
626
0
  return NS_OK;
627
0
}
628
629
NS_IMETHODIMP
630
nsTransferable::SetContentPolicyType(nsContentPolicyType aContentPolicyType)
631
0
{
632
0
  mContentPolicyType = aContentPolicyType;
633
0
  return NS_OK;
634
0
}