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