Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/file/uri/BlobURLProtocolHandler.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 "BlobURLProtocolHandler.h"
8
#include "BlobURLChannel.h"
9
#include "mozilla/dom/BlobURL.h"
10
11
#include "mozilla/dom/ChromeUtils.h"
12
#include "mozilla/dom/ContentChild.h"
13
#include "mozilla/dom/ContentParent.h"
14
#include "mozilla/dom/Exceptions.h"
15
#include "mozilla/dom/BlobImpl.h"
16
#include "mozilla/dom/IPCBlobUtils.h"
17
#include "mozilla/dom/MediaSource.h"
18
#include "mozilla/ipc/IPCStreamUtils.h"
19
#include "mozilla/LoadInfo.h"
20
#include "mozilla/ModuleUtils.h"
21
#include "mozilla/NullPrincipal.h"
22
#include "mozilla/Preferences.h"
23
#include "mozilla/SystemGroup.h"
24
#include "nsClassHashtable.h"
25
#include "nsContentUtils.h"
26
#include "nsError.h"
27
#include "nsIAsyncShutdown.h"
28
#include "nsIException.h" // for nsIStackFrame
29
#include "nsIMemoryReporter.h"
30
#include "nsIPrincipal.h"
31
#include "nsIUUIDGenerator.h"
32
#include "nsNetUtil.h"
33
34
0
#define RELEASING_TIMER 5000
35
36
namespace mozilla {
37
38
using namespace ipc;
39
40
namespace dom {
41
42
// -----------------------------------------------------------------------
43
// Hash table
44
struct DataInfo
45
{
46
  enum ObjectType {
47
    eBlobImpl,
48
    eMediaSource
49
  };
50
51
  DataInfo(BlobImpl* aBlobImpl, nsIPrincipal* aPrincipal)
52
    : mObjectType(eBlobImpl)
53
    , mBlobImpl(aBlobImpl)
54
    , mPrincipal(aPrincipal)
55
    , mRevoked(false)
56
0
  {
57
0
    MOZ_ASSERT(aPrincipal);
58
0
  }
59
60
  DataInfo(MediaSource* aMediaSource, nsIPrincipal* aPrincipal)
61
    : mObjectType(eMediaSource)
62
    , mMediaSource(aMediaSource)
63
    , mPrincipal(aPrincipal)
64
    , mRevoked(false)
65
0
  {
66
0
    MOZ_ASSERT(aPrincipal);
67
0
  }
68
69
  ObjectType mObjectType;
70
71
  RefPtr<BlobImpl> mBlobImpl;
72
  RefPtr<MediaSource> mMediaSource;
73
74
  nsCOMPtr<nsIPrincipal> mPrincipal;
75
  nsCString mStack;
76
77
  // When a blobURL is revoked, we keep it alive for RELEASING_TIMER
78
  // milliseconds in order to support pending operations such as navigation,
79
  // download and so on.
80
  bool mRevoked;
81
};
82
83
static nsClassHashtable<nsCStringHashKey, DataInfo>* gDataTable;
84
85
static DataInfo*
86
GetDataInfo(const nsACString& aUri, bool aAlsoIfRevoked = false)
87
1.77k
{
88
1.77k
  if (!gDataTable) {
89
1.77k
    return nullptr;
90
1.77k
  }
91
0
92
0
  DataInfo* res;
93
0
94
0
  // Let's remove any fragment and query from this URI.
95
0
  int32_t hasFragmentPos = aUri.FindChar('#');
96
0
  int32_t hasQueryPos = aUri.FindChar('?');
97
0
98
0
  int32_t pos = -1;
99
0
  if (hasFragmentPos >= 0 && hasQueryPos >= 0) {
100
0
    pos = std::min(hasFragmentPos, hasQueryPos);
101
0
  } else if (hasFragmentPos >= 0) {
102
0
    pos = hasFragmentPos;
103
0
  } else {
104
0
    pos = hasQueryPos;
105
0
  }
106
0
107
0
  if (pos < 0) {
108
0
    gDataTable->Get(aUri, &res);
109
0
  } else {
110
0
    gDataTable->Get(StringHead(aUri, pos), &res);
111
0
  }
112
0
113
0
  if (!aAlsoIfRevoked && res && res->mRevoked) {
114
0
    return nullptr;
115
0
  }
116
0
117
0
  return res;
118
0
}
119
120
static DataInfo*
121
GetDataInfoFromURI(nsIURI* aURI, bool aAlsoIfRevoked = false)
122
0
{
123
0
  if (!aURI) {
124
0
    return nullptr;
125
0
  }
126
0
127
0
  nsCString spec;
128
0
  nsresult rv = aURI->GetSpec(spec);
129
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
130
0
    return nullptr;
131
0
  }
132
0
133
0
  return GetDataInfo(spec, aAlsoIfRevoked);
134
0
}
135
136
// Memory reporting for the hash table.
137
void
138
BroadcastBlobURLRegistration(const nsACString& aURI,
139
                             BlobImpl* aBlobImpl,
140
                             nsIPrincipal* aPrincipal)
141
0
{
142
0
  MOZ_ASSERT(NS_IsMainThread());
143
0
  MOZ_ASSERT(aBlobImpl);
144
0
  MOZ_ASSERT(aPrincipal);
145
0
146
0
  if (XRE_IsParentProcess()) {
147
0
    dom::ContentParent::BroadcastBlobURLRegistration(aURI, aBlobImpl,
148
0
                                                     aPrincipal);
149
0
    return;
150
0
  }
151
0
152
0
  dom::ContentChild* cc = dom::ContentChild::GetSingleton();
153
0
154
0
  IPCBlob ipcBlob;
155
0
  nsresult rv = IPCBlobUtils::Serialize(aBlobImpl, cc, ipcBlob);
156
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
157
0
    return;
158
0
  }
159
0
160
0
  Unused << NS_WARN_IF(!cc->SendStoreAndBroadcastBlobURLRegistration(
161
0
    nsCString(aURI), ipcBlob, IPC::Principal(aPrincipal)));
162
0
}
163
164
void
165
BroadcastBlobURLUnregistration(const nsCString& aURI)
166
0
{
167
0
  MOZ_ASSERT(NS_IsMainThread());
168
0
169
0
  if (XRE_IsParentProcess()) {
170
0
    dom::ContentParent::BroadcastBlobURLUnregistration(aURI);
171
0
    return;
172
0
  }
173
0
174
0
  dom::ContentChild* cc = dom::ContentChild::GetSingleton();
175
0
  Unused << NS_WARN_IF(!cc->SendUnstoreAndBroadcastBlobURLUnregistration(aURI));
176
0
}
177
178
class BlobURLsReporter final : public nsIMemoryReporter
179
{
180
 public:
181
  NS_DECL_ISUPPORTS
182
183
  NS_IMETHOD CollectReports(nsIHandleReportCallback* aCallback,
184
                            nsISupports* aData, bool aAnonymize) override
185
0
  {
186
0
    if (!gDataTable) {
187
0
      return NS_OK;
188
0
    }
189
0
190
0
    nsDataHashtable<nsPtrHashKey<BlobImpl>, uint32_t> refCounts;
191
0
192
0
    // Determine number of URLs per BlobImpl, to handle the case where it's > 1.
193
0
    for (auto iter = gDataTable->Iter(); !iter.Done(); iter.Next()) {
194
0
      if (iter.UserData()->mObjectType != DataInfo::eBlobImpl) {
195
0
        continue;
196
0
      }
197
0
198
0
      BlobImpl* blobImpl = iter.UserData()->mBlobImpl;
199
0
      MOZ_ASSERT(blobImpl);
200
0
201
0
      refCounts.Put(blobImpl, refCounts.Get(blobImpl) + 1);
202
0
    }
203
0
204
0
    for (auto iter = gDataTable->Iter(); !iter.Done(); iter.Next()) {
205
0
      nsCStringHashKey::KeyType key = iter.Key();
206
0
      DataInfo* info = iter.UserData();
207
0
208
0
      if (iter.UserData()->mObjectType == DataInfo::eBlobImpl) {
209
0
        BlobImpl* blobImpl = iter.UserData()->mBlobImpl;
210
0
        MOZ_ASSERT(blobImpl);
211
0
212
0
        NS_NAMED_LITERAL_CSTRING(desc,
213
0
          "A blob URL allocated with URL.createObjectURL; the referenced "
214
0
          "blob cannot be freed until all URLs for it have been explicitly "
215
0
          "invalidated with URL.revokeObjectURL.");
216
0
        nsAutoCString path, url, owner, specialDesc;
217
0
        uint64_t size = 0;
218
0
        uint32_t refCount = 1;
219
0
        DebugOnly<bool> blobImplWasCounted;
220
0
221
0
        blobImplWasCounted = refCounts.Get(blobImpl, &refCount);
222
0
        MOZ_ASSERT(blobImplWasCounted);
223
0
        MOZ_ASSERT(refCount > 0);
224
0
225
0
        bool isMemoryFile = blobImpl->IsMemoryFile();
226
0
227
0
        if (isMemoryFile) {
228
0
          ErrorResult rv;
229
0
          size = blobImpl->GetSize(rv);
230
0
          if (NS_WARN_IF(rv.Failed())) {
231
0
            rv.SuppressException();
232
0
            size = 0;
233
0
          }
234
0
        }
235
0
236
0
        path = isMemoryFile ? "memory-blob-urls/" : "file-blob-urls/";
237
0
        BuildPath(path, key, info, aAnonymize);
238
0
239
0
        if (refCount > 1) {
240
0
          nsAutoCString addrStr;
241
0
242
0
          addrStr = "0x";
243
0
          addrStr.AppendInt((uint64_t)(BlobImpl*)blobImpl, 16);
244
0
245
0
          path += " ";
246
0
          path.AppendInt(refCount);
247
0
          path += "@";
248
0
          path += addrStr;
249
0
250
0
          specialDesc = desc;
251
0
          specialDesc += "\n\nNOTE: This blob (address ";
252
0
          specialDesc += addrStr;
253
0
          specialDesc += ") has ";
254
0
          specialDesc.AppendInt(refCount);
255
0
          specialDesc += " URLs.";
256
0
          if (isMemoryFile) {
257
0
            specialDesc += " Its size is divided ";
258
0
            specialDesc += refCount > 2 ? "among" : "between";
259
0
            specialDesc += " them in this report.";
260
0
          }
261
0
        }
262
0
263
0
        const nsACString& descString = specialDesc.IsEmpty()
264
0
            ? static_cast<const nsACString&>(desc)
265
0
            : static_cast<const nsACString&>(specialDesc);
266
0
        if (isMemoryFile) {
267
0
          aCallback->Callback(EmptyCString(),
268
0
              path,
269
0
              KIND_OTHER,
270
0
              UNITS_BYTES,
271
0
              size / refCount,
272
0
              descString,
273
0
              aData);
274
0
        } else {
275
0
          aCallback->Callback(EmptyCString(),
276
0
              path,
277
0
              KIND_OTHER,
278
0
              UNITS_COUNT,
279
0
              1,
280
0
              descString,
281
0
              aData);
282
0
        }
283
0
        continue;
284
0
      }
285
0
286
0
      // Just report the path for the MediaSource.
287
0
      nsAutoCString path;
288
0
      path = "media-source-urls/";
289
0
      BuildPath(path, key, info, aAnonymize);
290
0
291
0
      NS_NAMED_LITERAL_CSTRING(desc,
292
0
        "An object URL allocated with URL.createObjectURL; the referenced "
293
0
        "data cannot be freed until all URLs for it have been explicitly "
294
0
        "invalidated with URL.revokeObjectURL.");
295
0
296
0
      aCallback->Callback(EmptyCString(), path, KIND_OTHER, UNITS_COUNT, 1,
297
0
                          desc, aData);
298
0
    }
299
0
300
0
    return NS_OK;
301
0
  }
302
303
  // Initialize info->mStack to record JS stack info, if enabled.
304
  // The string generated here is used in ReportCallback, below.
305
  static void GetJSStackForBlob(DataInfo* aInfo)
306
0
  {
307
0
    nsCString& stack = aInfo->mStack;
308
0
    MOZ_ASSERT(stack.IsEmpty());
309
0
    const uint32_t maxFrames = Preferences::GetUint("memory.blob_report.stack_frames");
310
0
311
0
    if (maxFrames == 0) {
312
0
      return;
313
0
    }
314
0
315
0
    nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack(maxFrames);
316
0
317
0
    nsAutoCString origin;
318
0
    nsCOMPtr<nsIURI> principalURI;
319
0
    if (NS_SUCCEEDED(aInfo->mPrincipal->GetURI(getter_AddRefs(principalURI)))
320
0
        && principalURI) {
321
0
      principalURI->GetPrePath(origin);
322
0
    }
323
0
324
0
    // If we got a frame, we better have a current JSContext.  This is cheating
325
0
    // a bit; ideally we'd have our caller pass in a JSContext, or have
326
0
    // GetCurrentJSStack() hand out the JSContext it found.
327
0
    JSContext* cx = frame ? nsContentUtils::GetCurrentJSContext() : nullptr;
328
0
329
0
    for (uint32_t i = 0; frame; ++i) {
330
0
      nsString fileNameUTF16;
331
0
      frame->GetFilename(cx, fileNameUTF16);
332
0
333
0
      int32_t lineNumber = frame->GetLineNumber(cx);
334
0
335
0
      if (!fileNameUTF16.IsEmpty()) {
336
0
        NS_ConvertUTF16toUTF8 fileName(fileNameUTF16);
337
0
        stack += "js(";
338
0
        if (!origin.IsEmpty()) {
339
0
          // Make the file name root-relative for conciseness if possible.
340
0
          const char* originData;
341
0
          uint32_t originLen;
342
0
343
0
          originLen = origin.GetData(&originData);
344
0
          // If fileName starts with origin + "/", cut up to that "/".
345
0
          if (fileName.Length() >= originLen + 1 &&
346
0
              memcmp(fileName.get(), originData, originLen) == 0 &&
347
0
              fileName[originLen] == '/') {
348
0
            fileName.Cut(0, originLen);
349
0
          }
350
0
        }
351
0
        fileName.ReplaceChar('/', '\\');
352
0
        stack += fileName;
353
0
        if (lineNumber > 0) {
354
0
          stack += ", line=";
355
0
          stack.AppendInt(lineNumber);
356
0
        }
357
0
        stack += ")/";
358
0
      }
359
0
360
0
      frame = frame->GetCaller(cx);
361
0
    }
362
0
  }
363
364
 private:
365
0
  ~BlobURLsReporter() {}
366
367
  static void BuildPath(nsAutoCString& path,
368
                        nsCStringHashKey::KeyType aKey,
369
                        DataInfo* aInfo,
370
                        bool anonymize)
371
0
  {
372
0
    nsCOMPtr<nsIURI> principalURI;
373
0
    nsAutoCString url, owner;
374
0
    if (NS_SUCCEEDED(aInfo->mPrincipal->GetURI(getter_AddRefs(principalURI))) &&
375
0
        principalURI != nullptr &&
376
0
        NS_SUCCEEDED(principalURI->GetSpec(owner)) &&
377
0
        !owner.IsEmpty()) {
378
0
      owner.ReplaceChar('/', '\\');
379
0
      path += "owner(";
380
0
      if (anonymize) {
381
0
        path += "<anonymized>";
382
0
      } else {
383
0
        path += owner;
384
0
      }
385
0
      path += ")";
386
0
    } else {
387
0
      path += "owner unknown";
388
0
    }
389
0
    path += "/";
390
0
    if (anonymize) {
391
0
      path += "<anonymized-stack>";
392
0
    } else {
393
0
      path += aInfo->mStack;
394
0
    }
395
0
    url = aKey;
396
0
    url.ReplaceChar('/', '\\');
397
0
    if (anonymize) {
398
0
      path += "<anonymized-url>";
399
0
    } else {
400
0
      path += url;
401
0
    }
402
0
  }
403
};
404
405
NS_IMPL_ISUPPORTS(BlobURLsReporter, nsIMemoryReporter)
406
407
class ReleasingTimerHolder final : public Runnable
408
                                 , public nsITimerCallback
