already_AddRefed<BlobImpl> MultipartBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType, ErrorResult& aRv) { // If we clamped to nothing we create an empty blob nsTArray<nsRefPtr<BlobImpl>> blobImpls; uint64_t length = aLength; uint64_t skipStart = aStart; // Prune the list of blobs if we can uint32_t i; for (i = 0; length && skipStart && i < mBlobImpls.Length(); i++) { BlobImpl* blobImpl = mBlobImpls[i].get(); uint64_t l = blobImpl->GetSize(aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } if (skipStart < l) { uint64_t upperBound = std::min<uint64_t>(l - skipStart, length); nsRefPtr<BlobImpl> firstBlobImpl = blobImpl->CreateSlice(skipStart, upperBound, aContentType, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } // Avoid wrapping a single blob inside an MultipartBlobImpl if (length == upperBound) { return firstBlobImpl.forget(); } blobImpls.AppendElement(firstBlobImpl); length -= upperBound; i++; break; } skipStart -= l; } // Now append enough blobs until we're done for (; length && i < mBlobImpls.Length(); i++) { BlobImpl* blobImpl = mBlobImpls[i].get(); uint64_t l = blobImpl->GetSize(aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } if (length < l) { nsRefPtr<BlobImpl> lastBlobImpl = blobImpl->CreateSlice(0, length, aContentType, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } blobImpls.AppendElement(lastBlobImpl); } else { blobImpls.AppendElement(blobImpl); } length -= std::min<uint64_t>(l, length); } // we can create our blob now nsRefPtr<BlobImpl> impl = new MultipartBlobImpl(blobImpls, aContentType); return impl.forget(); }
nsresult FetchDriver::BasicFetch() { nsAutoCString url; mRequest->GetURL(url); nsCOMPtr<nsIURI> uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); return rv; } nsAutoCString scheme; rv = uri->GetScheme(scheme); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); return rv; } if (scheme.LowerCaseEqualsLiteral("about")) { if (url.EqualsLiteral("about:blank")) { nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK")); ErrorResult result; response->Headers()->Append(NS_LITERAL_CSTRING("content-type"), NS_LITERAL_CSTRING("text/html;charset=utf-8"), result); MOZ_ASSERT(!result.Failed()); nsCOMPtr<nsIInputStream> body; rv = NS_NewCStringInputStream(getter_AddRefs(body), EmptyCString()); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); return rv; } response->SetBody(body); BeginResponse(response); return SucceedWithResponse(); } return FailWithNetworkError(); } if (scheme.LowerCaseEqualsLiteral("blob")) { nsRefPtr<BlobImpl> blobImpl; rv = NS_GetBlobForBlobURI(uri, getter_AddRefs(blobImpl)); BlobImpl* blob = static_cast<BlobImpl*>(blobImpl.get()); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); return rv; } nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK")); ErrorResult result; uint64_t size = blob->GetSize(result); if (NS_WARN_IF(result.Failed())) { FailWithNetworkError(); return result.StealNSResult(); } nsAutoString sizeStr; sizeStr.AppendInt(size); response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"), NS_ConvertUTF16toUTF8(sizeStr), result); if (NS_WARN_IF(result.Failed())) { FailWithNetworkError(); return result.StealNSResult(); } nsAutoString type; blob->GetType(type); response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), NS_ConvertUTF16toUTF8(type), result); if (NS_WARN_IF(result.Failed())) { FailWithNetworkError(); return result.StealNSResult(); } nsCOMPtr<nsIInputStream> stream; blob->GetInternalStream(getter_AddRefs(stream), result); if (NS_WARN_IF(result.Failed())) { FailWithNetworkError(); return result.StealNSResult(); } response->SetBody(stream); BeginResponse(response); return SucceedWithResponse(); } if (scheme.LowerCaseEqualsLiteral("data")) { nsAutoCString method; mRequest->GetMethod(method); if (method.LowerCaseEqualsASCII("get")) { // Use nsDataHandler directly so that we can extract the content type. // XXX(nsm): Is there a way to acquire the charset without such tight // coupling with the DataHandler? nsIProtocolHandler does not provide // anything similar. nsAutoCString contentType, contentCharset, dataBuffer, hashRef; bool isBase64; rv = nsDataHandler::ParseURI(url, contentType, contentCharset, isBase64, dataBuffer, hashRef); if (NS_SUCCEEDED(rv)) { ErrorResult result; nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK")); if (!contentCharset.IsEmpty()) { contentType.Append(";charset="); contentType.Append(contentCharset); } response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, result); if (!result.Failed()) { nsCOMPtr<nsIInputStream> stream; rv = NS_NewCStringInputStream(getter_AddRefs(stream), dataBuffer); if (NS_SUCCEEDED(rv)) { response->SetBody(stream); BeginResponse(response); return SucceedWithResponse(); } } } } return FailWithNetworkError(); } if (scheme.LowerCaseEqualsLiteral("http") || scheme.LowerCaseEqualsLiteral("https") || scheme.LowerCaseEqualsLiteral("app")) { return HttpFetch(); } return FailWithNetworkError(); }
nsresult FetchDriver::BasicFetch() { nsAutoCString url; mRequest->GetURL(url); nsCOMPtr<nsIURI> uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); return rv; } nsAutoCString scheme; rv = uri->GetScheme(scheme); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); return rv; } if (scheme.LowerCaseEqualsLiteral("about")) { if (url.EqualsLiteral("about:blank")) { nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK")); ErrorResult result; response->Headers()->Append(NS_LITERAL_CSTRING("content-type"), NS_LITERAL_CSTRING("text/html;charset=utf-8"), result); MOZ_ASSERT(!result.Failed()); nsCOMPtr<nsIInputStream> body; rv = NS_NewCStringInputStream(getter_AddRefs(body), EmptyCString()); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); return rv; } response->SetBody(body); BeginResponse(response); return SucceedWithResponse(); } return FailWithNetworkError(); } if (scheme.LowerCaseEqualsLiteral("blob")) { nsRefPtr<BlobImpl> blobImpl; rv = NS_GetBlobForBlobURI(uri, getter_AddRefs(blobImpl)); BlobImpl* blob = static_cast<BlobImpl*>(blobImpl.get()); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); return rv; } nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK")); ErrorResult result; uint64_t size = blob->GetSize(result); if (NS_WARN_IF(result.Failed())) { FailWithNetworkError(); return result.StealNSResult(); } nsAutoString sizeStr; sizeStr.AppendInt(size); response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"), NS_ConvertUTF16toUTF8(sizeStr), result); if (NS_WARN_IF(result.Failed())) { FailWithNetworkError(); return result.StealNSResult(); } nsAutoString type; blob->GetType(type); response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), NS_ConvertUTF16toUTF8(type), result); if (NS_WARN_IF(result.Failed())) { FailWithNetworkError(); return result.StealNSResult(); } nsCOMPtr<nsIInputStream> stream; blob->GetInternalStream(getter_AddRefs(stream), result); if (NS_WARN_IF(result.Failed())) { FailWithNetworkError(); return result.StealNSResult(); } response->SetBody(stream); BeginResponse(response); return SucceedWithResponse(); } if (scheme.LowerCaseEqualsLiteral("data")) { nsAutoCString method; mRequest->GetMethod(method); if (method.LowerCaseEqualsASCII("get")) { nsresult rv; nsCOMPtr<nsIProtocolHandler> dataHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "data", &rv); if (NS_WARN_IF(NS_FAILED(rv))) { return FailWithNetworkError(); } nsCOMPtr<nsIChannel> channel; rv = dataHandler->NewChannel(uri, getter_AddRefs(channel)); if (NS_WARN_IF(NS_FAILED(rv))) { return FailWithNetworkError(); } nsCOMPtr<nsIInputStream> stream; rv = channel->Open(getter_AddRefs(stream)); if (NS_WARN_IF(NS_FAILED(rv))) { return FailWithNetworkError(); } // nsDataChannel will parse the data URI when it is Open()ed and set the // correct content type and charset. nsAutoCString contentType; if (NS_SUCCEEDED(channel->GetContentType(contentType))) { nsAutoCString charset; if (NS_SUCCEEDED(channel->GetContentCharset(charset)) && !charset.IsEmpty()) { contentType.AppendLiteral(";charset="); contentType.Append(charset); } } else { NS_WARNING("Could not get content type from data channel"); } nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK")); ErrorResult result; response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, result); if (NS_WARN_IF(result.Failed())) { FailWithNetworkError(); return result.StealNSResult(); } response->SetBody(stream); BeginResponse(response); return SucceedWithResponse(); } return FailWithNetworkError(); } if (scheme.LowerCaseEqualsLiteral("http") || scheme.LowerCaseEqualsLiteral("https") || scheme.LowerCaseEqualsLiteral("app")) { return HttpFetch(); } return FailWithNetworkError(); }
NS_DECL_ISUPPORTS NS_IMETHOD CollectReports(nsIHandleReportCallback* aCallback, nsISupports* aData, bool aAnonymize) override { if (!gDataTable) { return NS_OK; } nsDataHashtable<nsPtrHashKey<BlobImpl>, uint32_t> refCounts; // Determine number of URLs per BlobImpl, to handle the case where it's > 1. for (auto iter = gDataTable->Iter(); !iter.Done(); iter.Next()) { if (iter.UserData()->mObjectType != DataInfo::eBlobImpl) { continue; } BlobImpl* blobImpl = iter.UserData()->mBlobImpl; MOZ_ASSERT(blobImpl); refCounts.Put(blobImpl, refCounts.Get(blobImpl) + 1); } for (auto iter = gDataTable->Iter(); !iter.Done(); iter.Next()) { nsCStringHashKey::KeyType key = iter.Key(); DataInfo* info = iter.UserData(); if (iter.UserData()->mObjectType == DataInfo::eBlobImpl) { BlobImpl* blobImpl = iter.UserData()->mBlobImpl; MOZ_ASSERT(blobImpl); NS_NAMED_LITERAL_CSTRING(desc, "A blob URL allocated with URL.createObjectURL; the referenced " "blob cannot be freed until all URLs for it have been explicitly " "invalidated with URL.revokeObjectURL."); nsAutoCString path, url, owner, specialDesc; uint64_t size = 0; uint32_t refCount = 1; DebugOnly<bool> blobImplWasCounted; blobImplWasCounted = refCounts.Get(blobImpl, &refCount); MOZ_ASSERT(blobImplWasCounted); MOZ_ASSERT(refCount > 0); bool isMemoryFile = blobImpl->IsMemoryFile(); if (isMemoryFile) { ErrorResult rv; size = blobImpl->GetSize(rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); size = 0; } } path = isMemoryFile ? "memory-blob-urls/" : "file-blob-urls/"; BuildPath(path, key, info, aAnonymize); if (refCount > 1) { nsAutoCString addrStr; addrStr = "0x"; addrStr.AppendInt((uint64_t)(BlobImpl*)blobImpl, 16); path += " "; path.AppendInt(refCount); path += "@"; path += addrStr; specialDesc = desc; specialDesc += "\n\nNOTE: This blob (address "; specialDesc += addrStr; specialDesc += ") has "; specialDesc.AppendInt(refCount); specialDesc += " URLs."; if (isMemoryFile) { specialDesc += " Its size is divided "; specialDesc += refCount > 2 ? "among" : "between"; specialDesc += " them in this report."; } } const nsACString& descString = specialDesc.IsEmpty() ? static_cast<const nsACString&>(desc) : static_cast<const nsACString&>(specialDesc); if (isMemoryFile) { aCallback->Callback(EmptyCString(), path, KIND_OTHER, UNITS_BYTES, size / refCount, descString, aData); } else { aCallback->Callback(EmptyCString(), path, KIND_OTHER, UNITS_COUNT, 1, descString, aData); } continue; } // Just report the path for the DOMMediaStream or MediaSource. nsAutoCString path; path = iter.UserData()->mObjectType == DataInfo::eMediaSource ? "media-source-urls/" : "dom-media-stream-urls/"; BuildPath(path, key, info, aAnonymize); NS_NAMED_LITERAL_CSTRING(desc, "An object URL allocated with URL.createObjectURL; the referenced " "data cannot be freed until all URLs for it have been explicitly " "invalidated with URL.revokeObjectURL."); aCallback->Callback(EmptyCString(), path, KIND_OTHER, UNITS_COUNT, 1, desc, aData); } return NS_OK; }