File: idb-filelist-serialization.https.html

package info (click to toggle)
firefox 143.0.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,617,328 kB
  • sloc: cpp: 7,478,492; javascript: 6,417,157; ansic: 3,720,058; python: 1,396,372; xml: 627,523; asm: 438,677; java: 186,156; sh: 63,477; makefile: 19,171; objc: 13,059; perl: 12,983; yacc: 4,583; cs: 3,846; pascal: 3,405; lex: 1,720; ruby: 1,003; exp: 762; php: 436; lisp: 258; awk: 247; sql: 66; sed: 53; csh: 10
file content (228 lines) | stat: -rw-r--r-- 6,068 bytes parent folder | download | duplicates (13)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
<!--
  Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
  <meta charset="utf-8">
  <meta name="timeout" content="long">
  <script src="/resources/testharness.js"></script>
  <script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>

var fileCounter = 0;
var dbCounter = 0;

const fileListSize = 3;
const elementSize = 8196;

/**
 * Acknowledgement:
 * This test takes inspiration from IndexedDB/structured-clone.any.js
 * but the focus is on the variations of the filelist serialization.
 */
function addCloneTest(testName, orig, verifyFunc) {
  promise_test(async t => {
    const requestToFinish = req => {
      return new Promise((resolve, reject) => {
        req.onerror = () => {
          reject(req.error);
        };
        req.onblocked = () => {
          reject("Unexpected block");
        };
        req.onupgradeneeded = () => {
          reject("Unexpected upgrade");
        };
        req.onsuccess = ev => {
          resolve(ev.target.result);
        };
      });
    };

    const txEvents = [
      "abort",
      "complete",
      "error",
    ];

    const dbName = "db_" + dbCounter;
    ++dbCounter;

    const performRequest = async (query) => {
      const db = await new Promise((resolve, reject) => {
        const openReq = indexedDB.open(dbName, 1);
        openReq.onerror = () => {
          reject(openReq.error);
        };
        openReq.onupgradeneeded = ev => {
          const dbObj = ev.target.result;
          const store = dbObj.createObjectStore("store");
          // This index is not used, but evaluating key path on each put()
          // call will exercise (de)serialization.
          store.createIndex("index", "dummyKeyPath");
        };
        openReq.onsuccess = () => {
          resolve(openReq.result);
        };
      });

      t.add_cleanup(() => {
        if (db) {
          db.close();
          indexedDB.deleteDatabase(db.name);
        }
      });

      let result = undefined;
      try {
        const tx = db.transaction("store", "readwrite");
        const store = tx.objectStore("store");
        result = await requestToFinish(query(store));
        await new EventWatcher(t, tx, txEvents).wait_for("complete");
      } finally {
        db.close();
      }

      return result;
    };

    await performRequest(store => store.put(orig, "key"));
    const clone = await performRequest(store => store.get("key"));

    assert_not_equals(orig, clone);
    await verifyFunc(orig, clone);
  }, testName);
}

function makeFileList(dataGenerators) {
  const fileOpts = { type: "text/plain" };
  const dataTransfer = new DataTransfer();
  dataGenerators.forEach((generator, i) => {
    const file = new File(generator(i), "test_" + fileCounter, fileOpts);
    dataTransfer.items.add(file);
    ++fileCounter;
  });

  return dataTransfer.files;
}

const compareCloneToOrig = async (orig, clone) => {
  assert_equals(orig.length, clone.length);
  assert_equals(orig.length, fileListSize);
  for (let i = 0; i < orig.length; ++i) {
    const origFile = orig.item(i);
    const cloneFile = clone.item(i);
    assert_equals(origFile.name, cloneFile.name);
    assert_equals(await origFile.text(), await cloneFile.text());
  }
};

const compareObjects = async (orig, clone) => {
  assert_true("value" in orig);
  assert_true("value" in clone);

  return await compareCloneToOrig(orig.value, clone.value);
};

const compareArrays = async (orig, clone) => {
  assert_equals(orig.length, 1);
  assert_equals(clone.length, 1);

  return await compareCloneToOrig(orig[0], clone[0]);
};

const randomLetters = n => {
  const chars = "abcd";
  const someLetter = () => chars[Math.floor(Math.random() * chars.length)];
  return Array(n).fill().map(someLetter).join("");
};

// FileList - exposed in Workers, but not constructable.
if ("document" in self) {
  const addTestCases = (dataName, dataGenerator) => {
    const fileListStatic = makeFileList(
      Array(fileListSize).fill(dataGenerator)
    );

    addCloneTest(
      "Serialize filelist containing " + dataName,
      fileListStatic,
      compareCloneToOrig
    );

    addCloneTest(
      "Serialize object with filelist containing " + dataName,
      { value: fileListStatic },
      compareObjects
    );

    addCloneTest(
      "Serialize array with filelist containing " + dataName,
      [fileListStatic],
      compareArrays
    );

    const baseData = dataGenerator();

    // Currently it's legal for the same File to appear in a FileList
    // multiple times. This was the subject of some brief discussion
    // at TPAC 2024 and it's possible that as FileList moves entirely
    // into the HTML spec this may change.
    // In the meantime we want to make sure we support this case and
    // that IndexedDB's optimizations related to File-interning
    // don't break things, although that logic is tested more thoroughly
    // in test_file_filelist.html
    const fileListRepeated = makeFileList(
      Array(fileListSize).fill(() => {
        return baseData;
      })
    );

    addCloneTest(
      "Serialize filelist containing repeated " + dataName,
      fileListRepeated,
      compareCloneToOrig
    );

    addCloneTest(
      "Serialize object with filelist containing repeated " + dataName,
      { value: fileListRepeated },
      compareObjects
    );

    addCloneTest(
      "Serialize array with filelist containing repeated " + dataName,
      [fileListRepeated],
      compareArrays
    );
  };

  const genString = () => {
    return [randomLetters(elementSize)];
  };

  addTestCases("random string", genString);

  const genArray = () => {
    const array = new Uint32Array(elementSize);
    crypto.getRandomValues(array);
    return array;
  };

  addTestCases("random typed array", genArray);

  const genBlob = () => {
    const array = new Uint32Array(elementSize);
    crypto.getRandomValues(array);
    return [new Blob(array)];
  };

  addTestCases("random blob", genBlob);
}

</script>
</body>
</html>