409
                                 , public nsIAsyncShutdownBlocker
410
{
411
public:
412
  NS_DECL_ISUPPORTS_INHERITED
413
414
  static void
415
  Create(const nsACString& aURI)
416
0
  {
417
0
    MOZ_ASSERT(NS_IsMainThread());
418
0
419
0
    RefPtr<ReleasingTimerHolder> holder = new ReleasingTimerHolder(aURI);
420
0
421
0
    auto raii = MakeScopeExit([holder] {
422
0
      holder->CancelTimerAndRevokeURI();
423
0
    });
424
0
425
0
    nsresult rv =
426
0
      SystemGroup::EventTargetFor(TaskCategory::Other)->Dispatch(holder.forget());
427
0
    NS_ENSURE_SUCCESS_VOID(rv);
428
0
429
0
    raii.release();
430
0
  }
431
432
  // Runnable interface
433
434
  NS_IMETHOD
435
  Run() override
436
0
  {
437
0
    RefPtr<ReleasingTimerHolder> self = this;
438
0
    auto raii = MakeScopeExit([self] {
439
0
      self->CancelTimerAndRevokeURI();
440
0
    });
441
0
442
0
    nsresult rv = NS_NewTimerWithCallback(getter_AddRefs(mTimer),
443
0
                                          this, RELEASING_TIMER,
444
0
                                          nsITimer::TYPE_ONE_SHOT,
445
0
                                          SystemGroup::EventTargetFor(TaskCategory::Other));
446
0
    NS_ENSURE_SUCCESS(rv, NS_OK);
447
0
448
0
    nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
449
0
    NS_ENSURE_TRUE(!!phase, NS_OK);
450
0
451
0
    rv = phase->AddBlocker(this, NS_LITERAL_STRING(__FILE__), __LINE__,
452
0
                           NS_LITERAL_STRING("ReleasingTimerHolder shutdown"));
453
0
    NS_ENSURE_SUCCESS(rv, NS_OK);
454
0
455
0
    raii.release();
456
0
    return NS_OK;
457
0
  }
458
459
  // nsITimerCallback interface
460
461
  NS_IMETHOD
462
  Notify(nsITimer* aTimer) override
463
0
  {
464
0
    RevokeURI();
465
0
    return NS_OK;
466
0
  }
467
468
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
469
  using nsINamed::GetName;
470
#endif
471
472
  // nsIAsyncShutdownBlocker interface
473
474
  NS_IMETHOD
475
  GetName(nsAString& aName) override
476
0
  {
477
0
    aName.AssignLiteral("ReleasingTimerHolder for blobURL: ");
478
0
    aName.Append(NS_ConvertUTF8toUTF16(mURI));
479
0
    return NS_OK;
480
0
  }
481
482
  NS_IMETHOD
483
  BlockShutdown(nsIAsyncShutdownClient* aClient) override
484
0
  {
485
0
    CancelTimerAndRevokeURI();
486
0
    return NS_OK;
487
0
  }
488
489
  NS_IMETHOD
490
  GetState(nsIPropertyBag**) override
491
0
  {
492
0
    return NS_OK;
493
0
  }
494
495
private:
496
  explicit ReleasingTimerHolder(const nsACString& aURI)
497
    : Runnable("ReleasingTimerHolder")
498
    , mURI(aURI)
499
0
  {}
500
501
  ~ReleasingTimerHolder()
502
0
  {}
503
504
  void
505
  RevokeURI()
506
0
  {
507
0
    // Remove the shutting down blocker
508
0
    nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
509
0
    if (phase) {
510
0
      phase->RemoveBlocker(this);
511
0
    }
512
0
513
0
    DataInfo* info = GetDataInfo(mURI, true /* We care about revoked dataInfo */);
514
0
    if (!info) {
515
0
      // Already gone!
516
0
      return;
517
0
    }
518
0
519
0
    MOZ_ASSERT(info->mRevoked);
520
0
521
0
    gDataTable->Remove(mURI);
522
0
    if (gDataTable->Count() == 0) {
523
0
      delete gDataTable;
524
0
      gDataTable = nullptr;
525
0
    }
526
0
  }
527
528
  void
529
  CancelTimerAndRevokeURI()
530
0
  {
531
0
    if (mTimer) {
532
0
      mTimer->Cancel();
533
0
      mTimer = nullptr;
534
0
    }
535
0
536
0
    RevokeURI();
537
0
  }
538
539
  static nsCOMPtr<nsIAsyncShutdownClient>
540
  GetShutdownPhase()
541
0
  {
542
0
    nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
543
0
    NS_ENSURE_TRUE(!!svc, nullptr);
544
0
545
0
    nsCOMPtr<nsIAsyncShutdownClient> phase;
546
0
    nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(phase));
547
0
    NS_ENSURE_SUCCESS(rv, nullptr);
548
0
549
0
    return phase;
550
0
  }
