| <html> | 
 | <head> | 
 | <script src="../../resources/js-test.js"></script> | 
 | <script src="resources/shared.js"></script> | 
 | </head> | 
 | <body> | 
 | <input type="file" id="fileInput" multiple></input> | 
 | <script> | 
 |  | 
 | description("Test structured clone permutations in IndexedDB. File/FileList tests require DumpRenderTree."); | 
 |  | 
 | if (self.eventSender) { | 
 |     var fileInput = document.getElementById("fileInput"); | 
 |     var fileRect = fileInput.getClientRects()[0]; | 
 |     var targetX = fileRect.left + fileRect.width / 2; | 
 |     var targetY = fileRect.top + fileRect.height / 2; | 
 |     eventSender.beginDragWithFiles(['resources/test-data.html', 'resources/test-data.txt']); | 
 |     eventSender.mouseMoveTo(targetX, targetY); | 
 |     eventSender.mouseUp(); | 
 | } | 
 |  | 
 | indexedDBTest(prepareDatabase, startTests); | 
 | function prepareDatabase() | 
 | { | 
 |     db = event.target.result; | 
 |     evalAndLog("store = db.createObjectStore('storeName')"); | 
 |     debug("This index is not used, but evaluating key path on each put() call will exercise (de)serialization:"); | 
 |     evalAndLog("store.createIndex('indexName', 'dummyKeyPath')"); | 
 | } | 
 |  | 
 | function startTests() | 
 | { | 
 |     debug(""); | 
 |     debug("Running tests..."); | 
 |     var tests_to_run = [ | 
 |         testUndefined, | 
 |         testNull, | 
 |         testBoolean, | 
 |         testNumber, | 
 |         testString, | 
 |         testBooleanObject, | 
 |         testNumberObject, | 
 |         testStringObject, | 
 |         testDateObject, | 
 |         testRegExpObject, | 
 |         testImageData, | 
 |         testBlob, | 
 |         testFile, | 
 |         testFileList, | 
 |         testArray, | 
 |         testObject, | 
 |         testTypedArray | 
 |     ]; | 
 |  | 
 |     function nextTest() { | 
 |         debug(""); | 
 |         if (tests_to_run.length) { | 
 |             var test = tests_to_run.shift(); | 
 |             test(nextTest); // When done, call this again. | 
 |         } else { | 
 |             testBadTypes(); | 
 |         } | 
 |     } | 
 |     nextTest(); | 
 | } | 
 |  | 
 | function forEachWithCallback(testFunction, values, callback) | 
 | { | 
 |     function nextValue() { | 
 |         if (values.length) { | 
 |             testFunction(values.shift(), nextValue); | 
 |         } else { | 
 |             callback(); | 
 |         } | 
 |     } | 
 |  | 
 |     nextValue(); | 
 | } | 
 |  | 
 | function testValue(value, callback) | 
 | { | 
 |     // One transaction per test, since some tests require asynchronous | 
 |     // operations to verify the result (e.g. via FileReader) | 
 |     evalAndLog("transaction = db.transaction('storeName', 'readwrite')"); | 
 |     transaction.onerror = unexpectedErrorCallback; | 
 |     transaction.onabort = unexpectedAbortCallback; | 
 |     evalAndLog("store = transaction.objectStore('storeName')"); | 
 |  | 
 |     self.value = value; | 
 |     request = evalAndLog("store.put(value, 'key')"); | 
 |     request.onerror = unexpectedErrorCallback; | 
 |     request.onsuccess = function(e) { | 
 |         request = evalAndLog("store.get('key')"); | 
 |         request.onerror = unexpectedErrorCallback; | 
 |         request.onsuccess = function(e) { | 
 |             callback(request.result); | 
 |         }; | 
 |     }; | 
 | } | 
 |  | 
 | // Identity testing, sensitive to NaN and -0 | 
 | function is(x, y) { | 
 |     if (x === y) { | 
 |         return x !== 0 || 1 / x === 1 / y; | 
 |     } | 
 |     return x !== x && y !== y; | 
 | } | 
 |  | 
 | function arrayCompare(a, b) { | 
 |     if (a.length !== b.length) { | 
 |         return false; | 
 |     } | 
 |     for (var i = 0; i < a.length; ++i) { | 
 |         if (!is(a[i], b[i])) { | 
 |             return false; | 
 |         } | 
 |     } | 
 |     return true; | 
 | } | 
 |  | 
 | function testPrimitiveValue(string, callback) | 
 | { | 
 |     debug("Testing: " + string); | 
 |     var value = eval("value = (" + string + ")"); | 
 |     test_data = value; | 
 |     testValue(test_data, function(result) { | 
 |         self.result = result; | 
 |         shouldBeTrue("is(test_data, result)"); | 
 |         debug(""); | 
 |         callback(); | 
 |     }); | 
 | } | 
 |  | 
 | function testObjectWithValue(string, callback) | 
 | { | 
 |     debug("Testing: " + string); | 
 |     var value = eval("value = (" + string + ")"); | 
 |     test_data = value; | 
 |     testValue(test_data, function(result) { | 
 |         self.result = result; | 
 |         shouldBeEqualToString("typeof result", "object"); | 
 |         shouldBe("Object.prototype.toString.call(result)", "Object.prototype.toString.call(test_data)"); | 
 |         shouldBeTrue("test_data !== result"); | 
 |         shouldBe("result.toString()", "test_data.toString()"); | 
 |         shouldBeTrue("is(test_data.valueOf(), result.valueOf())"); | 
 |         debug(""); | 
 |         callback(); | 
 |     }); | 
 | } | 
 |  | 
 | function testUndefined(callback) | 
 | { | 
 |     testPrimitiveValue("undefined", callback); | 
 | } | 
 |  | 
 | function testNull(callback) | 
 | { | 
 |     testPrimitiveValue("null", callback); | 
 | } | 
 |  | 
 | function testBoolean(callback) | 
 | { | 
 |     debug("Testing boolean primitives"); | 
 |     debug(""); | 
 |     forEachWithCallback(testPrimitiveValue, ["true", "false"], callback); | 
 | } | 
 |  | 
 | function testBooleanObject(callback) | 
 | { | 
 |     debug("Testing Boolean objects"); | 
 |     debug(""); | 
 |     forEachWithCallback(testObjectWithValue, [ | 
 |         "new Boolean(true)", | 
 |         "new Boolean(false)" | 
 |     ], callback); | 
 | } | 
 |  | 
 | function testString(callback) | 
 | { | 
 |     debug("Testing string primitives"); | 
 |     debug(""); | 
 |     forEachWithCallback(testPrimitiveValue, [ | 
 |         "''", | 
 |         "'this is a sample string'", | 
 |         "'null(\\0)'" | 
 |     ], callback); | 
 | } | 
 |  | 
 | function testStringObject(callback) | 
 | { | 
 |     debug("Testing String objects"); | 
 |     debug(""); | 
 |     forEachWithCallback(testObjectWithValue, [ | 
 |         "new String()", | 
 |         "new String('this is a sample string')", | 
 |         "new String('null(\\0)')" | 
 |     ], callback); | 
 | } | 
 |  | 
 | function testNumber(callback) | 
 | { | 
 |     debug("Testing number primitives"); | 
 |     debug(""); | 
 |     forEachWithCallback(testPrimitiveValue, [ | 
 |         "NaN", | 
 |         "-Infinity", | 
 |         "-Number.MAX_VALUE", | 
 |         "-0xffffffff", | 
 |         "-0x80000000", | 
 |         "-0x7fffffff", | 
 |         "-1", | 
 |         "-Number.MIN_VALUE", | 
 |         "-0", | 
 |         "0", | 
 |         "1", | 
 |         "Number.MIN_VALUE", | 
 |         "0x7fffffff", | 
 |         "0x80000000", | 
 |         "0xffffffff", | 
 |         "Number.MAX_VALUE", | 
 |         "Infinity" | 
 |     ], callback); | 
 | } | 
 |  | 
 | function testNumberObject(callback) | 
 | { | 
 |     debug("Testing Number objects"); | 
 |     debug(""); | 
 |     forEachWithCallback(testObjectWithValue, [ | 
 |         "new Number(NaN)", | 
 |         "new Number(-Infinity)", | 
 |         "new Number(-Number.MAX_VALUE)", | 
 |         "new Number(-Number.MIN_VALUE)", | 
 |         "new Number(-0)", | 
 |         "new Number(0)", | 
 |         "new Number(Number.MIN_VALUE)", | 
 |         "new Number(Number.MAX_VALUE)", | 
 |         "new Number(Infinity)" | 
 |     ], callback); | 
 | } | 
 |  | 
 | function testDateObject(callback) | 
 | { | 
 |     debug("Testing Date objects"); | 
 |     debug(""); | 
 |     forEachWithCallback(testObjectWithValue, [ | 
 |         "new Date(-1e13)", | 
 |         "new Date(-1e12)", | 
 |         "new Date(-1e9)", | 
 |         "new Date(-1e6)", | 
 |         "new Date(-1e3)", | 
 |         "new Date(0)", | 
 |         "new Date(1e3)", | 
 |         "new Date(1e6)", | 
 |         "new Date(1e9)", | 
 |         "new Date(1e12)", | 
 |         "new Date(1e13)" | 
 |     ], callback); | 
 | } | 
 |  | 
 | function testRegExpObject(callback) | 
 | { | 
 |     debug("Testing RegExp objects"); | 
 |     debug(""); | 
 |     function testRegExp(string, callback) { | 
 |         debug("Testing RegExp: " + string); | 
 |         var value = eval("value = (" + string + ")"); | 
 |         test_data = value; | 
 |         testValue(test_data, function(result) { | 
 |             self.result = result; | 
 |             shouldBeTrue("test_data !== result"); | 
 |             shouldBeEqualToString("Object.prototype.toString.call(result)", "[object RegExp]"); | 
 |             shouldBe("result.toString()", "test_data.toString()"); | 
 |             debug(""); | 
 |             callback(); | 
 |         }); | 
 |     } | 
 |  | 
 |     forEachWithCallback(testRegExp, [ | 
 |         "new RegExp()", | 
 |         "/abc/", | 
 |         "/abc/g", | 
 |         "/abc/i", | 
 |         "/abc/gi", | 
 |         "/abc/m", | 
 |         "/abc/mg", | 
 |         "/abc/mi", | 
 |         "/abc/mgi" | 
 |     ], callback); | 
 | } | 
 |  | 
 | function testImageData(callback) | 
 | { | 
 |     debug("Testing ImageData"); | 
 |     evalAndLog("canvas = document.createElement('canvas')"); | 
 |     evalAndLog("canvas.width = 8"); | 
 |     evalAndLog("canvas.height = 8"); | 
 |     evalAndLog("test_data = canvas.getContext('2d').getImageData(0, 0, 8, 8)"); | 
 |  | 
 |     for (var i = 0; i < 256; ++i) { | 
 |         test_data.data[i] = i; | 
 |     } | 
 |  | 
 |     testValue(test_data, function(result) { | 
 |         self.result = result; | 
 |         shouldBeTrue("test_data !== result"); | 
 |         shouldBeEqualToString("Object.prototype.toString.call(result)", "[object ImageData]"); | 
 |         shouldBe("result.width", "test_data.width"); | 
 |         shouldBe("result.height", "test_data.height"); | 
 |         shouldBe("result.data.length", "test_data.data.length"); | 
 |         if (arrayCompare(test_data.data, result.data)) { | 
 |             testPassed("result data matches"); | 
 |         } else { | 
 |             testFailed("result data doesn't match"); | 
 |         } | 
 |         callback(); | 
 |     }); | 
 | } | 
 |  | 
 | function readBlobAsText(blob, callback) | 
 | { | 
 |     var reader = new FileReader(); | 
 |     reader.onload = function(e) { | 
 |         if (e.target.readyState === FileReader.DONE) { | 
 |             callback(e.target.result); | 
 |         } | 
 |     }; | 
 |     reader.onerror = function(e) { | 
 |         testFailed("Error reading blob as text: " + e); | 
 |         finishJSTest(); | 
 |     }; | 
 |     reader.readAsText(blob); | 
 | } | 
 |  | 
 | function checkBlobContents(blob, expected, callback) | 
 | { | 
 |     readBlobAsText(blob, function(text) { | 
 |         self.text = text; | 
 |         shouldBeEqualToString("text", expected); | 
 |         callback(); | 
 |     }); | 
 | } | 
 |  | 
 | function compareBlobs(blob1, blob2, callback) | 
 | { | 
 |     readBlobAsText(blob1, function(text1) { | 
 |         readBlobAsText(blob2, function(text2) { | 
 |             self.text1 = text1; | 
 |             self.text2 = text2; | 
 |             shouldBeEqualToString("text2", text1); | 
 |             callback(); | 
 |         }); | 
 |     }); | 
 | } | 
 |  | 
 | function testBlob(callback) | 
 | { | 
 |     debug("Testing Blob"); | 
 |  | 
 |     shouldBeTrue("FileReader != null"); | 
 |     evalAndLog("test_content = 'This is a test. This is only a test.'"); | 
 |     evalAndLog("test_data = new Blob([test_content])"); | 
 |     testValue(test_data, function(result) { | 
 |         self.result = result; | 
 |         shouldBeTrue("test_data !== result"); | 
 |         shouldBeEqualToString("Object.prototype.toString.call(result)", "[object Blob]"); | 
 |         shouldBe("result.size", "test_data.size"); | 
 |         shouldBe("result.type", "test_data.type"); | 
 |         checkBlobContents(result, test_content, callback); | 
 |     }); | 
 | } | 
 |  | 
 | function compareFiles(file1, file2, callback) | 
 | { | 
 |     self.file1 = file1; | 
 |     self.file2 = file2; | 
 |     shouldBeTrue("file1 !== file2"); | 
 |     shouldBeEqualToString("Object.prototype.toString.call(file1)", "[object File]"); | 
 |     shouldBeEqualToString("Object.prototype.toString.call(file2)", "[object File]"); | 
 |     debug("file1.size: " + file1.size); | 
 |     shouldBe("file1.size", "file2.size"); | 
 |     debug("file1.type: " + file1.type); | 
 |     shouldBe("file1.type", "file2.type"); | 
 |     debug("file1.name: " + file1.name); | 
 |     shouldBe("file1.name", "file2.name"); | 
 |     shouldBe("String(file1.lastModifiedDate)", "String(file2.lastModifiedDate)"); | 
 |     if (callback) { | 
 |         compareBlobs(file1, file2, callback); | 
 |     } | 
 | } | 
 |  | 
 | function testFile(callback) | 
 | { | 
 |     debug("Testing File"); | 
 |  | 
 |     evalAndLog("test_content = fileInput.files[0]"); | 
 |  | 
 |     self.test_data = test_content; | 
 |     testValue(test_data, function(result) { | 
 |         self.result = result; | 
 |         compareFiles(result, test_data, callback); | 
 |     }); | 
 | } | 
 |  | 
 |  | 
 | function testFileList(callback) | 
 | { | 
 |     debug("Testing FileList"); | 
 |  | 
 |     evalAndLog("test_content = fileInput.files"); | 
 |  | 
 |     self.test_data = test_content; | 
 |     testValue(test_data, function(result) { | 
 |         self.result = result; | 
 |         shouldBeTrue("test_data !== result"); | 
 |         shouldBeEqualToString("Object.prototype.toString.call(result)", "[object FileList]"); | 
 |         shouldBe("result.length", "test_data.length"); | 
 |         i = 0; | 
 |         function doNext() { | 
 |             if (i >= test_data.length) { | 
 |                 callback(); | 
 |             } else { | 
 |                 debug("comparing file[" + i + "]"); | 
 |                 compareFiles(result[i], test_data[i++], doNext); | 
 |             } | 
 |         } | 
 |         doNext(); | 
 |     }); | 
 | } | 
 |  | 
 | function testArray(callback) { | 
 |     debug("Testing Array"); | 
 |     evalAndLog("test_data = []"); | 
 |     evalAndLog("test_data[0] = 'foo'"); | 
 |     evalAndLog("test_data[1] = 'bar'"); | 
 |     evalAndLog("test_data[10] = true"); | 
 |     evalAndLog("test_data[11] = false"); | 
 |     evalAndLog("test_data[20] = 123"); | 
 |     evalAndLog("test_data[21] = 456"); | 
 |     evalAndLog("test_data[30] = null"); | 
 |  | 
 |     testValue(test_data, function(result) { | 
 |         self.result = result; | 
 |         shouldBeTrue("test_data !== result"); | 
 |         shouldBeTrue("test_data.length === result.length"); | 
 |         Object.keys(test_data).forEach( | 
 |             function(key) { | 
 |                 shouldBe("test_data[" + key + "]", "result[" + key + "]"); | 
 |             }); | 
 |         callback(); | 
 |     }); | 
 | } | 
 |  | 
 | function testObject(callback) { | 
 |     debug("Testing Object"); | 
 |     evalAndLog("test_data = []"); | 
 |     evalAndLog("test_data[0] = 'foo'"); | 
 |     evalAndLog("test_data[1] = 'bar'"); | 
 |     evalAndLog("test_data['a'] = true"); | 
 |     evalAndLog("test_data['b'] = false"); | 
 |     evalAndLog("test_data['foo'] = 123"); | 
 |     evalAndLog("test_data['bar'] = 456"); | 
 |     evalAndLog("test_data[''] = null"); | 
 |  | 
 |     testValue(test_data, function(result) { | 
 |         self.result = result; | 
 |         shouldBeTrue("test_data !== result"); | 
 |         shouldBeTrue("arrayCompare(Object.keys(result).sort(), Object.keys(test_data).sort())"); | 
 |         Object.keys(test_data).forEach( | 
 |             function(key) { | 
 |                 shouldBe("test_data[" + JSON.stringify(key) + "]", "result[" + JSON.stringify(key) + "]"); | 
 |             }); | 
 |         callback(); | 
 |     }); | 
 | } | 
 |  | 
 | function testTypedArray(callback) { | 
 |     debug("Testing TypedArray"); | 
 |  | 
 |     function testTypedArrayValue(string, callback) { | 
 |         evalAndLog("value = " + string); | 
 |         test_data = value; | 
 |         testValue(test_data, function(result) { | 
 |             self.result = result; | 
 |             shouldBeTrue("test_data !== result"); | 
 |             shouldBe("Object.prototype.toString.call(result)", "Object.prototype.toString.call(test_data)"); | 
 |             shouldBeTrue("test_data.length === result.length"); | 
 |             for (i = 0; i < test_data.length; ++i) { | 
 |                 shouldBeTrue("is(test_data[" + i + "], result[" + i + "])"); | 
 |             } | 
 |             debug(""); | 
 |             callback(); | 
 |         }); | 
 |     } | 
 |  | 
 |     forEachWithCallback(testTypedArrayValue, [ | 
 |         "new Uint8Array([])", | 
 |         "new Uint8Array([0, 1, 254, 255])", | 
 |         "new Uint16Array([0x0000, 0x0001, 0xFFFE, 0xFFFF])", | 
 |         "new Uint32Array([0x00000000, 0x00000001, 0xFFFFFFFE, 0xFFFFFFFF])", | 
 |         "new Int8Array([0, 1, 254, 255])", | 
 |         "new Int16Array([0x0000, 0x0001, 0xFFFE, 0xFFFF])", | 
 |         "new Int32Array([0x00000000, 0x00000001, 0xFFFFFFFE, 0xFFFFFFFF])", | 
 |         "new Uint8ClampedArray([0, 1, 254, 255])", | 
 |         "new Float32Array([-Infinity, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, Infinity, NaN])", | 
 |         "new Float64Array([-Infinity, -Number.MAX_VALUE, -Number.MIN_VALUE, 0, Number.MIN_VALUE, Number.MAX_VALUE, Infinity, NaN])" | 
 |     ], callback); | 
 | } | 
 |  | 
 | function testBadTypes() | 
 | { | 
 |     debug(""); | 
 |     debug("Test types that can't be cloned:"); | 
 |     debug(""); | 
 |  | 
 |     evalAndLog("transaction = db.transaction('storeName', 'readwrite')"); | 
 |     evalAndLog("store = transaction.objectStore('storeName')"); | 
 |     transaction.onerror = unexpectedErrorCallback; | 
 |     transaction.onabort = unexpectedAbortCallback; | 
 |     transaction.oncomplete = finishJSTest; | 
 |  | 
 |     debug(""); | 
 |     debug("Other JavaScript object types:"); | 
 |     evalAndExpectException("store.put(new Error, 'key')", "DOMException.DATA_CLONE_ERR"); | 
 |     evalAndExpectException("store.put(new Function, 'key')", "DOMException.DATA_CLONE_ERR"); | 
 |  | 
 |     debug(""); | 
 |     debug("Other host object types:"); | 
 |     evalAndExpectException("store.put(self, 'key')", "DOMException.DATA_CLONE_ERR"); | 
 |     evalAndExpectException("store.put(document, 'key')", "DOMException.DATA_CLONE_ERR"); | 
 |     evalAndExpectException("store.put(document.body, 'key')", "DOMException.DATA_CLONE_ERR"); | 
 | } | 
 |  | 
 | </script> | 
 | </body> | 
 | </html> |