| OLD | NEW | 
| (Empty) |  | 
 |    1 <!doctype html> | 
 |    2 <meta charset="utf8"> | 
 |    3 <meta name="timeout" content="long"> | 
 |    4 <title>IndexedDB: large nested objects are cloned correctly</title> | 
 |    5 <link rel="help" href="https://w3c.github.io/IndexedDB/#abort-transaction"> | 
 |    6 <link rel="author" href="pwnall@chromium.org" title="Victor Costan"> | 
 |    7 <script src="/resources/testharness.js"></script> | 
 |    8 <script src="/resources/testharnessreport.js"></script> | 
 |    9 <script src="support-promises.js"></script> | 
 |   10 <script> | 
 |   11 'use strict'; | 
 |   12  | 
 |   13 // Should be large enough to trigger large value handling in the IndexedDB | 
 |   14 // engines that have special code paths for large values. | 
 |   15 const wrapThreshold = 128 * 1024; | 
 |   16  | 
 |   17 // Returns an IndexedDB value created from a descriptor. | 
 |   18 // | 
 |   19 // See the bottom of the file for descriptor samples. | 
 |   20 function createValue(descriptor) { | 
 |   21   if (typeof(descriptor) != 'object') | 
 |   22     return descriptor; | 
 |   23  | 
 |   24   if (Array.isArray(descriptor)) | 
 |   25     return descriptor.map((element) => createValue(element)); | 
 |   26  | 
 |   27   if (!descriptor.hasOwnProperty('type')) { | 
 |   28     const value = {}; | 
 |   29     for (let property of Object.getOwnPropertyNames(descriptor)) | 
 |   30       value[property] = createValue(descriptor[property]); | 
 |   31     return value; | 
 |   32   } | 
 |   33  | 
 |   34   switch (descriptor.type) { | 
 |   35     case 'blob': | 
 |   36       return new Blob( | 
 |   37           [largeValue(descriptor.size, descriptor.seed)], | 
 |   38           { type: descriptor.mimeType }); | 
 |   39     case 'buffer': | 
 |   40       return largeValue(descriptor.size, descriptor.seed); | 
 |   41   } | 
 |   42 } | 
 |   43  | 
 |   44 // Checks an IndexedDB value against a descriptor. | 
 |   45 // | 
 |   46 // Returns a Promise that resolves if the value passes the check. | 
 |   47 // | 
 |   48 // See the bottom of the file for descriptor samples. | 
 |   49 function checkValue(testCase, value, descriptor) { | 
 |   50   if (typeof(descriptor) != 'object') { | 
 |   51     assert_equals( | 
 |   52         descriptor, value, | 
 |   53         'IndexedDB result should match put() argument'); | 
 |   54     return Promise.resolve(); | 
 |   55   } | 
 |   56  | 
 |   57   if (Array.isArray(descriptor)) { | 
 |   58     assert_true( | 
 |   59         Array.isArray(value), | 
 |   60         'IndexedDB result type should match put() argument'); | 
 |   61     assert_equals( | 
 |   62         descriptor.length, value.length, | 
 |   63         'IndexedDB result array size should match put() argument'); | 
 |   64  | 
 |   65     const subChecks = []; | 
 |   66     for (let i = 0; i < descriptor.length; ++i) | 
 |   67       subChecks.push(checkValue(testCase, value[i], descriptor[i])); | 
 |   68     return Promise.all(subChecks); | 
 |   69   } | 
 |   70  | 
 |   71   if (!descriptor.hasOwnProperty('type')) { | 
 |   72     assert_array_equals( | 
 |   73         Object.getOwnPropertyNames(value).sort(), | 
 |   74         Object.getOwnPropertyNames(descriptor).sort(), | 
 |   75         'IndexedDB result object properties should match put() argument'); | 
 |   76     const subChecks = []; | 
 |   77     return Promise.all(Object.getOwnPropertyNames(descriptor).map(property => | 
 |   78         checkValue(testCase, value[property], descriptor[property]))); | 
 |   79   } | 
 |   80  | 
 |   81   switch (descriptor.type) { | 
 |   82     case 'blob': | 
 |   83       assert_class_string( | 
 |   84           value, 'Blob', | 
 |   85           'IndexedDB result class should match put() argument'); | 
 |   86       assert_equals( | 
 |   87           descriptor.mimeType, value.type, | 
 |   88           'IndexedDB result Blob MIME type should match put() argument'); | 
 |   89       assert_equals(descriptor.size, value.size, 'incorrect Blob size'); | 
 |   90       return new Promise((resolve, reject) => { | 
 |   91         const reader = new FileReader(); | 
 |   92         reader.onloadend = testCase.step_func(() => { | 
 |   93           if (reader.error) { | 
 |   94             reject(reader.error); | 
 |   95             return; | 
 |   96           } | 
 |   97           const view = new Uint8Array(reader.result); | 
 |   98           assert_equals( | 
 |   99               view.join(','), | 
 |  100               largeValue(descriptor.size, descriptor.seed).join(','), | 
 |  101               'IndexedDB result Blob content should match put() argument'); | 
 |  102           resolve(); | 
 |  103         }); | 
 |  104         reader.readAsArrayBuffer(value); | 
 |  105       }); | 
 |  106  | 
 |  107     case 'buffer': | 
 |  108       assert_class_string( | 
 |  109           value, 'Uint8Array', | 
 |  110           'IndexedDB result type should match put() argument'); | 
 |  111       assert_equals( | 
 |  112           value.join(','), | 
 |  113           largeValue(descriptor.size, descriptor.seed).join(','), | 
 |  114           'IndexedDB result typed array content should match put() argument'); | 
 |  115       return Promise.resolve(); | 
 |  116   } | 
 |  117 } | 
 |  118  | 
 |  119 // Performs a series of put()s and verifies that get()s and getAll() match. | 
 |  120 // | 
 |  121 // Each element of the valueDescriptors array is fed into createValue(), and the | 
 |  122 // resulting value is written to IndexedDB via a put() request. After the writes | 
 |  123 // complete, the values are read in the same order in which they were written. | 
 |  124 // Last, all the results are read one more time via a getAll(). | 
 |  125 // | 
 |  126 // The test verifies that the get() / getAll() results match the arguments to | 
 |  127 // put() and that the order in which the get() result events are fired matches | 
 |  128 // the order of the get() requests. | 
 |  129 function cloningTest(label, valueDescriptors) { | 
 |  130   promise_test(testCase => { | 
 |  131     return createDatabase(testCase, (database, transaction) => { | 
 |  132       const store = database.createObjectStore('test-store'); | 
 |  133       for (let i = 0; i < valueDescriptors.length; ++i) { | 
 |  134         store.put(createValue(valueDescriptors[i]), i); | 
 |  135       } | 
 |  136     }).then(database => { | 
 |  137       const transaction = database.transaction(['test-store'], 'readonly'); | 
 |  138       const store = transaction.objectStore('test-store'); | 
 |  139       const subChecks = []; | 
 |  140       let resultIndex = 0; | 
 |  141       for (let i = 0; i < valueDescriptors.length; ++i) { | 
 |  142         subChecks.push(new Promise((resolve, reject) => { | 
 |  143           const requestIndex = i; | 
 |  144           const request = store.get(requestIndex); | 
 |  145           request.onerror = | 
 |  146               testCase.step_func(() => { reject(request.error); }); | 
 |  147           request.onsuccess = testCase.step_func(() => { | 
 |  148             assert_equals( | 
 |  149                 resultIndex, requestIndex, | 
 |  150                 'IDBRequest success events should be fired in request order'); | 
 |  151             ++resultIndex; | 
 |  152             resolve(checkValue( | 
 |  153                 testCase, request.result, valueDescriptors[requestIndex])); | 
 |  154           }); | 
 |  155         })); | 
 |  156       } | 
 |  157  | 
 |  158       subChecks.push(new Promise((resolve, reject) => { | 
 |  159         const requestIndex = valueDescriptors.length; | 
 |  160         const request = store.getAll(); | 
 |  161         request.onerror = | 
 |  162             testCase.step_func(() => { reject(request.error); }); | 
 |  163         request.onsuccess = testCase.step_func(() => { | 
 |  164           assert_equals( | 
 |  165               resultIndex, requestIndex, | 
 |  166               'IDBRequest success events should be fired in request order'); | 
 |  167           ++resultIndex; | 
 |  168           resolve(checkValue( | 
 |  169               testCase, request.result, valueDescriptors)); | 
 |  170         }); | 
 |  171       })); | 
 |  172  | 
 |  173       return Promise.all(subChecks); | 
 |  174     }); | 
 |  175   }, label); | 
 |  176 } | 
 |  177  | 
 |  178 cloningTest('small typed array', [ | 
 |  179   { type: 'buffer', size: 64, seed: 1 }, | 
 |  180 ]); | 
 |  181  | 
 |  182 cloningTest('large typed array', [ | 
 |  183   { type: 'buffer', size: wrapThreshold, seed: 1 }, | 
 |  184 ]) | 
 |  185  | 
 |  186 cloningTest('blob', [ | 
 |  187   { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-1', seed: 1 }, | 
 |  188 ]); | 
 |  189  | 
 |  190 cloningTest('blob with small typed array', [ | 
 |  191   { | 
 |  192     blob: { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-01', | 
 |  193             seed: 1 }, | 
 |  194     buffer: { type: 'buffer', size: 64, seed: 2 }, | 
 |  195   }, | 
 |  196 ]); | 
 |  197  | 
 |  198 cloningTest('blob with large typed array', [ | 
 |  199   { | 
 |  200     blob: { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-01', | 
 |  201             seed: 1 }, | 
 |  202     buffer: { type: 'buffer', size: wrapThreshold, seed: 2 }, | 
 |  203   }, | 
 |  204 ]); | 
 |  205  | 
 |  206 cloningTest('blob array', [ | 
 |  207   [ | 
 |  208     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-1', seed: 1 }, | 
 |  209     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-2', seed: 2 }, | 
 |  210     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-3', seed: 3 }, | 
 |  211   ], | 
 |  212 ]); | 
 |  213  | 
 |  214 cloningTest('array of blobs and small typed arrays', [ | 
 |  215   [ | 
 |  216     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-01', seed: 1 }, | 
 |  217     { type: 'buffer', size: 64, seed: 2 }, | 
 |  218     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-03', seed: 3 }, | 
 |  219     { type: 'buffer', size: 64, seed: 4 }, | 
 |  220     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-05', seed: 5 }, | 
 |  221   ], | 
 |  222 ]); | 
 |  223  | 
 |  224 cloningTest('array of blobs and large typed arrays', [ | 
 |  225   [ | 
 |  226     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-01', seed: 1 }, | 
 |  227     { type: 'buffer', size: wrapThreshold, seed: 2 }, | 
 |  228     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-03', seed: 3 }, | 
 |  229     { type: 'buffer', size: wrapThreshold, seed: 4 }, | 
 |  230     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-05', seed: 5 }, | 
 |  231   ], | 
 |  232 ]); | 
 |  233  | 
 |  234 cloningTest('object with blobs and large typed arrays', [ | 
 |  235   { | 
 |  236     blob: { type: 'blob', size: wrapThreshold, | 
 |  237             mimeType: 'text/x-blink1', seed: 1 }, | 
 |  238     more: [ | 
 |  239       { type: 'buffer', size: wrapThreshold, seed: 2 }, | 
 |  240       { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink3', seed: 3 }, | 
 |  241       { type: 'buffer', size: wrapThreshold, seed: 4 }, | 
 |  242     ], | 
 |  243     blob2: { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink5', | 
 |  244              seed: 5 }, | 
 |  245   }, | 
 |  246 ]); | 
 |  247  | 
 |  248 cloningTest('multiple requests of objects with blobs and large typed arrays', [ | 
 |  249   { | 
 |  250     blob: { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink1', | 
 |  251             seed: 1 }, | 
 |  252     more: [ | 
 |  253       { type: 'buffer', size: wrapThreshold, seed: 2 }, | 
 |  254       { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink3', seed: 3 }, | 
 |  255       { type: 'buffer', size: wrapThreshold, seed: 4 }, | 
 |  256     ], | 
 |  257     blob2: { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink5', | 
 |  258              seed: 5 }, | 
 |  259   }, | 
 |  260   [ | 
 |  261     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink06', seed: 6 }, | 
 |  262     { type: 'buffer', size: wrapThreshold, seed: 7 }, | 
 |  263     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink08', seed: 8 }, | 
 |  264     { type: 'buffer', size: wrapThreshold, seed: 9 }, | 
 |  265     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink10', seed: 10 }, | 
 |  266   ], | 
 |  267   { | 
 |  268     data: [ | 
 |  269       { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-11', | 
 |  270         seed: 11 }, | 
 |  271       { type: 'buffer', size: wrapThreshold, seed: 12 }, | 
 |  272       { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-13', | 
 |  273         seed: 13 }, | 
 |  274       { type: 'buffer', size: wrapThreshold, seed: 14 }, | 
 |  275       { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-15', | 
 |  276         seed: 15 }, | 
 |  277     ], | 
 |  278   }, | 
 |  279   [ | 
 |  280     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink16', seed: 16 }, | 
 |  281     { type: 'buffer', size: wrapThreshold, seed: 17 }, | 
 |  282     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink18', seed: 18 }, | 
 |  283     { type: 'buffer', size: wrapThreshold, seed: 19 }, | 
 |  284     { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink20', seed: 20 }, | 
 |  285   ], | 
 |  286 ]); | 
 |  287  | 
 |  288 </script> | 
| OLD | NEW |