551
552
  nsCString mURI;
553
  nsCOMPtr<nsITimer> mTimer;
554
};
555
556
NS_IMPL_ISUPPORTS_INHERITED(ReleasingTimerHolder, Runnable, nsITimerCallback,
557
                            nsIAsyncShutdownBlocker)
558
559
template<typename T>
560
static nsresult
561
AddDataEntryInternal(const nsACString& aURI, T aObject,
562
                     nsIPrincipal* aPrincipal)
563
0
{
564
0
  if (!gDataTable) {
565
0
    gDataTable = new nsClassHashtable<nsCStringHashKey, DataInfo>;
566
0
  }
567
0
568
0
  DataInfo* info = new DataInfo(aObject, aPrincipal);
569
0
  BlobURLsReporter::GetJSStackForBlob(info);
570
0
571
0
  gDataTable->Put(aURI, info);
572
0
  return NS_OK;
573
0
}
Unexecuted instantiation: Unified_cpp_dom_file_uri0.cpp:nsresult mozilla::dom::AddDataEntryInternal<mozilla::dom::BlobImpl*>(nsTSubstring<char> const&, mozilla::dom::BlobImpl*, nsIPrincipal*)
Unexecuted instantiation: Unified_cpp_dom_file_uri0.cpp:nsresult mozilla::dom::AddDataEntryInternal<mozilla::dom::MediaSource*>(nsTSubstring<char> const&, mozilla::dom::MediaSource*, nsIPrincipal*)
574
575
void
576
BlobURLProtocolHandler::Init(void)
577
1
{
578
1
  static bool initialized = false;
579
1
580
1
  if (!initialized) {
581
1
    initialized = true;
582
1
    RegisterStrongMemoryReporter(new BlobURLsReporter());
583
1
  }
584
1
}
585
586
BlobURLProtocolHandler::BlobURLProtocolHandler()
587
1
{
588
1
  Init();
589
1
}
590
591
0
BlobURLProtocolHandler::~BlobURLProtocolHandler() = default;
592
593
/* static */ nsresult
594
BlobURLProtocolHandler::AddDataEntry(BlobImpl* aBlobImpl,
595
                                     nsIPrincipal* aPrincipal,
596
                                     nsACString& aUri)
