Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | #include "nsCOMPtr.h" |
michael@0 | 6 | #include "nsIInputStream.h" |
michael@0 | 7 | #include "nsIStringStream.h" |
michael@0 | 8 | #include "nsNetUtil.h" |
michael@0 | 9 | #include "nsIJARURI.h" |
michael@0 | 10 | #include "nsIResProtocolHandler.h" |
michael@0 | 11 | #include "nsIChromeRegistry.h" |
michael@0 | 12 | #include "nsAutoPtr.h" |
michael@0 | 13 | #include "StartupCacheUtils.h" |
michael@0 | 14 | #include "mozilla/scache/StartupCache.h" |
michael@0 | 15 | #include "mozilla/Omnijar.h" |
michael@0 | 16 | |
michael@0 | 17 | namespace mozilla { |
michael@0 | 18 | namespace scache { |
michael@0 | 19 | |
michael@0 | 20 | NS_EXPORT nsresult |
michael@0 | 21 | NewObjectInputStreamFromBuffer(char* buffer, uint32_t len, |
michael@0 | 22 | nsIObjectInputStream** stream) |
michael@0 | 23 | { |
michael@0 | 24 | nsCOMPtr<nsIStringInputStream> stringStream |
michael@0 | 25 | = do_CreateInstance("@mozilla.org/io/string-input-stream;1"); |
michael@0 | 26 | nsCOMPtr<nsIObjectInputStream> objectInput |
michael@0 | 27 | = do_CreateInstance("@mozilla.org/binaryinputstream;1"); |
michael@0 | 28 | |
michael@0 | 29 | stringStream->AdoptData(buffer, len); |
michael@0 | 30 | objectInput->SetInputStream(stringStream); |
michael@0 | 31 | |
michael@0 | 32 | objectInput.forget(stream); |
michael@0 | 33 | return NS_OK; |
michael@0 | 34 | } |
michael@0 | 35 | |
michael@0 | 36 | NS_EXPORT nsresult |
michael@0 | 37 | NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream, |
michael@0 | 38 | nsIStorageStream** stream, |
michael@0 | 39 | bool wantDebugStream) |
michael@0 | 40 | { |
michael@0 | 41 | nsCOMPtr<nsIStorageStream> storageStream; |
michael@0 | 42 | |
michael@0 | 43 | nsresult rv = NS_NewStorageStream(256, UINT32_MAX, getter_AddRefs(storageStream)); |
michael@0 | 44 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 45 | |
michael@0 | 46 | nsCOMPtr<nsIObjectOutputStream> objectOutput |
michael@0 | 47 | = do_CreateInstance("@mozilla.org/binaryoutputstream;1"); |
michael@0 | 48 | nsCOMPtr<nsIOutputStream> outputStream |
michael@0 | 49 | = do_QueryInterface(storageStream); |
michael@0 | 50 | |
michael@0 | 51 | objectOutput->SetOutputStream(outputStream); |
michael@0 | 52 | |
michael@0 | 53 | #ifdef DEBUG |
michael@0 | 54 | if (wantDebugStream) { |
michael@0 | 55 | // Wrap in debug stream to detect unsupported writes of |
michael@0 | 56 | // multiply-referenced non-singleton objects |
michael@0 | 57 | StartupCache* sc = StartupCache::GetSingleton(); |
michael@0 | 58 | NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED); |
michael@0 | 59 | nsCOMPtr<nsIObjectOutputStream> debugStream; |
michael@0 | 60 | sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream)); |
michael@0 | 61 | debugStream.forget(wrapperStream); |
michael@0 | 62 | } else { |
michael@0 | 63 | objectOutput.forget(wrapperStream); |
michael@0 | 64 | } |
michael@0 | 65 | #else |
michael@0 | 66 | objectOutput.forget(wrapperStream); |
michael@0 | 67 | #endif |
michael@0 | 68 | |
michael@0 | 69 | storageStream.forget(stream); |
michael@0 | 70 | return NS_OK; |
michael@0 | 71 | } |
michael@0 | 72 | |
michael@0 | 73 | NS_EXPORT nsresult |
michael@0 | 74 | NewBufferFromStorageStream(nsIStorageStream *storageStream, |
michael@0 | 75 | char** buffer, uint32_t* len) |
michael@0 | 76 | { |
michael@0 | 77 | nsresult rv; |
michael@0 | 78 | nsCOMPtr<nsIInputStream> inputStream; |
michael@0 | 79 | rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream)); |
michael@0 | 80 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 81 | |
michael@0 | 82 | uint64_t avail64; |
michael@0 | 83 | rv = inputStream->Available(&avail64); |
michael@0 | 84 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 85 | NS_ENSURE_TRUE(avail64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG); |
michael@0 | 86 | |
michael@0 | 87 | uint32_t avail = (uint32_t)avail64; |
michael@0 | 88 | nsAutoArrayPtr<char> temp (new char[avail]); |
michael@0 | 89 | uint32_t read; |
michael@0 | 90 | rv = inputStream->Read(temp, avail, &read); |
michael@0 | 91 | if (NS_SUCCEEDED(rv) && avail != read) |
michael@0 | 92 | rv = NS_ERROR_UNEXPECTED; |
michael@0 | 93 | |
michael@0 | 94 | if (NS_FAILED(rv)) { |
michael@0 | 95 | return rv; |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | *len = avail; |
michael@0 | 99 | *buffer = temp.forget(); |
michael@0 | 100 | return NS_OK; |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | static const char baseName[2][5] = { "gre/", "app/" }; |
michael@0 | 104 | |
michael@0 | 105 | static inline bool |
michael@0 | 106 | canonicalizeBase(nsAutoCString &spec, |
michael@0 | 107 | nsACString &out) |
michael@0 | 108 | { |
michael@0 | 109 | nsAutoCString greBase, appBase; |
michael@0 | 110 | nsresult rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greBase); |
michael@0 | 111 | if (NS_FAILED(rv) || !greBase.Length()) |
michael@0 | 112 | return false; |
michael@0 | 113 | |
michael@0 | 114 | rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appBase); |
michael@0 | 115 | if (NS_FAILED(rv)) |
michael@0 | 116 | return false; |
michael@0 | 117 | |
michael@0 | 118 | bool underGre = !greBase.Compare(spec.get(), false, greBase.Length()); |
michael@0 | 119 | bool underApp = appBase.Length() && |
michael@0 | 120 | !appBase.Compare(spec.get(), false, appBase.Length()); |
michael@0 | 121 | |
michael@0 | 122 | if (!underGre && !underApp) |
michael@0 | 123 | return false; |
michael@0 | 124 | |
michael@0 | 125 | /** |
michael@0 | 126 | * At this point, if both underGre and underApp are true, it can be one |
michael@0 | 127 | * of the two following cases: |
michael@0 | 128 | * - the GRE directory points to a subdirectory of the APP directory, |
michael@0 | 129 | * meaning spec points under GRE. |
michael@0 | 130 | * - the APP directory points to a subdirectory of the GRE directory, |
michael@0 | 131 | * meaning spec points under APP. |
michael@0 | 132 | * Checking the GRE and APP path length is enough to know in which case |
michael@0 | 133 | * we are. |
michael@0 | 134 | */ |
michael@0 | 135 | if (underGre && underApp && greBase.Length() < appBase.Length()) |
michael@0 | 136 | underGre = false; |
michael@0 | 137 | |
michael@0 | 138 | out.Append("/resource/"); |
michael@0 | 139 | out.Append(baseName[underGre ? mozilla::Omnijar::GRE : mozilla::Omnijar::APP]); |
michael@0 | 140 | out.Append(Substring(spec, underGre ? greBase.Length() : appBase.Length())); |
michael@0 | 141 | return true; |
michael@0 | 142 | } |
michael@0 | 143 | |
michael@0 | 144 | /** |
michael@0 | 145 | * PathifyURI transforms uris into useful zip paths |
michael@0 | 146 | * to make it easier to manipulate startup cache entries |
michael@0 | 147 | * using standard zip tools. |
michael@0 | 148 | * Transformations applied: |
michael@0 | 149 | * * resource:// URIs are resolved to their corresponding file/jar URI to |
michael@0 | 150 | * canonicalize resources URIs other than gre and app. |
michael@0 | 151 | * * Paths under GRE or APP directory have their base path replaced with |
michael@0 | 152 | * resource/gre or resource/app to avoid depending on install location. |
michael@0 | 153 | * * jar:file:///path/to/file.jar!/sub/path urls are replaced with |
michael@0 | 154 | * /path/to/file.jar/sub/path |
michael@0 | 155 | * |
michael@0 | 156 | * The result is appended to the string passed in. Adding a prefix before |
michael@0 | 157 | * calling is recommended to avoid colliding with other cache users. |
michael@0 | 158 | * |
michael@0 | 159 | * For example, in the js loader (string is prefixed with jsloader by caller): |
michael@0 | 160 | * resource://gre/modules/XPCOMUtils.jsm or |
michael@0 | 161 | * file://$GRE_DIR/modules/XPCOMUtils.jsm or |
michael@0 | 162 | * jar:file://$GRE_DIR/omni.jar!/modules/XPCOMUtils.jsm becomes |
michael@0 | 163 | * jsloader/resource/gre/modules/XPCOMUtils.jsm |
michael@0 | 164 | * file://$PROFILE_DIR/extensions/{uuid}/components/component.js becomes |
michael@0 | 165 | * jsloader/$PROFILE_DIR/extensions/%7Buuid%7D/components/component.js |
michael@0 | 166 | * jar:file://$PROFILE_DIR/extensions/some.xpi!/components/component.js becomes |
michael@0 | 167 | * jsloader/$PROFILE_DIR/extensions/some.xpi/components/component.js |
michael@0 | 168 | */ |
michael@0 | 169 | NS_EXPORT nsresult |
michael@0 | 170 | PathifyURI(nsIURI *in, nsACString &out) |
michael@0 | 171 | { |
michael@0 | 172 | bool equals; |
michael@0 | 173 | nsresult rv; |
michael@0 | 174 | nsCOMPtr<nsIURI> uri = in; |
michael@0 | 175 | nsAutoCString spec; |
michael@0 | 176 | |
michael@0 | 177 | // Resolve resource:// URIs. At the end of this if/else block, we |
michael@0 | 178 | // have both spec and uri variables identifying the same URI. |
michael@0 | 179 | if (NS_SUCCEEDED(in->SchemeIs("resource", &equals)) && equals) { |
michael@0 | 180 | nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); |
michael@0 | 181 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 182 | |
michael@0 | 183 | nsCOMPtr<nsIProtocolHandler> ph; |
michael@0 | 184 | rv = ioService->GetProtocolHandler("resource", getter_AddRefs(ph)); |
michael@0 | 185 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 186 | |
michael@0 | 187 | nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph, &rv)); |
michael@0 | 188 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 189 | |
michael@0 | 190 | rv = irph->ResolveURI(in, spec); |
michael@0 | 191 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 192 | |
michael@0 | 193 | rv = ioService->NewURI(spec, nullptr, nullptr, getter_AddRefs(uri)); |
michael@0 | 194 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 195 | } else { |
michael@0 | 196 | if (NS_SUCCEEDED(in->SchemeIs("chrome", &equals)) && equals) { |
michael@0 | 197 | nsCOMPtr<nsIChromeRegistry> chromeReg = |
michael@0 | 198 | mozilla::services::GetChromeRegistryService(); |
michael@0 | 199 | if (!chromeReg) |
michael@0 | 200 | return NS_ERROR_UNEXPECTED; |
michael@0 | 201 | |
michael@0 | 202 | rv = chromeReg->ConvertChromeURL(in, getter_AddRefs(uri)); |
michael@0 | 203 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | rv = uri->GetSpec(spec); |
michael@0 | 207 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 208 | } |
michael@0 | 209 | |
michael@0 | 210 | if (!canonicalizeBase(spec, out)) { |
michael@0 | 211 | if (NS_SUCCEEDED(uri->SchemeIs("file", &equals)) && equals) { |
michael@0 | 212 | nsCOMPtr<nsIFileURL> baseFileURL; |
michael@0 | 213 | baseFileURL = do_QueryInterface(uri, &rv); |
michael@0 | 214 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 215 | |
michael@0 | 216 | nsAutoCString path; |
michael@0 | 217 | rv = baseFileURL->GetPath(path); |
michael@0 | 218 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 219 | |
michael@0 | 220 | out.Append(path); |
michael@0 | 221 | } else if (NS_SUCCEEDED(uri->SchemeIs("jar", &equals)) && equals) { |
michael@0 | 222 | nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv); |
michael@0 | 223 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 224 | |
michael@0 | 225 | nsCOMPtr<nsIURI> jarFileURI; |
michael@0 | 226 | rv = jarURI->GetJARFile(getter_AddRefs(jarFileURI)); |
michael@0 | 227 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 228 | |
michael@0 | 229 | rv = PathifyURI(jarFileURI, out); |
michael@0 | 230 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 231 | |
michael@0 | 232 | nsAutoCString path; |
michael@0 | 233 | rv = jarURI->GetJAREntry(path); |
michael@0 | 234 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 235 | out.Append("/"); |
michael@0 | 236 | out.Append(path); |
michael@0 | 237 | } else { // Very unlikely |
michael@0 | 238 | nsAutoCString spec; |
michael@0 | 239 | rv = uri->GetSpec(spec); |
michael@0 | 240 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 241 | |
michael@0 | 242 | out.Append("/"); |
michael@0 | 243 | out.Append(spec); |
michael@0 | 244 | } |
michael@0 | 245 | } |
michael@0 | 246 | return NS_OK; |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | } |
michael@0 | 250 | } |