| Index: third_party/WebKit/LayoutTests/external/wpt/IndexedDB/large-requests-abort.html
 | 
| diff --git a/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/large-requests-abort.html b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/large-requests-abort.html
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..9f30281e9fa59b623299e1b23ab1b9a577899ca1
 | 
| --- /dev/null
 | 
| +++ b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/large-requests-abort.html
 | 
| @@ -0,0 +1,244 @@
 | 
| +<!doctype html>
 | 
| +<meta charset="utf8">
 | 
| +<meta name="timeout" content="long">
 | 
| +<title>IndexedDB: transactions with large request results are aborted correctly</title>
 | 
| +<link rel="help" href="https://w3c.github.io/IndexedDB/#abort-transaction">
 | 
| +<link rel="author" href="pwnall@chromium.org" title="Victor Costan">
 | 
| +<script src="/resources/testharness.js"></script>
 | 
| +<script src="/resources/testharnessreport.js"></script>
 | 
| +<script src="support-promises.js"></script>
 | 
| +<script>
 | 
| +'use strict';
 | 
| +
 | 
| +// Should be large enough to trigger large value handling in the IndexedDB
 | 
| +// engines that have special code paths for large values.
 | 
| +const wrapThreshold = 128 * 1024;
 | 
| +
 | 
| +function populateStore(store) {
 | 
| +  store.put({id: 1, key: 'k1', value: largeValue(wrapThreshold, 1) });
 | 
| +  store.put({id: 2, key: 'k2', value: ['small-2'] });
 | 
| +  store.put({id: 3, key: 'k3', value: largeValue(wrapThreshold, 3) });
 | 
| +  store.put({id: 4, key: 'k4', value: ['small-4'] });
 | 
| +}
 | 
| +
 | 
| +// Opens index cursors for operations that require open cursors.
 | 
| +//
 | 
| +// onsuccess is called if all cursors are opened successfully. Otherwise,
 | 
| +// onerror will be called at least once.
 | 
| +function openCursors(testCase, index, operations, onerror, onsuccess) {
 | 
| +  let pendingCursors = 0;
 | 
| +
 | 
| +  for (let operation of operations) {
 | 
| +    const opcode = operation[0];
 | 
| +    const primaryKey = operation[1];
 | 
| +    let request;
 | 
| +    switch (opcode) {
 | 
| +      case 'continue':
 | 
| +        request = index.openCursor(
 | 
| +            IDBKeyRange.lowerBound(`k${primaryKey - 1}`));
 | 
| +        break;
 | 
| +      case 'continue-empty':
 | 
| +        // k4 is the last key in the data set, so calling continue() will get
 | 
| +        // the cursor past the end of the store.
 | 
| +        request = index.openCursor(IDBKeyRange.lowerBound('k4'));
 | 
| +        break;
 | 
| +      default:
 | 
| +        continue;
 | 
| +    }
 | 
| +
 | 
| +    operation[2] = request;
 | 
| +    ++pendingCursors;
 | 
| +
 | 
| +    request.onsuccess = testCase.step_func(() => {
 | 
| +      --pendingCursors;
 | 
| +      if (!pendingCursors)
 | 
| +        onsuccess();
 | 
| +    });
 | 
| +    request.onerror = testCase.step_func(onerror);
 | 
| +  }
 | 
| +
 | 
| +  if (!pendingCursors)
 | 
| +    onsuccess();
 | 
| +}
 | 
| +
 | 
| +function doOperation(testCase, store, index, operation, requestId, results) {
 | 
| +  const opcode = operation[0];
 | 
| +  const primaryKey = operation[1];
 | 
| +  const cursor = operation[2];
 | 
| +
 | 
| +  return new Promise((resolve, reject) => {
 | 
| +    let request;
 | 
| +    switch (opcode) {
 | 
| +      case 'add':  // Tests returning a primary key.
 | 
| +        request = store.add(
 | 
| +            { key: `k${primaryKey}`, value: [`small-${primaryKey}`] });
 | 
| +        break;
 | 
| +      case 'put':  // Tests returning a primary key.
 | 
| +        request = store.put(
 | 
| +            { key: `k${primaryKey}`, value: [`small-${primaryKey}`] });
 | 
| +        break;
 | 
| +      case 'put-with-id':  // Tests returning success or a primary key.
 | 
| +        request = store.put(
 | 
| +            { key: `k${primaryKey}`, value: [`small-${primaryKey}`],
 | 
| +              id: primaryKey });
 | 
| +        break;
 | 
| +      case 'get':  // Tests returning a value.
 | 
| +      case 'get-empty':  // Tests returning undefined.
 | 
| +        request = store.get(primaryKey);
 | 
| +        break;
 | 
| +      case 'getall':  // Tests returning an array of values.
 | 
| +        request = store.getAll();
 | 
| +        break;
 | 
| +      case 'error':  // Tests returning an error.
 | 
| +        request = store.put(
 | 
| +            { key: `k${primaryKey}`, value: [`small-${primaryKey}`] });
 | 
| +        break;
 | 
| +      case 'continue':  // Tests returning a key, primary key, and value.
 | 
| +      case 'continue-empty':   // Tests returning null.
 | 
| +        request = cursor;
 | 
| +        cursor.result.continue();
 | 
| +        break;
 | 
| +      case 'open':  // Tests returning a cursor, key, primary key, and value.
 | 
| +      case 'open-empty':  // Tests returning null.
 | 
| +        request = index.openCursor(IDBKeyRange.lowerBound(`k${primaryKey}`));
 | 
| +        break;
 | 
| +      case 'count':  // Tests returning a numeric result.
 | 
| +        request = index.count();
 | 
| +        break;
 | 
| +    };
 | 
| +
 | 
| +    request.onsuccess = testCase.step_func(() => {
 | 
| +      reject(new Error(
 | 
| +          'requests should not succeed after the transaction is aborted'));
 | 
| +    });
 | 
| +    request.onerror = testCase.step_func(event => {
 | 
| +      event.preventDefault();
 | 
| +      results.push([requestId, request.error]);
 | 
| +      resolve();
 | 
| +    });
 | 
| +  });
 | 
| +}
 | 