597
0
{
598
0
  MOZ_ASSERT(aBlobImpl);
599
0
  MOZ_ASSERT(aPrincipal);
600
0
601
0
  Init();
602
0
603
0
  nsresult rv = GenerateURIString(aPrincipal, aUri);
604
0
  NS_ENSURE_SUCCESS(rv, rv);
605
0
606
0
  rv = AddDataEntryInternal(aUri, aBlobImpl, aPrincipal);
607
0
  NS_ENSURE_SUCCESS(rv, rv);
608
0
609
0
  BroadcastBlobURLRegistration(aUri, aBlobImpl, aPrincipal);
610
0
  return NS_OK;
611
0
}
612
613
/* static */ nsresult
614
BlobURLProtocolHandler::AddDataEntry(MediaSource* aMediaSource,
615
                                     nsIPrincipal* aPrincipal,
616
                                     nsACString& aUri)
617
0
{
618
0
  MOZ_ASSERT(aMediaSource);
619
0
  MOZ_ASSERT(aPrincipal);
620
0
621
0
  Init();
622
0
623
0
  nsresult rv = GenerateURIString(aPrincipal, aUri);
624
0
  NS_ENSURE_SUCCESS(rv, rv);
625
0
626
0
  rv = AddDataEntryInternal(aUri, aMediaSource, aPrincipal);
627
0
  NS_ENSURE_SUCCESS(rv, rv);
628
0
629
0
  return NS_OK;
630
0
}
631
632
/* static */ nsresult
633
BlobURLProtocolHandler::AddDataEntry(const nsACString& aURI,
634
                                     nsIPrincipal* aPrincipal,
635
                                     BlobImpl* aBlobImpl)
