Bug 1484496: Part 4a - Add JS iterator support to nsISimpleEnumerator. r=mccr8
authorKris Maglione <maglione.k@gmail.com>
Sat, 18 Aug 2018 16:02:49 -0700
changeset 488330 8ca4845423179d1f0a2e929a827014b393564b76
parent 488329 15a738855cb06dd495c14fde3c8b54dde513c098
child 488331 e8c65dc566057853a19c477e0b30cd9e81d6326b
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1484496
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1484496: Part 4a - Add JS iterator support to nsISimpleEnumerator. r=mccr8 This patch adds simple stubs to convert between the nsISimpleEnumerator iteration protocol and the JS iteration protocol. Each iterable object is required to have an @@iterator method which returns an object implementing the iterator protocol. The later objects, by convention, also have such a method which returns the object itself. This patch adds both a @@iterator() and entries() methods to nsISimpleEnumerator. The former returns an iterator which returns plain nsISupports objects. The latter accepts an IID and queries each element to that IID before returning it. If any element fails to query, the error is propagated to the caller. Differential Revision: https://phabricator.services.mozilla.com/D3727
dom/chrome-webidl/IteratorResult.webidl
dom/chrome-webidl/moz.build
netwerk/test/httpserver/httpd.js
testing/specialpowers/content/MockFilePicker.jsm
xpcom/ds/nsISimpleEnumerator.idl
xpcom/ds/nsSimpleEnumerator.cpp
xpcom/ds/nsSimpleEnumerator.h
xpcom/io/nsLocalFileUnix.cpp
xpcom/io/nsLocalFileWin.cpp
new file mode 100644
--- /dev/null
+++ b/dom/chrome-webidl/IteratorResult.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+/**
+ * A dictionary which represents the result of a call to a next() method on a
+ * JS iterator object.
+ */
+dictionary IteratorResult {
+  required boolean done;
+  any value;
+};
--- a/dom/chrome-webidl/moz.build
+++ b/dom/chrome-webidl/moz.build
@@ -29,16 +29,17 @@ PREPROCESSED_WEBIDL_FILES = [
     'ChromeUtils.webidl',
 ]
 
 WEBIDL_FILES = [
     'ChannelWrapper.webidl',
     'DominatorTree.webidl',
     'HeapSnapshot.webidl',
     'InspectorUtils.webidl',
+    'IteratorResult.webidl',
     'MatchGlob.webidl',
     'MatchPattern.webidl',
     'MessageManager.webidl',
     'MozDocumentObserver.webidl',
     'MozSharedMap.webidl',
     'MozStorageAsyncStatementParams.webidl',
     'MozStorageStatementParams.webidl',
     'MozStorageStatementRow.webidl',
--- a/netwerk/test/httpserver/httpd.js
+++ b/netwerk/test/httpserver/httpd.js
@@ -5125,16 +5125,19 @@ nsSimpleEnumerator.prototype =
   },
   getNext: function()
   {
     if (!this.hasMoreElements())
       throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
 
     return this._items[this._nextIndex++];
   },
+  [Symbol.iterator]() {
+    return this._items.values();
+  },
   QueryInterface: function(aIID)
   {
     if (Ci.nsISimpleEnumerator.equals(aIID) ||
         Ci.nsISupports.equals(aIID))
       return this;
 
     throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
   }
