nsDataChannel.cpp (6069B)
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 // data implementation 7 8 #include "nsDataChannel.h" 9 10 #include "mozilla/Base64.h" 11 #include "mozilla/dom/MimeType.h" 12 #include "mozilla/net/NeckoChild.h" 13 #include "mozilla/net/NeckoCommon.h" 14 #include "nsDataHandler.h" 15 #include "nsIInputStream.h" 16 #include "nsEscape.h" 17 #include "nsISupports.h" 18 #include "nsStringStream.h" 19 #include "nsIObserverService.h" 20 #include "mozilla/dom/ContentChild.h" 21 #include "../protocol/http/nsHttpHandler.h" 22 23 using namespace mozilla; 24 using namespace mozilla::net; 25 26 NS_IMPL_ISUPPORTS_INHERITED(nsDataChannel, nsBaseChannel, nsIDataChannel, 27 nsIIdentChannel, nsIChildChannel) 28 29 /** 30 * Helper for performing a fallible unescape. 31 * 32 * @param aStr The string to unescape. 33 * @param aBuffer Buffer to unescape into if necessary. 34 * @param rv Out: nsresult indicating success or failure of unescaping. 35 * @return Reference to the string containing the unescaped data. 36 */ 37 const nsACString& Unescape(const nsACString& aStr, nsACString& aBuffer, 38 nsresult* rv) { 39 MOZ_ASSERT(rv); 40 41 bool appended = false; 42 *rv = NS_UnescapeURL(aStr.Data(), aStr.Length(), /* aFlags = */ 0, aBuffer, 43 appended, mozilla::fallible); 44 if (NS_FAILED(*rv) || !appended) { 45 return aStr; 46 } 47 48 return aBuffer; 49 } 50 51 nsresult nsDataChannel::OpenContentStream(bool async, nsIInputStream** result, 52 nsIChannel** channel) { 53 NS_ENSURE_TRUE(URI(), NS_ERROR_NOT_INITIALIZED); 54 55 nsresult rv; 56 57 // In general, a `data:` URI is stored as a `nsSimpleURI`, which holds a 58 // single mSpec string. This can be read most efficiently with `GetSpec`, as 59 // the underlying string buffer will be shared. 60 // 61 // NOTE: In the case where the `data:` URI is parsed with `DefaultURI`, this 62 // will be inefficient, as there is no way to share the string buffer with 63 // MozURL. 64 65 nsAutoCString spec; 66 rv = URI()->GetSpec(spec); 67 if (NS_FAILED(rv)) return rv; 68 69 nsCString contentType, contentCharset; 70 nsDependentCSubstring dataRange; 71 RefPtr<CMimeType> fullMimeType; 72 bool lBase64; 73 rv = nsDataHandler::ParseURI(spec, contentType, &contentCharset, lBase64, 74 &dataRange, &fullMimeType); 75 if (NS_FAILED(rv)) return rv; 76 77 // This will avoid a copy if nothing needs to be unescaped. 78 nsAutoCString unescapedBuffer; 79 const nsACString& data = Unescape(dataRange, unescapedBuffer, &rv); 80 if (NS_FAILED(rv)) { 81 return rv; 82 } 83 84 if (lBase64 && &data == &unescapedBuffer) { 85 // Don't allow spaces in base64-encoded content. This is only 86 // relevant for escaped spaces; other spaces are stripped in 87 // NewURI. We know there were no escaped spaces if the data buffer 88 // wasn't used in |Unescape|. 89 unescapedBuffer.StripWhitespace(); 90 } 91 92 nsCOMPtr<nsIInputStream> bufInStream; 93 uint32_t contentLen; 94 if (lBase64) { 95 nsAutoCString decodedData; 96 rv = Base64Decode(data, decodedData); 97 if (NS_FAILED(rv)) { 98 // Returning this error code instead of what Base64Decode returns 99 // (NS_ERROR_ILLEGAL_VALUE) will prevent rendering of redirect response 100 // content by HTTP channels. It's also more logical error to return. 101 // Here we know the URL is actually corrupted. 102 return NS_ERROR_MALFORMED_URI; 103 } 104 105 contentLen = decodedData.Length(); 106 rv = NS_NewCStringInputStream(getter_AddRefs(bufInStream), decodedData); 107 } else { 108 contentLen = data.Length(); 109 rv = NS_NewCStringInputStream(getter_AddRefs(bufInStream), data); 110 } 111 112 if (NS_FAILED(rv)) return rv; 113 114 SetContentType(contentType); 115 SetContentCharset(contentCharset); 116 SetFullMimeType(std::move(fullMimeType)); 117 mContentLength = contentLen; 118 119 // notify "data-channel-opened" observers 120 MaybeSendDataChannelOpenNotification(); 121 122 bufInStream.forget(result); 123 124 return NS_OK; 125 } 126 127 nsresult nsDataChannel::Init() { 128 NS_ENSURE_STATE(mLoadInfo); 129 130 RefPtr<nsHttpHandler> handler = nsHttpHandler::GetInstance(); 131 mChannelId = handler->NewChannelId(); 132 return NS_OK; 133 } 134 135 nsresult nsDataChannel::MaybeSendDataChannelOpenNotification() { 136 nsCOMPtr<nsIObserverService> obsService = services::GetObserverService(); 137 if (!obsService) { 138 return NS_OK; 139 } 140 141 nsCOMPtr<nsILoadInfo> loadInfo; 142 nsresult rv = GetLoadInfo(getter_AddRefs(loadInfo)); 143 if (NS_FAILED(rv)) { 144 return rv; 145 } 146 147 bool isTopLevel; 148 rv = loadInfo->GetIsTopLevelLoad(&isTopLevel); 149 if (NS_FAILED(rv)) { 150 return rv; 151 } 152 153 uint64_t browsingContextID; 154 rv = loadInfo->GetBrowsingContextID(&browsingContextID); 155 if (NS_FAILED(rv)) { 156 return rv; 157 } 158 159 if ((browsingContextID != 0 && isTopLevel) || 160 !loadInfo->TriggeringPrincipal()->IsSystemPrincipal()) { 161 obsService->NotifyObservers(static_cast<nsIIdentChannel*>(this), 162 "data-channel-opened", nullptr); 163 } 164 return NS_OK; 165 } 166 167 //----------------------------------------------------------------------------- 168 // nsDataChannel::nsIIdentChannel 169 170 NS_IMETHODIMP 171 nsDataChannel::GetChannelId(uint64_t* aChannelId) { 172 *aChannelId = mChannelId; 173 return NS_OK; 174 } 175 176 NS_IMETHODIMP 177 nsDataChannel::SetChannelId(uint64_t aChannelId) { 178 mChannelId = aChannelId; 179 return NS_OK; 180 } 181 182 //----------------------------------------------------------------------------- 183 // nsDataChannel::nsIChildChannel 184 185 NS_IMETHODIMP 186 nsDataChannel::ConnectParent(uint32_t aId) { 187 if (!IsNeckoChild()) { 188 return NS_ERROR_NOT_IMPLEMENTED; 189 } 190 191 mozilla::dom::ContentChild* cc = 192 static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager()); 193 if (cc->IsShuttingDown()) { 194 return NS_ERROR_FAILURE; 195 } 196 197 gNeckoChild->SendConnectBaseChannel(aId); 198 return NS_OK; 199 } 200 201 NS_IMETHODIMP 202 nsDataChannel::CompleteRedirectSetup(nsIStreamListener* aListener) { 203 return AsyncOpen(aListener); 204 }