636
0
{
637
0
  MOZ_ASSERT(aPrincipal);
638
0
  MOZ_ASSERT(aBlobImpl);
639
0
640
0
  return AddDataEntryInternal(aURI, aBlobImpl, aPrincipal);
641
0
}
642
643
/* static */ bool
644
BlobURLProtocolHandler::GetAllBlobURLEntries(nsTArray<BlobURLRegistrationData>& aRegistrations,
645
                                             ContentParent* aCP)
646
0
{
647
0
  MOZ_ASSERT(aCP);
648
0
649
0
  if (!gDataTable) {
650
0
    return true;
651
0
  }
652
0
653
0
  for (auto iter = gDataTable->ConstIter(); !iter.Done(); iter.Next()) {
654
0
    DataInfo* info = iter.UserData();
655
0
    MOZ_ASSERT(info);
656
0
657
0
    if (info->mObjectType != DataInfo::eBlobImpl) {
658
0
      continue;
659
0
    }
660
0
661
0
    MOZ_ASSERT(info->mBlobImpl);
662
0
663
0
    IPCBlob ipcBlob;
664
0
    nsresult rv = IPCBlobUtils::Serialize(info->mBlobImpl, aCP, ipcBlob);
665
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
666
0
      return false;
667
0
    }
668
0
669
0
    aRegistrations.AppendElement(BlobURLRegistrationData(
670
0
      nsCString(iter.Key()), ipcBlob, IPC::Principal(info->mPrincipal),
671
0
                info->mRevoked));
672
0
  }