--- a/testing/specialpowers/content/MockFilePicker.jsm
+++ b/testing/specialpowers/content/MockFilePicker.jsm
@@ -209,22 +209,25 @@ MockFilePickerInstance.prototype = {
     }
 
     return null;
   },
   get files() {
     return {
       index: 0,
       QueryInterface: ChromeUtils.generateQI([Ci.nsISimpleEnumerator]),
+      [Symbol.iterator]() {
+        return Array.from(MockFilePicker.returnData, d => d.nsIFile).values();
+      },
       hasMoreElements() {
         return this.index < MockFilePicker.returnData.length;
       },
       getNext() {
         if (!MockFilePicker.returnData[this.index].nsIFile) {
-          return null;
+          throw Components.Exception("", Cr.NS_ERROR_FAILURE);
         }
         return MockFilePicker.returnData[this.index++].nsIFile;
       }
     };
   },
   get domFileOrDirectoryEnumerator() {
     return {
       index: 0,
--- a/xpcom/ds/nsISimpleEnumerator.idl
+++ b/xpcom/ds/nsISimpleEnumerator.idl
@@ -9,30 +9,60 @@
  * Used to enumerate over elements defined by its implementor.
  * Although hasMoreElements() can be called independently of getNext(),
  * getNext() must be pre-ceeded by a call to hasMoreElements(). There is
  * no way to "reset" an enumerator, once you obtain one.
  *
  * @version 1.0
  */
 
+/**
+ * A wrapper for an nsISimpleEnumerator instance which implements the
+ * JavaScript iteration protocol.
+ */
+[scriptable, uuid(4432e8ae-d4d3-42a6-a4d1-829f1c29512b)]
+interface nsIJSEnumerator : nsISupports {
+  [symbol]
+  nsIJSEnumerator iterator();
+
+  [implicit_jscontext]
+  jsval next();
+};
+
+[scriptable, uuid(796f340d-0a2a-490b-9c60-640765e99782)]
+interface nsISimpleEnumeratorBase : nsISupports {
+  /**
+   * Returns a JavaScript iterator for all remaining entries in the enumerator.
+   * Each entry is queried only to nsISupports.
+   */
+  [symbol]
+  nsIJSEnumerator iterator();
+
+  /**
+   * Returns JavaScript iterator for all remaining entries in the enumerator.
+   * Each entry is queried only to the supplied interface. If any element
+   * fails to query to that interface, the error is propagated to the caller.
+   */
+  nsIJSEnumerator entries(in nsIIDRef aIface);
+};
+
 [scriptable, uuid(D1899240-F9D2-11D2-BDD6-000064657374)]
-interface nsISimpleEnumerator : nsISupports {
+interface nsISimpleEnumerator : nsISimpleEnumeratorBase {
   /**
    * Called to determine whether or not the enumerator has
    * any elements that can be returned via getNext(). This method
    * is generally used to determine whether or not to initiate or
    * continue iteration over the enumerator, though it can be
    * called without subsequent getNext() calls. Does not affect
    * internal state of enumerator.
    *
    * @see getNext()
    * @return true if there are remaining elements in the enumerator.
    *         false if there are no more elements in the enumerator.
-   */                                            
+   */
   boolean hasMoreElements();
 
   /**
    * Called to retrieve the next element in the enumerator. The "next"
    * element is the first element upon the first call. Must be
    * pre-ceeded by a call to hasMoreElements() which returns PR_TRUE.
    * This method is generally called within a loop to iterate over
    * the elements in the enumerator.
--- a/xpcom/ds/nsSimpleEnumerator.cpp
+++ b/xpcom/ds/nsSimpleEnumerator.cpp
@@ -1,9 +1,87 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "nsSimpleEnumerator.h"
 
-NS_IMPL_ISUPPORTS(nsSimpleEnumerator, nsISimpleEnumerator)
+#include "mozilla/dom/IteratorResultBinding.h"
+#include "mozilla/dom/RootedDictionary.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/ResultExtensions.h"
+#include "nsContentUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+namespace {
+
+class JSEnumerator final : public nsIJSEnumerator
+{
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIJSENUMERATOR
+
+  explicit JSEnumerator(nsISimpleEnumerator* aEnumerator, const nsID& aIID)
+    : mEnumerator(aEnumerator)
+    , mIID(aIID)
+  {}
+
+private:
+  ~JSEnumerator() = default;
+
+  nsCOMPtr<nsISimpleEnumerator> mEnumerator;
+  const nsID mIID;
+};
+
+} // anonymous namespace
+
+nsresult
+JSEnumerator::Iterator(nsIJSEnumerator** aResult)
+{
+  RefPtr<JSEnumerator> result(this);
+  result.forget(aResult);
+  return NS_OK;
+}
+
+nsresult
+JSEnumerator::Next(JSContext* aCx, JS::MutableHandleValue aResult)
+{
+  RootedDictionary<IteratorResult> result(aCx);
+
+  nsCOMPtr<nsISupports> elem;
+  if (NS_FAILED(mEnumerator->GetNext(getter_AddRefs(elem)))) {
+    result.mDone = true;
+  } else {
+    result.mDone = false;
+
+    JS::RootedValue value(aCx);
+    MOZ_TRY(nsContentUtils::WrapNative(aCx, elem, &mIID, &value));
+    result.mValue = value;
+  }
+
+  if (!ToJSValue(aCx, result, aResult)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(JSEnumerator, nsIJSEnumerator)
+
+nsresult
+nsSimpleEnumerator::Iterator(nsIJSEnumerator **aResult)
+{
+  auto result = MakeRefPtr<JSEnumerator>(this, NS_GET_IID(nsISupports));
+  result.forget(aResult);
+  return NS_OK;
+}
+
+nsresult
+nsSimpleEnumerator::Entries(const nsIID& aIface, nsIJSEnumerator **aResult)
+{
+  auto result = MakeRefPtr<JSEnumerator>(this, aIface);
+  result.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsSimpleEnumerator, nsISimpleEnumerator, nsISimpleEnumeratorBase)
--- a/xpcom/ds/nsSimpleEnumerator.h
+++ b/xpcom/ds/nsSimpleEnumerator.h
@@ -7,14 +7,15 @@
 #ifndef nsSimpleEnumerator_h
 #define nsSimpleEnumerator_h
 
 #include "nsISimpleEnumerator.h"
 
 class nsSimpleEnumerator : public nsISimpleEnumerator
 {
   NS_DECL_ISUPPORTS
+  NS_DECL_NSISIMPLEENUMERATORBASE
 
 protected:
   virtual ~nsSimpleEnumerator() = default;
 };
 
 #endif
--- a/xpcom/io/nsLocalFileUnix.cpp
+++ b/xpcom/io/nsLocalFileUnix.cpp
@@ -103,16 +103,18 @@ public:
   // nsISimpleEnumerator interface
   NS_DECL_NSISIMPLEENUMERATOR
 
   // nsIDirectoryEnumerator interface
   NS_DECL_NSIDIRECTORYENUMERATOR
 
   NS_IMETHOD Init(nsLocalFile* aParent, bool aIgnored);
 
+  NS_FORWARD_NSISIMPLEENUMERATORBASE(nsSimpleEnumerator::)
+
 private:
   ~nsDirEnumeratorUnix() override;
 
 protected:
   NS_IMETHOD GetNextEntry();
 
   DIR*           mDir;
   struct dirent* mEntry;
--- a/xpcom/io/nsLocalFileWin.cpp
+++ b/xpcom/io/nsLocalFileWin.cpp
@@ -233,16 +233,17 @@ private:
 
 class nsDriveEnumerator : public nsSimpleEnumerator
                         , public nsIDirectoryEnumerator
 {
 public:
   nsDriveEnumerator();
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSISIMPLEENUMERATOR
+  NS_FORWARD_NSISIMPLEENUMERATORBASE(nsSimpleEnumerator::)
   nsresult Init();
 
   NS_IMETHOD GetNextFile(nsIFile** aResult) override
   {
     bool hasMore = false;
     nsresult rv = HasMoreElements(&hasMore);
     if (NS_FAILED(rv) || !hasMore) {
       return rv;
@@ -689,16 +690,18 @@ private:
   ~nsDirEnumerator()
   {
     Close();
   }
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
+  NS_FORWARD_NSISIMPLEENUMERATORBASE(nsSimpleEnumerator::)
+
   nsDirEnumerator() : mDir(nullptr)
   {
   }
 
   nsresult Init(nsIFile* aParent)
   {
     nsAutoString filepath;
     aParent->GetTarget(filepath);