StartupCacheUtils.cpp (7062B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "nsCOMPtr.h" 6 #include "nsIInputStream.h" 7 #include "nsNetUtil.h" 8 #include "nsIFileURL.h" 9 #include "nsIJARURI.h" 10 #include "nsISubstitutingProtocolHandler.h" 11 #include "nsIChromeRegistry.h" 12 #include "nsStringStream.h" 13 #include "StartupCacheUtils.h" 14 #include "mozilla/scache/StartupCache.h" 15 #include "mozilla/Omnijar.h" 16 17 namespace mozilla { 18 namespace scache { 19 20 nsresult NewObjectInputStreamFromBuffer(const char* buffer, uint32_t len, 21 nsIObjectInputStream** stream) { 22 nsCOMPtr<nsIInputStream> stringStream; 23 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), 24 Span(buffer, len), NS_ASSIGNMENT_DEPEND); 25 MOZ_ALWAYS_SUCCEEDS(rv); 26 27 nsCOMPtr<nsIObjectInputStream> objectInput = 28 NS_NewObjectInputStream(stringStream); 29 30 objectInput.forget(stream); 31 return NS_OK; 32 } 33 34 nsresult NewObjectOutputWrappedStorageStream( 35 nsIObjectOutputStream** wrapperStream, nsIStorageStream** stream, 36 bool wantDebugStream) { 37 nsCOMPtr<nsIStorageStream> storageStream; 38 39 nsresult rv = 40 NS_NewStorageStream(256, UINT32_MAX, getter_AddRefs(storageStream)); 41 NS_ENSURE_SUCCESS(rv, rv); 42 43 nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(storageStream); 44 45 nsCOMPtr<nsIObjectOutputStream> objectOutput = 46 NS_NewObjectOutputStream(outputStream); 47 48 #ifdef DEBUG 49 if (wantDebugStream) { 50 // Wrap in debug stream to detect unsupported writes of 51 // multiply-referenced non-singleton objects 52 StartupCache* sc = StartupCache::GetSingleton(); 53 NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED); 54 nsCOMPtr<nsIObjectOutputStream> debugStream; 55 sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream)); 56 debugStream.forget(wrapperStream); 57 } else { 58 objectOutput.forget(wrapperStream); 59 } 60 #else 61 objectOutput.forget(wrapperStream); 62 #endif 63 64 storageStream.forget(stream); 65 return NS_OK; 66 } 67 68 nsresult NewBufferFromStorageStream(nsIStorageStream* storageStream, 69 UniqueFreePtr<char[]>* buffer, 70 uint32_t* len) { 71 nsresult rv; 72 nsCOMPtr<nsIInputStream> inputStream; 73 rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream)); 74 NS_ENSURE_SUCCESS(rv, rv); 75 76 uint64_t avail64; 77 rv = inputStream->Available(&avail64); 78 NS_ENSURE_SUCCESS(rv, rv); 79 NS_ENSURE_TRUE(avail64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG); 80 81 uint32_t avail = (uint32_t)avail64; 82 auto temp = UniqueFreePtr<char[]>( 83 reinterpret_cast<char*>(malloc(sizeof(char) * avail))); 84 uint32_t read; 85 rv = inputStream->Read(temp.get(), avail, &read); 86 if (NS_SUCCEEDED(rv) && avail != read) rv = NS_ERROR_UNEXPECTED; 87 88 if (NS_FAILED(rv)) { 89 return rv; 90 } 91 92 *len = avail; 93 *buffer = std::move(temp); 94 return NS_OK; 95 } 96 97 static const char baseName[2][5] = {"gre/", "app/"}; 98 99 static inline bool canonicalizeBase(nsAutoCString& spec, nsACString& out) { 100 nsAutoCString greBase, appBase; 101 nsresult rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greBase); 102 if (NS_FAILED(rv) || !greBase.Length()) return false; 103 104 rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appBase); 105 if (NS_FAILED(rv)) return false; 106 107 bool underGre = StringBeginsWith(spec, greBase); 108 bool underApp = appBase.Length() && StringBeginsWith(spec, appBase); 109 110 if (!underGre && !underApp) return false; 111 112 /** 113 * At this point, if both underGre and underApp are true, it can be one 114 * of the two following cases: 115 * - the GRE directory points to a subdirectory of the APP directory, 116 * meaning spec points under GRE. 117 * - the APP directory points to a subdirectory of the GRE directory, 118 * meaning spec points under APP. 119 * Checking the GRE and APP path length is enough to know in which case 120 * we are. 121 */ 122 if (underGre && underApp && greBase.Length() < appBase.Length()) 123 underGre = false; 124 125 out.AppendLiteral("/resource/"); 126 out.Append( 127 baseName[underGre ? mozilla::Omnijar::GRE : mozilla::Omnijar::APP]); 128 out.Append(Substring(spec, underGre ? greBase.Length() : appBase.Length())); 129 return true; 130 } 131 132 /** 133 * ResolveURI transforms a chrome: or resource: URI into the URI for its 134 * underlying resource, or returns any other URI unchanged. 135 */ 136 nsresult ResolveURI(nsIURI* in, nsIURI** out) { 137 nsresult rv; 138 139 nsAutoCString scheme; 140 in->GetScheme(scheme); 141 142 // Resolve resource:// URIs. At the end of this if/else block, we 143 // have both spec and uri variables identifying the same URI. 144 if (scheme.EqualsLiteral("resource") || scheme.EqualsLiteral("moz-src")) { 145 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); 146 NS_ENSURE_SUCCESS(rv, rv); 147 148 nsCOMPtr<nsIProtocolHandler> ph; 149 rv = ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ph)); 150 NS_ENSURE_SUCCESS(rv, rv); 151 152 nsCOMPtr<nsISubstitutingProtocolHandler> irph(do_QueryInterface(ph, &rv)); 153 NS_ENSURE_SUCCESS(rv, rv); 154 155 nsAutoCString spec; 156 rv = irph->ResolveURI(in, spec); 157 NS_ENSURE_SUCCESS(rv, rv); 158 159 return ioService->NewURI(spec, nullptr, nullptr, out); 160 } 161 if (scheme.EqualsLiteral("chrome")) { 162 nsCOMPtr<nsIChromeRegistry> chromeReg = 163 mozilla::services::GetChromeRegistry(); 164 if (!chromeReg) return NS_ERROR_UNEXPECTED; 165 166 return chromeReg->ConvertChromeURL(in, out); 167 } 168 169 *out = do_AddRef(in).take(); 170 return NS_OK; 171 } 172 173 static nsresult PathifyURIImpl(nsIURI* in, nsACString& out) { 174 nsCOMPtr<nsIURI> uri; 175 nsresult rv = ResolveURI(in, getter_AddRefs(uri)); 176 NS_ENSURE_SUCCESS(rv, rv); 177 178 nsAutoCString spec; 179 rv = uri->GetSpec(spec); 180 NS_ENSURE_SUCCESS(rv, rv); 181 182 if (!canonicalizeBase(spec, out)) { 183 if (uri->SchemeIs("file")) { 184 nsCOMPtr<nsIFileURL> baseFileURL; 185 baseFileURL = do_QueryInterface(uri, &rv); 186 NS_ENSURE_SUCCESS(rv, rv); 187 188 nsAutoCString path; 189 rv = baseFileURL->GetPathQueryRef(path); 190 NS_ENSURE_SUCCESS(rv, rv); 191 192 out.Append(path); 193 } else if (uri->SchemeIs("jar")) { 194 nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv); 195 NS_ENSURE_SUCCESS(rv, rv); 196 197 nsCOMPtr<nsIURI> jarFileURI; 198 rv = jarURI->GetJARFile(getter_AddRefs(jarFileURI)); 199 NS_ENSURE_SUCCESS(rv, rv); 200 201 rv = PathifyURIImpl(jarFileURI, out); 202 NS_ENSURE_SUCCESS(rv, rv); 203 204 nsAutoCString path; 205 rv = jarURI->GetJAREntry(path); 206 NS_ENSURE_SUCCESS(rv, rv); 207 out.Append('/'); 208 out.Append(path); 209 } else { // Very unlikely 210 rv = uri->GetSpec(spec); 211 NS_ENSURE_SUCCESS(rv, rv); 212 213 out.Append('/'); 214 out.Append(spec); 215 } 216 } 217 return NS_OK; 218 } 219 220 nsresult PathifyURI(const char* loaderType, size_t loaderTypeLength, nsIURI* in, 221 nsACString& out) { 222 out.AssignASCII(loaderType, loaderTypeLength); 223 224 return PathifyURIImpl(in, out); 225 } 226 227 } // namespace scache 228 } // namespace mozilla