673
0
674
0
  return true;
675
0
}
676
677
/*static */ void
678
BlobURLProtocolHandler::RemoveDataEntry(const nsACString& aUri,
679
                                        bool aBroadcastToOtherProcesses)
680
0
{
681
0
  if (!gDataTable) {
682
0
    return;
683
0
  }
684
0
685
0
  DataInfo* info = GetDataInfo(aUri);
686
0
  if (!info) {
687
0
    return;
688
0
  }
689
0
690
0
  info->mRevoked = true;
691
0
692
0
  if (aBroadcastToOtherProcesses && info->mObjectType == DataInfo::eBlobImpl) {
693
0
    BroadcastBlobURLUnregistration(nsCString(aUri));
694
0
  }
695
0
696
0
  // The timer will take care of removing the entry for real after
697
0
  // RELEASING_TIMER milliseconds. In the meantime, the DataInfo, marked as
698
0
  // revoked, will not be exposed.
699
0
  ReleasingTimerHolder::Create(aUri);
700
0
}
701
702
/* static */ void
703
BlobURLProtocolHandler::RemoveDataEntries()
704
0
{
705
0
  if (!gDataTable) {
706
0
    return;
707
0
  }
708
0
709
0
  gDataTable->Clear();
710
0
  delete gDataTable;
711
0
  gDataTable = nullptr;
712
0
}
713
714
/* static */ bool
715
BlobURLProtocolHandler::HasDataEntry(const nsACString& aUri)
716
0
{
717
0
  return !!GetDataInfo(aUri);
718
0
}
719
720
/* static */ nsresult
721
BlobURLProtocolHandler::GenerateURIString(nsIPrincipal* aPrincipal,
722
                                          nsACString& aUri)
723
0
{
724
0
  nsresult rv;
725
0
  nsCOMPtr<nsIUUIDGenerator> uuidgen =
726
0
    do_GetService("@mozilla.org/uuid-generator;1", &rv);
727
0
  NS_ENSURE_SUCCESS(rv, rv);
728
0
729
0
  nsID id;
730
0
  rv = uuidgen->GenerateUUIDInPlace(&id);
731
0
  NS_ENSURE_SUCCESS(rv, rv);
732
0
733
0
  char chars[NSID_LENGTH];
734
0
  id.ToProvidedString(chars);
735
0
736
0
  aUri.AssignLiteral(BLOBURI_SCHEME);
737
0
  aUri.Append(':');
738
0
739
0
  if (aPrincipal) {
740
0
    nsAutoCString origin;
741
0
    rv = nsContentUtils::GetASCIIOrigin(aPrincipal, origin);
742
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
743
0
      return rv;
744
0
    }
745
0
746
0
    aUri.Append(origin);
747
0
    aUri.Append('/');
748
0
  }
749
0
750
0
  aUri += Substring(chars + 1, chars + NSID_LENGTH - 2);
751
0
752
0
  return NS_OK;
753
0
}
754
755
/* static */ nsIPrincipal*
756
BlobURLProtocolHandler::GetDataEntryPrincipal(const nsACString& aUri)
757
0
{
758
0
  if (!gDataTable) {
759
0
    return nullptr;
760
0
  }
761
0
762
0
  DataInfo* res = GetDataInfo(aUri);
763
0
764
0
  if (!res) {
765
0
    return nullptr;
766
0
  }
767
0
768
0
  return res->mPrincipal;
769
0
}
770
771
/* static */ void
772
BlobURLProtocolHandler::Traverse(const nsACString& aUri,
773
                                 nsCycleCollectionTraversalCallback& aCallback)
774
0
{
775
0
  if (!gDataTable) {
776
0
    return;
777
0
  }
778
0
779
0
  DataInfo* res;
780
0
  gDataTable->Get(aUri, &res);
781
0
  if (!res) {
782
0
    return;
783
0
  }
784
0
785
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCallback, "BlobURLProtocolHandler DataInfo.mBlobImpl");
786
0
  aCallback.NoteXPCOMChild(res->mBlobImpl);
787
0
788
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCallback, "BlobURLProtocolHandler DataInfo.mMediaSource");
789
0
  aCallback.NoteXPCOMChild(res->mMediaSource);
790
0
}
791
792
NS_IMPL_ISUPPORTS(BlobURLProtocolHandler, nsIProtocolHandler,
793
                  nsISupportsWeakReference)
794
795
NS_IMETHODIMP
796
BlobURLProtocolHandler::GetDefaultPort(int32_t *result)
797
0
{
798
0
  *result = -1;
799
0
  return NS_OK;
800
0
}
801
802
NS_IMETHODIMP
803
BlobURLProtocolHandler::GetProtocolFlags(uint32_t *result)
804
0
{
805
0
  *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_SUBSUMERS |
806
0
            URI_NON_PERSISTABLE | URI_IS_LOCAL_RESOURCE;
807
0
  return NS_OK;
808
0
}
809
810
NS_IMETHODIMP
811
BlobURLProtocolHandler::GetFlagsForURI(nsIURI *aURI, uint32_t *aResult)
812
0
{
813
0
  Unused << BlobURLProtocolHandler::GetProtocolFlags(aResult);
814
0
  if (IsBlobURI(aURI)) {
815
0
    *aResult |= URI_IS_LOCAL_RESOURCE;
816
0
  }
817
0
818
0
  return NS_OK;
819
0
}
820
821
NS_IMETHODIMP
822
BlobURLProtocolHandler::NewURI(const nsACString& aSpec,
823
                               const char *aCharset,
824
                               nsIURI *aBaseURI,
825
                               nsIURI **aResult)
