nsresult NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream) { NS_ASSERTION(IsBlobURI(aURI), "Only call this with blob URIs"); *aStream = nullptr; nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(GetDataObject(aURI)); if (!blob) { return NS_ERROR_DOM_BAD_URI; } return blob->GetInternalStream(aStream); }
nsresult FileMediaResource::Open(nsIStreamListener** aStreamListener) { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); MOZ_ASSERT(aStreamListener); *aStreamListener = nullptr; nsresult rv = NS_OK; // The channel is already open. We need a synchronous stream that // implements nsISeekableStream, so we have to find the underlying // file and reopen it nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(mChannel)); if (fc) { nsCOMPtr<nsIFile> file; rv = fc->GetFile(getter_AddRefs(file)); NS_ENSURE_SUCCESS(rv, rv); rv = NS_NewLocalFileInputStream( getter_AddRefs(mInput), file, -1, -1, nsIFileInputStream::SHARE_DELETE); } else if (IsBlobURI(mURI)) { rv = NS_GetStreamForBlobURI(mURI, getter_AddRefs(mInput)); } NS_ENSURE_SUCCESS(rv, rv); mSeekable = do_QueryInterface(mInput); if (!mSeekable) { // XXX The file may just be a .url or similar // shortcut that points to a Web site. We need to fix this by // doing an async open and waiting until we locate the real resource, // then using that (if it's still a file!). return NS_ERROR_FAILURE; } return NS_OK; }
// This function implements the "HTTP Fetch" algorithm from the Fetch spec. // Functionality is often split between here, the CORS listener proxy and the // Necko HTTP implementation. nsresult FetchDriver::HttpFetch(const nsACString& aPreferredAlternativeDataType) { MOZ_ASSERT(NS_IsMainThread()); // Step 1. "Let response be null." mResponse = nullptr; mOnStopRequestCalled = false; nsresult rv; nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString url; mRequest->GetURL(url); nsCOMPtr<nsIURI> uri; rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr, ios); NS_ENSURE_SUCCESS(rv, rv); // Unsafe requests aren't allowed with when using no-core mode. if (mRequest->Mode() == RequestMode::No_cors && mRequest->UnsafeRequest() && (!mRequest->HasSimpleMethod() || !mRequest->Headers()->HasOnlySimpleHeaders())) { MOZ_ASSERT(false, "The API should have caught this"); return NS_ERROR_DOM_BAD_URI; } // non-GET requests aren't allowed for blob. if (IsBlobURI(uri)) { nsAutoCString method; mRequest->GetMethod(method); if (!method.EqualsLiteral("GET")) { return NS_ERROR_DOM_NETWORK_ERR; } } // Step 2 deals with letting ServiceWorkers intercept requests. This is // handled by Necko after the channel is opened. // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be // set based on the Request's flag. // Step 3.1 "If the CORS preflight flag is set and one of these conditions is // true..." is handled by the CORS proxy. // // Step 3.2 "Set request's skip service worker flag." This isn't required // since Necko will fall back to the network if the ServiceWorker does not // respond with a valid Response. // // NS_StartCORSPreflight() will automatically kick off the original request // if it succeeds, so we need to have everything setup for the original // request too. // Step 3.3 "Let credentials flag be set if one of // - request's credentials mode is "include" // - request's credentials mode is "same-origin" and either the CORS flag // is unset or response tainting is "opaque" // is true, and unset otherwise." // Set skip serviceworker flag. // While the spec also gates on the client being a ServiceWorker, we can't // infer that here. Instead we rely on callers to set the flag correctly. const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() ? nsIChannel::LOAD_BYPASS_SERVICE_WORKER : 0; nsSecurityFlags secFlags = 0; if (mRequest->Mode() == RequestMode::Cors) { secFlags |= nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; } else if (mRequest->Mode() == RequestMode::Same_origin || mRequest->Mode() == RequestMode::Navigate) { secFlags |= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS; } else if (mRequest->Mode() == RequestMode::No_cors) { secFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS; } else { MOZ_ASSERT_UNREACHABLE("Unexpected request mode!"); return NS_ERROR_UNEXPECTED; } if (mRequest->GetRedirectMode() != RequestRedirect::Follow) { secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS; } // This is handles the use credentials flag in "HTTP // network or cache fetch" in the spec and decides whether to transmit // cookies and other identifying information. if (mRequest->GetCredentialsMode() == RequestCredentials::Include) { secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) { secFlags |= nsILoadInfo::SEC_COOKIES_OMIT; } else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin) { secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; } else { MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!"); return NS_ERROR_UNEXPECTED; } // From here on we create a channel and set its properties with the // information from the InternalRequest. This is an implementation detail. MOZ_ASSERT(mLoadGroup); nsCOMPtr<nsIChannel> chan; nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | bypassFlag | nsIChannel::LOAD_CLASSIFY_URI; if (mDocument) { MOZ_ASSERT(mDocument->NodePrincipal() == mPrincipal); rv = NS_NewChannel(getter_AddRefs(chan), uri, mDocument, secFlags, mRequest->ContentPolicyType(), nullptr, /* aPerformanceStorage */ mLoadGroup, nullptr, /* aCallbacks */ loadFlags, ios); } else if (mClientInfo.isSome()) { rv = NS_NewChannel(getter_AddRefs(chan), uri, mPrincipal, mClientInfo.ref(), mController, secFlags, mRequest->ContentPolicyType(), mPerformanceStorage, mLoadGroup, nullptr, /* aCallbacks */ loadFlags, ios); } else { rv = NS_NewChannel(getter_AddRefs(chan), uri, mPrincipal, secFlags, mRequest->ContentPolicyType(), mPerformanceStorage, mLoadGroup, nullptr, /* aCallbacks */ loadFlags, ios); } NS_ENSURE_SUCCESS(rv, rv); // Insert ourselves into the notification callbacks chain so we can set // headers on redirects. #ifdef DEBUG { nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks; chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks)); MOZ_ASSERT(!notificationCallbacks); } #endif chan->SetNotificationCallbacks(this); nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(chan)); // Mark channel as urgent-start if the Fetch is triggered by user input // events. if (cos && EventStateManager::IsHandlingUserInput()) { cos->AddClassFlags(nsIClassOfService::UrgentStart); } // Step 3.5 begins "HTTP network or cache fetch". // HTTP network or cache fetch // --------------------------- // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest. nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan); if (httpChan) { // Copy the method. nsAutoCString method; mRequest->GetMethod(method); rv = httpChan->SetRequestMethod(method); NS_ENSURE_SUCCESS(rv, rv); // Set the same headers. SetRequestHeaders(httpChan); // Step 5 of https://fetch.spec.whatwg.org/#main-fetch // If request's referrer policy is the empty string and request's client is // non-null, then set request's referrer policy to request's client's // associated referrer policy. // Basically, "client" is not in our implementation, we use // EnvironmentReferrerPolicy of the worker or document context net::ReferrerPolicy net_referrerPolicy = mRequest->GetEnvironmentReferrerPolicy(); if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) { mRequest->SetReferrerPolicy(net_referrerPolicy); } // Step 6 of https://fetch.spec.whatwg.org/#main-fetch // If request’s referrer policy is the empty string, // then set request’s referrer policy to the user-set default policy. if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) { nsCOMPtr<nsILoadInfo> loadInfo = httpChan->GetLoadInfo(); bool isPrivate = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0; net::ReferrerPolicy referrerPolicy = static_cast<net::ReferrerPolicy>(NS_GetDefaultReferrerPolicy(isPrivate)); mRequest->SetReferrerPolicy(referrerPolicy); } rv = FetchUtil::SetRequestReferrer(mPrincipal, mDocument, httpChan, mRequest); NS_ENSURE_SUCCESS(rv, rv); // Bug 1120722 - Authorization will be handled later. // Auth may require prompting, we don't support it yet. // The next patch in this same bug prevents this from aborting the request. // Credentials checks for CORS are handled by nsCORSListenerProxy, nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan); // Conversion between enumerations is safe due to static asserts in // dom/workers/ServiceWorkerManager.cpp rv = internalChan->SetCorsMode(static_cast<uint32_t>(mRequest->Mode())); MOZ_ASSERT(NS_SUCCEEDED(rv)); rv = internalChan->SetRedirectMode(static_cast<uint32_t>(mRequest->GetRedirectMode())); MOZ_ASSERT(NS_SUCCEEDED(rv)); mRequest->MaybeSkipCacheIfPerformingRevalidation(); rv = internalChan->SetFetchCacheMode(static_cast<uint32_t>(mRequest->GetCacheMode())); MOZ_ASSERT(NS_SUCCEEDED(rv)); rv = internalChan->SetIntegrityMetadata(mRequest->GetIntegrity()); MOZ_ASSERT(NS_SUCCEEDED(rv)); // Set the initiator type nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan)); if (timedChannel) { timedChannel->SetInitiatorType(NS_LITERAL_STRING("fetch")); } } // Step 5. Proxy authentication will be handled by Necko. // Continue setting up 'HTTPRequest'. Content-Type and body data. nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan); if (uploadChan) { nsAutoCString contentType; ErrorResult result; mRequest->Headers()->GetFirst(NS_LITERAL_CSTRING("content-type"), contentType, result); // We don't actually expect "result" to have failed here: that only happens // for invalid header names. But if for some reason it did, just propagate // it out. if (result.Failed()) { return result.StealNSResult(); } // Now contentType is the header that was set in mRequest->Headers(), or a // void string if no header was set. #ifdef DEBUG bool hasContentTypeHeader = mRequest->Headers()->Has(NS_LITERAL_CSTRING("content-type"), result); MOZ_ASSERT(!result.Failed()); MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid()); #endif // DEBUG int64_t bodyLength; nsCOMPtr<nsIInputStream> bodyStream; mRequest->GetBody(getter_AddRefs(bodyStream), &bodyLength); if (bodyStream) { nsAutoCString method; mRequest->GetMethod(method); rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, bodyLength, method, false /* aStreamHasHeaders */); NS_ENSURE_SUCCESS(rv, rv); } } // If preflight is required, start a "CORS preflight fetch" // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the // implementation is handled by the http channel calling into // nsCORSListenerProxy. We just inform it which unsafe headers are included // in the request. if (mRequest->Mode() == RequestMode::Cors) { AutoTArray<nsCString, 5> unsafeHeaders; mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders); nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo(); if (loadInfo) { loadInfo->SetCorsPreflightInfo(unsafeHeaders, false); } } if (mIsTrackingFetch && nsContentUtils::IsTailingEnabled()) { cos->AddClassFlags(nsIClassOfService::Throttleable | nsIClassOfService::Tail); } if (mIsTrackingFetch && nsContentUtils::IsLowerNetworkPriority()) { nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(chan); if (p) { p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST); } } // if the preferred alternative data type in InternalRequest is not empty, set // the data type on the created channel and also create a AlternativeDataStreamListener // to be the stream listener of the channel. if (!aPreferredAlternativeDataType.IsEmpty()) { nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(chan); if (cic) { cic->PreferAlternativeDataType(aPreferredAlternativeDataType); MOZ_ASSERT(!mAltDataListener); mAltDataListener = new AlternativeDataStreamListener(this, chan, aPreferredAlternativeDataType); rv = chan->AsyncOpen2(mAltDataListener); } else { rv = chan->AsyncOpen2(this); } } else { rv = chan->AsyncOpen2(this); } if (NS_FAILED(rv)) { return rv; } // Step 4 onwards of "HTTP Fetch" is handled internally by Necko. mChannel = chan; return NS_OK; }