Bug 1492737 - Part 2: Avoid searching the prototype chain when extracting keys. r=asuth, a=pascalc
authorJan Varga <jan.varga@gmail.com>
Tue, 25 Sep 2018 11:53:49 +0200
changeset 490265 1c577d6a48f0
parent 490264 d43c7f7c0d15
child 490266 c79baa59f78f
push id9969
push userryanvm@gmail.com
push dateThu, 11 Oct 2018 17:41:24 +0000
treeherdermozilla-beta@f439e5f9e3d3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth, pascalc
bugs1492737
milestone63.0
Bug 1492737 - Part 2: Avoid searching the prototype chain when extracting keys. r=asuth, a=pascalc
dom/indexedDB/KeyPath.cpp
testing/web-platform/meta/IndexedDB/keypath-exceptions.htm.ini
--- a/dom/indexedDB/KeyPath.cpp
+++ b/dom/indexedDB/KeyPath.cpp
@@ -10,16 +10,17 @@
 #include "ReportInternalError.h"
 
 #include "nsCharSeparatedTokenizer.h"
 #include "nsJSUtils.h"
 #include "nsPrintfCString.h"
 #include "xpcpublic.h"
 
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/IDBObjectStoreBinding.h"
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 namespace {
 
@@ -96,42 +97,97 @@ GetJSValFromKeyPathString(JSContext* aCx
   while (tokenizer.hasMoreTokens()) {
     const nsDependentSubstring& token = tokenizer.nextToken();
 
     NS_ASSERTION(!token.IsEmpty(), "Should be a valid keypath");
 
     const char16_t* keyPathChars = token.BeginReading();
     const size_t keyPathLen = token.Length();
 
-    bool hasProp;
     if (!targetObject) {
       // We're still walking the chain of existing objects
       // http://w3c.github.io/IndexedDB/#evaluate-a-key-path-on-a-value
       // step 4 substep 1: check for .length on a String value.
       if (currentVal.isString() && !tokenizer.hasMoreTokens() &&
           token.EqualsLiteral("length")) {
         aKeyJSVal->setNumber(double(JS_GetStringLength(currentVal.toString())));
         break;
       }
 
       if (!currentVal.isObject()) {
         return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
       }
       obj = &currentVal.toObject();
 
-      bool ok = JS_HasUCProperty(aCx, obj, keyPathChars, keyPathLen,
-                                 &hasProp);
+      // We call JS_GetOwnUCPropertyDescriptor on purpose (as opposed to
+      // JS_GetUCPropertyDescriptor) to avoid searching the prototype chain.
+      JS::Rooted<JS::PropertyDescriptor> desc(aCx);
+      bool ok = JS_GetOwnUCPropertyDescriptor(aCx, obj, keyPathChars,
+                                              keyPathLen, &desc);
       IDB_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
+      JS::Rooted<JS::Value> intermediate(aCx);
+      bool hasProp = false;
+
+      if (desc.object()) {
+        intermediate = desc.value();
+        hasProp = true;
+      } else {
+        // If we get here it means the object doesn't have the property or the
+        // property is available throuch a getter. We don't want to call any
+        // getters to avoid potential re-entrancy.
+        // The blob object is special since its properties are available
+        // only through getters but we still want to support them for key
+        // extraction. So they need to be handled manually.
+        Blob* blob;
+        if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
+          if (token.EqualsLiteral("size")) {
+            ErrorResult rv;
+            uint64_t size = blob->GetSize(rv);
+            MOZ_ALWAYS_TRUE(!rv.Failed());
+
+            intermediate = JS_NumberValue(size);
+            hasProp = true;
+          } else if (token.EqualsLiteral("type")) {
+            nsString type;
+            blob->GetType(type);
+
+            JSString* string =
+              JS_NewUCStringCopyN(aCx, type.get(), type.Length());
+
+            intermediate = JS::StringValue(string);
+            hasProp = true;
+          } else {
+            RefPtr<File> file = blob->ToFile();
+            if (file) {
+              if (token.EqualsLiteral("name")) {
+                nsString name;
+                file->GetName(name);
+
+                JSString* string =
+                  JS_NewUCStringCopyN(aCx, name.get(), name.Length());
+
+                intermediate = JS::StringValue(string);
+                hasProp = true;
+              } else if (token.EqualsLiteral("lastModified")) {
+                ErrorResult rv;
+                int64_t lastModifiedDate = file->GetLastModified(rv);
+                MOZ_ALWAYS_TRUE(!rv.Failed());
+
+                intermediate = JS_NumberValue(lastModifiedDate);
+                hasProp = true;
+              }
+              // The spec also lists "lastModifiedDate", but we deprecated and
+              // removed support for it.
+            }
+          }
+        }
+      }
+
       if (hasProp) {
-        // Get if the property exists...
-        JS::Rooted<JS::Value> intermediate(aCx);
-        bool ok = JS_GetUCProperty(aCx, obj, keyPathChars, keyPathLen, &intermediate);
-        IDB_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
         // Treat explicitly undefined as an error.
         if (intermediate.isUndefined()) {
           return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
         }
         if (tokenizer.hasMoreTokens()) {
           // ...and walk to it if there are more steps...
           currentVal = intermediate;
         }
deleted file mode 100644
--- a/testing/web-platform/meta/IndexedDB/keypath-exceptions.htm.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[keypath-exceptions.htm]
-  [Key path evaluation: Exceptions from non-enumerable getters on prototype]
-    expected: FAIL
-
-  [Key path evaluation: Exceptions from enumerable getters on prototype]
-    expected: FAIL