826
1.77k
{
827
1.77k
  *aResult = nullptr;
828
1.77k
829
1.77k
  nsCOMPtr<nsIURI> uri;
830
1.77k
  nsresult rv = NS_MutateURI(new BlobURL::Mutator())
831
1.77k
                  .SetSpec(aSpec)
832
1.77k
                  .Finalize(uri);
833
1.77k
  NS_ENSURE_SUCCESS(rv, rv);
834
1.77k
835
1.77k
  bool revoked = true;
836
1.77k
  DataInfo* info = GetDataInfo(aSpec);
837
1.77k
  if (info && info->mObjectType == DataInfo::eBlobImpl) {
838
0
    revoked = info->mRevoked;
839
0
  }
840
1.77k
841
1.77k
  RefPtr<BlobURL> blobURL;
842
1.77k
  rv = uri->QueryInterface(kHOSTOBJECTURICID,
843
1.77k
                           getter_AddRefs(blobURL));
844
1.77k
  NS_ENSURE_SUCCESS(rv, rv);
845
1.77k
846
1.77k
  MOZ_ASSERT(blobURL);
847
1.77k
  blobURL->mRevoked = revoked;
848
1.77k
849
1.77k
  uri.forget(aResult);
850
1.77k
  return NS_OK;
851
1.77k
}
852
853
NS_IMETHODIMP
854
BlobURLProtocolHandler::NewChannel2(nsIURI* aURI,
855
                                    nsILoadInfo* aLoadInfo,
856
                                    nsIChannel** aResult)
857
0
{
858
0
  RefPtr<BlobURLChannel> channel = new BlobURLChannel(aURI, aLoadInfo);
859
0
860
0
  auto raii = MakeScopeExit([&] {
861
0
    channel->InitFailed();
862
0
    channel.forget(aResult);
863
0
  });
864
0
865
0
  RefPtr<BlobURL> blobURL;
866
0
  nsresult rv = aURI->QueryInterface(kHOSTOBJECTURICID,
867
0
                                     getter_AddRefs(blobURL));
868
0
  if (NS_FAILED(rv) || !blobURL) {
869
0
    return NS_OK;
870
0
  }
871
0
872
0
  DataInfo* info = GetDataInfoFromURI(aURI, true /*aAlsoIfRevoked */);
873
0
  if (!info || info->mObjectType != DataInfo::eBlobImpl || !info->mBlobImpl) {
874
0
    return NS_OK;
875
0
  }
876
0
877
0
  if (blobURL->Revoked()) {
878
0
    return NS_OK;
879
0
  }
880
0
881
0
  // We want to be sure that we stop the creation of the channel if the blob URL
882
0
  // is copy-and-pasted on a different context (ex. private browsing or
883
0
  // containers).
884
0
  //
885
0
  // We also allow the system principal to create the channel regardless of the
886
0
  // OriginAttributes.  This is primarily for the benefit of mechanisms like
887
0
  // the Download API that explicitly create a channel with the system
888
0
  // principal and which is never mutated to have a non-zero mPrivateBrowsingId
889
0
  // or container.
890
0
  if (aLoadInfo &&
891
0
      !nsContentUtils::IsSystemPrincipal(aLoadInfo->LoadingPrincipal()) &&
892
0
      !ChromeUtils::IsOriginAttributesEqualIgnoringFPD(aLoadInfo->GetOriginAttributes(),
893
0
                                                       BasePrincipal::Cast(info->mPrincipal)->OriginAttributesRef())) {
894
0
    return NS_OK;
895
0
  }
896
0
897
0
  raii.release();
898
0
899
0
  channel->Initialize(info->mBlobImpl);
900
0
  channel.forget(aResult);
901
0
  return NS_OK;
902
0
}
903
904
NS_IMETHODIMP
905
BlobURLProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
906
0
{
907
0
  return NewChannel2(uri, nullptr, result);
908
0
}
909
910
NS_IMETHODIMP
911
BlobURLProtocolHandler::AllowPort(int32_t port, const char *scheme,
912
                                  bool *_retval)
913
0
{
914
0
  // don't override anything.
915
0
  *_retval = false;
916
0
  return NS_OK;
917
0
}
918
919
NS_IMETHODIMP
920
BlobURLProtocolHandler::GetScheme(nsACString &result)
921
0
{
922
0
  result.AssignLiteral(BLOBURI_SCHEME);
923
0
  return NS_OK;
924
0
}
925
926
/* static */ bool
927
BlobURLProtocolHandler::GetBlobURLPrincipal(nsIURI* aURI,
928
                                            nsIPrincipal** aPrincipal)
