Bug 1378727: Part 1 - Add helper to enumerate cached ZipReader without locking issues. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Thu, 13 Jul 2017 12:30:15 -0700
changeset 608503 75f767caea3104b2c86982725ff03245d2ba0b0e
parent 608124 d259c59353d307f80f0d4c0ee37e73cab13f7a03
child 608504 23b1f4b3ae59f115f8400811e08fb0f5cbcd6ded
push id68302
push usermaglione.k@gmail.com
push dateThu, 13 Jul 2017 19:33:06 +0000
reviewersaswan
bugs1378727
milestone56.0a1
Bug 1378727: Part 1 - Add helper to enumerate cached ZipReader without locking issues. r?aswan MozReview-Commit-ID: Kuw58LE1nYg
toolkit/mozapps/extensions/AddonManagerStartup.cpp
toolkit/mozapps/extensions/amIAddonManagerStartup.idl
--- a/toolkit/mozapps/extensions/AddonManagerStartup.cpp
+++ b/toolkit/mozapps/extensions/AddonManagerStartup.cpp
@@ -20,16 +20,21 @@
 #include "mozilla/Unused.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsAppRunner.h"
 #include "nsContentUtils.h"
 #include "nsIAddonInterposition.h"
+#include "nsIIOService.h"
+#include "nsIJARProtocolHandler.h"
+#include "nsIStringEnumerator.h"
+#include "nsIZipReader.h"
+#include "nsReadableUtils.h"
 #include "nsXULAppAPI.h"
 
 #include <stdlib.h>
 
 namespace mozilla {
 
 template <>
 class MOZ_MUST_USE_TYPE GenericErrorResult<nsresult>
@@ -242,16 +247,34 @@ static bool
 ParseJSON(JSContext* cx, nsACString& jsonData, JS::MutableHandleValue result)
 {
   NS_ConvertUTF8toUTF16 str(jsonData);
   jsonData.Truncate();
 
   return JS_ParseJSON(cx, str.Data(), str.Length(), result);
 }
 
+static Result<nsCOMPtr<nsIZipReaderCache>, nsresult>
+GetJarCache()
+{
+  RefPtr<nsIIOService> ios = services::GetIOService();
+  NS_ENSURE_TRUE(ios, Err(NS_ERROR_FAILURE));
+
+  nsCOMPtr<nsIProtocolHandler> jarProto;
+  NS_TRY(ios->GetProtocolHandler("jar", getter_AddRefs(jarProto)));
+
+  nsCOMPtr<nsIJARProtocolHandler> jar = do_QueryInterface(jarProto);
+  MOZ_ASSERT(jar);
+
+  nsCOMPtr<nsIZipReaderCache> zipCache;
+  NS_TRY(jar->GetJARCache(getter_AddRefs(zipCache)));
+
+  return Move(zipCache);
+}
+
 
 /*****************************************************************************
  * JSON data handling
  *****************************************************************************/
 
 class MOZ_STACK_CLASS WrapperBase {
 protected:
   WrapperBase(JSContext* cx, JSObject* object)
@@ -694,16 +717,53 @@ AddonManagerStartup::DecodeBlob(JS::Hand
   NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
 
   ErrorResult rv;
   holder.Read(cx, result, rv);
   return rv.StealNSResult();;
 }
 
 nsresult
+AddonManagerStartup::EnumerateZipFile(nsIFile* file, const nsACString& pattern,
+                                      uint32_t* countOut, char16_t*** entriesOut)
+{
+  NS_ENSURE_ARG_POINTER(file);
+  NS_ENSURE_ARG_POINTER(countOut);
+  NS_ENSURE_ARG_POINTER(entriesOut);
+
+  nsCOMPtr<nsIZipReaderCache> zipCache;
+  MOZ_TRY_VAR(zipCache, GetJarCache());
+
+  nsCOMPtr<nsIZipReader> zip;
+  NS_TRY(zipCache->GetZip(file, getter_AddRefs(zip)));
+
+  nsCOMPtr<nsIUTF8StringEnumerator> entries;
+  NS_TRY(zip->FindEntries(pattern, getter_AddRefs(entries)));
+
+  nsTArray<nsString> results;
+  bool hasMore;
+  while (NS_SUCCEEDED(entries->HasMore(&hasMore)) && hasMore) {
+    nsCString name;
+    NS_TRY(entries->GetNext(name));
+
+    results.AppendElement(NS_ConvertUTF8toUTF16(name));
+  }
+
+  auto strResults = MakeUnique<char16_t*[]>(results.Length());
+  for (uint32_t i = 0; i < results.Length(); i++) {
+    strResults[i] = ToNewUnicode(results[i]);
+  }
+
+  *countOut = results.Length();
+  *entriesOut = strResults.release();
+
+  return NS_OK;
+}
+
+nsresult
 AddonManagerStartup::Reset()
 {
   MOZ_RELEASE_ASSERT(xpc::IsInAutomation());
 
   mInitialized = false;
 
   mExtensionPaths.Clear();
   mThemePaths.Clear();
--- a/toolkit/mozapps/extensions/amIAddonManagerStartup.idl
+++ b/toolkit/mozapps/extensions/amIAddonManagerStartup.idl
@@ -1,14 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
+interface nsIFile;
+
 [scriptable, builtinclass, uuid(01dfa47b-87e4-4135-877b-586d033e1b5d)]
 interface amIAddonManagerStartup : nsISupports
 {
   /**
    * Reads and parses startup data from the addonState.json.lz4 file, checks
    * for modifications, and returns the result.
    *
    * Returns null for an empty or nonexistent state file, but throws for an
@@ -26,16 +28,32 @@ interface amIAddonManagerStartup : nsISu
 
   [implicit_jscontext]
   jsval encodeBlob(in jsval value);
 
   [implicit_jscontext]
   jsval decodeBlob(in jsval value);
 
   /**
+   * Enumerates over all entries in the given zip file matching the given
+   * pattern, and returns an array of their paths.
+   *
+   * This should be used in preference to manually opening or retrieving a
+   * ZipReader from the zip cache, since the former causes main thread IO and
+   * the latter can lead to file locking issues due to unpredictable GC behavior
+   * keeping the cached ZipReader alive after the cache is flushed.
+   *
+   * @param file The zip file to enumerate.
+   * @param pattern The pattern to match, as passed to nsIZipReader.findEntries.
+   */
+  void enumerateZipFile(in nsIFile file, in AUTF8String pattern,
+                        [optional] out unsigned long count,
+                        [retval, array, size_is(count)] out wstring entries);
+
+  /**
    * Resets the internal state of the startup service, and allows
    * initializeExtensions() to be called again. Does *not* fully unregister
    * chrome registry locations for previously registered add-ons.
    *
    * NOT FOR USE OUTSIDE OF UNIT TESTS.
    */
   void reset();
 };