| +
 | 
| +function abortTest(label, operations) {
 | 
| +  promise_test(testCase => {
 | 
| +    return createDatabase(testCase, (database, transaction) => {
 | 
| +      const store = database.createObjectStore(
 | 
| +          'test-store', { autoIncrement: true, keyPath: 'id' });
 | 
| +      store.createIndex('test-index', 'key', { unique: true });
 | 
| +      populateStore(store);
 | 
| +    }).then(database => {
 | 
| +      const transaction = database.transaction(['test-store'], 'readwrite');
 | 
| +      const store = transaction.objectStore('test-store');
 | 
| +      const index = store.index('test-index');
 | 
| +      return new Promise((resolve, reject) => {
 | 
| +        openCursors(testCase, index, operations, reject, () => {
 | 
| +          const results = [];
 | 
| +          const promises = [];
 | 
| +          for (let i = 0; i < operations.length; ++i) {
 | 
| +            const promise = doOperation(
 | 
| +                testCase, store, index, operations[i], i, results);
 | 
| +            promises.push(promise);
 | 
| +          };
 | 
| +          transaction.abort();
 | 
| +          resolve(Promise.all(promises).then(() => results));
 | 
| +        });
 | 
| +      });
 | 
| +    }).then(results => {
 | 
| +      assert_equals(
 | 
| +          results.length, operations.length,
 | 
| +          'Promise.all should resolve after all sub-promises resolve');
 | 
| +      for (let i = 0; i < operations.length; ++i) {
 | 
| +        assert_equals(
 | 
| +            results[i][0], i,
 | 
| +            'error event order should match request order');
 | 
| +        assert_equals(
 | 
| +            results[i][1].name, 'AbortError',
 | 
| +            'transaction aborting should result in AbortError on all requests');
 | 
| +      }
 | 
| +    });
 | 
| +  }, label);
 | 
| +}
 | 
| +
 | 
| +abortTest('small values', [
 | 
| +  ['get', 2],
 | 
| +  ['count', null],
 | 
| +  ['continue-empty', null],
 | 
| +  ['get-empty', 5],
 | 
| +  ['add', 5],
 | 
| +  ['open', 2],
 | 
| +  ['continue', 2],
 | 
| +  ['get', 4],
 | 
| +  ['get-empty', 6],
 | 
| +  ['count', null],
 | 
| +  ['put-with-id', 5],
 | 
| +  ['put', 6],
 | 
| +  ['error', 3],
 | 
| +  ['continue', 4],
 | 
| +  ['count', null],
 | 
| +  ['get-empty', 7],
 | 
| +  ['open', 4],
 | 
| +  ['open-empty', 7],
 | 
| +  ['add', 7],
 | 
| +]);
 | 
| +
 | 
| +abortTest('large values', [
 | 
| +  ['open', 1],
 | 
| +  ['get', 1],
 | 
| +  ['getall', 4],
 | 
| +  ['get', 3],
 | 
| +  ['continue', 3],
 | 
| +  ['open', 3],
 | 
| +]);
 | 
| +
 | 
| +abortTest('large value followed by small values', [
 | 
| +  ['get', 1],
 | 
| +  ['getall', null],
 | 
| +  ['open', 2],
 | 
| +  ['continue-empty', null],
 | 
| +  ['get', 2],
 | 
| +  ['get-empty', 5],
 | 
| +  ['count', null],
 | 
| +  ['continue-empty', null],
 | 
| +  ['open-empty', 5],
 | 
| +  ['add', 5],
 | 
| +  ['error', 1],
 | 
| +  ['continue', 2],
 | 
| +  ['get-empty', 6],
 | 
| +  ['put-with-id', 5],
 | 
| +  ['put', 6],
 | 
| +]);
 | 
| +
 | 
| +abortTest('large values mixed with small values', [
 | 
| +  ['get', 1],
 | 
| +  ['get', 2],
 | 
| +  ['get-empty', 5],
 | 
| +  ['count', null],
 | 
| +  ['continue-empty', null],
 | 
| +  ['open', 1],
 | 
| +  ['continue', 2],
 | 
| +  ['open-empty', 5],
 | 
| +  ['getall', 4],
 | 
| +  ['open', 2],
 | 
| +  ['continue-empty', null],
 | 
| +  ['add', 5],
 | 
| +  ['get', 3],
 | 
| +  ['count', null],
 | 
| +  ['get-empty', 6],
 | 
| +  ['put-with-id', 5],
 | 
| +  ['getall', null],
 | 
| +  ['continue', 3],
 | 
| +  ['open-empty', 6],
 | 
| +  ['put', 6],
 | 
| +  ['error', 1],
 | 
| +  ['continue', 2],
 | 
| +  ['open', 4],
 | 
| +  ['get-empty', 7],
 | 
| +  ['count', null],
 | 
| +  ['continue', 3],
 | 
| +  ['add', 7],
 | 
| +  ['getall', null],
 | 
| +  ['error', 3],
 | 
| +  ['count', null],
 | 
| +]);
 | 
| +
 | 
| +</script>
 | 
| 
 |