StartupCacheUtils.cpp 8.4 KB

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