929
11.5k
{
930
11.5k
  MOZ_ASSERT(aURI);
931
11.5k
  MOZ_ASSERT(aPrincipal);
932
11.5k
933
11.5k
  RefPtr<BlobURL> blobURL;
934
11.5k
  nsresult rv = aURI->QueryInterface(kHOSTOBJECTURICID,
935
11.5k
                                     getter_AddRefs(blobURL));
936
11.5k
  if (NS_FAILED(rv) || !blobURL) {
937
11.5k
    return false;
938
11.5k
  }
939
0
940
0
  DataInfo* info = GetDataInfoFromURI(aURI, true /*aAlsoIfRevoked */);
941
0
  if (!info || info->mObjectType != DataInfo::eBlobImpl || !info->mBlobImpl) {
942
0
    return false;
943
0
  }
944
0
945
0
  nsCOMPtr<nsIPrincipal> principal;
946
0
947
0
  if (blobURL->Revoked()) {
948
0
    principal =
949
0
      NullPrincipal::Create(BasePrincipal::Cast(info->mPrincipal)->OriginAttributesRef());
950
0
  } else {
951
0
    principal = info->mPrincipal;
952
0
  }
953
0
954
0
  principal.forget(aPrincipal);
955
0
  return true;
956
0
}
957
958
} // dom namespace
959
} // mozilla namespace
960
961
nsresult
962
NS_GetBlobForBlobURI(nsIURI* aURI, BlobImpl** aBlob)
963
0
{
964
0
  *aBlob = nullptr;
965
0
966
0
  DataInfo* info = GetDataInfoFromURI(aURI, false /* aAlsoIfRevoked */);
967
0
  if (!info || info->mObjectType != DataInfo::eBlobImpl) {
968
0
    return NS_ERROR_DOM_BAD_URI;
969
0
  }
970
0
971
0
  RefPtr<BlobImpl> blob = info->mBlobImpl;
972
0
  blob.forget(aBlob);
973
0
  return NS_OK;
974
0
}
975
976
nsresult
977
NS_GetBlobForBlobURISpec(const nsACString& aSpec, BlobImpl** aBlob)
978
0
{
979
0
  *aBlob = nullptr;
980
0
981
0
  DataInfo* info = GetDataInfo(aSpec);
982
0
  if (!info || info->mObjectType != DataInfo::eBlobImpl) {
983
0
    return NS_ERROR_DOM_BAD_URI;
984
0
  }
985
0
986
0
  RefPtr<BlobImpl> blob = info->mBlobImpl;
987
0
  blob.forget(aBlob);
988
0
  return NS_OK;
989
0
}
990
991
nsresult
992
NS_GetSourceForMediaSourceURI(nsIURI* aURI, MediaSource** aSource)
993
0
{
994
0
  *aSource = nullptr;
995
0
996
0
  DataInfo* info = GetDataInfoFromURI(aURI);
997
0
  if (!info || info->mObjectType != DataInfo::eMediaSource) {
998
0
    return NS_ERROR_DOM_BAD_URI;
999
0
  }
1000
0
1001
0
  RefPtr<MediaSource> mediaSource = info->mMediaSource;
1002
0
  mediaSource.forget(aSource);
1003
0
  return NS_OK;
1004
0
}
1005
1006
namespace mozilla {
1007
namespace dom {
1008
1009
#define NS_BLOBPROTOCOLHANDLER_CID \
1010
{ 0xb43964aa, 0xa078, 0x44b2, \
1011
  { 0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 } }
1012
1013
NS_GENERIC_FACTORY_CONSTRUCTOR(BlobURLProtocolHandler)
1014
1015
NS_DEFINE_NAMED_CID(NS_BLOBPROTOCOLHANDLER_CID);
1016
1017
static const Module::CIDEntry kBlobURLProtocolHandlerCIDs[] = {
1018
  { &kNS_BLOBPROTOCOLHANDLER_CID, false, nullptr, BlobURLProtocolHandlerConstructor },
1019
  { nullptr }
1020
};
1021
1022
static const Module::ContractIDEntry kBlobURLProtocolHandlerContracts[] = {
1023
  { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX BLOBURI_SCHEME, &kNS_BLOBPROTOCOLHANDLER_CID },
1024
  { nullptr }
1025
};
1026
1027
static const Module kBlobURLProtocolHandlerModule = {
1028
  Module::kVersion,
1029
  kBlobURLProtocolHandlerCIDs,
1030
  kBlobURLProtocolHandlerContracts
1031
};
1032
1033
NSMODULE_DEFN(BlobURLProtocolHandler) = &kBlobURLProtocolHandlerModule;
1034
1035
bool IsType(nsIURI* aUri, DataInfo::ObjectType aType)
1036
0
{
1037
0
  DataInfo* info = GetDataInfoFromURI(aUri);
1038
0
  if (!info) {
1039
0
    return false;
1040
0
  }
1041
0
1042
0
  return info->mObjectType == aType;
1043
0
}
1044
1045
bool IsBlobURI(nsIURI* aUri)
1046
0
{
1047
0
  return IsType(aUri, DataInfo::eBlobImpl);
1048
0
}
1049
1050
bool IsMediaSourceURI(nsIURI* aUri)
1051
0
{
1052
0
  return IsType(aUri, DataInfo::eMediaSource);
1053
0
}
1054
1055
} // dom namespace
1056
} // mozilla namespace