1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/startupcache/StartupCacheUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,250 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "nsCOMPtr.h" 1.9 +#include "nsIInputStream.h" 1.10 +#include "nsIStringStream.h" 1.11 +#include "nsNetUtil.h" 1.12 +#include "nsIJARURI.h" 1.13 +#include "nsIResProtocolHandler.h" 1.14 +#include "nsIChromeRegistry.h" 1.15 +#include "nsAutoPtr.h" 1.16 +#include "StartupCacheUtils.h" 1.17 +#include "mozilla/scache/StartupCache.h" 1.18 +#include "mozilla/Omnijar.h" 1.19 + 1.20 +namespace mozilla { 1.21 +namespace scache { 1.22 + 1.23 +NS_EXPORT nsresult 1.24 +NewObjectInputStreamFromBuffer(char* buffer, uint32_t len, 1.25 + nsIObjectInputStream** stream) 1.26 +{ 1.27 + nsCOMPtr<nsIStringInputStream> stringStream 1.28 + = do_CreateInstance("@mozilla.org/io/string-input-stream;1"); 1.29 + nsCOMPtr<nsIObjectInputStream> objectInput 1.30 + = do_CreateInstance("@mozilla.org/binaryinputstream;1"); 1.31 + 1.32 + stringStream->AdoptData(buffer, len); 1.33 + objectInput->SetInputStream(stringStream); 1.34 + 1.35 + objectInput.forget(stream); 1.36 + return NS_OK; 1.37 +} 1.38 + 1.39 +NS_EXPORT nsresult 1.40 +NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream, 1.41 + nsIStorageStream** stream, 1.42 + bool wantDebugStream) 1.43 +{ 1.44 + nsCOMPtr<nsIStorageStream> storageStream; 1.45 + 1.46 + nsresult rv = NS_NewStorageStream(256, UINT32_MAX, getter_AddRefs(storageStream)); 1.47 + NS_ENSURE_SUCCESS(rv, rv); 1.48 + 1.49 + nsCOMPtr<nsIObjectOutputStream> objectOutput 1.50 + = do_CreateInstance("@mozilla.org/binaryoutputstream;1"); 1.51 + nsCOMPtr<nsIOutputStream> outputStream 1.52 + = do_QueryInterface(storageStream); 1.53 + 1.54 + objectOutput->SetOutputStream(outputStream); 1.55 + 1.56 +#ifdef DEBUG 1.57 + if (wantDebugStream) { 1.58 + // Wrap in debug stream to detect unsupported writes of 1.59 + // multiply-referenced non-singleton objects 1.60 + StartupCache* sc = StartupCache::GetSingleton(); 1.61 + NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED); 1.62 + nsCOMPtr<nsIObjectOutputStream> debugStream; 1.63 + sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream)); 1.64 + debugStream.forget(wrapperStream); 1.65 + } else { 1.66 + objectOutput.forget(wrapperStream); 1.67 + } 1.68 +#else 1.69 + objectOutput.forget(wrapperStream); 1.70 +#endif 1.71 + 1.72 + storageStream.forget(stream); 1.73 + return NS_OK; 1.74 +} 1.75 + 1.76 +NS_EXPORT nsresult 1.77 +NewBufferFromStorageStream(nsIStorageStream *storageStream, 1.78 + char** buffer, uint32_t* len) 1.79 +{ 1.80 + nsresult rv; 1.81 + nsCOMPtr<nsIInputStream> inputStream; 1.82 + rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream)); 1.83 + NS_ENSURE_SUCCESS(rv, rv); 1.84 + 1.85 + uint64_t avail64; 1.86 + rv = inputStream->Available(&avail64); 1.87 + NS_ENSURE_SUCCESS(rv, rv); 1.88 + NS_ENSURE_TRUE(avail64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG); 1.89 + 1.90 + uint32_t avail = (uint32_t)avail64; 1.91 + nsAutoArrayPtr<char> temp (new char[avail]); 1.92 + uint32_t read; 1.93 + rv = inputStream->Read(temp, avail, &read); 1.94 + if (NS_SUCCEEDED(rv) && avail != read) 1.95 + rv = NS_ERROR_UNEXPECTED; 1.96 + 1.97 + if (NS_FAILED(rv)) { 1.98 + return rv; 1.99 + } 1.100 + 1.101 + *len = avail; 1.102 + *buffer = temp.forget(); 1.103 + return NS_OK; 1.104 +} 1.105 + 1.106 +static const char baseName[2][5] = { "gre/", "app/" }; 1.107 + 1.108 +static inline bool 1.109 +canonicalizeBase(nsAutoCString &spec, 1.110 + nsACString &out) 1.111 +{ 1.112 + nsAutoCString greBase, appBase; 1.113 + nsresult rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greBase); 1.114 + if (NS_FAILED(rv) || !greBase.Length()) 1.115 + return false; 1.116 + 1.117 + rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appBase); 1.118 + if (NS_FAILED(rv)) 1.119 + return false; 1.120 + 1.121 + bool underGre = !greBase.Compare(spec.get(), false, greBase.Length()); 1.122 + bool underApp = appBase.Length() && 1.123 + !appBase.Compare(spec.get(), false, appBase.Length()); 1.124 + 1.125 + if (!underGre && !underApp) 1.126 + return false; 1.127 + 1.128 + /** 1.129 + * At this point, if both underGre and underApp are true, it can be one 1.130 + * of the two following cases: 1.131 + * - the GRE directory points to a subdirectory of the APP directory, 1.132 + * meaning spec points under GRE. 1.133 + * - the APP directory points to a subdirectory of the GRE directory, 1.134 + * meaning spec points under APP. 1.135 + * Checking the GRE and APP path length is enough to know in which case 1.136 + * we are. 1.137 + */ 1.138 + if (underGre && underApp && greBase.Length() < appBase.Length()) 1.139 + underGre = false; 1.140 + 1.141 + out.Append("/resource/"); 1.142 + out.Append(baseName[underGre ? mozilla::Omnijar::GRE : mozilla::Omnijar::APP]); 1.143 + out.Append(Substring(spec, underGre ? greBase.Length() : appBase.Length())); 1.144 + return true; 1.145 +} 1.146 + 1.147 +/** 1.148 + * PathifyURI transforms uris into useful zip paths 1.149 + * to make it easier to manipulate startup cache entries 1.150 + * using standard zip tools. 1.151 + * Transformations applied: 1.152 + * * resource:// URIs are resolved to their corresponding file/jar URI to 1.153 + * canonicalize resources URIs other than gre and app. 1.154 + * * Paths under GRE or APP directory have their base path replaced with 1.155 + * resource/gre or resource/app to avoid depending on install location. 1.156 + * * jar:file:///path/to/file.jar!/sub/path urls are replaced with 1.157 + * /path/to/file.jar/sub/path 1.158 + * 1.159 + * The result is appended to the string passed in. Adding a prefix before 1.160 + * calling is recommended to avoid colliding with other cache users. 1.161 + * 1.162 + * For example, in the js loader (string is prefixed with jsloader by caller): 1.163 + * resource://gre/modules/XPCOMUtils.jsm or 1.164 + * file://$GRE_DIR/modules/XPCOMUtils.jsm or 1.165 + * jar:file://$GRE_DIR/omni.jar!/modules/XPCOMUtils.jsm becomes 1.166 + * jsloader/resource/gre/modules/XPCOMUtils.jsm 1.167 + * file://$PROFILE_DIR/extensions/{uuid}/components/component.js becomes 1.168 + * jsloader/$PROFILE_DIR/extensions/%7Buuid%7D/components/component.js 1.169 + * jar:file://$PROFILE_DIR/extensions/some.xpi!/components/component.js becomes 1.170 + * jsloader/$PROFILE_DIR/extensions/some.xpi/components/component.js 1.171 + */ 1.172 +NS_EXPORT nsresult 1.173 +PathifyURI(nsIURI *in, nsACString &out) 1.174 +{ 1.175 + bool equals; 1.176 + nsresult rv; 1.177 + nsCOMPtr<nsIURI> uri = in; 1.178 + nsAutoCString spec; 1.179 + 1.180 + // Resolve resource:// URIs. At the end of this if/else block, we 1.181 + // have both spec and uri variables identifying the same URI. 1.182 + if (NS_SUCCEEDED(in->SchemeIs("resource", &equals)) && equals) { 1.183 + nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); 1.184 + NS_ENSURE_SUCCESS(rv, rv); 1.185 + 1.186 + nsCOMPtr<nsIProtocolHandler> ph; 1.187 + rv = ioService->GetProtocolHandler("resource", getter_AddRefs(ph)); 1.188 + NS_ENSURE_SUCCESS(rv, rv); 1.189 + 1.190 + nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph, &rv)); 1.191 + NS_ENSURE_SUCCESS(rv, rv); 1.192 + 1.193 + rv = irph->ResolveURI(in, spec); 1.194 + NS_ENSURE_SUCCESS(rv, rv); 1.195 + 1.196 + rv = ioService->NewURI(spec, nullptr, nullptr, getter_AddRefs(uri)); 1.197 + NS_ENSURE_SUCCESS(rv, rv); 1.198 + } else { 1.199 + if (NS_SUCCEEDED(in->SchemeIs("chrome", &equals)) && equals) { 1.200 + nsCOMPtr<nsIChromeRegistry> chromeReg = 1.201 + mozilla::services::GetChromeRegistryService(); 1.202 + if (!chromeReg) 1.203 + return NS_ERROR_UNEXPECTED; 1.204 + 1.205 + rv = chromeReg->ConvertChromeURL(in, getter_AddRefs(uri)); 1.206 + NS_ENSURE_SUCCESS(rv, rv); 1.207 + } 1.208 + 1.209 + rv = uri->GetSpec(spec); 1.210 + NS_ENSURE_SUCCESS(rv, rv); 1.211 + } 1.212 + 1.213 + if (!canonicalizeBase(spec, out)) { 1.214 + if (NS_SUCCEEDED(uri->SchemeIs("file", &equals)) && equals) { 1.215 + nsCOMPtr<nsIFileURL> baseFileURL; 1.216 + baseFileURL = do_QueryInterface(uri, &rv); 1.217 + NS_ENSURE_SUCCESS(rv, rv); 1.218 + 1.219 + nsAutoCString path; 1.220 + rv = baseFileURL->GetPath(path); 1.221 + NS_ENSURE_SUCCESS(rv, rv); 1.222 + 1.223 + out.Append(path); 1.224 + } else if (NS_SUCCEEDED(uri->SchemeIs("jar", &equals)) && equals) { 1.225 + nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv); 1.226 + NS_ENSURE_SUCCESS(rv, rv); 1.227 + 1.228 + nsCOMPtr<nsIURI> jarFileURI; 1.229 + rv = jarURI->GetJARFile(getter_AddRefs(jarFileURI)); 1.230 + NS_ENSURE_SUCCESS(rv, rv); 1.231 + 1.232 + rv = PathifyURI(jarFileURI, out); 1.233 + NS_ENSURE_SUCCESS(rv, rv); 1.234 + 1.235 + nsAutoCString path; 1.236 + rv = jarURI->GetJAREntry(path); 1.237 + NS_ENSURE_SUCCESS(rv, rv); 1.238 + out.Append("/"); 1.239 + out.Append(path); 1.240 + } else { // Very unlikely 1.241 + nsAutoCString spec; 1.242 + rv = uri->GetSpec(spec); 1.243 + NS_ENSURE_SUCCESS(rv, rv); 1.244 + 1.245 + out.Append("/"); 1.246 + out.Append(spec); 1.247 + } 1.248 + } 1.249 + return NS_OK; 1.250 +} 1.251 + 1.252 +} 1.253 +}