From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- dom/indexedDB/ActorsChild.cpp | 3588 +++ dom/indexedDB/ActorsChild.h | 877 + dom/indexedDB/ActorsParent.cpp | 29795 +++++++++++++++++++ dom/indexedDB/ActorsParent.h | 73 + dom/indexedDB/FileInfo.cpp | 260 + dom/indexedDB/FileInfo.h | 103 + dom/indexedDB/FileManager.h | 150 + dom/indexedDB/FileSnapshot.cpp | 311 + dom/indexedDB/FileSnapshot.h | 210 + dom/indexedDB/IDBCursor.cpp | 1006 + dom/indexedDB/IDBCursor.h | 224 + dom/indexedDB/IDBDatabase.cpp | 1435 + dom/indexedDB/IDBDatabase.h | 347 + dom/indexedDB/IDBEvents.cpp | 98 + dom/indexedDB/IDBEvents.h | 134 + dom/indexedDB/IDBFactory.cpp | 934 + dom/indexedDB/IDBFactory.h | 258 + dom/indexedDB/IDBFileHandle.cpp | 199 + dom/indexedDB/IDBFileHandle.h | 149 + dom/indexedDB/IDBFileRequest.cpp | 157 + dom/indexedDB/IDBFileRequest.h | 94 + dom/indexedDB/IDBIndex.cpp | 679 + dom/indexedDB/IDBIndex.h | 234 + dom/indexedDB/IDBKeyRange.cpp | 499 + dom/indexedDB/IDBKeyRange.h | 218 + dom/indexedDB/IDBMutableFile.cpp | 283 + dom/indexedDB/IDBMutableFile.h | 139 + dom/indexedDB/IDBObjectStore.cpp | 2503 ++ dom/indexedDB/IDBObjectStore.h | 378 + dom/indexedDB/IDBRequest.cpp | 661 + dom/indexedDB/IDBRequest.h | 293 + dom/indexedDB/IDBTransaction.cpp | 1049 + dom/indexedDB/IDBTransaction.h | 344 + dom/indexedDB/IDBWrapperCache.cpp | 80 + dom/indexedDB/IDBWrapperCache.h | 55 + dom/indexedDB/IndexedDatabase.h | 92 + dom/indexedDB/IndexedDatabaseInlines.h | 106 + dom/indexedDB/IndexedDatabaseManager.cpp | 1461 + dom/indexedDB/IndexedDatabaseManager.h | 257 + dom/indexedDB/Key.cpp | 842 + dom/indexedDB/Key.h | 365 + dom/indexedDB/KeyPath.cpp | 539 + dom/indexedDB/KeyPath.h | 127 + dom/indexedDB/PBackgroundIDBCursor.ipdl | 100 + dom/indexedDB/PBackgroundIDBDatabase.ipdl | 87 + dom/indexedDB/PBackgroundIDBDatabaseFile.ipdl | 21 + dom/indexedDB/PBackgroundIDBDatabaseRequest.ipdl | 33 + dom/indexedDB/PBackgroundIDBFactory.ipdl | 66 + dom/indexedDB/PBackgroundIDBFactoryRequest.ipdl | 48 + dom/indexedDB/PBackgroundIDBRequest.ipdl | 169 + dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh | 306 + dom/indexedDB/PBackgroundIDBTransaction.ipdl | 42 + .../PBackgroundIDBVersionChangeTransaction.ipdl | 56 + dom/indexedDB/PBackgroundIndexedDBUtils.ipdl | 37 + dom/indexedDB/PIndexedDBPermissionRequest.ipdl | 27 + dom/indexedDB/PermissionRequestBase.cpp | 269 + dom/indexedDB/PermissionRequestBase.h | 81 + dom/indexedDB/ProfilerHelpers.h | 350 + dom/indexedDB/ReportInternalError.cpp | 36 + dom/indexedDB/ReportInternalError.h | 58 + dom/indexedDB/ScriptErrorHelper.cpp | 249 + dom/indexedDB/ScriptErrorHelper.h | 41 + dom/indexedDB/SerializationHelpers.h | 96 + dom/indexedDB/crashtests/726376-1.html | 7 + dom/indexedDB/crashtests/crashtests.list | 1 + dom/indexedDB/moz.build | 111 + dom/indexedDB/test/bfcache_iframe1.html | 27 + dom/indexedDB/test/bfcache_iframe2.html | 29 + dom/indexedDB/test/blob_worker_crash_iframe.html | 98 + dom/indexedDB/test/browser.ini | 21 + dom/indexedDB/test/browserHelpers.js | 56 + dom/indexedDB/test/browser_bug839193.js | 41 + dom/indexedDB/test/browser_forgetThisSite.js | 109 + dom/indexedDB/test/browser_forgetThisSiteAdd.html | 39 + dom/indexedDB/test/browser_forgetThisSiteGet.html | 36 + dom/indexedDB/test/browser_permissionsPrompt.html | 41 + .../test/browser_permissionsPromptAllow.js | 90 + .../test/browser_permissionsPromptDeny.js | 110 + .../test/browser_permissionsPromptWorker.js | 91 + .../test/browser_permissionsSharedWorker.html | 34 + .../test/browser_permissionsSharedWorker.js | 14 + dom/indexedDB/test/browser_permissionsWorker.html | 34 + dom/indexedDB/test/browser_permissionsWorker.js | 12 + .../test/browser_perwindow_privateBrowsing.js | 69 + dom/indexedDB/test/bug839193.js | 32 + dom/indexedDB/test/bug839193.xul | 17 + dom/indexedDB/test/chrome.ini | 5 + dom/indexedDB/test/chromeHelpers.js | 42 + .../error_events_abort_transactions_iframe.html | 241 + dom/indexedDB/test/event_propagation_iframe.html | 148 + .../test/exceptions_in_events_iframe.html | 182 + dom/indexedDB/test/extensions/bootstrap.js | 84 + .../test/extensions/indexedDB-test@mozilla.org.xpi | Bin 0 -> 5605 bytes dom/indexedDB/test/extensions/install.rdf | 31 + dom/indexedDB/test/extensions/moz.build | 16 + dom/indexedDB/test/file.js | 266 + dom/indexedDB/test/file_app_isolation.html | 88 + dom/indexedDB/test/file_app_isolation.js | 161 + dom/indexedDB/test/head.js | 158 + dom/indexedDB/test/helpers.js | 628 + dom/indexedDB/test/leaving_page_iframe.html | 43 + dom/indexedDB/test/mochitest-intl-api.ini | 10 + dom/indexedDB/test/mochitest.ini | 274 + dom/indexedDB/test/service_worker.js | 10 + dom/indexedDB/test/service_worker_client.html | 28 + dom/indexedDB/test/test_abort_deleted_index.html | 19 + .../test/test_abort_deleted_objectStore.html | 19 + dom/indexedDB/test/test_add_put.html | 18 + dom/indexedDB/test/test_add_twice_failure.html | 19 + dom/indexedDB/test/test_advance.html | 18 + dom/indexedDB/test/test_app_isolation_inproc.html | 21 + dom/indexedDB/test/test_app_isolation_oop.html | 21 + dom/indexedDB/test/test_autoIncrement.html | 19 + dom/indexedDB/test/test_autoIncrement_indexes.html | 19 + dom/indexedDB/test/test_bfcache.html | 67 + dom/indexedDB/test/test_blob_archive.html | 127 + dom/indexedDB/test/test_blob_file_backed.html | 18 + dom/indexedDB/test/test_blob_simple.html | 281 + dom/indexedDB/test/test_blob_worker_crash.html | 61 + dom/indexedDB/test/test_blob_worker_xhr_post.html | 113 + .../test/test_blob_worker_xhr_post_multifile.html | 113 + dom/indexedDB/test/test_blob_worker_xhr_read.html | 114 + .../test/test_blob_worker_xhr_read_slice.html | 116 + dom/indexedDB/test/test_blocked_order.html | 18 + dom/indexedDB/test/test_bug847147.html | 57 + dom/indexedDB/test/test_bug937006.html | 30 + dom/indexedDB/test/test_clear.html | 19 + dom/indexedDB/test/test_complex_keyPaths.html | 18 + dom/indexedDB/test/test_count.html | 18 + dom/indexedDB/test/test_create_index.html | 18 + .../test/test_create_index_with_integer_keys.html | 18 + .../test/test_create_locale_aware_index.html | 18 + dom/indexedDB/test/test_create_objectStore.html | 19 + dom/indexedDB/test/test_cursor_mutation.html | 19 + .../test/test_cursor_update_updates_indexes.html | 18 + dom/indexedDB/test/test_cursors.html | 19 + dom/indexedDB/test/test_database_onclose.html | 19 + dom/indexedDB/test/test_deleteDatabase.html | 19 + .../test/test_deleteDatabase_interactions.html | 19 + .../test/test_deleteDatabase_onblocked.html | 19 + ...leteDatabase_onblocked_duringVersionChange.html | 19 + .../test/test_error_events_abort_transactions.html | 30 + dom/indexedDB/test/test_event_propagation.html | 30 + dom/indexedDB/test/test_event_source.html | 18 + dom/indexedDB/test/test_exceptions_in_events.html | 30 + dom/indexedDB/test/test_file_array.html | 87 + .../test/test_file_cross_database_copying.html | 108 + dom/indexedDB/test/test_file_delete.html | 137 + dom/indexedDB/test/test_file_os_delete.html | 109 + dom/indexedDB/test/test_file_put_deleted.html | 156 + dom/indexedDB/test/test_file_put_get_object.html | 90 + dom/indexedDB/test/test_file_put_get_values.html | 104 + dom/indexedDB/test/test_file_replace.html | 70 + .../test/test_file_resurrection_delete.html | 133 + .../test_file_resurrection_transaction_abort.html | 92 + dom/indexedDB/test/test_file_sharing.html | 103 + .../test/test_file_transaction_abort.html | 77 + .../test/test_filehandle_append_read_data.html | 20 + dom/indexedDB/test/test_filehandle_compat.html | 52 + .../test/test_filehandle_disabled_pref.html | 204 + dom/indexedDB/test/test_filehandle_getFile.html | 53 + dom/indexedDB/test/test_filehandle_iteration.html | 77 + dom/indexedDB/test/test_filehandle_lifetimes.html | 57 + .../test/test_filehandle_lifetimes_nested.html | 69 + dom/indexedDB/test/test_filehandle_location.html | 104 + dom/indexedDB/test/test_filehandle_ordering.html | 62 + .../test/test_filehandle_overlapping.html | 73 + .../test/test_filehandle_progress_events.html | 79 + .../test/test_filehandle_readonly_exceptions.html | 81 + .../test/test_filehandle_request_readyState.html | 69 + .../test/test_filehandle_serialization.html | 101 + .../test/test_filehandle_store_snapshot.html | 98 + .../test/test_filehandle_stream_tracking.html | 112 + ...test_filehandle_success_events_after_abort.html | 74 + dom/indexedDB/test/test_filehandle_truncate.html | 91 + dom/indexedDB/test/test_filehandle_workers.html | 151 + .../test/test_filehandle_write_read_data.html | 110 + dom/indexedDB/test/test_getAll.html | 19 + dom/indexedDB/test/test_getFileId.html | 32 + dom/indexedDB/test/test_globalObjects_chrome.xul | 43 + dom/indexedDB/test/test_globalObjects_content.html | 38 + dom/indexedDB/test/test_globalObjects_other.xul | 64 + dom/indexedDB/test/test_global_data.html | 18 + dom/indexedDB/test/test_index_empty_keyPath.html | 18 + dom/indexedDB/test/test_index_getAll.html | 19 + dom/indexedDB/test/test_index_getAllObjects.html | 19 + dom/indexedDB/test/test_index_object_cursors.html | 18 + dom/indexedDB/test/test_index_update_delete.html | 19 + dom/indexedDB/test/test_indexes.html | 18 + dom/indexedDB/test/test_indexes_bad_values.html | 18 + dom/indexedDB/test/test_indexes_funny_things.html | 18 + dom/indexedDB/test/test_invalid_cursor.html | 18 + dom/indexedDB/test/test_invalid_version.html | 19 + dom/indexedDB/test/test_invalidate.html | 18 + dom/indexedDB/test/test_key_requirements.html | 19 + dom/indexedDB/test/test_keys.html | 18 + dom/indexedDB/test/test_leaving_page.html | 49 + .../test/test_locale_aware_index_getAll.html | 19 + .../test_locale_aware_index_getAllObjects.html | 19 + dom/indexedDB/test/test_locale_aware_indexes.html | 18 + dom/indexedDB/test/test_lowDiskSpace.html | 19 + .../test/test_maximal_serialized_object_size.html | 18 + dom/indexedDB/test/test_message_manager_ipc.html | 343 + dom/indexedDB/test/test_multientry.html | 18 + dom/indexedDB/test/test_names_sorted.html | 18 + dom/indexedDB/test/test_objectCursors.html | 19 + .../test/test_objectStore_getAllKeys.html | 19 + ...tore_inline_autoincrement_key_added_on_put.html | 19 + .../test/test_objectStore_openKeyCursor.html | 19 + .../test/test_objectStore_remove_values.html | 19 + dom/indexedDB/test/test_object_identity.html | 19 + dom/indexedDB/test/test_odd_result_order.html | 18 + dom/indexedDB/test/test_open_empty_db.html | 19 + dom/indexedDB/test/test_open_for_principal.html | 31 + dom/indexedDB/test/test_open_objectStore.html | 19 + dom/indexedDB/test/test_optionalArguments.html | 18 + .../test/test_overlapping_transactions.html | 19 + dom/indexedDB/test/test_persistenceType.html | 93 + dom/indexedDB/test/test_put_get_values.html | 19 + .../test/test_put_get_values_autoIncrement.html | 19 + dom/indexedDB/test/test_readonly_transactions.html | 19 + .../test/test_readwriteflush_disabled.html | 19 + dom/indexedDB/test/test_remove_index.html | 19 + dom/indexedDB/test/test_remove_objectStore.html | 19 + dom/indexedDB/test/test_rename_index.html | 19 + dom/indexedDB/test/test_rename_index_errors.html | 19 + dom/indexedDB/test/test_rename_objectStore.html | 19 + .../test/test_rename_objectStore_errors.html | 19 + dom/indexedDB/test/test_request_readyState.html | 19 + dom/indexedDB/test/test_sandbox.html | 101 + dom/indexedDB/test/test_serviceworker.html | 78 + dom/indexedDB/test/test_setVersion.html | 19 + dom/indexedDB/test/test_setVersion_abort.html | 19 + dom/indexedDB/test/test_setVersion_events.html | 19 + dom/indexedDB/test/test_setVersion_exclusion.html | 19 + dom/indexedDB/test/test_setVersion_throw.html | 19 + .../test/test_storage_manager_estimate.html | 19 + .../test/test_success_events_after_abort.html | 18 + dom/indexedDB/test/test_table_locks.html | 18 + dom/indexedDB/test/test_table_rollback.html | 19 + dom/indexedDB/test/test_third_party.html | 103 + dom/indexedDB/test/test_traffic_jam.html | 19 + dom/indexedDB/test/test_transaction_abort.html | 19 + .../test/test_transaction_abort_hang.html | 19 + .../test_transaction_duplicate_store_names.html | 16 + dom/indexedDB/test/test_transaction_error.html | 18 + dom/indexedDB/test/test_transaction_lifetimes.html | 19 + .../test/test_transaction_lifetimes_nested.html | 19 + dom/indexedDB/test/test_transaction_ordering.html | 19 + dom/indexedDB/test/test_unique_index_update.html | 19 + dom/indexedDB/test/test_view_put_get_values.html | 20 + dom/indexedDB/test/test_wasm_cursors.html | 20 + dom/indexedDB/test/test_wasm_getAll.html | 20 + .../test/test_wasm_index_getAllObjects.html | 20 + dom/indexedDB/test/test_wasm_indexes.html | 20 + dom/indexedDB/test/test_wasm_put_get_values.html | 20 + dom/indexedDB/test/test_writer_starvation.html | 19 + dom/indexedDB/test/third_party_iframe1.html | 28 + dom/indexedDB/test/third_party_iframe2.html | 34 + dom/indexedDB/test/unit/GlobalObjectsChild.js | 38 + dom/indexedDB/test/unit/GlobalObjectsComponent.js | 43 + .../test/unit/GlobalObjectsComponent.manifest | 2 + dom/indexedDB/test/unit/GlobalObjectsModule.jsm | 36 + dom/indexedDB/test/unit/GlobalObjectsSandbox.js | 22 + dom/indexedDB/test/unit/bug1056939_profile.zip | Bin 0 -> 8029 bytes .../test/unit/defaultStorageUpgrade_profile.zip | Bin 0 -> 71933 bytes dom/indexedDB/test/unit/getUsage_profile.zip | Bin 0 -> 24698 bytes .../test/unit/idbSubdirUpgrade1_profile.zip | Bin 0 -> 5530 bytes .../test/unit/idbSubdirUpgrade2_profile.zip | Bin 0 -> 5809 bytes .../test/unit/metadata2Restore_profile.zip | Bin 0 -> 56162 bytes .../test/unit/metadataRestore_profile.zip | Bin 0 -> 34962 bytes .../test/unit/mutableFileUpgrade_profile.zip | Bin 0 -> 9051 bytes dom/indexedDB/test/unit/oldDirectories_profile.zip | Bin 0 -> 9241 bytes .../test/unit/schema18upgrade_profile.zip | Bin 0 -> 19089 bytes .../test/unit/schema21upgrade_profile.zip | Bin 0 -> 13292 bytes .../test/unit/schema23upgrade_profile.zip | Bin 0 -> 5735 bytes dom/indexedDB/test/unit/snappyUpgrade_profile.zip | Bin 0 -> 4360 bytes .../test/unit/storagePersistentUpgrade_profile.zip | Bin 0 -> 6469 bytes .../test/unit/test_abort_deleted_index.js | 78 + .../test/unit/test_abort_deleted_objectStore.js | 74 + dom/indexedDB/test/unit/test_add_put.js | 165 + dom/indexedDB/test/unit/test_add_twice_failure.js | 43 + dom/indexedDB/test/unit/test_advance.js | 192 + dom/indexedDB/test/unit/test_autoIncrement.js | 400 + .../test/unit/test_autoIncrement_indexes.js | 56 + dom/indexedDB/test/unit/test_blob_file_backed.js | 78 + dom/indexedDB/test/unit/test_blocked_order.js | 179 + dom/indexedDB/test/unit/test_bug1056939.js | 73 + .../test/unit/test_cleanup_transaction.js | 155 + dom/indexedDB/test/unit/test_clear.js | 97 + dom/indexedDB/test/unit/test_complex_keyPaths.js | 266 + dom/indexedDB/test/unit/test_count.js | 354 + dom/indexedDB/test/unit/test_create_index.js | 121 + .../unit/test_create_index_with_integer_keys.js | 66 + .../test/unit/test_create_locale_aware_index.js | 123 + dom/indexedDB/test/unit/test_create_objectStore.js | 134 + dom/indexedDB/test/unit/test_cursor_cycle.js | 41 + dom/indexedDB/test/unit/test_cursor_mutation.js | 118 + .../unit/test_cursor_update_updates_indexes.js | 99 + dom/indexedDB/test/unit/test_cursors.js | 383 + .../unit/test_database_close_without_onclose.js | 49 + dom/indexedDB/test/unit/test_database_onclose.js | 245 + .../test/unit/test_defaultStorageUpgrade.js | 160 + dom/indexedDB/test/unit/test_deleteDatabase.js | 106 + .../test/unit/test_deleteDatabase_interactions.js | 62 + .../test/unit/test_deleteDatabase_onblocked.js | 83 + ...deleteDatabase_onblocked_duringVersionChange.js | 84 + dom/indexedDB/test/unit/test_event_source.js | 36 + dom/indexedDB/test/unit/test_file_copy_failure.js | 75 + .../test/unit/test_filehandle_append_read_data.js | 98 + dom/indexedDB/test/unit/test_getAll.js | 195 + dom/indexedDB/test/unit/test_getUsage.js | 128 + dom/indexedDB/test/unit/test_globalObjects_ipc.js | 19 + .../test/unit/test_globalObjects_other.js | 60 + dom/indexedDB/test/unit/test_globalObjects_xpc.js | 26 + dom/indexedDB/test/unit/test_global_data.js | 57 + dom/indexedDB/test/unit/test_idbSubdirUpgrade.js | 68 + dom/indexedDB/test/unit/test_idle_maintenance.js | 174 + .../test/unit/test_index_empty_keyPath.js | 83 + dom/indexedDB/test/unit/test_index_getAll.js | 191 + .../test/unit/test_index_getAllObjects.js | 233 + .../test/unit/test_index_object_cursors.js | 147 + .../test/unit/test_index_update_delete.js | 171 + dom/indexedDB/test/unit/test_indexes.js | 1261 + dom/indexedDB/test/unit/test_indexes_bad_values.js | 130 + .../test/unit/test_indexes_funny_things.js | 168 + dom/indexedDB/test/unit/test_invalid_cursor.js | 64 + dom/indexedDB/test/unit/test_invalid_version.js | 50 + dom/indexedDB/test/unit/test_invalidate.js | 82 + dom/indexedDB/test/unit/test_key_requirements.js | 285 + dom/indexedDB/test/unit/test_keys.js | 269 + .../test/unit/test_locale_aware_index_getAll.js | 191 + .../unit/test_locale_aware_index_getAllObjects.js | 233 + .../test/unit/test_locale_aware_indexes.js | 1268 + dom/indexedDB/test/unit/test_lowDiskSpace.js | 754 + .../unit/test_maximal_serialized_object_size.js | 95 + dom/indexedDB/test/unit/test_metadata2Restore.js | 268 + dom/indexedDB/test/unit/test_metadataRestore.js | 109 + dom/indexedDB/test/unit/test_multientry.js | 218 + dom/indexedDB/test/unit/test_mutableFileUpgrade.js | 122 + dom/indexedDB/test/unit/test_names_sorted.js | 114 + dom/indexedDB/test/unit/test_objectCursors.js | 85 + .../test/unit/test_objectStore_getAllKeys.js | 123 + ...tStore_inline_autoincrement_key_added_on_put.js | 55 + .../test/unit/test_objectStore_openKeyCursor.js | 400 + .../test/unit/test_objectStore_remove_values.js | 92 + dom/indexedDB/test/unit/test_object_identity.js | 48 + dom/indexedDB/test/unit/test_odd_result_order.js | 76 + dom/indexedDB/test/unit/test_oldDirectories.js | 72 + dom/indexedDB/test/unit/test_open_empty_db.js | 46 + dom/indexedDB/test/unit/test_open_for_principal.js | 90 + dom/indexedDB/test/unit/test_open_objectStore.js | 39 + dom/indexedDB/test/unit/test_optionalArguments.js | 1711 ++ .../test/unit/test_overlapping_transactions.js | 92 + dom/indexedDB/test/unit/test_persistenceType.js | 86 + dom/indexedDB/test/unit/test_put_get_values.js | 55 + .../test/unit/test_put_get_values_autoIncrement.js | 54 + .../test/unit/test_quotaExceeded_recovery.js | 141 + .../test/unit/test_readonly_transactions.js | 174 + .../test/unit/test_readwriteflush_disabled.js | 72 + dom/indexedDB/test/unit/test_remove_index.js | 58 + dom/indexedDB/test/unit/test_remove_objectStore.js | 129 + dom/indexedDB/test/unit/test_rename_index.js | 193 + .../test/unit/test_rename_index_errors.js | 129 + dom/indexedDB/test/unit/test_rename_objectStore.js | 171 + .../test/unit/test_rename_objectStore_errors.js | 127 + dom/indexedDB/test/unit/test_request_readyState.js | 51 + dom/indexedDB/test/unit/test_sandbox.js | 78 + dom/indexedDB/test/unit/test_schema18upgrade.js | 336 + dom/indexedDB/test/unit/test_schema21upgrade.js | 336 + dom/indexedDB/test/unit/test_schema23upgrade.js | 66 + dom/indexedDB/test/unit/test_setVersion.js | 51 + dom/indexedDB/test/unit/test_setVersion_abort.js | 97 + dom/indexedDB/test/unit/test_setVersion_events.js | 165 + .../test/unit/test_setVersion_exclusion.js | 95 + dom/indexedDB/test/unit/test_setVersion_throw.js | 54 + dom/indexedDB/test/unit/test_snappyUpgrade.js | 44 + .../test/unit/test_storagePersistentUpgrade.js | 66 + .../test/unit/test_storage_manager_estimate.js | 63 + .../test/unit/test_success_events_after_abort.js | 60 + dom/indexedDB/test/unit/test_table_locks.js | 116 + dom/indexedDB/test/unit/test_table_rollback.js | 115 + dom/indexedDB/test/unit/test_temporary_storage.js | 258 + dom/indexedDB/test/unit/test_traffic_jam.js | 87 + dom/indexedDB/test/unit/test_transaction_abort.js | 384 + .../test/unit/test_transaction_abort_hang.js | 91 + .../unit/test_transaction_duplicate_store_names.js | 43 + dom/indexedDB/test/unit/test_transaction_error.js | 136 + .../test/unit/test_transaction_lifetimes.js | 91 + .../test/unit/test_transaction_lifetimes_nested.js | 52 + .../test/unit/test_transaction_ordering.js | 49 + .../test/unit/test_unique_index_update.js | 64 + .../test/unit/test_view_put_get_values.js | 102 + dom/indexedDB/test/unit/test_wasm_cursors.js | 67 + dom/indexedDB/test/unit/test_wasm_getAll.js | 136 + .../test/unit/test_wasm_index_getAllObjects.js | 111 + dom/indexedDB/test/unit/test_wasm_indexes.js | 80 + .../test/unit/test_wasm_put_get_values.js | 83 + dom/indexedDB/test/unit/test_wasm_recompile.js | 124 + dom/indexedDB/test/unit/test_writer_starvation.js | 104 + dom/indexedDB/test/unit/wasm_recompile_profile.zip | Bin 0 -> 5446 bytes dom/indexedDB/test/unit/xpcshell-child-process.ini | 19 + .../test/unit/xpcshell-head-child-process.js | 27 + .../test/unit/xpcshell-head-parent-process.js | 700 + .../test/unit/xpcshell-parent-process.ini | 72 + dom/indexedDB/test/unit/xpcshell-shared.ini | 96 + 406 files changed, 85077 insertions(+) create mode 100644 dom/indexedDB/ActorsChild.cpp create mode 100644 dom/indexedDB/ActorsChild.h create mode 100644 dom/indexedDB/ActorsParent.cpp create mode 100644 dom/indexedDB/ActorsParent.h create mode 100644 dom/indexedDB/FileInfo.cpp create mode 100644 dom/indexedDB/FileInfo.h create mode 100644 dom/indexedDB/FileManager.h create mode 100644 dom/indexedDB/FileSnapshot.cpp create mode 100644 dom/indexedDB/FileSnapshot.h create mode 100644 dom/indexedDB/IDBCursor.cpp create mode 100644 dom/indexedDB/IDBCursor.h create mode 100644 dom/indexedDB/IDBDatabase.cpp create mode 100644 dom/indexedDB/IDBDatabase.h create mode 100644 dom/indexedDB/IDBEvents.cpp create mode 100644 dom/indexedDB/IDBEvents.h create mode 100644 dom/indexedDB/IDBFactory.cpp create mode 100644 dom/indexedDB/IDBFactory.h create mode 100644 dom/indexedDB/IDBFileHandle.cpp create mode 100644 dom/indexedDB/IDBFileHandle.h create mode 100644 dom/indexedDB/IDBFileRequest.cpp create mode 100644 dom/indexedDB/IDBFileRequest.h create mode 100644 dom/indexedDB/IDBIndex.cpp create mode 100644 dom/indexedDB/IDBIndex.h create mode 100644 dom/indexedDB/IDBKeyRange.cpp create mode 100644 dom/indexedDB/IDBKeyRange.h create mode 100644 dom/indexedDB/IDBMutableFile.cpp create mode 100644 dom/indexedDB/IDBMutableFile.h create mode 100644 dom/indexedDB/IDBObjectStore.cpp create mode 100644 dom/indexedDB/IDBObjectStore.h create mode 100644 dom/indexedDB/IDBRequest.cpp create mode 100644 dom/indexedDB/IDBRequest.h create mode 100644 dom/indexedDB/IDBTransaction.cpp create mode 100644 dom/indexedDB/IDBTransaction.h create mode 100644 dom/indexedDB/IDBWrapperCache.cpp create mode 100644 dom/indexedDB/IDBWrapperCache.h create mode 100644 dom/indexedDB/IndexedDatabase.h create mode 100644 dom/indexedDB/IndexedDatabaseInlines.h create mode 100644 dom/indexedDB/IndexedDatabaseManager.cpp create mode 100644 dom/indexedDB/IndexedDatabaseManager.h create mode 100644 dom/indexedDB/Key.cpp create mode 100644 dom/indexedDB/Key.h create mode 100644 dom/indexedDB/KeyPath.cpp create mode 100644 dom/indexedDB/KeyPath.h create mode 100644 dom/indexedDB/PBackgroundIDBCursor.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBDatabase.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBDatabaseFile.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBDatabaseRequest.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBFactory.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBFactoryRequest.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBRequest.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh create mode 100644 dom/indexedDB/PBackgroundIDBTransaction.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl create mode 100644 dom/indexedDB/PBackgroundIndexedDBUtils.ipdl create mode 100644 dom/indexedDB/PIndexedDBPermissionRequest.ipdl create mode 100644 dom/indexedDB/PermissionRequestBase.cpp create mode 100644 dom/indexedDB/PermissionRequestBase.h create mode 100644 dom/indexedDB/ProfilerHelpers.h create mode 100644 dom/indexedDB/ReportInternalError.cpp create mode 100644 dom/indexedDB/ReportInternalError.h create mode 100644 dom/indexedDB/ScriptErrorHelper.cpp create mode 100644 dom/indexedDB/ScriptErrorHelper.h create mode 100644 dom/indexedDB/SerializationHelpers.h create mode 100644 dom/indexedDB/crashtests/726376-1.html create mode 100644 dom/indexedDB/crashtests/crashtests.list create mode 100644 dom/indexedDB/moz.build create mode 100644 dom/indexedDB/test/bfcache_iframe1.html create mode 100644 dom/indexedDB/test/bfcache_iframe2.html create mode 100644 dom/indexedDB/test/blob_worker_crash_iframe.html create mode 100644 dom/indexedDB/test/browser.ini create mode 100644 dom/indexedDB/test/browserHelpers.js create mode 100644 dom/indexedDB/test/browser_bug839193.js create mode 100644 dom/indexedDB/test/browser_forgetThisSite.js create mode 100644 dom/indexedDB/test/browser_forgetThisSiteAdd.html create mode 100644 dom/indexedDB/test/browser_forgetThisSiteGet.html create mode 100644 dom/indexedDB/test/browser_permissionsPrompt.html create mode 100644 dom/indexedDB/test/browser_permissionsPromptAllow.js create mode 100644 dom/indexedDB/test/browser_permissionsPromptDeny.js create mode 100644 dom/indexedDB/test/browser_permissionsPromptWorker.js create mode 100644 dom/indexedDB/test/browser_permissionsSharedWorker.html create mode 100644 dom/indexedDB/test/browser_permissionsSharedWorker.js create mode 100644 dom/indexedDB/test/browser_permissionsWorker.html create mode 100644 dom/indexedDB/test/browser_permissionsWorker.js create mode 100644 dom/indexedDB/test/browser_perwindow_privateBrowsing.js create mode 100644 dom/indexedDB/test/bug839193.js create mode 100644 dom/indexedDB/test/bug839193.xul create mode 100644 dom/indexedDB/test/chrome.ini create mode 100644 dom/indexedDB/test/chromeHelpers.js create mode 100644 dom/indexedDB/test/error_events_abort_transactions_iframe.html create mode 100644 dom/indexedDB/test/event_propagation_iframe.html create mode 100644 dom/indexedDB/test/exceptions_in_events_iframe.html create mode 100644 dom/indexedDB/test/extensions/bootstrap.js create mode 100644 dom/indexedDB/test/extensions/indexedDB-test@mozilla.org.xpi create mode 100644 dom/indexedDB/test/extensions/install.rdf create mode 100644 dom/indexedDB/test/extensions/moz.build create mode 100644 dom/indexedDB/test/file.js create mode 100644 dom/indexedDB/test/file_app_isolation.html create mode 100644 dom/indexedDB/test/file_app_isolation.js create mode 100644 dom/indexedDB/test/head.js create mode 100644 dom/indexedDB/test/helpers.js create mode 100644 dom/indexedDB/test/leaving_page_iframe.html create mode 100644 dom/indexedDB/test/mochitest-intl-api.ini create mode 100644 dom/indexedDB/test/mochitest.ini create mode 100644 dom/indexedDB/test/service_worker.js create mode 100644 dom/indexedDB/test/service_worker_client.html create mode 100644 dom/indexedDB/test/test_abort_deleted_index.html create mode 100644 dom/indexedDB/test/test_abort_deleted_objectStore.html create mode 100644 dom/indexedDB/test/test_add_put.html create mode 100644 dom/indexedDB/test/test_add_twice_failure.html create mode 100644 dom/indexedDB/test/test_advance.html create mode 100644 dom/indexedDB/test/test_app_isolation_inproc.html create mode 100644 dom/indexedDB/test/test_app_isolation_oop.html create mode 100644 dom/indexedDB/test/test_autoIncrement.html create mode 100644 dom/indexedDB/test/test_autoIncrement_indexes.html create mode 100644 dom/indexedDB/test/test_bfcache.html create mode 100644 dom/indexedDB/test/test_blob_archive.html create mode 100644 dom/indexedDB/test/test_blob_file_backed.html create mode 100644 dom/indexedDB/test/test_blob_simple.html create mode 100644 dom/indexedDB/test/test_blob_worker_crash.html create mode 100644 dom/indexedDB/test/test_blob_worker_xhr_post.html create mode 100644 dom/indexedDB/test/test_blob_worker_xhr_post_multifile.html create mode 100644 dom/indexedDB/test/test_blob_worker_xhr_read.html create mode 100644 dom/indexedDB/test/test_blob_worker_xhr_read_slice.html create mode 100644 dom/indexedDB/test/test_blocked_order.html create mode 100644 dom/indexedDB/test/test_bug847147.html create mode 100644 dom/indexedDB/test/test_bug937006.html create mode 100644 dom/indexedDB/test/test_clear.html create mode 100644 dom/indexedDB/test/test_complex_keyPaths.html create mode 100644 dom/indexedDB/test/test_count.html create mode 100644 dom/indexedDB/test/test_create_index.html create mode 100644 dom/indexedDB/test/test_create_index_with_integer_keys.html create mode 100644 dom/indexedDB/test/test_create_locale_aware_index.html create mode 100644 dom/indexedDB/test/test_create_objectStore.html create mode 100644 dom/indexedDB/test/test_cursor_mutation.html create mode 100644 dom/indexedDB/test/test_cursor_update_updates_indexes.html create mode 100644 dom/indexedDB/test/test_cursors.html create mode 100644 dom/indexedDB/test/test_database_onclose.html create mode 100644 dom/indexedDB/test/test_deleteDatabase.html create mode 100644 dom/indexedDB/test/test_deleteDatabase_interactions.html create mode 100644 dom/indexedDB/test/test_deleteDatabase_onblocked.html create mode 100644 dom/indexedDB/test/test_deleteDatabase_onblocked_duringVersionChange.html create mode 100644 dom/indexedDB/test/test_error_events_abort_transactions.html create mode 100644 dom/indexedDB/test/test_event_propagation.html create mode 100644 dom/indexedDB/test/test_event_source.html create mode 100644 dom/indexedDB/test/test_exceptions_in_events.html create mode 100644 dom/indexedDB/test/test_file_array.html create mode 100644 dom/indexedDB/test/test_file_cross_database_copying.html create mode 100644 dom/indexedDB/test/test_file_delete.html create mode 100644 dom/indexedDB/test/test_file_os_delete.html create mode 100644 dom/indexedDB/test/test_file_put_deleted.html create mode 100644 dom/indexedDB/test/test_file_put_get_object.html create mode 100644 dom/indexedDB/test/test_file_put_get_values.html create mode 100644 dom/indexedDB/test/test_file_replace.html create mode 100644 dom/indexedDB/test/test_file_resurrection_delete.html create mode 100644 dom/indexedDB/test/test_file_resurrection_transaction_abort.html create mode 100644 dom/indexedDB/test/test_file_sharing.html create mode 100644 dom/indexedDB/test/test_file_transaction_abort.html create mode 100644 dom/indexedDB/test/test_filehandle_append_read_data.html create mode 100644 dom/indexedDB/test/test_filehandle_compat.html create mode 100644 dom/indexedDB/test/test_filehandle_disabled_pref.html create mode 100644 dom/indexedDB/test/test_filehandle_getFile.html create mode 100644 dom/indexedDB/test/test_filehandle_iteration.html create mode 100644 dom/indexedDB/test/test_filehandle_lifetimes.html create mode 100644 dom/indexedDB/test/test_filehandle_lifetimes_nested.html create mode 100644 dom/indexedDB/test/test_filehandle_location.html create mode 100644 dom/indexedDB/test/test_filehandle_ordering.html create mode 100644 dom/indexedDB/test/test_filehandle_overlapping.html create mode 100644 dom/indexedDB/test/test_filehandle_progress_events.html create mode 100644 dom/indexedDB/test/test_filehandle_readonly_exceptions.html create mode 100644 dom/indexedDB/test/test_filehandle_request_readyState.html create mode 100644 dom/indexedDB/test/test_filehandle_serialization.html create mode 100644 dom/indexedDB/test/test_filehandle_store_snapshot.html create mode 100644 dom/indexedDB/test/test_filehandle_stream_tracking.html create mode 100644 dom/indexedDB/test/test_filehandle_success_events_after_abort.html create mode 100644 dom/indexedDB/test/test_filehandle_truncate.html create mode 100644 dom/indexedDB/test/test_filehandle_workers.html create mode 100644 dom/indexedDB/test/test_filehandle_write_read_data.html create mode 100644 dom/indexedDB/test/test_getAll.html create mode 100644 dom/indexedDB/test/test_getFileId.html create mode 100644 dom/indexedDB/test/test_globalObjects_chrome.xul create mode 100644 dom/indexedDB/test/test_globalObjects_content.html create mode 100644 dom/indexedDB/test/test_globalObjects_other.xul create mode 100644 dom/indexedDB/test/test_global_data.html create mode 100644 dom/indexedDB/test/test_index_empty_keyPath.html create mode 100644 dom/indexedDB/test/test_index_getAll.html create mode 100644 dom/indexedDB/test/test_index_getAllObjects.html create mode 100644 dom/indexedDB/test/test_index_object_cursors.html create mode 100644 dom/indexedDB/test/test_index_update_delete.html create mode 100644 dom/indexedDB/test/test_indexes.html create mode 100644 dom/indexedDB/test/test_indexes_bad_values.html create mode 100644 dom/indexedDB/test/test_indexes_funny_things.html create mode 100644 dom/indexedDB/test/test_invalid_cursor.html create mode 100644 dom/indexedDB/test/test_invalid_version.html create mode 100644 dom/indexedDB/test/test_invalidate.html create mode 100644 dom/indexedDB/test/test_key_requirements.html create mode 100644 dom/indexedDB/test/test_keys.html create mode 100644 dom/indexedDB/test/test_leaving_page.html create mode 100644 dom/indexedDB/test/test_locale_aware_index_getAll.html create mode 100644 dom/indexedDB/test/test_locale_aware_index_getAllObjects.html create mode 100644 dom/indexedDB/test/test_locale_aware_indexes.html create mode 100644 dom/indexedDB/test/test_lowDiskSpace.html create mode 100644 dom/indexedDB/test/test_maximal_serialized_object_size.html create mode 100644 dom/indexedDB/test/test_message_manager_ipc.html create mode 100644 dom/indexedDB/test/test_multientry.html create mode 100644 dom/indexedDB/test/test_names_sorted.html create mode 100644 dom/indexedDB/test/test_objectCursors.html create mode 100644 dom/indexedDB/test/test_objectStore_getAllKeys.html create mode 100644 dom/indexedDB/test/test_objectStore_inline_autoincrement_key_added_on_put.html create mode 100644 dom/indexedDB/test/test_objectStore_openKeyCursor.html create mode 100644 dom/indexedDB/test/test_objectStore_remove_values.html create mode 100644 dom/indexedDB/test/test_object_identity.html create mode 100644 dom/indexedDB/test/test_odd_result_order.html create mode 100644 dom/indexedDB/test/test_open_empty_db.html create mode 100644 dom/indexedDB/test/test_open_for_principal.html create mode 100644 dom/indexedDB/test/test_open_objectStore.html create mode 100644 dom/indexedDB/test/test_optionalArguments.html create mode 100644 dom/indexedDB/test/test_overlapping_transactions.html create mode 100644 dom/indexedDB/test/test_persistenceType.html create mode 100644 dom/indexedDB/test/test_put_get_values.html create mode 100644 dom/indexedDB/test/test_put_get_values_autoIncrement.html create mode 100644 dom/indexedDB/test/test_readonly_transactions.html create mode 100644 dom/indexedDB/test/test_readwriteflush_disabled.html create mode 100644 dom/indexedDB/test/test_remove_index.html create mode 100644 dom/indexedDB/test/test_remove_objectStore.html create mode 100644 dom/indexedDB/test/test_rename_index.html create mode 100644 dom/indexedDB/test/test_rename_index_errors.html create mode 100644 dom/indexedDB/test/test_rename_objectStore.html create mode 100644 dom/indexedDB/test/test_rename_objectStore_errors.html create mode 100644 dom/indexedDB/test/test_request_readyState.html create mode 100644 dom/indexedDB/test/test_sandbox.html create mode 100644 dom/indexedDB/test/test_serviceworker.html create mode 100644 dom/indexedDB/test/test_setVersion.html create mode 100644 dom/indexedDB/test/test_setVersion_abort.html create mode 100644 dom/indexedDB/test/test_setVersion_events.html create mode 100644 dom/indexedDB/test/test_setVersion_exclusion.html create mode 100644 dom/indexedDB/test/test_setVersion_throw.html create mode 100644 dom/indexedDB/test/test_storage_manager_estimate.html create mode 100644 dom/indexedDB/test/test_success_events_after_abort.html create mode 100644 dom/indexedDB/test/test_table_locks.html create mode 100644 dom/indexedDB/test/test_table_rollback.html create mode 100644 dom/indexedDB/test/test_third_party.html create mode 100644 dom/indexedDB/test/test_traffic_jam.html create mode 100644 dom/indexedDB/test/test_transaction_abort.html create mode 100644 dom/indexedDB/test/test_transaction_abort_hang.html create mode 100644 dom/indexedDB/test/test_transaction_duplicate_store_names.html create mode 100644 dom/indexedDB/test/test_transaction_error.html create mode 100644 dom/indexedDB/test/test_transaction_lifetimes.html create mode 100644 dom/indexedDB/test/test_transaction_lifetimes_nested.html create mode 100644 dom/indexedDB/test/test_transaction_ordering.html create mode 100644 dom/indexedDB/test/test_unique_index_update.html create mode 100644 dom/indexedDB/test/test_view_put_get_values.html create mode 100644 dom/indexedDB/test/test_wasm_cursors.html create mode 100644 dom/indexedDB/test/test_wasm_getAll.html create mode 100644 dom/indexedDB/test/test_wasm_index_getAllObjects.html create mode 100644 dom/indexedDB/test/test_wasm_indexes.html create mode 100644 dom/indexedDB/test/test_wasm_put_get_values.html create mode 100644 dom/indexedDB/test/test_writer_starvation.html create mode 100644 dom/indexedDB/test/third_party_iframe1.html create mode 100644 dom/indexedDB/test/third_party_iframe2.html create mode 100644 dom/indexedDB/test/unit/GlobalObjectsChild.js create mode 100644 dom/indexedDB/test/unit/GlobalObjectsComponent.js create mode 100644 dom/indexedDB/test/unit/GlobalObjectsComponent.manifest create mode 100644 dom/indexedDB/test/unit/GlobalObjectsModule.jsm create mode 100644 dom/indexedDB/test/unit/GlobalObjectsSandbox.js create mode 100644 dom/indexedDB/test/unit/bug1056939_profile.zip create mode 100644 dom/indexedDB/test/unit/defaultStorageUpgrade_profile.zip create mode 100644 dom/indexedDB/test/unit/getUsage_profile.zip create mode 100644 dom/indexedDB/test/unit/idbSubdirUpgrade1_profile.zip create mode 100644 dom/indexedDB/test/unit/idbSubdirUpgrade2_profile.zip create mode 100644 dom/indexedDB/test/unit/metadata2Restore_profile.zip create mode 100644 dom/indexedDB/test/unit/metadataRestore_profile.zip create mode 100644 dom/indexedDB/test/unit/mutableFileUpgrade_profile.zip create mode 100644 dom/indexedDB/test/unit/oldDirectories_profile.zip create mode 100644 dom/indexedDB/test/unit/schema18upgrade_profile.zip create mode 100644 dom/indexedDB/test/unit/schema21upgrade_profile.zip create mode 100644 dom/indexedDB/test/unit/schema23upgrade_profile.zip create mode 100644 dom/indexedDB/test/unit/snappyUpgrade_profile.zip create mode 100644 dom/indexedDB/test/unit/storagePersistentUpgrade_profile.zip create mode 100644 dom/indexedDB/test/unit/test_abort_deleted_index.js create mode 100644 dom/indexedDB/test/unit/test_abort_deleted_objectStore.js create mode 100644 dom/indexedDB/test/unit/test_add_put.js create mode 100644 dom/indexedDB/test/unit/test_add_twice_failure.js create mode 100644 dom/indexedDB/test/unit/test_advance.js create mode 100644 dom/indexedDB/test/unit/test_autoIncrement.js create mode 100644 dom/indexedDB/test/unit/test_autoIncrement_indexes.js create mode 100644 dom/indexedDB/test/unit/test_blob_file_backed.js create mode 100644 dom/indexedDB/test/unit/test_blocked_order.js create mode 100644 dom/indexedDB/test/unit/test_bug1056939.js create mode 100644 dom/indexedDB/test/unit/test_cleanup_transaction.js create mode 100644 dom/indexedDB/test/unit/test_clear.js create mode 100644 dom/indexedDB/test/unit/test_complex_keyPaths.js create mode 100644 dom/indexedDB/test/unit/test_count.js create mode 100644 dom/indexedDB/test/unit/test_create_index.js create mode 100644 dom/indexedDB/test/unit/test_create_index_with_integer_keys.js create mode 100644 dom/indexedDB/test/unit/test_create_locale_aware_index.js create mode 100644 dom/indexedDB/test/unit/test_create_objectStore.js create mode 100644 dom/indexedDB/test/unit/test_cursor_cycle.js create mode 100644 dom/indexedDB/test/unit/test_cursor_mutation.js create mode 100644 dom/indexedDB/test/unit/test_cursor_update_updates_indexes.js create mode 100644 dom/indexedDB/test/unit/test_cursors.js create mode 100644 dom/indexedDB/test/unit/test_database_close_without_onclose.js create mode 100644 dom/indexedDB/test/unit/test_database_onclose.js create mode 100644 dom/indexedDB/test/unit/test_defaultStorageUpgrade.js create mode 100644 dom/indexedDB/test/unit/test_deleteDatabase.js create mode 100644 dom/indexedDB/test/unit/test_deleteDatabase_interactions.js create mode 100644 dom/indexedDB/test/unit/test_deleteDatabase_onblocked.js create mode 100644 dom/indexedDB/test/unit/test_deleteDatabase_onblocked_duringVersionChange.js create mode 100644 dom/indexedDB/test/unit/test_event_source.js create mode 100644 dom/indexedDB/test/unit/test_file_copy_failure.js create mode 100644 dom/indexedDB/test/unit/test_filehandle_append_read_data.js create mode 100644 dom/indexedDB/test/unit/test_getAll.js create mode 100644 dom/indexedDB/test/unit/test_getUsage.js create mode 100644 dom/indexedDB/test/unit/test_globalObjects_ipc.js create mode 100644 dom/indexedDB/test/unit/test_globalObjects_other.js create mode 100644 dom/indexedDB/test/unit/test_globalObjects_xpc.js create mode 100644 dom/indexedDB/test/unit/test_global_data.js create mode 100644 dom/indexedDB/test/unit/test_idbSubdirUpgrade.js create mode 100644 dom/indexedDB/test/unit/test_idle_maintenance.js create mode 100644 dom/indexedDB/test/unit/test_index_empty_keyPath.js create mode 100644 dom/indexedDB/test/unit/test_index_getAll.js create mode 100644 dom/indexedDB/test/unit/test_index_getAllObjects.js create mode 100644 dom/indexedDB/test/unit/test_index_object_cursors.js create mode 100644 dom/indexedDB/test/unit/test_index_update_delete.js create mode 100644 dom/indexedDB/test/unit/test_indexes.js create mode 100644 dom/indexedDB/test/unit/test_indexes_bad_values.js create mode 100644 dom/indexedDB/test/unit/test_indexes_funny_things.js create mode 100644 dom/indexedDB/test/unit/test_invalid_cursor.js create mode 100644 dom/indexedDB/test/unit/test_invalid_version.js create mode 100644 dom/indexedDB/test/unit/test_invalidate.js create mode 100644 dom/indexedDB/test/unit/test_key_requirements.js create mode 100644 dom/indexedDB/test/unit/test_keys.js create mode 100644 dom/indexedDB/test/unit/test_locale_aware_index_getAll.js create mode 100644 dom/indexedDB/test/unit/test_locale_aware_index_getAllObjects.js create mode 100644 dom/indexedDB/test/unit/test_locale_aware_indexes.js create mode 100644 dom/indexedDB/test/unit/test_lowDiskSpace.js create mode 100644 dom/indexedDB/test/unit/test_maximal_serialized_object_size.js create mode 100644 dom/indexedDB/test/unit/test_metadata2Restore.js create mode 100644 dom/indexedDB/test/unit/test_metadataRestore.js create mode 100644 dom/indexedDB/test/unit/test_multientry.js create mode 100644 dom/indexedDB/test/unit/test_mutableFileUpgrade.js create mode 100644 dom/indexedDB/test/unit/test_names_sorted.js create mode 100644 dom/indexedDB/test/unit/test_objectCursors.js create mode 100644 dom/indexedDB/test/unit/test_objectStore_getAllKeys.js create mode 100644 dom/indexedDB/test/unit/test_objectStore_inline_autoincrement_key_added_on_put.js create mode 100644 dom/indexedDB/test/unit/test_objectStore_openKeyCursor.js create mode 100644 dom/indexedDB/test/unit/test_objectStore_remove_values.js create mode 100644 dom/indexedDB/test/unit/test_object_identity.js create mode 100644 dom/indexedDB/test/unit/test_odd_result_order.js create mode 100644 dom/indexedDB/test/unit/test_oldDirectories.js create mode 100644 dom/indexedDB/test/unit/test_open_empty_db.js create mode 100644 dom/indexedDB/test/unit/test_open_for_principal.js create mode 100644 dom/indexedDB/test/unit/test_open_objectStore.js create mode 100644 dom/indexedDB/test/unit/test_optionalArguments.js create mode 100644 dom/indexedDB/test/unit/test_overlapping_transactions.js create mode 100644 dom/indexedDB/test/unit/test_persistenceType.js create mode 100644 dom/indexedDB/test/unit/test_put_get_values.js create mode 100644 dom/indexedDB/test/unit/test_put_get_values_autoIncrement.js create mode 100644 dom/indexedDB/test/unit/test_quotaExceeded_recovery.js create mode 100644 dom/indexedDB/test/unit/test_readonly_transactions.js create mode 100644 dom/indexedDB/test/unit/test_readwriteflush_disabled.js create mode 100644 dom/indexedDB/test/unit/test_remove_index.js create mode 100644 dom/indexedDB/test/unit/test_remove_objectStore.js create mode 100644 dom/indexedDB/test/unit/test_rename_index.js create mode 100644 dom/indexedDB/test/unit/test_rename_index_errors.js create mode 100644 dom/indexedDB/test/unit/test_rename_objectStore.js create mode 100644 dom/indexedDB/test/unit/test_rename_objectStore_errors.js create mode 100644 dom/indexedDB/test/unit/test_request_readyState.js create mode 100644 dom/indexedDB/test/unit/test_sandbox.js create mode 100644 dom/indexedDB/test/unit/test_schema18upgrade.js create mode 100644 dom/indexedDB/test/unit/test_schema21upgrade.js create mode 100644 dom/indexedDB/test/unit/test_schema23upgrade.js create mode 100644 dom/indexedDB/test/unit/test_setVersion.js create mode 100644 dom/indexedDB/test/unit/test_setVersion_abort.js create mode 100644 dom/indexedDB/test/unit/test_setVersion_events.js create mode 100644 dom/indexedDB/test/unit/test_setVersion_exclusion.js create mode 100644 dom/indexedDB/test/unit/test_setVersion_throw.js create mode 100644 dom/indexedDB/test/unit/test_snappyUpgrade.js create mode 100644 dom/indexedDB/test/unit/test_storagePersistentUpgrade.js create mode 100644 dom/indexedDB/test/unit/test_storage_manager_estimate.js create mode 100644 dom/indexedDB/test/unit/test_success_events_after_abort.js create mode 100644 dom/indexedDB/test/unit/test_table_locks.js create mode 100644 dom/indexedDB/test/unit/test_table_rollback.js create mode 100644 dom/indexedDB/test/unit/test_temporary_storage.js create mode 100644 dom/indexedDB/test/unit/test_traffic_jam.js create mode 100644 dom/indexedDB/test/unit/test_transaction_abort.js create mode 100644 dom/indexedDB/test/unit/test_transaction_abort_hang.js create mode 100644 dom/indexedDB/test/unit/test_transaction_duplicate_store_names.js create mode 100644 dom/indexedDB/test/unit/test_transaction_error.js create mode 100644 dom/indexedDB/test/unit/test_transaction_lifetimes.js create mode 100644 dom/indexedDB/test/unit/test_transaction_lifetimes_nested.js create mode 100644 dom/indexedDB/test/unit/test_transaction_ordering.js create mode 100644 dom/indexedDB/test/unit/test_unique_index_update.js create mode 100644 dom/indexedDB/test/unit/test_view_put_get_values.js create mode 100644 dom/indexedDB/test/unit/test_wasm_cursors.js create mode 100644 dom/indexedDB/test/unit/test_wasm_getAll.js create mode 100644 dom/indexedDB/test/unit/test_wasm_index_getAllObjects.js create mode 100644 dom/indexedDB/test/unit/test_wasm_indexes.js create mode 100644 dom/indexedDB/test/unit/test_wasm_put_get_values.js create mode 100644 dom/indexedDB/test/unit/test_wasm_recompile.js create mode 100644 dom/indexedDB/test/unit/test_writer_starvation.js create mode 100644 dom/indexedDB/test/unit/wasm_recompile_profile.zip create mode 100644 dom/indexedDB/test/unit/xpcshell-child-process.ini create mode 100644 dom/indexedDB/test/unit/xpcshell-head-child-process.js create mode 100644 dom/indexedDB/test/unit/xpcshell-head-parent-process.js create mode 100644 dom/indexedDB/test/unit/xpcshell-parent-process.ini create mode 100644 dom/indexedDB/test/unit/xpcshell-shared.ini (limited to 'dom/indexedDB') diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp new file mode 100644 index 000000000..3e8f97348 --- /dev/null +++ b/dom/indexedDB/ActorsChild.cpp @@ -0,0 +1,3588 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ActorsChild.h" + +#include "BackgroundChildImpl.h" +#include "IDBDatabase.h" +#include "IDBEvents.h" +#include "IDBFactory.h" +#include "IDBIndex.h" +#include "IDBMutableFile.h" +#include "IDBObjectStore.h" +#include "IDBMutableFile.h" +#include "IDBRequest.h" +#include "IDBTransaction.h" +#include "IndexedDatabase.h" +#include "IndexedDatabaseInlines.h" +#include "mozilla/BasicEvents.h" +#include "mozilla/Maybe.h" +#include "mozilla/TypeTraits.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/PermissionMessageUtils.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h" +#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsIBFCacheEntry.h" +#include "nsIDocument.h" +#include "nsIDOMEvent.h" +#include "nsIEventTarget.h" +#include "nsIFileStreams.h" +#include "nsNetCID.h" +#include "nsPIDOMWindow.h" +#include "nsThreadUtils.h" +#include "nsTraceRefcnt.h" +#include "PermissionRequestBase.h" +#include "ProfilerHelpers.h" +#include "ReportInternalError.h" +#include "WorkerPrivate.h" +#include "WorkerRunnable.h" + +#ifdef DEBUG +#include "IndexedDatabaseManager.h" +#endif + +#define GC_ON_IPC_MESSAGES 0 + +#if defined(DEBUG) || GC_ON_IPC_MESSAGES + +#include "js/GCAPI.h" +#include "nsJSEnvironment.h" + +#define BUILD_GC_ON_IPC_MESSAGES + +#endif // DEBUG || GC_ON_IPC_MESSAGES + +namespace mozilla { + +using ipc::PrincipalInfo; + +namespace dom { + +using namespace workers; + +namespace indexedDB { + +/******************************************************************************* + * ThreadLocal + ******************************************************************************/ + +ThreadLocal::ThreadLocal(const nsID& aBackgroundChildLoggingId) + : mLoggingInfo(aBackgroundChildLoggingId, 1, -1, 1) + , mCurrentTransaction(0) +#ifdef DEBUG + , mOwningThread(PR_GetCurrentThread()) +#endif +{ + MOZ_ASSERT(mOwningThread); + + MOZ_COUNT_CTOR(mozilla::dom::indexedDB::ThreadLocal); + + // NSID_LENGTH counts the null terminator, SetLength() does not. + mLoggingIdString.SetLength(NSID_LENGTH - 1); + + aBackgroundChildLoggingId.ToProvidedString( + *reinterpret_cast(mLoggingIdString.BeginWriting())); +} + +ThreadLocal::~ThreadLocal() +{ + MOZ_COUNT_DTOR(mozilla::dom::indexedDB::ThreadLocal); +} + +#ifdef DEBUG + +void +ThreadLocal::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread); +} + +#endif // DEBUG + +/******************************************************************************* + * Helpers + ******************************************************************************/ + +namespace { + +void +MaybeCollectGarbageOnIPCMessage() +{ +#ifdef BUILD_GC_ON_IPC_MESSAGES + static const bool kCollectGarbageOnIPCMessages = +#if GC_ON_IPC_MESSAGES + true; +#else + false; +#endif // GC_ON_IPC_MESSAGES + + if (!kCollectGarbageOnIPCMessages) { + return; + } + + static bool haveWarnedAboutGC = false; + static bool haveWarnedAboutNonMainThread = false; + + if (!haveWarnedAboutGC) { + haveWarnedAboutGC = true; + NS_WARNING("IndexedDB child actor GC debugging enabled!"); + } + + if (!NS_IsMainThread()) { + if (!haveWarnedAboutNonMainThread) { + haveWarnedAboutNonMainThread = true; + NS_WARNING("Don't know how to GC on a non-main thread yet."); + } + return; + } + + nsJSContext::GarbageCollectNow(JS::gcreason::DOM_IPC); + nsJSContext::CycleCollectNow(); +#endif // BUILD_GC_ON_IPC_MESSAGES +} + +class MOZ_STACK_CLASS AutoSetCurrentTransaction final +{ + typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl; + + IDBTransaction* const mTransaction; + IDBTransaction* mPreviousTransaction; + ThreadLocal* mThreadLocal; + +public: + explicit AutoSetCurrentTransaction(IDBTransaction* aTransaction) + : mTransaction(aTransaction) + , mPreviousTransaction(nullptr) + , mThreadLocal(nullptr) + { + if (aTransaction) { + BackgroundChildImpl::ThreadLocal* threadLocal = + BackgroundChildImpl::GetThreadLocalForCurrentThread(); + MOZ_ASSERT(threadLocal); + + // Hang onto this for resetting later. + mThreadLocal = threadLocal->mIndexedDBThreadLocal; + MOZ_ASSERT(mThreadLocal); + + // Save the current value. + mPreviousTransaction = mThreadLocal->GetCurrentTransaction(); + + // Set the new value. + mThreadLocal->SetCurrentTransaction(aTransaction); + } + } + + ~AutoSetCurrentTransaction() + { + MOZ_ASSERT_IF(mThreadLocal, mTransaction); + MOZ_ASSERT_IF(mThreadLocal, + mThreadLocal->GetCurrentTransaction() == mTransaction); + + if (mThreadLocal) { + // Reset old value. + mThreadLocal->SetCurrentTransaction(mPreviousTransaction); + } + } + + IDBTransaction* + Transaction() const + { + return mTransaction; + } +}; + +class MOZ_STACK_CLASS ResultHelper final + : public IDBRequest::ResultCallback +{ + IDBRequest* mRequest; + AutoSetCurrentTransaction mAutoTransaction; + + union + { + IDBDatabase* mDatabase; + IDBCursor* mCursor; + IDBMutableFile* mMutableFile; + StructuredCloneReadInfo* mStructuredClone; + const nsTArray* mStructuredCloneArray; + const Key* mKey; + const nsTArray* mKeyArray; + const JS::Value* mJSVal; + const JS::Handle* mJSValHandle; + } mResult; + + enum + { + ResultTypeDatabase, + ResultTypeCursor, + ResultTypeMutableFile, + ResultTypeStructuredClone, + ResultTypeStructuredCloneArray, + ResultTypeKey, + ResultTypeKeyArray, + ResultTypeJSVal, + ResultTypeJSValHandle, + } mResultType; + +public: + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBDatabase* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeDatabase) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aResult); + + mResult.mDatabase = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBCursor* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeCursor) + { + MOZ_ASSERT(aRequest); + + mResult.mCursor = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBMutableFile* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeMutableFile) + { + MOZ_ASSERT(aRequest); + + mResult.mMutableFile = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + StructuredCloneReadInfo* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeStructuredClone) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aResult); + + mResult.mStructuredClone = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + const nsTArray* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeStructuredCloneArray) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aResult); + + mResult.mStructuredCloneArray = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + const Key* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeKey) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aResult); + + mResult.mKey = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + const nsTArray* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeKeyArray) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aResult); + + mResult.mKeyArray = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + const JS::Value* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeJSVal) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(!aResult->isGCThing()); + + mResult.mJSVal = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + const JS::Handle* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeJSValHandle) + { + MOZ_ASSERT(aRequest); + + mResult.mJSValHandle = aResult; + } + + IDBRequest* + Request() const + { + return mRequest; + } + + IDBTransaction* + Transaction() const + { + return mAutoTransaction.Transaction(); + } + + virtual nsresult + GetResult(JSContext* aCx, JS::MutableHandle aResult) override + { + MOZ_ASSERT(aCx); + MOZ_ASSERT(mRequest); + + switch (mResultType) { + case ResultTypeDatabase: + return GetResult(aCx, mResult.mDatabase, aResult); + + case ResultTypeCursor: + return GetResult(aCx, mResult.mCursor, aResult); + + case ResultTypeMutableFile: + return GetResult(aCx, mResult.mMutableFile, aResult); + + case ResultTypeStructuredClone: + return GetResult(aCx, mResult.mStructuredClone, aResult); + + case ResultTypeStructuredCloneArray: + return GetResult(aCx, mResult.mStructuredCloneArray, aResult); + + case ResultTypeKey: + return GetResult(aCx, mResult.mKey, aResult); + + case ResultTypeKeyArray: + return GetResult(aCx, mResult.mKeyArray, aResult); + + case ResultTypeJSVal: + aResult.set(*mResult.mJSVal); + return NS_OK; + + case ResultTypeJSValHandle: + aResult.set(*mResult.mJSValHandle); + return NS_OK; + + default: + MOZ_CRASH("Unknown result type!"); + } + + MOZ_CRASH("Should never get here!"); + } + +private: + template + typename EnableIf::value || + IsSame::value || + IsSame::value, + nsresult>::Type + GetResult(JSContext* aCx, + T* aDOMObject, + JS::MutableHandle aResult) + { + if (!aDOMObject) { + aResult.setNull(); + return NS_OK; + } + + bool ok = GetOrCreateDOMReflector(aCx, aDOMObject, aResult); + if (NS_WARN_IF(!ok)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + StructuredCloneReadInfo* aCloneInfo, + JS::MutableHandle aResult) + { + bool ok = IDBObjectStore::DeserializeValue(aCx, *aCloneInfo, aResult); + + if (NS_WARN_IF(!ok)) { + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + const nsTArray* aCloneInfos, + JS::MutableHandle aResult) + { + JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); + if (NS_WARN_IF(!array)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (!aCloneInfos->IsEmpty()) { + const uint32_t count = aCloneInfos->Length(); + + if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + for (uint32_t index = 0; index < count; index++) { + auto& cloneInfo = + const_cast(aCloneInfos->ElementAt(index)); + + JS::Rooted value(aCx); + + nsresult rv = GetResult(aCx, &cloneInfo, &value); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!JS_DefineElement(aCx, array, index, value, + JSPROP_ENUMERATE))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } + } + + aResult.setObject(*array); + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + const Key* aKey, + JS::MutableHandle aResult) + { + nsresult rv = aKey->ToJSVal(aCx, aResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + const nsTArray* aKeys, + JS::MutableHandle aResult) + { + JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); + if (NS_WARN_IF(!array)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (!aKeys->IsEmpty()) { + const uint32_t count = aKeys->Length(); + + if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + for (uint32_t index = 0; index < count; index++) { + const Key& key = aKeys->ElementAt(index); + MOZ_ASSERT(!key.IsUnset()); + + JS::Rooted value(aCx); + + nsresult rv = GetResult(aCx, &key, &value); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!JS_DefineElement(aCx, array, index, value, + JSPROP_ENUMERATE))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } + } + + aResult.setObject(*array); + return NS_OK; + } +}; + +class PermissionRequestMainProcessHelper final + : public PermissionRequestBase +{ + BackgroundFactoryRequestChild* mActor; + RefPtr mFactory; + +public: + PermissionRequestMainProcessHelper(BackgroundFactoryRequestChild* aActor, + IDBFactory* aFactory, + Element* aOwnerElement, + nsIPrincipal* aPrincipal) + : PermissionRequestBase(aOwnerElement, aPrincipal) + , mActor(aActor) + , mFactory(aFactory) + { + MOZ_ASSERT(aActor); + MOZ_ASSERT(aFactory); + aActor->AssertIsOnOwningThread(); + } + +protected: + ~PermissionRequestMainProcessHelper() + { } + +private: + virtual void + OnPromptComplete(PermissionValue aPermissionValue) override; +}; + +class PermissionRequestChildProcessActor final + : public PIndexedDBPermissionRequestChild +{ + BackgroundFactoryRequestChild* mActor; + RefPtr mFactory; + +public: + PermissionRequestChildProcessActor(BackgroundFactoryRequestChild* aActor, + IDBFactory* aFactory) + : mActor(aActor) + , mFactory(aFactory) + { + MOZ_ASSERT(aActor); + MOZ_ASSERT(aFactory); + aActor->AssertIsOnOwningThread(); + } + +protected: + ~PermissionRequestChildProcessActor() + { } + + virtual bool + Recv__delete__(const uint32_t& aPermission) override; +}; + +void +DeserializeStructuredCloneFiles( + IDBDatabase* aDatabase, + const nsTArray& aSerializedFiles, + const nsTArray>* aModuleSet, + nsTArray& aFiles) +{ + MOZ_ASSERT_IF(aModuleSet, !aModuleSet->IsEmpty()); + MOZ_ASSERT(aFiles.IsEmpty()); + + if (!aSerializedFiles.IsEmpty()) { + uint32_t moduleIndex = 0; + + const uint32_t count = aSerializedFiles.Length(); + aFiles.SetCapacity(count); + + for (uint32_t index = 0; index < count; index++) { + const SerializedStructuredCloneFile& serializedFile = + aSerializedFiles[index]; + + const BlobOrMutableFile& blobOrMutableFile = serializedFile.file(); + + switch (serializedFile.type()) { + case StructuredCloneFile::eBlob: { + MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::TPBlobChild); + + auto* actor = + static_cast(blobOrMutableFile.get_PBlobChild()); + + RefPtr blobImpl = actor->GetBlobImpl(); + MOZ_ASSERT(blobImpl); + + RefPtr blob = Blob::Create(aDatabase->GetOwner(), blobImpl); + + aDatabase->NoteReceivedBlob(blob); + + StructuredCloneFile* file = aFiles.AppendElement(); + MOZ_ASSERT(file); + + file->mType = StructuredCloneFile::eBlob; + file->mBlob.swap(blob); + + break; + } + + case StructuredCloneFile::eMutableFile: { + MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t || + blobOrMutableFile.type() == + BlobOrMutableFile::TPBackgroundMutableFileChild); + + switch (blobOrMutableFile.type()) { + case BlobOrMutableFile::Tnull_t: { + StructuredCloneFile* file = aFiles.AppendElement(); + MOZ_ASSERT(file); + + file->mType = StructuredCloneFile::eMutableFile; + + break; + } + + case BlobOrMutableFile::TPBackgroundMutableFileChild: { + auto* actor = + static_cast( + blobOrMutableFile.get_PBackgroundMutableFileChild()); + MOZ_ASSERT(actor); + + actor->EnsureDOMObject(); + + auto* mutableFile = + static_cast(actor->GetDOMObject()); + MOZ_ASSERT(mutableFile); + + StructuredCloneFile* file = aFiles.AppendElement(); + MOZ_ASSERT(file); + + file->mType = StructuredCloneFile::eMutableFile; + file->mMutableFile = mutableFile; + + actor->ReleaseDOMObject(); + + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + break; + } + + case StructuredCloneFile::eStructuredClone: { + StructuredCloneFile* file = aFiles.AppendElement(); + MOZ_ASSERT(file); + + file->mType = StructuredCloneFile::eStructuredClone; + + break; + } + + case StructuredCloneFile::eWasmBytecode: + case StructuredCloneFile::eWasmCompiled: { + if (aModuleSet) { + MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t); + + StructuredCloneFile* file = aFiles.AppendElement(); + MOZ_ASSERT(file); + + file->mType = serializedFile.type(); + + MOZ_ASSERT(moduleIndex < aModuleSet->Length()); + file->mWasmModule = aModuleSet->ElementAt(moduleIndex); + + if (serializedFile.type() == StructuredCloneFile::eWasmCompiled) { + moduleIndex++; + } + + break; + } + + MOZ_ASSERT(blobOrMutableFile.type() == + BlobOrMutableFile::TPBlobChild); + + auto* actor = + static_cast(blobOrMutableFile.get_PBlobChild()); + + RefPtr blobImpl = actor->GetBlobImpl(); + MOZ_ASSERT(blobImpl); + + RefPtr blob = Blob::Create(aDatabase->GetOwner(), blobImpl); + + aDatabase->NoteReceivedBlob(blob); + + StructuredCloneFile* file = aFiles.AppendElement(); + MOZ_ASSERT(file); + + file->mType = serializedFile.type(); + file->mBlob.swap(blob); + + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + } + } +} + +void +DispatchErrorEvent(IDBRequest* aRequest, + nsresult aErrorCode, + IDBTransaction* aTransaction = nullptr, + nsIDOMEvent* aEvent = nullptr) +{ + MOZ_ASSERT(aRequest); + aRequest->AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aErrorCode)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB); + + PROFILER_LABEL("IndexedDB", + "DispatchErrorEvent", + js::ProfileEntry::Category::STORAGE); + + RefPtr request = aRequest; + RefPtr transaction = aTransaction; + + request->SetError(aErrorCode); + + nsCOMPtr errorEvent; + if (!aEvent) { + // Make an error event and fire it at the target. + errorEvent = CreateGenericEvent(request, + nsDependentString(kErrorEventType), + eDoesBubble, + eCancelable); + MOZ_ASSERT(errorEvent); + + aEvent = errorEvent; + } + + Maybe asct; + if (aTransaction) { + asct.emplace(aTransaction); + } + + if (transaction) { + IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " + "Firing %s event with error 0x%x", + "IndexedDB %s: C T[%lld] R[%llu]: %s (0x%x)", + IDB_LOG_ID_STRING(), + transaction->LoggingSerialNumber(), + request->LoggingSerialNumber(), + IDB_LOG_STRINGIFY(aEvent, kErrorEventType), + aErrorCode); + } else { + IDB_LOG_MARK("IndexedDB %s: Child Request[%llu]: " + "Firing %s event with error 0x%x", + "IndexedDB %s: C R[%llu]: %s (0x%x)", + IDB_LOG_ID_STRING(), + request->LoggingSerialNumber(), + IDB_LOG_STRINGIFY(aEvent, kErrorEventType), + aErrorCode); + } + + bool doDefault; + nsresult rv = request->DispatchEvent(aEvent, &doDefault); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + MOZ_ASSERT(!transaction || transaction->IsOpen() || transaction->IsAborted()); + + // Do not abort the transaction here if this request is failed due to the + // abortion of its transaction to ensure that the correct error cause of + // the abort event be set in IDBTransaction::FireCompleteOrAbortEvents() later. + if (transaction && transaction->IsOpen() && + aErrorCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) { + WidgetEvent* internalEvent = aEvent->WidgetEventPtr(); + MOZ_ASSERT(internalEvent); + + if (internalEvent->mFlags.mExceptionWasRaised) { + transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); + } else if (doDefault) { + transaction->Abort(request); + } + } +} + +void +DispatchSuccessEvent(ResultHelper* aResultHelper, + nsIDOMEvent* aEvent = nullptr) +{ + MOZ_ASSERT(aResultHelper); + + PROFILER_LABEL("IndexedDB", + "DispatchSuccessEvent", + js::ProfileEntry::Category::STORAGE); + + RefPtr request = aResultHelper->Request(); + MOZ_ASSERT(request); + request->AssertIsOnOwningThread(); + + RefPtr transaction = aResultHelper->Transaction(); + + if (transaction && transaction->IsAborted()) { + DispatchErrorEvent(request, transaction->AbortCode(), transaction); + return; + } + + nsCOMPtr successEvent; + if (!aEvent) { + successEvent = CreateGenericEvent(request, + nsDependentString(kSuccessEventType), + eDoesNotBubble, + eNotCancelable); + MOZ_ASSERT(successEvent); + + aEvent = successEvent; + } + + request->SetResultCallback(aResultHelper); + + MOZ_ASSERT(aEvent); + MOZ_ASSERT_IF(transaction, transaction->IsOpen()); + + if (transaction) { + IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " + "Firing %s event", + "IndexedDB %s: C T[%lld] R[%llu]: %s", + IDB_LOG_ID_STRING(), + transaction->LoggingSerialNumber(), + request->LoggingSerialNumber(), + IDB_LOG_STRINGIFY(aEvent, kSuccessEventType)); + } else { + IDB_LOG_MARK("IndexedDB %s: Child Request[%llu]: Firing %s event", + "IndexedDB %s: C R[%llu]: %s", + IDB_LOG_ID_STRING(), + request->LoggingSerialNumber(), + IDB_LOG_STRINGIFY(aEvent, kSuccessEventType)); + } + + bool dummy; + nsresult rv = request->DispatchEvent(aEvent, &dummy); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + MOZ_ASSERT_IF(transaction, + transaction->IsOpen() || transaction->IsAborted()); + + WidgetEvent* internalEvent = aEvent->WidgetEventPtr(); + MOZ_ASSERT(internalEvent); + + if (transaction && + transaction->IsOpen() && + internalEvent->mFlags.mExceptionWasRaised) { + transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); + } +} + +PRFileDesc* +GetFileDescriptorFromStream(nsIInputStream* aStream) +{ + MOZ_ASSERT(aStream); + + nsCOMPtr fileMetadata = do_QueryInterface(aStream); + if (NS_WARN_IF(!fileMetadata)) { + return nullptr; + } + + PRFileDesc* fileDesc; + nsresult rv = fileMetadata->GetFileDescriptor(&fileDesc); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + MOZ_ASSERT(fileDesc); + + return fileDesc; +} + +class WorkerPermissionChallenge; + +// This class calles WorkerPermissionChallenge::OperationCompleted() in the +// worker thread. +class WorkerPermissionOperationCompleted final : public WorkerControlRunnable +{ + RefPtr mChallenge; + +public: + WorkerPermissionOperationCompleted(WorkerPrivate* aWorkerPrivate, + WorkerPermissionChallenge* aChallenge) + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) + , mChallenge(aChallenge) + { + MOZ_ASSERT(NS_IsMainThread()); + } + + virtual bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; +}; + +// This class used to do prompting in the main thread and main process. +class WorkerPermissionRequest final : public PermissionRequestBase +{ + RefPtr mChallenge; + +public: + WorkerPermissionRequest(Element* aElement, + nsIPrincipal* aPrincipal, + WorkerPermissionChallenge* aChallenge) + : PermissionRequestBase(aElement, aPrincipal) + , mChallenge(aChallenge) + { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aChallenge); + } + +private: + ~WorkerPermissionRequest() + { + MOZ_ASSERT(NS_IsMainThread()); + } + + virtual void + OnPromptComplete(PermissionValue aPermissionValue) override; +}; + +// This class is used in the main thread of all child processes. +class WorkerPermissionRequestChildProcessActor final + : public PIndexedDBPermissionRequestChild +{ + RefPtr mChallenge; + +public: + explicit WorkerPermissionRequestChildProcessActor( + WorkerPermissionChallenge* aChallenge) + : mChallenge(aChallenge) + { + MOZ_ASSERT(!XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aChallenge); + } + +protected: + ~WorkerPermissionRequestChildProcessActor() + {} + + virtual bool + Recv__delete__(const uint32_t& aPermission) override; +}; + +class WorkerPermissionChallenge final : public Runnable +{ +public: + WorkerPermissionChallenge(WorkerPrivate* aWorkerPrivate, + BackgroundFactoryRequestChild* aActor, + IDBFactory* aFactory, + const PrincipalInfo& aPrincipalInfo) + : mWorkerPrivate(aWorkerPrivate) + , mActor(aActor) + , mFactory(aFactory) + , mPrincipalInfo(aPrincipalInfo) + { + MOZ_ASSERT(mWorkerPrivate); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aFactory); + mWorkerPrivate->AssertIsOnWorkerThread(); + } + + bool + Dispatch() + { + mWorkerPrivate->AssertIsOnWorkerThread(); + if (NS_WARN_IF(!mWorkerPrivate->ModifyBusyCountFromWorker(true))) { + return false; + } + + if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) { + mWorkerPrivate->ModifyBusyCountFromWorker(false); + return false; + } + + return true; + } + + NS_IMETHOD + Run() override + { + bool completed = RunInternal(); + if (completed) { + OperationCompleted(); + } + + return NS_OK; + } + + void + OperationCompleted() + { + if (NS_IsMainThread()) { + RefPtr runnable = + new WorkerPermissionOperationCompleted(mWorkerPrivate, this); + + MOZ_ALWAYS_TRUE(runnable->Dispatch()); + return; + } + + MOZ_ASSERT(mActor); + mActor->AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + RefPtr factory; + mFactory.swap(factory); + + mActor->SendPermissionRetry(); + mActor = nullptr; + + mWorkerPrivate->AssertIsOnWorkerThread(); + mWorkerPrivate->ModifyBusyCountFromWorker(false); + } + +private: + bool + RunInternal() + { + MOZ_ASSERT(NS_IsMainThread()); + + // Walk up to our containing page + WorkerPrivate* wp = mWorkerPrivate; + while (wp->GetParent()) { + wp = wp->GetParent(); + } + + nsPIDOMWindowInner* window = wp->GetWindow(); + if (!window) { + return true; + } + + nsresult rv; + nsCOMPtr principal = + mozilla::ipc::PrincipalInfoToPrincipal(mPrincipalInfo, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return true; + } + + if (XRE_IsParentProcess()) { + nsCOMPtr ownerElement = + do_QueryInterface(window->GetChromeEventHandler()); + if (NS_WARN_IF(!ownerElement)) { + return true; + } + + RefPtr helper = + new WorkerPermissionRequest(ownerElement, principal, this); + + PermissionRequestBase::PermissionValue permission; + if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) { + return true; + } + + MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed || + permission == PermissionRequestBase::kPermissionDenied || + permission == PermissionRequestBase::kPermissionPrompt); + + return permission != PermissionRequestBase::kPermissionPrompt; + } + + TabChild* tabChild = TabChild::GetFrom(window); + MOZ_ASSERT(tabChild); + + IPC::Principal ipcPrincipal(principal); + + auto* actor = new WorkerPermissionRequestChildProcessActor(this); + tabChild->SendPIndexedDBPermissionRequestConstructor(actor, ipcPrincipal); + return false; + } + +private: + WorkerPrivate* mWorkerPrivate; + BackgroundFactoryRequestChild* mActor; + RefPtr mFactory; + PrincipalInfo mPrincipalInfo; +}; + +void +WorkerPermissionRequest::OnPromptComplete(PermissionValue aPermissionValue) +{ + MOZ_ASSERT(NS_IsMainThread()); + mChallenge->OperationCompleted(); +} + +bool +WorkerPermissionOperationCompleted::WorkerRun(JSContext* aCx, + WorkerPrivate* aWorkerPrivate) +{ + aWorkerPrivate->AssertIsOnWorkerThread(); + mChallenge->OperationCompleted(); + return true; +} + +bool +WorkerPermissionRequestChildProcessActor::Recv__delete__( + const uint32_t& /* aPermission */) +{ + MOZ_ASSERT(NS_IsMainThread()); + mChallenge->OperationCompleted(); + return true; +} + +} // namespace + +/******************************************************************************* + * Actor class declarations + ******************************************************************************/ + +// CancelableRunnable is used to make workers happy. +class BackgroundRequestChild::PreprocessHelper final + : public CancelableRunnable +{ + typedef std::pair, + nsCOMPtr> StreamPair; + + nsCOMPtr mOwningThread; + nsTArray mStreamPairs; + nsTArray> mModuleSet; + BackgroundRequestChild* mActor; + uint32_t mModuleSetIndex; + nsresult mResultCode; + +public: + PreprocessHelper(uint32_t aModuleSetIndex, BackgroundRequestChild* aActor) + : mOwningThread(NS_GetCurrentThread()) + , mActor(aActor) + , mModuleSetIndex(aModuleSetIndex) + , mResultCode(NS_OK) + { + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + aActor->AssertIsOnOwningThread(); + } + + bool + IsOnOwningThread() const + { + MOZ_ASSERT(mOwningThread); + + bool current; + return NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t)) && current; + } + + void + AssertIsOnOwningThread() const + { + MOZ_ASSERT(IsOnOwningThread()); + } + + void + ClearActor() + { + AssertIsOnOwningThread(); + + mActor = nullptr; + } + + nsresult + Init(const nsTArray& aFiles); + + nsresult + Dispatch(); + +private: + ~PreprocessHelper() + { } + + void + RunOnOwningThread(); + + nsresult + RunOnStreamTransportThread(); + + NS_DECL_NSIRUNNABLE + + virtual nsresult + Cancel() override; +}; + +/******************************************************************************* + * Local class implementations + ******************************************************************************/ + +void +PermissionRequestMainProcessHelper::OnPromptComplete( + PermissionValue aPermissionValue) +{ + MOZ_ASSERT(mActor); + mActor->AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + mActor->SendPermissionRetry(); + + mActor = nullptr; + mFactory = nullptr; +} + +bool +PermissionRequestChildProcessActor::Recv__delete__( + const uint32_t& /* aPermission */) +{ + MOZ_ASSERT(mActor); + mActor->AssertIsOnOwningThread(); + MOZ_ASSERT(mFactory); + + MaybeCollectGarbageOnIPCMessage(); + + RefPtr factory; + mFactory.swap(factory); + + mActor->SendPermissionRetry(); + mActor = nullptr; + + return true; +} + +/******************************************************************************* + * BackgroundRequestChildBase + ******************************************************************************/ + +BackgroundRequestChildBase::BackgroundRequestChildBase(IDBRequest* aRequest) + : mRequest(aRequest) +{ + MOZ_ASSERT(aRequest); + aRequest->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChildBase); +} + +BackgroundRequestChildBase::~BackgroundRequestChildBase() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChildBase); +} + +#ifdef DEBUG + +void +BackgroundRequestChildBase::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mRequest); + mRequest->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +/******************************************************************************* + * BackgroundFactoryChild + ******************************************************************************/ + +BackgroundFactoryChild::BackgroundFactoryChild(IDBFactory* aFactory) + : mFactory(aFactory) +#ifdef DEBUG + , mOwningThread(NS_GetCurrentThread()) +#endif +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFactory); + aFactory->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryChild); +} + +BackgroundFactoryChild::~BackgroundFactoryChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryChild); +} + +#ifdef DEBUG + +void +BackgroundFactoryChild::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread); + + bool current; + MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); +} + +nsIEventTarget* +BackgroundFactoryChild::OwningThread() const +{ + MOZ_ASSERT(mOwningThread); + return mOwningThread; +} + +#endif // DEBUG + +void +BackgroundFactoryChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + + if (mFactory) { + mFactory->ClearBackgroundActor(); + mFactory = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundIDBFactoryChild::SendDeleteMe()); + } +} + +void +BackgroundFactoryChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (mFactory) { + mFactory->ClearBackgroundActor(); +#ifdef DEBUG + mFactory = nullptr; +#endif + } +} + +PBackgroundIDBFactoryRequestChild* +BackgroundFactoryChild::AllocPBackgroundIDBFactoryRequestChild( + const FactoryRequestParams& aParams) +{ + MOZ_CRASH("PBackgroundIDBFactoryRequestChild actors should be manually " + "constructed!"); +} + +bool +BackgroundFactoryChild::DeallocPBackgroundIDBFactoryRequestChild( + PBackgroundIDBFactoryRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +PBackgroundIDBDatabaseChild* +BackgroundFactoryChild::AllocPBackgroundIDBDatabaseChild( + const DatabaseSpec& aSpec, + PBackgroundIDBFactoryRequestChild* aRequest) +{ + AssertIsOnOwningThread(); + + auto request = static_cast(aRequest); + MOZ_ASSERT(request); + + return new BackgroundDatabaseChild(aSpec, request); +} + +bool +BackgroundFactoryChild::DeallocPBackgroundIDBDatabaseChild( + PBackgroundIDBDatabaseChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +/******************************************************************************* + * BackgroundFactoryRequestChild + ******************************************************************************/ + +BackgroundFactoryRequestChild::BackgroundFactoryRequestChild( + IDBFactory* aFactory, + IDBOpenDBRequest* aOpenRequest, + bool aIsDeleteOp, + uint64_t aRequestedVersion) + : BackgroundRequestChildBase(aOpenRequest) + , mFactory(aFactory) + , mRequestedVersion(aRequestedVersion) + , mIsDeleteOp(aIsDeleteOp) +{ + // Can't assert owning thread here because IPDL has not yet set our manager! + MOZ_ASSERT(aFactory); + aFactory->AssertIsOnOwningThread(); + MOZ_ASSERT(aOpenRequest); + + MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryRequestChild); +} + +BackgroundFactoryRequestChild::~BackgroundFactoryRequestChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryRequestChild); +} + +IDBOpenDBRequest* +BackgroundFactoryRequestChild::GetOpenDBRequest() const +{ + AssertIsOnOwningThread(); + + return static_cast(mRequest.get()); +} + +bool +BackgroundFactoryRequestChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); + + mRequest->Reset(); + + DispatchErrorEvent(mRequest, aResponse); + + return true; +} + +bool +BackgroundFactoryRequestChild::HandleResponse( + const OpenDatabaseRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + + mRequest->Reset(); + + auto databaseActor = + static_cast(aResponse.databaseChild()); + MOZ_ASSERT(databaseActor); + + IDBDatabase* database = databaseActor->GetDOMObject(); + if (!database) { + databaseActor->EnsureDOMObject(); + + database = databaseActor->GetDOMObject(); + MOZ_ASSERT(database); + + MOZ_ASSERT(!database->IsClosed()); + } + + if (database->IsClosed()) { + // If the database was closed already, which is only possible if we fired an + // "upgradeneeded" event, then we shouldn't fire a "success" event here. + // Instead we fire an error event with AbortErr. + DispatchErrorEvent(mRequest, NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); + } else { + ResultHelper helper(mRequest, nullptr, database); + + DispatchSuccessEvent(&helper); + } + + databaseActor->ReleaseDOMObject(); + + return true; +} + +bool +BackgroundFactoryRequestChild::HandleResponse( + const DeleteDatabaseRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mRequest, nullptr, &JS::UndefinedHandleValue); + + nsCOMPtr successEvent = + IDBVersionChangeEvent::Create(mRequest, + nsDependentString(kSuccessEventType), + aResponse.previousVersion()); + MOZ_ASSERT(successEvent); + + DispatchSuccessEvent(&helper, successEvent); + + return true; +} + +void +BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (aWhy != Deletion) { + IDBOpenDBRequest* openRequest = GetOpenDBRequest(); + if (openRequest) { + openRequest->NoteComplete(); + } + } +} + +bool +BackgroundFactoryRequestChild::Recv__delete__( + const FactoryRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + + MaybeCollectGarbageOnIPCMessage(); + + bool result; + + switch (aResponse.type()) { + case FactoryRequestResponse::Tnsresult: + result = HandleResponse(aResponse.get_nsresult()); + break; + + case FactoryRequestResponse::TOpenDatabaseRequestResponse: + result = HandleResponse(aResponse.get_OpenDatabaseRequestResponse()); + break; + + case FactoryRequestResponse::TDeleteDatabaseRequestResponse: + result = HandleResponse(aResponse.get_DeleteDatabaseRequestResponse()); + break; + + default: + MOZ_CRASH("Unknown response type!"); + } + + IDBOpenDBRequest* request = GetOpenDBRequest(); + MOZ_ASSERT(request); + + request->NoteComplete(); + + if (NS_WARN_IF(!result)) { + return false; + } + + return true; +} + +bool +BackgroundFactoryRequestChild::RecvPermissionChallenge( + const PrincipalInfo& aPrincipalInfo) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (!NS_IsMainThread()) { + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + workerPrivate->AssertIsOnWorkerThread(); + + RefPtr challenge = + new WorkerPermissionChallenge(workerPrivate, this, mFactory, + aPrincipalInfo); + return challenge->Dispatch(); + } + + nsresult rv; + nsCOMPtr principal = + mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + if (XRE_IsParentProcess()) { + nsCOMPtr window = mFactory->GetParentObject(); + MOZ_ASSERT(window); + + nsCOMPtr ownerElement = + do_QueryInterface(window->GetChromeEventHandler()); + if (NS_WARN_IF(!ownerElement)) { + // If this fails, the page was navigated. Fail the permission check by + // forcing an immediate retry. + return SendPermissionRetry(); + } + + RefPtr helper = + new PermissionRequestMainProcessHelper(this, mFactory, ownerElement, principal); + + PermissionRequestBase::PermissionValue permission; + if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) { + return false; + } + + MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed || + permission == PermissionRequestBase::kPermissionDenied || + permission == PermissionRequestBase::kPermissionPrompt); + + if (permission != PermissionRequestBase::kPermissionPrompt) { + SendPermissionRetry(); + } + return true; + } + + RefPtr tabChild = mFactory->GetTabChild(); + MOZ_ASSERT(tabChild); + + IPC::Principal ipcPrincipal(principal); + + auto* actor = new PermissionRequestChildProcessActor(this, mFactory); + + tabChild->SendPIndexedDBPermissionRequestConstructor(actor, ipcPrincipal); + + return true; +} + +bool +BackgroundFactoryRequestChild::RecvBlocked(const uint64_t& aCurrentVersion) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + + MaybeCollectGarbageOnIPCMessage(); + + const nsDependentString type(kBlockedEventType); + + nsCOMPtr blockedEvent; + if (mIsDeleteOp) { + blockedEvent = + IDBVersionChangeEvent::Create(mRequest, type, aCurrentVersion); + MOZ_ASSERT(blockedEvent); + } else { + blockedEvent = + IDBVersionChangeEvent::Create(mRequest, + type, + aCurrentVersion, + mRequestedVersion); + MOZ_ASSERT(blockedEvent); + } + + RefPtr kungFuDeathGrip = mRequest; + + IDB_LOG_MARK("IndexedDB %s: Child Request[%llu]: Firing \"blocked\" event", + "IndexedDB %s: C R[%llu]: \"blocked\"", + IDB_LOG_ID_STRING(), + kungFuDeathGrip->LoggingSerialNumber()); + + bool dummy; + if (NS_FAILED(kungFuDeathGrip->DispatchEvent(blockedEvent, &dummy))) { + NS_WARNING("Failed to dispatch event!"); + } + + return true; +} + +/******************************************************************************* + * BackgroundDatabaseChild + ******************************************************************************/ + +BackgroundDatabaseChild::BackgroundDatabaseChild( + const DatabaseSpec& aSpec, + BackgroundFactoryRequestChild* aOpenRequestActor) + : mSpec(new DatabaseSpec(aSpec)) + , mOpenRequestActor(aOpenRequestActor) + , mDatabase(nullptr) +{ + // Can't assert owning thread here because IPDL has not yet set our manager! + MOZ_ASSERT(aOpenRequestActor); + + MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseChild); +} + +BackgroundDatabaseChild::~BackgroundDatabaseChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseChild); +} + +void +BackgroundDatabaseChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mTemporaryStrongDatabase); + MOZ_ASSERT(!mOpenRequestActor); + + if (mDatabase) { + mDatabase->ClearBackgroundActor(); + mDatabase = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundIDBDatabaseChild::SendDeleteMe()); + } +} + +void +BackgroundDatabaseChild::EnsureDOMObject() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mOpenRequestActor); + + if (mTemporaryStrongDatabase) { + MOZ_ASSERT(!mSpec); + return; + } + + MOZ_ASSERT(mSpec); + + auto request = mOpenRequestActor->GetOpenDBRequest(); + MOZ_ASSERT(request); + + auto factory = + static_cast(Manager())->GetDOMObject(); + MOZ_ASSERT(factory); + + mTemporaryStrongDatabase = + IDBDatabase::Create(request, factory, this, mSpec); + + MOZ_ASSERT(mTemporaryStrongDatabase); + mTemporaryStrongDatabase->AssertIsOnOwningThread(); + + mDatabase = mTemporaryStrongDatabase; + mSpec.forget(); +} + +void +BackgroundDatabaseChild::ReleaseDOMObject() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mTemporaryStrongDatabase); + mTemporaryStrongDatabase->AssertIsOnOwningThread(); + MOZ_ASSERT(mOpenRequestActor); + MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase); + + mOpenRequestActor = nullptr; + + // This may be the final reference to the IDBDatabase object so we may end up + // calling SendDeleteMeInternal() here. Make sure everything is cleaned up + // properly before proceeding. + mTemporaryStrongDatabase = nullptr; +} + +void +BackgroundDatabaseChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (mDatabase) { + mDatabase->ClearBackgroundActor(); +#ifdef DEBUG + mDatabase = nullptr; +#endif + } +} + +PBackgroundIDBDatabaseFileChild* +BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseFileChild( + PBlobChild* aBlobChild) +{ + MOZ_CRASH("PBackgroundIDBFileChild actors should be manually constructed!"); +} + +bool +BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseFileChild( + PBackgroundIDBDatabaseFileChild* aActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + + delete aActor; + return true; +} + +PBackgroundIDBDatabaseRequestChild* +BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseRequestChild( + const DatabaseRequestParams& aParams) +{ + MOZ_CRASH("PBackgroundIDBDatabaseRequestChild actors should be manually " + "constructed!"); +} + +bool +BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseRequestChild( + PBackgroundIDBDatabaseRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +PBackgroundIDBTransactionChild* +BackgroundDatabaseChild::AllocPBackgroundIDBTransactionChild( + const nsTArray& aObjectStoreNames, + const Mode& aMode) +{ + MOZ_CRASH("PBackgroundIDBTransactionChild actors should be manually " + "constructed!"); +} + +bool +BackgroundDatabaseChild::DeallocPBackgroundIDBTransactionChild( + PBackgroundIDBTransactionChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +PBackgroundIDBVersionChangeTransactionChild* +BackgroundDatabaseChild::AllocPBackgroundIDBVersionChangeTransactionChild( + const uint64_t& aCurrentVersion, + const uint64_t& aRequestedVersion, + const int64_t& aNextObjectStoreId, + const int64_t& aNextIndexId) +{ + AssertIsOnOwningThread(); + + IDBOpenDBRequest* request = mOpenRequestActor->GetOpenDBRequest(); + MOZ_ASSERT(request); + + return new BackgroundVersionChangeTransactionChild(request); +} + +bool +BackgroundDatabaseChild::RecvPBackgroundIDBVersionChangeTransactionConstructor( + PBackgroundIDBVersionChangeTransactionChild* aActor, + const uint64_t& aCurrentVersion, + const uint64_t& aRequestedVersion, + const int64_t& aNextObjectStoreId, + const int64_t& aNextIndexId) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(mOpenRequestActor); + + MaybeCollectGarbageOnIPCMessage(); + + EnsureDOMObject(); + + auto* actor = static_cast(aActor); + + RefPtr request = mOpenRequestActor->GetOpenDBRequest(); + MOZ_ASSERT(request); + + RefPtr transaction = + IDBTransaction::CreateVersionChange(mDatabase, + actor, + request, + aNextObjectStoreId, + aNextIndexId); + if (NS_WARN_IF(!transaction)) { + // This can happen if we receive events after a worker has begun its + // shutdown process. + MOZ_ASSERT(!NS_IsMainThread()); + + // Report this to the console. + IDB_REPORT_INTERNAL_ERR(); + + MOZ_ALWAYS_TRUE(aActor->SendDeleteMe()); + return true; + } + + transaction->AssertIsOnOwningThread(); + + actor->SetDOMTransaction(transaction); + + mDatabase->EnterSetVersionTransaction(aRequestedVersion); + + request->SetTransaction(transaction); + + nsCOMPtr upgradeNeededEvent = + IDBVersionChangeEvent::Create(request, + nsDependentString(kUpgradeNeededEventType), + aCurrentVersion, + aRequestedVersion); + MOZ_ASSERT(upgradeNeededEvent); + + ResultHelper helper(request, transaction, mDatabase); + + DispatchSuccessEvent(&helper, upgradeNeededEvent); + + return true; +} + +bool +BackgroundDatabaseChild::DeallocPBackgroundIDBVersionChangeTransactionChild( + PBackgroundIDBVersionChangeTransactionChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +PBackgroundMutableFileChild* +BackgroundDatabaseChild::AllocPBackgroundMutableFileChild(const nsString& aName, + const nsString& aType) +{ + AssertIsOnOwningThread(); + +#ifdef DEBUG + nsCOMPtr owningThread = do_QueryInterface(OwningThread()); + + PRThread* owningPRThread; + owningThread->GetPRThread(&owningPRThread); +#endif + + return new BackgroundMutableFileChild(DEBUGONLY(owningPRThread,) + aName, + aType); +} + +bool +BackgroundDatabaseChild::DeallocPBackgroundMutableFileChild( + PBackgroundMutableFileChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +bool +BackgroundDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion, + const NullableVersion& aNewVersion) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (!mDatabase || mDatabase->IsClosed()) { + return true; + } + + RefPtr kungFuDeathGrip = mDatabase; + + // Handle bfcache'd windows. + if (nsPIDOMWindowInner* owner = kungFuDeathGrip->GetOwner()) { + // The database must be closed if the window is already frozen. + bool shouldAbortAndClose = owner->IsFrozen(); + + // Anything in the bfcache has to be evicted and then we have to close the + // database also. + if (nsCOMPtr doc = owner->GetExtantDoc()) { + if (nsCOMPtr bfCacheEntry = doc->GetBFCacheEntry()) { + bfCacheEntry->RemoveFromBFCacheSync(); + shouldAbortAndClose = true; + } + } + + if (shouldAbortAndClose) { + // Invalidate() doesn't close the database in the parent, so we have + // to call Close() and AbortTransactions() manually. + kungFuDeathGrip->AbortTransactions(/* aShouldWarn */ false); + kungFuDeathGrip->Close(); + return true; + } + } + + // Otherwise fire a versionchange event. + const nsDependentString type(kVersionChangeEventType); + + nsCOMPtr versionChangeEvent; + + switch (aNewVersion.type()) { + case NullableVersion::Tnull_t: + versionChangeEvent = + IDBVersionChangeEvent::Create(kungFuDeathGrip, type, aOldVersion); + MOZ_ASSERT(versionChangeEvent); + break; + + case NullableVersion::Tuint64_t: + versionChangeEvent = + IDBVersionChangeEvent::Create(kungFuDeathGrip, + type, + aOldVersion, + aNewVersion.get_uint64_t()); + MOZ_ASSERT(versionChangeEvent); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + IDB_LOG_MARK("IndexedDB %s: Child : Firing \"versionchange\" event", + "IndexedDB %s: C: IDBDatabase \"versionchange\" event", + IDB_LOG_ID_STRING()); + + bool dummy; + if (NS_FAILED(kungFuDeathGrip->DispatchEvent(versionChangeEvent, &dummy))) { + NS_WARNING("Failed to dispatch event!"); + } + + if (!kungFuDeathGrip->IsClosed()) { + SendBlocked(); + } + + return true; +} + +bool +BackgroundDatabaseChild::RecvInvalidate() +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (mDatabase) { + mDatabase->Invalidate(); + } + + return true; +} + +bool +BackgroundDatabaseChild::RecvCloseAfterInvalidationComplete() +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (mDatabase) { + mDatabase->DispatchTrustedEvent(nsDependentString(kCloseEventType)); + } + + return true; +} + +/******************************************************************************* + * BackgroundDatabaseRequestChild + ******************************************************************************/ + +BackgroundDatabaseRequestChild::BackgroundDatabaseRequestChild( + IDBDatabase* aDatabase, + IDBRequest* aRequest) + : BackgroundRequestChildBase(aRequest) + , mDatabase(aDatabase) +{ + // Can't assert owning thread here because IPDL has not yet set our manager! + MOZ_ASSERT(aDatabase); + aDatabase->AssertIsOnOwningThread(); + MOZ_ASSERT(aRequest); + + MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseRequestChild); +} + +BackgroundDatabaseRequestChild::~BackgroundDatabaseRequestChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseRequestChild); +} + +bool +BackgroundDatabaseRequestChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); + + mRequest->Reset(); + + DispatchErrorEvent(mRequest, aResponse); + + return true; +} + +bool +BackgroundDatabaseRequestChild::HandleResponse( + const CreateFileRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + + mRequest->Reset(); + + auto mutableFileActor = + static_cast(aResponse.mutableFileChild()); + MOZ_ASSERT(mutableFileActor); + + mutableFileActor->EnsureDOMObject(); + + auto mutableFile = + static_cast(mutableFileActor->GetDOMObject()); + MOZ_ASSERT(mutableFile); + + ResultHelper helper(mRequest, nullptr, mutableFile); + + DispatchSuccessEvent(&helper); + + mutableFileActor->ReleaseDOMObject(); + + return true; +} + +bool +BackgroundDatabaseRequestChild::Recv__delete__( + const DatabaseRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + + switch (aResponse.type()) { + case DatabaseRequestResponse::Tnsresult: + return HandleResponse(aResponse.get_nsresult()); + + case DatabaseRequestResponse::TCreateFileRequestResponse: + return HandleResponse(aResponse.get_CreateFileRequestResponse()); + + default: + MOZ_CRASH("Unknown response type!"); + } + + MOZ_CRASH("Should never get here!"); +} + +/******************************************************************************* + * BackgroundTransactionBase + ******************************************************************************/ + +BackgroundTransactionBase::BackgroundTransactionBase() +: mTransaction(nullptr) +{ + MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase); +} + +BackgroundTransactionBase::BackgroundTransactionBase( + IDBTransaction* aTransaction) + : mTemporaryStrongTransaction(aTransaction) + , mTransaction(aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase); +} + +BackgroundTransactionBase::~BackgroundTransactionBase() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionBase); +} + +#ifdef DEBUG + +void +BackgroundTransactionBase::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mTransaction); + mTransaction->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +void +BackgroundTransactionBase::NoteActorDestroyed() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mTemporaryStrongTransaction, mTransaction); + + if (mTransaction) { + mTransaction->ClearBackgroundActor(); + + // Normally this would be DEBUG-only but NoteActorDestroyed is also called + // from SendDeleteMeInternal. In that case we're going to receive an actual + // ActorDestroy call later and we don't want to touch a dead object. + mTemporaryStrongTransaction = nullptr; + mTransaction = nullptr; + } +} + +void +BackgroundTransactionBase::SetDOMTransaction(IDBTransaction* aTransaction) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnOwningThread(); + MOZ_ASSERT(!mTemporaryStrongTransaction); + MOZ_ASSERT(!mTransaction); + + mTemporaryStrongTransaction = aTransaction; + mTransaction = aTransaction; +} + +void +BackgroundTransactionBase::NoteComplete() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mTransaction, mTemporaryStrongTransaction); + + mTemporaryStrongTransaction = nullptr; +} + +/******************************************************************************* + * BackgroundTransactionChild + ******************************************************************************/ + +BackgroundTransactionChild::BackgroundTransactionChild( + IDBTransaction* aTransaction) + : BackgroundTransactionBase(aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionChild); +} + +BackgroundTransactionChild::~BackgroundTransactionChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionChild); +} + +#ifdef DEBUG + +void +BackgroundTransactionChild::AssertIsOnOwningThread() const +{ + static_cast(Manager())->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +void +BackgroundTransactionChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + + if (mTransaction) { + NoteActorDestroyed(); + + MOZ_ALWAYS_TRUE(PBackgroundIDBTransactionChild::SendDeleteMe()); + } +} + +void +BackgroundTransactionChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + NoteActorDestroyed(); +} + +bool +BackgroundTransactionChild::RecvComplete(const nsresult& aResult) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mTransaction); + + MaybeCollectGarbageOnIPCMessage(); + + mTransaction->FireCompleteOrAbortEvents(aResult); + + NoteComplete(); + return true; +} + +PBackgroundIDBRequestChild* +BackgroundTransactionChild::AllocPBackgroundIDBRequestChild( + const RequestParams& aParams) +{ + MOZ_CRASH("PBackgroundIDBRequestChild actors should be manually " + "constructed!"); +} + +bool +BackgroundTransactionChild::DeallocPBackgroundIDBRequestChild( + PBackgroundIDBRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +PBackgroundIDBCursorChild* +BackgroundTransactionChild::AllocPBackgroundIDBCursorChild( + const OpenCursorParams& aParams) +{ + AssertIsOnOwningThread(); + + MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!"); +} + +bool +BackgroundTransactionChild::DeallocPBackgroundIDBCursorChild( + PBackgroundIDBCursorChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +/******************************************************************************* + * BackgroundVersionChangeTransactionChild + ******************************************************************************/ + +BackgroundVersionChangeTransactionChild:: +BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest) + : mOpenDBRequest(aOpenDBRequest) +{ + MOZ_ASSERT(aOpenDBRequest); + aOpenDBRequest->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundVersionChangeTransactionChild); +} + +BackgroundVersionChangeTransactionChild:: +~BackgroundVersionChangeTransactionChild() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(indexedDB::BackgroundVersionChangeTransactionChild); +} + +#ifdef DEBUG + +void +BackgroundVersionChangeTransactionChild::AssertIsOnOwningThread() const +{ + static_cast(Manager())->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +void +BackgroundVersionChangeTransactionChild::SendDeleteMeInternal( + bool aFailedConstructor) +{ + AssertIsOnOwningThread(); + + if (mTransaction || aFailedConstructor) { + NoteActorDestroyed(); + + MOZ_ALWAYS_TRUE(PBackgroundIDBVersionChangeTransactionChild:: + SendDeleteMe()); + } +} + +void +BackgroundVersionChangeTransactionChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + mOpenDBRequest = nullptr; + + NoteActorDestroyed(); +} + +bool +BackgroundVersionChangeTransactionChild::RecvComplete(const nsresult& aResult) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (!mTransaction) { + return true; + } + + MOZ_ASSERT(mOpenDBRequest); + + IDBDatabase* database = mTransaction->Database(); + MOZ_ASSERT(database); + + database->ExitSetVersionTransaction(); + + if (NS_FAILED(aResult)) { + database->Close(); + } + + mTransaction->FireCompleteOrAbortEvents(aResult); + + mOpenDBRequest->SetTransaction(nullptr); + mOpenDBRequest = nullptr; + + NoteComplete(); + return true; +} + +PBackgroundIDBRequestChild* +BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBRequestChild( + const RequestParams& aParams) +{ + MOZ_CRASH("PBackgroundIDBRequestChild actors should be manually " + "constructed!"); +} + +bool +BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBRequestChild( + PBackgroundIDBRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +PBackgroundIDBCursorChild* +BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBCursorChild( + const OpenCursorParams& aParams) +{ + AssertIsOnOwningThread(); + + MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!"); +} + +bool +BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBCursorChild( + PBackgroundIDBCursorChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +/******************************************************************************* + * BackgroundMutableFileChild + ******************************************************************************/ + +BackgroundMutableFileChild::BackgroundMutableFileChild( + DEBUGONLY(PRThread* aOwningThread,) + const nsAString& aName, + const nsAString& aType) + : BackgroundMutableFileChildBase(DEBUGONLY(aOwningThread)) + , mName(aName) + , mType(aType) +{ + // Can't assert owning thread here because IPDL has not yet set our manager! + MOZ_COUNT_CTOR(indexedDB::BackgroundMutableFileChild); +} + +BackgroundMutableFileChild::~BackgroundMutableFileChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundMutableFileChild); +} + +already_AddRefed +BackgroundMutableFileChild::CreateMutableFile() +{ + auto database = + static_cast(Manager())->GetDOMObject(); + MOZ_ASSERT(database); + + RefPtr mutableFile = + new IDBMutableFile(database, this, mName, mType); + + return mutableFile.forget(); +} + +/******************************************************************************* + * BackgroundRequestChild + ******************************************************************************/ + +BackgroundRequestChild::BackgroundRequestChild(IDBRequest* aRequest) + : BackgroundRequestChildBase(aRequest) + , mTransaction(aRequest->GetTransaction()) + , mRunningPreprocessHelpers(0) + , mCurrentModuleSetIndex(0) + , mPreprocessResultCode(NS_OK) + , mGetAll(false) +{ + MOZ_ASSERT(mTransaction); + mTransaction->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChild); +} + +BackgroundRequestChild::~BackgroundRequestChild() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mTransaction); + + MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild); +} + +void +BackgroundRequestChild::MaybeSendContinue() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRunningPreprocessHelpers > 0); + + if (--mRunningPreprocessHelpers == 0) { + PreprocessResponse response; + + if (NS_SUCCEEDED(mPreprocessResultCode)) { + if (mGetAll) { + response = ObjectStoreGetAllPreprocessResponse(); + } else { + response = ObjectStoreGetPreprocessResponse(); + } + } else { + response = mPreprocessResultCode; + } + + MOZ_ALWAYS_TRUE(SendContinue(response)); + } +} + +void +BackgroundRequestChild::OnPreprocessFinished( + uint32_t aModuleSetIndex, + nsTArray>& aModuleSet) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aModuleSetIndex < mPreprocessHelpers.Length()); + MOZ_ASSERT(!aModuleSet.IsEmpty()); + MOZ_ASSERT(mPreprocessHelpers[aModuleSetIndex]); + MOZ_ASSERT(mModuleSets[aModuleSetIndex].IsEmpty()); + + mModuleSets[aModuleSetIndex].SwapElements(aModuleSet); + + MaybeSendContinue(); + + mPreprocessHelpers[aModuleSetIndex] = nullptr; +} + +void +BackgroundRequestChild::OnPreprocessFailed(uint32_t aModuleSetIndex, + nsresult aErrorCode) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aModuleSetIndex < mPreprocessHelpers.Length()); + MOZ_ASSERT(NS_FAILED(aErrorCode)); + MOZ_ASSERT(mPreprocessHelpers[aModuleSetIndex]); + MOZ_ASSERT(mModuleSets[aModuleSetIndex].IsEmpty()); + + if (NS_SUCCEEDED(mPreprocessResultCode)) { + mPreprocessResultCode = aErrorCode; + } + + MaybeSendContinue(); + + mPreprocessHelpers[aModuleSetIndex] = nullptr; +} + +const nsTArray>* +BackgroundRequestChild::GetNextModuleSet(const StructuredCloneReadInfo& aInfo) +{ + if (!aInfo.mHasPreprocessInfo) { + return nullptr; + } + + MOZ_ASSERT(mCurrentModuleSetIndex < mModuleSets.Length()); + MOZ_ASSERT(!mModuleSets[mCurrentModuleSetIndex].IsEmpty()); + return &mModuleSets[mCurrentModuleSetIndex++]; +} + +void +BackgroundRequestChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); + MOZ_ASSERT(mTransaction); + + DispatchErrorEvent(mRequest, aResponse, mTransaction); +} + +void +BackgroundRequestChild::HandleResponse(const Key& aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mRequest, mTransaction, &aResponse); + + DispatchSuccessEvent(&helper); +} + +void +BackgroundRequestChild::HandleResponse(const nsTArray& aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mRequest, mTransaction, &aResponse); + + DispatchSuccessEvent(&helper); +} + +void +BackgroundRequestChild::HandleResponse( + const SerializedStructuredCloneReadInfo& aResponse) +{ + AssertIsOnOwningThread(); + + // XXX Fix this somehow... + auto& serializedCloneInfo = + const_cast(aResponse); + + StructuredCloneReadInfo cloneReadInfo(Move(serializedCloneInfo)); + + DeserializeStructuredCloneFiles(mTransaction->Database(), + aResponse.files(), + GetNextModuleSet(cloneReadInfo), + cloneReadInfo.mFiles); + + ResultHelper helper(mRequest, mTransaction, &cloneReadInfo); + + DispatchSuccessEvent(&helper); +} + +void +BackgroundRequestChild::HandleResponse( + const nsTArray& aResponse) +{ + AssertIsOnOwningThread(); + + nsTArray cloneReadInfos; + + if (!aResponse.IsEmpty()) { + const uint32_t count = aResponse.Length(); + + cloneReadInfos.SetCapacity(count); + + IDBDatabase* database = mTransaction->Database(); + + for (uint32_t index = 0; index < count; index++) { + // XXX Fix this somehow... + auto& serializedCloneInfo = + const_cast(aResponse[index]); + + StructuredCloneReadInfo* cloneReadInfo = cloneReadInfos.AppendElement(); + + // Move relevant data into the cloneReadInfo + *cloneReadInfo = Move(serializedCloneInfo); + + // Get the files + nsTArray files; + DeserializeStructuredCloneFiles(database, + serializedCloneInfo.files(), + GetNextModuleSet(*cloneReadInfo), + files); + + cloneReadInfo->mFiles = Move(files); + } + } + + ResultHelper helper(mRequest, mTransaction, &cloneReadInfos); + + DispatchSuccessEvent(&helper); +} + +void +BackgroundRequestChild::HandleResponse(JS::Handle aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mRequest, mTransaction, &aResponse); + + DispatchSuccessEvent(&helper); +} + +void +BackgroundRequestChild::HandleResponse(uint64_t aResponse) +{ + AssertIsOnOwningThread(); + + JS::Value response(JS::NumberValue(aResponse)); + + ResultHelper helper(mRequest, mTransaction, &response); + + DispatchSuccessEvent(&helper); +} + +nsresult +BackgroundRequestChild::HandlePreprocess( + const WasmModulePreprocessInfo& aPreprocessInfo) +{ + AssertIsOnOwningThread(); + + IDBDatabase* database = mTransaction->Database(); + + mPreprocessHelpers.SetLength(1); + + nsTArray files; + DeserializeStructuredCloneFiles(database, + aPreprocessInfo.files(), + nullptr, + files); + + + RefPtr& preprocessHelper = mPreprocessHelpers[0]; + preprocessHelper = new PreprocessHelper(0, this); + + nsresult rv = preprocessHelper->Init(files); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = preprocessHelper->Dispatch(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mRunningPreprocessHelpers++; + + mModuleSets.SetLength(1); + + return NS_OK; +} + +nsresult +BackgroundRequestChild::HandlePreprocess( + const nsTArray& aPreprocessInfos) +{ + AssertIsOnOwningThread(); + + IDBDatabase* database = mTransaction->Database(); + + uint32_t count = aPreprocessInfos.Length(); + + mPreprocessHelpers.SetLength(count); + + // TODO: Since we use the stream transport service, this can spawn 25 threads + // and has the potential to cause some annoying browser hiccups. + // Consider using a single thread or a very small threadpool. + for (uint32_t index = 0; index < count; index++) { + const WasmModulePreprocessInfo& preprocessInfo = aPreprocessInfos[index]; + + nsTArray files; + DeserializeStructuredCloneFiles(database, + preprocessInfo.files(), + nullptr, + files); + + + RefPtr& preprocessHelper = mPreprocessHelpers[index]; + preprocessHelper = new PreprocessHelper(index, this); + + nsresult rv = preprocessHelper->Init(files); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = preprocessHelper->Dispatch(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mRunningPreprocessHelpers++; + } + + mModuleSets.SetLength(count); + + mGetAll = true; + + return NS_OK; +} + +void +BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + for (uint32_t count = mPreprocessHelpers.Length(), index = 0; + index < count; + index++) { + RefPtr& preprocessHelper = mPreprocessHelpers[index]; + + if (preprocessHelper) { + preprocessHelper->ClearActor(); + + preprocessHelper = nullptr; + } + } + + if (mTransaction) { + mTransaction->AssertIsOnOwningThread(); + + mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ + aWhy == Deletion); +#ifdef DEBUG + mTransaction = nullptr; +#endif + } +} + +bool +BackgroundRequestChild::Recv__delete__(const RequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + + MaybeCollectGarbageOnIPCMessage(); + + if (mTransaction->IsAborted()) { + // Always fire an "error" event with ABORT_ERR if the transaction was + // aborted, even if the request succeeded or failed with another error. + HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); + } else { + switch (aResponse.type()) { + case RequestResponse::Tnsresult: + HandleResponse(aResponse.get_nsresult()); + break; + + case RequestResponse::TObjectStoreAddResponse: + HandleResponse(aResponse.get_ObjectStoreAddResponse().key()); + break; + + case RequestResponse::TObjectStorePutResponse: + HandleResponse(aResponse.get_ObjectStorePutResponse().key()); + break; + + case RequestResponse::TObjectStoreGetResponse: + HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo()); + break; + + case RequestResponse::TObjectStoreGetKeyResponse: + HandleResponse(aResponse.get_ObjectStoreGetKeyResponse().key()); + break; + + case RequestResponse::TObjectStoreGetAllResponse: + HandleResponse(aResponse.get_ObjectStoreGetAllResponse().cloneInfos()); + break; + + case RequestResponse::TObjectStoreGetAllKeysResponse: + HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse().keys()); + break; + + case RequestResponse::TObjectStoreDeleteResponse: + HandleResponse(JS::UndefinedHandleValue); + break; + + case RequestResponse::TObjectStoreClearResponse: + HandleResponse(JS::UndefinedHandleValue); + break; + + case RequestResponse::TObjectStoreCountResponse: + HandleResponse(aResponse.get_ObjectStoreCountResponse().count()); + break; + + case RequestResponse::TIndexGetResponse: + HandleResponse(aResponse.get_IndexGetResponse().cloneInfo()); + break; + + case RequestResponse::TIndexGetKeyResponse: + HandleResponse(aResponse.get_IndexGetKeyResponse().key()); + break; + + case RequestResponse::TIndexGetAllResponse: + HandleResponse(aResponse.get_IndexGetAllResponse().cloneInfos()); + break; + + case RequestResponse::TIndexGetAllKeysResponse: + HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys()); + break; + + case RequestResponse::TIndexCountResponse: + HandleResponse(aResponse.get_IndexCountResponse().count()); + break; + + default: + MOZ_CRASH("Unknown response type!"); + } + } + + mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ true); + + // Null this out so that we don't try to call OnRequestFinished() again in + // ActorDestroy. + mTransaction = nullptr; + + return true; +} + +bool +BackgroundRequestChild::RecvPreprocess(const PreprocessParams& aParams) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mTransaction); + + MaybeCollectGarbageOnIPCMessage(); + + nsresult rv; + + switch (aParams.type()) { + case PreprocessParams::TObjectStoreGetPreprocessParams: { + ObjectStoreGetPreprocessParams params = + aParams.get_ObjectStoreGetPreprocessParams(); + + rv = HandlePreprocess(params.preprocessInfo()); + + break; + } + + case PreprocessParams::TObjectStoreGetAllPreprocessParams: { + ObjectStoreGetAllPreprocessParams params = + aParams.get_ObjectStoreGetAllPreprocessParams(); + + rv = HandlePreprocess(params.preprocessInfos()); + + break; + } + + default: + MOZ_CRASH("Unknown params type!"); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return SendContinue(rv); + } + + return true; +} + +nsresult +BackgroundRequestChild:: +PreprocessHelper::Init(const nsTArray& aFiles) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!aFiles.IsEmpty()); + + uint32_t count = aFiles.Length(); + + // We should receive even number of files. + MOZ_ASSERT(count % 2 == 0); + + // Let's process it as pairs. + count = count / 2; + + nsTArray streamPairs; + for (uint32_t index = 0; index < count; index++) { + uint32_t bytecodeIndex = index * 2; + uint32_t compiledIndex = bytecodeIndex + 1; + + const StructuredCloneFile& bytecodeFile = aFiles[bytecodeIndex]; + const StructuredCloneFile& compiledFile = aFiles[compiledIndex]; + + MOZ_ASSERT(bytecodeFile.mType == StructuredCloneFile::eWasmBytecode); + MOZ_ASSERT(bytecodeFile.mBlob); + MOZ_ASSERT(compiledFile.mType == StructuredCloneFile::eWasmCompiled); + MOZ_ASSERT(compiledFile.mBlob); + + ErrorResult errorResult; + + nsCOMPtr bytecodeStream; + bytecodeFile.mBlob->GetInternalStream(getter_AddRefs(bytecodeStream), + errorResult); + if (NS_WARN_IF(errorResult.Failed())) { + return errorResult.StealNSResult(); + } + + nsCOMPtr compiledStream; + compiledFile.mBlob->GetInternalStream(getter_AddRefs(compiledStream), + errorResult); + if (NS_WARN_IF(errorResult.Failed())) { + return errorResult.StealNSResult(); + } + + streamPairs.AppendElement(StreamPair(bytecodeStream, compiledStream)); + } + + mStreamPairs = Move(streamPairs); + + return NS_OK; +} + +nsresult +BackgroundRequestChild:: +PreprocessHelper::Dispatch() +{ + AssertIsOnOwningThread(); + + // The stream transport service is used for asynchronous processing. It has + // a threadpool with a high cap of 25 threads. Fortunately, the service can + // be used on workers too. + nsCOMPtr target = + do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + MOZ_ASSERT(target); + + nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +void +BackgroundRequestChild:: +PreprocessHelper::RunOnOwningThread() +{ + AssertIsOnOwningThread(); + + if (mActor) { + if (NS_SUCCEEDED(mResultCode)) { + mActor->OnPreprocessFinished(mModuleSetIndex, mModuleSet); + + MOZ_ASSERT(mModuleSet.IsEmpty()); + } else { + mActor->OnPreprocessFailed(mModuleSetIndex, mResultCode); + } + } +} + +nsresult +BackgroundRequestChild:: +PreprocessHelper::RunOnStreamTransportThread() +{ + MOZ_ASSERT(!IsOnOwningThread()); + MOZ_ASSERT(!mStreamPairs.IsEmpty()); + MOZ_ASSERT(mModuleSet.IsEmpty()); + + const uint32_t count = mStreamPairs.Length(); + + for (uint32_t index = 0; index < count; index++) { + const StreamPair& streamPair = mStreamPairs[index]; + + const nsCOMPtr& bytecodeStream = streamPair.first; + + MOZ_ASSERT(bytecodeStream); + + PRFileDesc* bytecodeFileDesc = GetFileDescriptorFromStream(bytecodeStream); + if (NS_WARN_IF(!bytecodeFileDesc)) { + return NS_ERROR_FAILURE; + } + + const nsCOMPtr& compiledStream = streamPair.second; + + MOZ_ASSERT(compiledStream); + + PRFileDesc* compiledFileDesc = GetFileDescriptorFromStream(compiledStream); + if (NS_WARN_IF(!compiledFileDesc)) { + return NS_ERROR_FAILURE; + } + + JS::BuildIdCharVector buildId; + bool ok = GetBuildId(&buildId); + if (NS_WARN_IF(!ok)) { + return NS_ERROR_FAILURE; + } + + RefPtr module = JS::DeserializeWasmModule(bytecodeFileDesc, + compiledFileDesc, + Move(buildId), + nullptr, + 0, + 0); + if (NS_WARN_IF(!module)) { + return NS_ERROR_FAILURE; + } + + mModuleSet.AppendElement(module); + } + + mStreamPairs.Clear(); + + return NS_OK; +} + +NS_IMETHODIMP +BackgroundRequestChild:: +PreprocessHelper::Run() +{ + if (IsOnOwningThread()) { + RunOnOwningThread(); + } else { + nsresult rv = RunOnStreamTransportThread(); + if (NS_WARN_IF(NS_FAILED(rv))) { + MOZ_ASSERT(mResultCode == NS_OK); + mResultCode = rv; + } + + MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL)); + } + + return NS_OK; +} + +nsresult +BackgroundRequestChild:: +PreprocessHelper::Cancel() +{ + return NS_OK; +} + +/******************************************************************************* + * BackgroundCursorChild + ******************************************************************************/ + +// Does not need to be threadsafe since this only runs on one thread, but +// inheriting from CancelableRunnable is easy. +class BackgroundCursorChild::DelayedActionRunnable final + : public CancelableRunnable +{ + using ActionFunc = void (BackgroundCursorChild::*)(); + + BackgroundCursorChild* mActor; + RefPtr mRequest; + ActionFunc mActionFunc; + +public: + explicit + DelayedActionRunnable(BackgroundCursorChild* aActor, ActionFunc aActionFunc) + : mActor(aActor) + , mRequest(aActor->mRequest) + , mActionFunc(aActionFunc) + { + MOZ_ASSERT(aActor); + aActor->AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mActionFunc); + } + +private: + ~DelayedActionRunnable() + { } + + NS_DECL_NSIRUNNABLE + nsresult Cancel() override; +}; + +BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + Direction aDirection) + : mRequest(aRequest) + , mTransaction(aRequest->GetTransaction()) + , mObjectStore(aObjectStore) + , mIndex(nullptr) + , mCursor(nullptr) + , mStrongRequest(aRequest) + , mDirection(aDirection) +{ + MOZ_ASSERT(aObjectStore); + aObjectStore->AssertIsOnOwningThread(); + MOZ_ASSERT(mTransaction); + + MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild); + +#ifdef DEBUG + mOwningThread = PR_GetCurrentThread(); + MOZ_ASSERT(mOwningThread); +#endif +} + +BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest, + IDBIndex* aIndex, + Direction aDirection) + : mRequest(aRequest) + , mTransaction(aRequest->GetTransaction()) + , mObjectStore(nullptr) + , mIndex(aIndex) + , mCursor(nullptr) + , mStrongRequest(aRequest) + , mDirection(aDirection) +{ + MOZ_ASSERT(aIndex); + aIndex->AssertIsOnOwningThread(); + MOZ_ASSERT(mTransaction); + + MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild); + +#ifdef DEBUG + mOwningThread = PR_GetCurrentThread(); + MOZ_ASSERT(mOwningThread); +#endif +} + +BackgroundCursorChild::~BackgroundCursorChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundCursorChild); +} + +#ifdef DEBUG + +void +BackgroundCursorChild::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread == PR_GetCurrentThread()); +} + +#endif // DEBUG + +void +BackgroundCursorChild::SendContinueInternal(const CursorRequestParams& aParams) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mCursor); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + // Make sure all our DOM objects stay alive. + mStrongCursor = mCursor; + + MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done); + mRequest->Reset(); + + mTransaction->OnNewRequest(); + + MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendContinue(aParams)); +} + +void +BackgroundCursorChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + mRequest = nullptr; + mTransaction = nullptr; + mObjectStore = nullptr; + mIndex = nullptr; + + if (mCursor) { + mCursor->ClearBackgroundActor(); + mCursor = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendDeleteMe()); + } +} + +void +BackgroundCursorChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + DispatchErrorEvent(mRequest, aResponse, mTransaction); +} + +void +BackgroundCursorChild::HandleResponse(const void_t& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + if (mCursor) { + mCursor->Reset(); + } + + ResultHelper helper(mRequest, mTransaction, &JS::NullHandleValue); + DispatchSuccessEvent(&helper); + + if (!mCursor) { + nsCOMPtr deleteRunnable = new DelayedActionRunnable( + this, &BackgroundCursorChild::SendDeleteMeInternal); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(deleteRunnable)); + } +} + +void +BackgroundCursorChild::HandleResponse( + const nsTArray& aResponses) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mObjectStore); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + MOZ_ASSERT(aResponses.Length() == 1); + + // XXX Fix this somehow... + auto& responses = + const_cast&>(aResponses); + + for (ObjectStoreCursorResponse& response : responses) { + StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo())); + cloneReadInfo.mDatabase = mTransaction->Database(); + + DeserializeStructuredCloneFiles(mTransaction->Database(), + response.cloneInfo().files(), + nullptr, + cloneReadInfo.mFiles); + + RefPtr newCursor; + + if (mCursor) { + mCursor->Reset(Move(response.key()), Move(cloneReadInfo)); + } else { + newCursor = IDBCursor::Create(this, + Move(response.key()), + Move(cloneReadInfo)); + mCursor = newCursor; + } + } + + ResultHelper helper(mRequest, mTransaction, mCursor); + DispatchSuccessEvent(&helper); +} + +void +BackgroundCursorChild::HandleResponse( + const ObjectStoreKeyCursorResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mObjectStore); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + // XXX Fix this somehow... + auto& response = const_cast(aResponse); + + RefPtr newCursor; + + if (mCursor) { + mCursor->Reset(Move(response.key())); + } else { + newCursor = IDBCursor::Create(this, Move(response.key())); + mCursor = newCursor; + } + + ResultHelper helper(mRequest, mTransaction, mCursor); + DispatchSuccessEvent(&helper); +} + +void +BackgroundCursorChild::HandleResponse(const IndexCursorResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mIndex); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + // XXX Fix this somehow... + auto& response = const_cast(aResponse); + + StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo())); + cloneReadInfo.mDatabase = mTransaction->Database(); + + DeserializeStructuredCloneFiles(mTransaction->Database(), + aResponse.cloneInfo().files(), + nullptr, + cloneReadInfo.mFiles); + + RefPtr newCursor; + + if (mCursor) { + mCursor->Reset(Move(response.key()), + Move(response.sortKey()), + Move(response.objectKey()), + Move(cloneReadInfo)); + } else { + newCursor = IDBCursor::Create(this, + Move(response.key()), + Move(response.sortKey()), + Move(response.objectKey()), + Move(cloneReadInfo)); + mCursor = newCursor; + } + + ResultHelper helper(mRequest, mTransaction, mCursor); + DispatchSuccessEvent(&helper); +} + +void +BackgroundCursorChild::HandleResponse(const IndexKeyCursorResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mIndex); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + // XXX Fix this somehow... + auto& response = const_cast(aResponse); + + RefPtr newCursor; + + if (mCursor) { + mCursor->Reset(Move(response.key()), + Move(response.sortKey()), + Move(response.objectKey())); + } else { + newCursor = IDBCursor::Create(this, + Move(response.key()), + Move(response.sortKey()), + Move(response.objectKey())); + mCursor = newCursor; + } + + ResultHelper helper(mRequest, mTransaction, mCursor); + DispatchSuccessEvent(&helper); +} + +void +BackgroundCursorChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(aWhy == Deletion, !mStrongRequest); + MOZ_ASSERT_IF(aWhy == Deletion, !mStrongCursor); + + MaybeCollectGarbageOnIPCMessage(); + + if (mStrongRequest && !mStrongCursor && mTransaction) { + mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ + aWhy == Deletion); + } + + if (mCursor) { + mCursor->ClearBackgroundActor(); +#ifdef DEBUG + mCursor = nullptr; +#endif + } + +#ifdef DEBUG + mRequest = nullptr; + mTransaction = nullptr; + mObjectStore = nullptr; + mIndex = nullptr; +#endif +} + +bool +BackgroundCursorChild::RecvResponse(const CursorResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aResponse.type() != CursorResponse::T__None); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT_IF(mCursor, mStrongCursor); + MOZ_ASSERT_IF(!mCursor, mStrongRequest); + + MaybeCollectGarbageOnIPCMessage(); + + RefPtr request; + mStrongRequest.swap(request); + + RefPtr cursor; + mStrongCursor.swap(cursor); + + switch (aResponse.type()) { + case CursorResponse::Tnsresult: + HandleResponse(aResponse.get_nsresult()); + break; + + case CursorResponse::Tvoid_t: + HandleResponse(aResponse.get_void_t()); + break; + + case CursorResponse::TArrayOfObjectStoreCursorResponse: + HandleResponse(aResponse.get_ArrayOfObjectStoreCursorResponse()); + break; + + case CursorResponse::TObjectStoreKeyCursorResponse: + HandleResponse(aResponse.get_ObjectStoreKeyCursorResponse()); + break; + + case CursorResponse::TIndexCursorResponse: + HandleResponse(aResponse.get_IndexCursorResponse()); + break; + + case CursorResponse::TIndexKeyCursorResponse: + HandleResponse(aResponse.get_IndexKeyCursorResponse()); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ true); + + return true; +} + +NS_IMETHODIMP +BackgroundCursorChild:: +DelayedActionRunnable::Run() +{ + MOZ_ASSERT(mActor); + mActor->AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mActionFunc); + + (mActor->*mActionFunc)(); + + mActor = nullptr; + mRequest = nullptr; + + return NS_OK; +} + +nsresult +BackgroundCursorChild:: +DelayedActionRunnable::Cancel() +{ + if (NS_WARN_IF(!mActor)) { + return NS_ERROR_UNEXPECTED; + } + + // This must always run to clean up our state. + Run(); + + return NS_OK; +} + +/******************************************************************************* + * BackgroundUtilsChild + ******************************************************************************/ + +BackgroundUtilsChild::BackgroundUtilsChild(IndexedDatabaseManager* aManager) + : mManager(aManager) +#ifdef DEBUG + , mOwningThread(NS_GetCurrentThread()) +#endif +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aManager); + + MOZ_COUNT_CTOR(indexedDB::BackgroundUtilsChild); +} + +BackgroundUtilsChild::~BackgroundUtilsChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundUtilsChild); +} + +#ifdef DEBUG + +void +BackgroundUtilsChild::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread); + + bool current; + MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); +} + +#endif // DEBUG + +void +BackgroundUtilsChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + + if (mManager) { + mManager->ClearBackgroundActor(); + mManager = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundIndexedDBUtilsChild::SendDeleteMe()); + } +} + +void +BackgroundUtilsChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + if (mManager) { + mManager->ClearBackgroundActor(); +#ifdef DEBUG + mManager = nullptr; +#endif + } +} + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ActorsChild.h b/dom/indexedDB/ActorsChild.h new file mode 100644 index 000000000..58059787c --- /dev/null +++ b/dom/indexedDB/ActorsChild.h @@ -0,0 +1,877 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_actorschild_h__ +#define mozilla_dom_indexeddb_actorschild_h__ + +#include "IDBTransaction.h" +#include "js/RootingAPI.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/filehandle/ActorsChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBCursorChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBRequestChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBTransactionChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h" +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsTArray.h" + +class nsIEventTarget; +struct nsID; +struct PRThread; + +namespace JS { +struct WasmModule; +} // namespace JS + +namespace mozilla { +namespace ipc { + +class BackgroundChildImpl; + +} // namespace ipc + +namespace dom { + +class IDBCursor; +class IDBDatabase; +class IDBFactory; +class IDBMutableFile; +class IDBOpenDBRequest; +class IDBRequest; +class IndexedDatabaseManager; + +namespace indexedDB { + +class Key; +class PermissionRequestChild; +class PermissionRequestParent; +class SerializedStructuredCloneReadInfo; + +class ThreadLocal +{ + friend class nsAutoPtr; + friend IDBFactory; + + LoggingInfo mLoggingInfo; + IDBTransaction* mCurrentTransaction; + nsCString mLoggingIdString; + +#ifdef DEBUG + PRThread* mOwningThread; +#endif + +public: + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + const LoggingInfo& + GetLoggingInfo() const + { + AssertIsOnOwningThread(); + + return mLoggingInfo; + } + + const nsID& + Id() const + { + AssertIsOnOwningThread(); + + return mLoggingInfo.backgroundChildLoggingId(); + } + + const nsCString& + IdString() const + { + AssertIsOnOwningThread(); + + return mLoggingIdString; + } + + int64_t + NextTransactionSN(IDBTransaction::Mode aMode) + { + AssertIsOnOwningThread(); + MOZ_ASSERT(mLoggingInfo.nextTransactionSerialNumber() < INT64_MAX); + MOZ_ASSERT(mLoggingInfo.nextVersionChangeTransactionSerialNumber() > + INT64_MIN); + + if (aMode == IDBTransaction::VERSION_CHANGE) { + return mLoggingInfo.nextVersionChangeTransactionSerialNumber()--; + } + + return mLoggingInfo.nextTransactionSerialNumber()++; + } + + uint64_t + NextRequestSN() + { + AssertIsOnOwningThread(); + MOZ_ASSERT(mLoggingInfo.nextRequestSerialNumber() < UINT64_MAX); + + return mLoggingInfo.nextRequestSerialNumber()++; + } + + void + SetCurrentTransaction(IDBTransaction* aCurrentTransaction) + { + AssertIsOnOwningThread(); + + mCurrentTransaction = aCurrentTransaction; + } + + IDBTransaction* + GetCurrentTransaction() const + { + AssertIsOnOwningThread(); + + return mCurrentTransaction; + } + +private: + explicit ThreadLocal(const nsID& aBackgroundChildLoggingId); + ~ThreadLocal(); + + ThreadLocal() = delete; + ThreadLocal(const ThreadLocal& aOther) = delete; +}; + +class BackgroundFactoryChild final + : public PBackgroundIDBFactoryChild +{ + friend class mozilla::ipc::BackgroundChildImpl; + friend IDBFactory; + + IDBFactory* mFactory; + +#ifdef DEBUG + nsCOMPtr mOwningThread; +#endif + +public: +#ifdef DEBUG + void + AssertIsOnOwningThread() const; + + nsIEventTarget* + OwningThread() const; +#else + void + AssertIsOnOwningThread() const + { } +#endif + + IDBFactory* + GetDOMObject() const + { + AssertIsOnOwningThread(); + return mFactory; + } + +private: + // Only created by IDBFactory. + explicit BackgroundFactoryChild(IDBFactory* aFactory); + + // Only destroyed by mozilla::ipc::BackgroundChildImpl. + ~BackgroundFactoryChild(); + + void + SendDeleteMeInternal(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual PBackgroundIDBFactoryRequestChild* + AllocPBackgroundIDBFactoryRequestChild(const FactoryRequestParams& aParams) + override; + + virtual bool + DeallocPBackgroundIDBFactoryRequestChild( + PBackgroundIDBFactoryRequestChild* aActor) + override; + + virtual PBackgroundIDBDatabaseChild* + AllocPBackgroundIDBDatabaseChild(const DatabaseSpec& aSpec, + PBackgroundIDBFactoryRequestChild* aRequest) + override; + + virtual bool + DeallocPBackgroundIDBDatabaseChild(PBackgroundIDBDatabaseChild* aActor) + override; + + bool + SendDeleteMe() = delete; +}; + +class BackgroundDatabaseChild; + +class BackgroundRequestChildBase +{ +protected: + RefPtr mRequest; + +public: + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + +protected: + explicit BackgroundRequestChildBase(IDBRequest* aRequest); + + virtual + ~BackgroundRequestChildBase(); +}; + +class BackgroundFactoryRequestChild final + : public BackgroundRequestChildBase + , public PBackgroundIDBFactoryRequestChild +{ + typedef mozilla::dom::quota::PersistenceType PersistenceType; + + friend IDBFactory; + friend class BackgroundFactoryChild; + friend class BackgroundDatabaseChild; + friend class PermissionRequestChild; + friend class PermissionRequestParent; + + RefPtr mFactory; + const uint64_t mRequestedVersion; + const bool mIsDeleteOp; + +public: + IDBOpenDBRequest* + GetOpenDBRequest() const; + +private: + // Only created by IDBFactory. + BackgroundFactoryRequestChild(IDBFactory* aFactory, + IDBOpenDBRequest* aOpenRequest, + bool aIsDeleteOp, + uint64_t aRequestedVersion); + + // Only destroyed by BackgroundFactoryChild. + ~BackgroundFactoryRequestChild(); + + bool + HandleResponse(nsresult aResponse); + + bool + HandleResponse(const OpenDatabaseRequestResponse& aResponse); + + bool + HandleResponse(const DeleteDatabaseRequestResponse& aResponse); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool + Recv__delete__(const FactoryRequestResponse& aResponse) override; + + virtual bool + RecvPermissionChallenge(const PrincipalInfo& aPrincipalInfo) override; + + virtual bool + RecvBlocked(const uint64_t& aCurrentVersion) override; +}; + +class BackgroundDatabaseChild final + : public PBackgroundIDBDatabaseChild +{ + friend class BackgroundFactoryChild; + friend class BackgroundFactoryRequestChild; + friend IDBDatabase; + + nsAutoPtr mSpec; + RefPtr mTemporaryStrongDatabase; + BackgroundFactoryRequestChild* mOpenRequestActor; + IDBDatabase* mDatabase; + +public: + void + AssertIsOnOwningThread() const + { + static_cast(Manager())->AssertIsOnOwningThread(); + } + +#ifdef DEBUG + nsIEventTarget* + OwningThread() const + { + return static_cast(Manager())->OwningThread(); + } +#endif + + const DatabaseSpec* + Spec() const + { + AssertIsOnOwningThread(); + return mSpec; + } + + IDBDatabase* + GetDOMObject() const + { + AssertIsOnOwningThread(); + return mDatabase; + } + +private: + // Only constructed by BackgroundFactoryChild. + BackgroundDatabaseChild(const DatabaseSpec& aSpec, + BackgroundFactoryRequestChild* aOpenRequest); + + // Only destroyed by BackgroundFactoryChild. + ~BackgroundDatabaseChild(); + + void + SendDeleteMeInternal(); + + void + EnsureDOMObject(); + + void + ReleaseDOMObject(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual PBackgroundIDBDatabaseFileChild* + AllocPBackgroundIDBDatabaseFileChild(PBlobChild* aBlobChild) + override; + + virtual bool + DeallocPBackgroundIDBDatabaseFileChild( + PBackgroundIDBDatabaseFileChild* aActor) + override; + + virtual PBackgroundIDBDatabaseRequestChild* + AllocPBackgroundIDBDatabaseRequestChild(const DatabaseRequestParams& aParams) + override; + + virtual bool + DeallocPBackgroundIDBDatabaseRequestChild( + PBackgroundIDBDatabaseRequestChild* aActor) + override; + + virtual PBackgroundIDBTransactionChild* + AllocPBackgroundIDBTransactionChild( + const nsTArray& aObjectStoreNames, + const Mode& aMode) + override; + + virtual bool + DeallocPBackgroundIDBTransactionChild(PBackgroundIDBTransactionChild* aActor) + override; + + virtual PBackgroundIDBVersionChangeTransactionChild* + AllocPBackgroundIDBVersionChangeTransactionChild( + const uint64_t& aCurrentVersion, + const uint64_t& aRequestedVersion, + const int64_t& aNextObjectStoreId, + const int64_t& aNextIndexId) + override; + + virtual bool + RecvPBackgroundIDBVersionChangeTransactionConstructor( + PBackgroundIDBVersionChangeTransactionChild* aActor, + const uint64_t& aCurrentVersion, + const uint64_t& aRequestedVersion, + const int64_t& aNextObjectStoreId, + const int64_t& aNextIndexId) + override; + + virtual bool + DeallocPBackgroundIDBVersionChangeTransactionChild( + PBackgroundIDBVersionChangeTransactionChild* aActor) + override; + + virtual PBackgroundMutableFileChild* + AllocPBackgroundMutableFileChild(const nsString& aName, + const nsString& aType) override; + + virtual bool + DeallocPBackgroundMutableFileChild(PBackgroundMutableFileChild* aActor) + override; + + virtual bool + RecvVersionChange(const uint64_t& aOldVersion, + const NullableVersion& aNewVersion) + override; + + virtual bool + RecvInvalidate() override; + + virtual bool + RecvCloseAfterInvalidationComplete() override; + + bool + SendDeleteMe() = delete; +}; + +class BackgroundDatabaseRequestChild final + : public BackgroundRequestChildBase + , public PBackgroundIDBDatabaseRequestChild +{ + friend class BackgroundDatabaseChild; + friend IDBDatabase; + + RefPtr mDatabase; + +private: + // Only created by IDBDatabase. + BackgroundDatabaseRequestChild(IDBDatabase* aDatabase, + IDBRequest* aRequest); + + // Only destroyed by BackgroundDatabaseChild. + ~BackgroundDatabaseRequestChild(); + + bool + HandleResponse(nsresult aResponse); + + bool + HandleResponse(const CreateFileRequestResponse& aResponse); + + // IPDL methods are only called by IPDL. + virtual bool + Recv__delete__(const DatabaseRequestResponse& aResponse) override; +}; + +class BackgroundVersionChangeTransactionChild; + +class BackgroundTransactionBase +{ + friend class BackgroundVersionChangeTransactionChild; + + // mTemporaryStrongTransaction is strong and is only valid until the end of + // NoteComplete() member function or until the NoteActorDestroyed() member + // function is called. + RefPtr mTemporaryStrongTransaction; + +protected: + // mTransaction is weak and is valid until the NoteActorDestroyed() member + // function is called. + IDBTransaction* mTransaction; + +public: +#ifdef DEBUG + virtual void + AssertIsOnOwningThread() const = 0; +#else + void + AssertIsOnOwningThread() const + { } +#endif + + IDBTransaction* + GetDOMObject() const + { + AssertIsOnOwningThread(); + return mTransaction; + } + +protected: + BackgroundTransactionBase(); + explicit BackgroundTransactionBase(IDBTransaction* aTransaction); + + virtual + ~BackgroundTransactionBase(); + + void + NoteActorDestroyed(); + + void + NoteComplete(); + +private: + // Only called by BackgroundVersionChangeTransactionChild. + void + SetDOMTransaction(IDBTransaction* aDOMObject); +}; + +class BackgroundTransactionChild final + : public BackgroundTransactionBase + , public PBackgroundIDBTransactionChild +{ + friend class BackgroundDatabaseChild; + friend IDBDatabase; + +public: +#ifdef DEBUG + virtual void + AssertIsOnOwningThread() const override; +#endif + + void + SendDeleteMeInternal(); + +private: + // Only created by IDBDatabase. + explicit BackgroundTransactionChild(IDBTransaction* aTransaction); + + // Only destroyed by BackgroundDatabaseChild. + ~BackgroundTransactionChild(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + bool + RecvComplete(const nsresult& aResult) override; + + virtual PBackgroundIDBRequestChild* + AllocPBackgroundIDBRequestChild(const RequestParams& aParams) override; + + virtual bool + DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor) + override; + + virtual PBackgroundIDBCursorChild* + AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) override; + + virtual bool + DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor) + override; + + bool + SendDeleteMe() = delete; +}; + +class BackgroundVersionChangeTransactionChild final + : public BackgroundTransactionBase + , public PBackgroundIDBVersionChangeTransactionChild +{ + friend class BackgroundDatabaseChild; + + IDBOpenDBRequest* mOpenDBRequest; + +public: +#ifdef DEBUG + virtual void + AssertIsOnOwningThread() const override; +#endif + + void + SendDeleteMeInternal(bool aFailedConstructor); + +private: + // Only created by BackgroundDatabaseChild. + explicit BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest); + + // Only destroyed by BackgroundDatabaseChild. + ~BackgroundVersionChangeTransactionChild(); + + // Only called by BackgroundDatabaseChild. + void + SetDOMTransaction(IDBTransaction* aDOMObject) + { + BackgroundTransactionBase::SetDOMTransaction(aDOMObject); + } + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + bool + RecvComplete(const nsresult& aResult) override; + + virtual PBackgroundIDBRequestChild* + AllocPBackgroundIDBRequestChild(const RequestParams& aParams) override; + + virtual bool + DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor) + override; + + virtual PBackgroundIDBCursorChild* + AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) override; + + virtual bool + DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor) + override; + + bool + SendDeleteMe() = delete; +}; + +class BackgroundMutableFileChild final + : public mozilla::dom::BackgroundMutableFileChildBase +{ + friend class BackgroundDatabaseChild; + + nsString mName; + nsString mType; + +private: + // Only constructed by BackgroundDatabaseChild. + BackgroundMutableFileChild(DEBUGONLY(PRThread* aOwningThread,) + const nsAString& aName, + const nsAString& aType); + + // Only destroyed by BackgroundDatabaseChild. + ~BackgroundMutableFileChild(); + + // BackgroundMutableFileChildBase + virtual already_AddRefed + CreateMutableFile() override; +}; + +class BackgroundRequestChild final + : public BackgroundRequestChildBase + , public PBackgroundIDBRequestChild +{ + friend class BackgroundTransactionChild; + friend class BackgroundVersionChangeTransactionChild; + friend IDBTransaction; + + class PreprocessHelper; + + RefPtr mTransaction; + nsTArray> mPreprocessHelpers; + nsTArray>> mModuleSets; + uint32_t mRunningPreprocessHelpers; + uint32_t mCurrentModuleSetIndex; + nsresult mPreprocessResultCode; + bool mGetAll; + +private: + // Only created by IDBTransaction. + explicit + BackgroundRequestChild(IDBRequest* aRequest); + + // Only destroyed by BackgroundTransactionChild or + // BackgroundVersionChangeTransactionChild. + ~BackgroundRequestChild(); + + void + MaybeSendContinue(); + + void + OnPreprocessFinished(uint32_t aModuleSetIndex, + nsTArray>& aModuleSet); + + void + OnPreprocessFailed(uint32_t aModuleSetIndex, nsresult aErrorCode); + + const nsTArray>* + GetNextModuleSet(const StructuredCloneReadInfo& aInfo); + + void + HandleResponse(nsresult aResponse); + + void + HandleResponse(const Key& aResponse); + + void + HandleResponse(const nsTArray& aResponse); + + void + HandleResponse(const SerializedStructuredCloneReadInfo& aResponse); + + void + HandleResponse(const nsTArray& aResponse); + + void + HandleResponse(JS::Handle aResponse); + + void + HandleResponse(uint64_t aResponse); + + nsresult + HandlePreprocess(const WasmModulePreprocessInfo& aPreprocessInfo); + + nsresult + HandlePreprocess(const nsTArray& aPreprocessInfos); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool + Recv__delete__(const RequestResponse& aResponse) override; + + virtual bool + RecvPreprocess(const PreprocessParams& aParams) override; +}; + +class BackgroundCursorChild final + : public PBackgroundIDBCursorChild +{ + friend class BackgroundTransactionChild; + friend class BackgroundVersionChangeTransactionChild; + + class DelayedActionRunnable; + + IDBRequest* mRequest; + IDBTransaction* mTransaction; + IDBObjectStore* mObjectStore; + IDBIndex* mIndex; + IDBCursor* mCursor; + + // These are only set while a request is in progress. + RefPtr mStrongRequest; + RefPtr mStrongCursor; + + Direction mDirection; + +#ifdef DEBUG + PRThread* mOwningThread; +#endif + +public: + BackgroundCursorChild(IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + Direction aDirection); + + BackgroundCursorChild(IDBRequest* aRequest, + IDBIndex* aIndex, + Direction aDirection); + + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + void + SendContinueInternal(const CursorRequestParams& aParams); + + void + SendDeleteMeInternal(); + + IDBRequest* + GetRequest() const + { + AssertIsOnOwningThread(); + + return mRequest; + } + + IDBObjectStore* + GetObjectStore() const + { + AssertIsOnOwningThread(); + + return mObjectStore; + } + + IDBIndex* + GetIndex() const + { + AssertIsOnOwningThread(); + + return mIndex; + } + + Direction + GetDirection() const + { + AssertIsOnOwningThread(); + + return mDirection; + } + +private: + // Only destroyed by BackgroundTransactionChild or + // BackgroundVersionChangeTransactionChild. + ~BackgroundCursorChild(); + + void + HandleResponse(nsresult aResponse); + + void + HandleResponse(const void_t& aResponse); + + void + HandleResponse(const nsTArray& aResponse); + + void + HandleResponse(const ObjectStoreKeyCursorResponse& aResponse); + + void + HandleResponse(const IndexCursorResponse& aResponse); + + void + HandleResponse(const IndexKeyCursorResponse& aResponse); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool + RecvResponse(const CursorResponse& aResponse) override; + + // Force callers to use SendContinueInternal. + bool + SendContinue(const CursorRequestParams& aParams) = delete; + + bool + SendDeleteMe() = delete; +}; + +class BackgroundUtilsChild final + : public PBackgroundIndexedDBUtilsChild +{ + friend class mozilla::ipc::BackgroundChildImpl; + friend IndexedDatabaseManager; + + IndexedDatabaseManager* mManager; + +#ifdef DEBUG + nsCOMPtr mOwningThread; +#endif + +public: + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + +private: + // Only created by IndexedDatabaseManager. + explicit BackgroundUtilsChild(IndexedDatabaseManager* aManager); + + // Only destroyed by mozilla::ipc::BackgroundChildImpl. + ~BackgroundUtilsChild(); + + void + SendDeleteMeInternal(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + bool + SendDeleteMe() = delete; +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_indexeddb_actorschild_h__ diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp new file mode 100644 index 000000000..702d5c985 --- /dev/null +++ b/dom/indexedDB/ActorsParent.cpp @@ -0,0 +1,29795 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ActorsParent.h" + +#include +#include "FileInfo.h" +#include "FileManager.h" +#include "IDBObjectStore.h" +#include "IDBTransaction.h" +#include "IndexedDatabase.h" +#include "IndexedDatabaseInlines.h" +#include "IndexedDatabaseManager.h" +#include "js/StructuredClone.h" +#include "js/Value.h" +#include "jsapi.h" +#include "KeyPath.h" +#include "mozilla/Attributes.h" +#include "mozilla/AppProcessChecker.h" +#include "mozilla/AutoRestore.h" +#include "mozilla/Casting.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/ErrorNames.h" +#include "mozilla/LazyIdleThread.h" +#include "mozilla/Maybe.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "mozilla/SnappyCompressOutputStream.h" +#include "mozilla/SnappyUncompressInputStream.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/storage.h" +#include "mozilla/Unused.h" +#include "mozilla/UniquePtrExtensions.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/StructuredCloneTags.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/dom/filehandle/ActorsParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsParent.h" +#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h" +#include "mozilla/dom/ipc/BlobParent.h" +#include "mozilla/dom/quota/Client.h" +#include "mozilla/dom/quota/FileStreams.h" +#include "mozilla/dom/quota/OriginScope.h" +#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/dom/quota/UsageInfo.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/InputStreamParams.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/ipc/PBackground.h" +#include "mozilla/Scoped.h" +#include "mozilla/storage/Variant.h" +#include "nsAutoPtr.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsClassHashtable.h" +#include "nsCOMPtr.h" +#include "nsDataHashtable.h" +#include "nsEscape.h" +#include "nsHashKeys.h" +#include "nsNetUtil.h" +#include "nsISimpleEnumerator.h" +#include "nsIAppsService.h" +#include "nsIEventTarget.h" +#include "nsIFile.h" +#include "nsIFileURL.h" +#include "nsIFileProtocolHandler.h" +#include "nsIInputStream.h" +#include "nsIInterfaceRequestor.h" +#include "nsInterfaceHashtable.h" +#include "nsIOutputStream.h" +#include "nsIPipe.h" +#include "nsIPrincipal.h" +#include "nsIScriptSecurityManager.h" +#include "nsISupports.h" +#include "nsISupportsImpl.h" +#include "nsISupportsPriority.h" +#include "nsIThread.h" +#include "nsITimer.h" +#include "nsIURI.h" +#include "nsNetUtil.h" +#include "nsPrintfCString.h" +#include "nsQueryObject.h" +#include "nsRefPtrHashtable.h" +#include "nsStreamUtils.h" +#include "nsString.h" +#include "nsStringStream.h" +#include "nsThreadPool.h" +#include "nsThreadUtils.h" +#include "nsXPCOMCID.h" +#include "PermissionRequestBase.h" +#include "ProfilerHelpers.h" +#include "prsystem.h" +#include "prtime.h" +#include "ReportInternalError.h" +#include "snappy/snappy.h" + +#define DISABLE_ASSERTS_FOR_FUZZING 0 + +#if DISABLE_ASSERTS_FOR_FUZZING +#define ASSERT_UNLESS_FUZZING(...) do { } while (0) +#else +#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__) +#endif + +#define IDB_DEBUG_LOG(_args) \ + MOZ_LOG(IndexedDatabaseManager::GetLoggingModule(), \ + LogLevel::Debug, \ + _args ) + +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) +#define IDB_MOBILE +#endif + +#define BLOB_IMPL_STORED_FILE_IID \ + {0x6b505c84, 0x2c60, 0x4ffb, {0x8b, 0x91, 0xfe, 0x22, 0xb1, 0xec, 0x75, 0xe2}} + +namespace mozilla { + +MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, + PRFileDesc, + PR_Close); + +namespace dom { +namespace indexedDB { + +using namespace mozilla::dom::quota; +using namespace mozilla::ipc; + +namespace { + +class ConnectionPool; +class Cursor; +class Database; +struct DatabaseActorInfo; +class DatabaseFile; +class DatabaseLoggingInfo; +class DatabaseMaintenance; +class Factory; +class Maintenance; +class MutableFile; +class OpenDatabaseOp; +class TransactionBase; +class TransactionDatabaseOperationBase; +class VersionChangeTransaction; + +/******************************************************************************* + * Constants + ******************************************************************************/ + +// If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major +// schema version. +static_assert(JS_STRUCTURED_CLONE_VERSION == 8, + "Need to update the major schema version."); + +// Major schema version. Bump for almost everything. +const uint32_t kMajorSchemaVersion = 25; + +// Minor schema version. Should almost always be 0 (maybe bump on release +// branches if we have to). +const uint32_t kMinorSchemaVersion = 0; + +// The schema version we store in the SQLite database is a (signed) 32-bit +// integer. The major version is left-shifted 4 bits so the max value is +// 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF. +static_assert(kMajorSchemaVersion <= 0xFFFFFFF, + "Major version needs to fit in 28 bits."); +static_assert(kMinorSchemaVersion <= 0xF, + "Minor version needs to fit in 4 bits."); + +const int32_t kSQLiteSchemaVersion = + int32_t((kMajorSchemaVersion << 4) + kMinorSchemaVersion); + +const int32_t kStorageProgressGranularity = 1000; + +// Changing the value here will override the page size of new databases only. +// A journal mode change and VACUUM are needed to change existing databases, so +// the best way to do that is to use the schema version upgrade mechanism. +const uint32_t kSQLitePageSizeOverride = +#ifdef IDB_MOBILE + 2048; +#else + 4096; +#endif + +static_assert(kSQLitePageSizeOverride == /* mozStorage default */ 0 || + (kSQLitePageSizeOverride % 2 == 0 && + kSQLitePageSizeOverride >= 512 && + kSQLitePageSizeOverride <= 65536), + "Must be 0 (disabled) or a power of 2 between 512 and 65536!"); + +// Set to -1 to use SQLite's default, 0 to disable, or some positive number to +// enforce a custom limit. +const int32_t kMaxWALPages = 5000; // 20MB on desktop, 10MB on mobile. + +// Set to some multiple of the page size to grow the database in larger chunks. +const uint32_t kSQLiteGrowthIncrement = kSQLitePageSizeOverride * 2; + +static_assert(kSQLiteGrowthIncrement >= 0 && + kSQLiteGrowthIncrement % kSQLitePageSizeOverride == 0 && + kSQLiteGrowthIncrement < uint32_t(INT32_MAX), + "Must be 0 (disabled) or a positive multiple of the page size!"); + +// The maximum number of threads that can be used for database activity at a +// single time. +const uint32_t kMaxConnectionThreadCount = 20; + +static_assert(kMaxConnectionThreadCount, "Must have at least one thread!"); + +// The maximum number of threads to keep when idle. Threads that become idle in +// excess of this number will be shut down immediately. +const uint32_t kMaxIdleConnectionThreadCount = 2; + +static_assert(kMaxConnectionThreadCount >= kMaxIdleConnectionThreadCount, + "Idle thread limit must be less than total thread limit!"); + +// The length of time that database connections will be held open after all +// transactions have completed before doing idle maintenance. +const uint32_t kConnectionIdleMaintenanceMS = 2 * 1000; // 2 seconds + +// The length of time that database connections will be held open after all +// transactions and maintenance have completed. +const uint32_t kConnectionIdleCloseMS = 10 * 1000; // 10 seconds + +// The length of time that idle threads will stay alive before being shut down. +const uint32_t kConnectionThreadIdleMS = 30 * 1000; // 30 seconds + +#define SAVEPOINT_CLAUSE "SAVEPOINT sp;" + +const uint32_t kFileCopyBufferSize = 32768; + +#define JOURNAL_DIRECTORY_NAME "journals" + +const char kFileManagerDirectoryNameSuffix[] = ".files"; +const char kSQLiteJournalSuffix[] = ".sqlite-journal"; +const char kSQLiteSHMSuffix[] = ".sqlite-shm"; +const char kSQLiteWALSuffix[] = ".sqlite-wal"; + +const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled"; + +const char kPrefFileHandleEnabled[] = "dom.fileHandle.enabled"; + +#define IDB_PREFIX "indexedDB" + +#define PERMISSION_STRING_CHROME_BASE IDB_PREFIX "-chrome-" +#define PERMISSION_STRING_CHROME_READ_SUFFIX "-read" +#define PERMISSION_STRING_CHROME_WRITE_SUFFIX "-write" + +#ifdef DEBUG + +const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL; +const uint32_t kDEBUGThreadSleepMS = 0; + +const int32_t kDEBUGTransactionThreadPriority = + nsISupportsPriority::PRIORITY_NORMAL; +const uint32_t kDEBUGTransactionThreadSleepMS = 0; + +#endif + +template +constexpr size_t +LiteralStringLength(const char (&aArr)[N]) +{ + static_assert(N, "Zero-length string literal?!"); + + // Don't include the null terminator. + return N - 1; +} + +/******************************************************************************* + * Metadata classes + ******************************************************************************/ + +struct FullIndexMetadata +{ + IndexMetadata mCommonMetadata; + + bool mDeleted; + +public: + FullIndexMetadata() + : mCommonMetadata(0, nsString(), KeyPath(0), nsCString(), false, false, false) + , mDeleted(false) + { + // This can happen either on the QuotaManager IO thread or on a + // versionchange transaction thread. These threads can never race so this is + // totally safe. + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata) + +private: + ~FullIndexMetadata() + { } +}; + +typedef nsRefPtrHashtable IndexTable; + +struct FullObjectStoreMetadata +{ + ObjectStoreMetadata mCommonMetadata; + IndexTable mIndexes; + + // These two members are only ever touched on a transaction thread! + int64_t mNextAutoIncrementId; + int64_t mCommittedAutoIncrementId; + + bool mDeleted; + +public: + FullObjectStoreMetadata() + : mCommonMetadata(0, nsString(), KeyPath(0), false) + , mNextAutoIncrementId(0) + , mCommittedAutoIncrementId(0) + , mDeleted(false) + { + // This can happen either on the QuotaManager IO thread or on a + // versionchange transaction thread. These threads can never race so this is + // totally safe. + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata); + + bool + HasLiveIndexes() const; + +private: + ~FullObjectStoreMetadata() + { } +}; + +typedef nsRefPtrHashtable + ObjectStoreTable; + +struct FullDatabaseMetadata +{ + DatabaseMetadata mCommonMetadata; + nsCString mDatabaseId; + nsString mFilePath; + ObjectStoreTable mObjectStores; + + int64_t mNextObjectStoreId; + int64_t mNextIndexId; + +public: + explicit FullDatabaseMetadata(const DatabaseMetadata& aCommonMetadata) + : mCommonMetadata(aCommonMetadata) + , mNextObjectStoreId(0) + , mNextIndexId(0) + { + AssertIsOnBackgroundThread(); + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullDatabaseMetadata) + + already_AddRefed + Duplicate() const; + +private: + ~FullDatabaseMetadata() + { } +}; + +template +class MOZ_STACK_CLASS MetadataNameOrIdMatcher final +{ + typedef MetadataNameOrIdMatcher SelfType; + + const int64_t mId; + const nsString mName; + RefPtr mMetadata; + bool mCheckName; + +public: + template + static MetadataType* + Match(const Enumerable& aEnumerable, uint64_t aId, const nsAString& aName) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aId); + + SelfType closure(aId, aName); + MatchHelper(aEnumerable, &closure); + + return closure.mMetadata; + } + + template + static MetadataType* + Match(const Enumerable& aEnumerable, uint64_t aId) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aId); + + SelfType closure(aId); + MatchHelper(aEnumerable, &closure); + + return closure.mMetadata; + } + +private: + MetadataNameOrIdMatcher(const int64_t& aId, const nsAString& aName) + : mId(aId) + , mName(PromiseFlatString(aName)) + , mMetadata(nullptr) + , mCheckName(true) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aId); + } + + explicit MetadataNameOrIdMatcher(const int64_t& aId) + : mId(aId) + , mMetadata(nullptr) + , mCheckName(false) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aId); + } + + template + static void + MatchHelper(const Enumerable& aEnumerable, SelfType* aClosure) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aClosure); + + for (auto iter = aEnumerable.ConstIter(); !iter.Done(); iter.Next()) { +#ifdef DEBUG + const uint64_t key = iter.Key(); +#endif + MetadataType* value = iter.UserData(); + MOZ_ASSERT(key != 0); + MOZ_ASSERT(value); + + if (!value->mDeleted && + (aClosure->mId == value->mCommonMetadata.id() || + (aClosure->mCheckName && + aClosure->mName == value->mCommonMetadata.name()))) { + aClosure->mMetadata = value; + break; + } + } + } +}; + +struct IndexDataValue final +{ + int64_t mIndexId; + Key mKey; + Key mSortKey; + bool mUnique; + + IndexDataValue() + : mIndexId(0) + , mUnique(false) + { + MOZ_COUNT_CTOR(IndexDataValue); + } + + explicit + IndexDataValue(const IndexDataValue& aOther) + : mIndexId(aOther.mIndexId) + , mKey(aOther.mKey) + , mSortKey(aOther.mSortKey) + , mUnique(aOther.mUnique) + { + MOZ_ASSERT(!aOther.mKey.IsUnset()); + + MOZ_COUNT_CTOR(IndexDataValue); + } + + IndexDataValue(int64_t aIndexId, bool aUnique, const Key& aKey) + : mIndexId(aIndexId) + , mKey(aKey) + , mUnique(aUnique) + { + MOZ_ASSERT(!aKey.IsUnset()); + + MOZ_COUNT_CTOR(IndexDataValue); + } + + IndexDataValue(int64_t aIndexId, bool aUnique, const Key& aKey, + const Key& aSortKey) + : mIndexId(aIndexId) + , mKey(aKey) + , mSortKey(aSortKey) + , mUnique(aUnique) + { + MOZ_ASSERT(!aKey.IsUnset()); + + MOZ_COUNT_CTOR(IndexDataValue); + } + + ~IndexDataValue() + { + MOZ_COUNT_DTOR(IndexDataValue); + } + + bool + operator==(const IndexDataValue& aOther) const + { + if (mIndexId != aOther.mIndexId) { + return false; + } + if (mSortKey.IsUnset()) { + return mKey == aOther.mKey; + } + return mSortKey == aOther.mSortKey; + } + + bool + operator<(const IndexDataValue& aOther) const + { + if (mIndexId == aOther.mIndexId) { + if (mSortKey.IsUnset()) { + return mKey < aOther.mKey; + } + return mSortKey < aOther.mSortKey; + } + + return mIndexId < aOther.mIndexId; + } +}; + +/******************************************************************************* + * SQLite functions + ******************************************************************************/ + +int32_t +MakeSchemaVersion(uint32_t aMajorSchemaVersion, + uint32_t aMinorSchemaVersion) +{ + return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion); +} + +uint32_t +HashName(const nsAString& aName) +{ + struct Helper + { + static uint32_t + RotateBitsLeft32(uint32_t aValue, uint8_t aBits) + { + MOZ_ASSERT(aBits < 32); + return (aValue << aBits) | (aValue >> (32 - aBits)); + } + }; + + static const uint32_t kGoldenRatioU32 = 0x9e3779b9u; + + const char16_t* str = aName.BeginReading(); + size_t length = aName.Length(); + + uint32_t hash = 0; + for (size_t i = 0; i < length; i++) { + hash = kGoldenRatioU32 * (Helper::RotateBitsLeft32(hash, 5) ^ str[i]); + } + + return hash; +} + +nsresult +ClampResultCode(nsresult aResultCode) +{ + if (NS_SUCCEEDED(aResultCode) || + NS_ERROR_GET_MODULE(aResultCode) == NS_ERROR_MODULE_DOM_INDEXEDDB) { + return aResultCode; + } + + switch (aResultCode) { + case NS_ERROR_FILE_NO_DEVICE_SPACE: + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + case NS_ERROR_STORAGE_CONSTRAINT: + return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; + default: +#ifdef DEBUG + nsPrintfCString message("Converting non-IndexedDB error code (0x%X) to " + "NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR", + aResultCode); + NS_WARNING(message.get()); +#else + ; +#endif + } + + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; +} + +void +GetDatabaseFilename(const nsAString& aName, + nsAutoString& aDatabaseFilename) +{ + MOZ_ASSERT(aDatabaseFilename.IsEmpty()); + + aDatabaseFilename.AppendInt(HashName(aName)); + + nsCString escapedName; + if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) { + MOZ_CRASH("Can't escape database name!"); + } + + const char* forwardIter = escapedName.BeginReading(); + const char* backwardIter = escapedName.EndReading() - 1; + + nsAutoCString substring; + while (forwardIter <= backwardIter && substring.Length() < 21) { + if (substring.Length() % 2) { + substring.Append(*backwardIter--); + } else { + substring.Append(*forwardIter++); + } + } + + aDatabaseFilename.AppendASCII(substring.get(), substring.Length()); +} + +uint32_t +CompressedByteCountForNumber(uint64_t aNumber) +{ + // All bytes have 7 bits available. + uint32_t count = 1; + while ((aNumber >>= 7)) { + count++; + } + + return count; +} + +uint32_t +CompressedByteCountForIndexId(int64_t aIndexId) +{ + MOZ_ASSERT(aIndexId); + MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId), + "Overflow!"); + + return CompressedByteCountForNumber(uint64_t(aIndexId * 2)); +} + +void +WriteCompressedNumber(uint64_t aNumber, uint8_t** aIterator) +{ + MOZ_ASSERT(aIterator); + MOZ_ASSERT(*aIterator); + + uint8_t*& buffer = *aIterator; + +#ifdef DEBUG + const uint8_t* bufferStart = buffer; + const uint64_t originalNumber = aNumber; +#endif + + while (true) { + uint64_t shiftedNumber = aNumber >> 7; + if (shiftedNumber) { + *buffer++ = uint8_t(0x80 | (aNumber & 0x7f)); + aNumber = shiftedNumber; + } else { + *buffer++ = uint8_t(aNumber); + break; + } + } + + MOZ_ASSERT(buffer > bufferStart); + MOZ_ASSERT(uint32_t(buffer - bufferStart) == + CompressedByteCountForNumber(originalNumber)); +} + +uint64_t +ReadCompressedNumber(const uint8_t** aIterator, const uint8_t* aEnd) +{ + MOZ_ASSERT(aIterator); + MOZ_ASSERT(*aIterator); + MOZ_ASSERT(aEnd); + MOZ_ASSERT(*aIterator < aEnd); + + const uint8_t*& buffer = *aIterator; + + uint8_t shiftCounter = 0; + uint64_t result = 0; + + while (true) { + MOZ_ASSERT(shiftCounter <= 56, "Shifted too many bits!"); + + result += (uint64_t(*buffer & 0x7f) << shiftCounter); + shiftCounter += 7; + + if (!(*buffer++ & 0x80)) { + break; + } + + if (NS_WARN_IF(buffer == aEnd)) { + MOZ_ASSERT(false); + break; + } + } + + return result; +} + +void +WriteCompressedIndexId(int64_t aIndexId, bool aUnique, uint8_t** aIterator) +{ + MOZ_ASSERT(aIndexId); + MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId), + "Overflow!"); + MOZ_ASSERT(aIterator); + MOZ_ASSERT(*aIterator); + + const uint64_t indexId = (uint64_t(aIndexId * 2) | (aUnique ? 1 : 0)); + WriteCompressedNumber(indexId, aIterator); +} + +void +ReadCompressedIndexId(const uint8_t** aIterator, + const uint8_t* aEnd, + int64_t* aIndexId, + bool* aUnique) +{ + MOZ_ASSERT(aIterator); + MOZ_ASSERT(*aIterator); + MOZ_ASSERT(aIndexId); + MOZ_ASSERT(aUnique); + + uint64_t indexId = ReadCompressedNumber(aIterator, aEnd); + + if (indexId % 2) { + *aUnique = true; + indexId--; + } else { + *aUnique = false; + } + + MOZ_ASSERT(UINT64_MAX / 2 >= uint64_t(indexId), "Bad index id!"); + + *aIndexId = int64_t(indexId / 2); +} + +// static +nsresult +MakeCompressedIndexDataValues( + const FallibleTArray& aIndexValues, + UniqueFreePtr& aCompressedIndexDataValues, + uint32_t* aCompressedIndexDataValuesLength) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(!aCompressedIndexDataValues); + MOZ_ASSERT(aCompressedIndexDataValuesLength); + + PROFILER_LABEL("IndexedDB", + "MakeCompressedIndexDataValues", + js::ProfileEntry::Category::STORAGE); + + const uint32_t arrayLength = aIndexValues.Length(); + if (!arrayLength) { + *aCompressedIndexDataValuesLength = 0; + return NS_OK; + } + + // First calculate the size of the final buffer. + uint32_t blobDataLength = 0; + + for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { + const IndexDataValue& info = aIndexValues[arrayIndex]; + const nsCString& keyBuffer = info.mKey.GetBuffer(); + const nsCString& sortKeyBuffer = info.mSortKey.GetBuffer(); + const uint32_t keyBufferLength = keyBuffer.Length(); + const uint32_t sortKeyBufferLength = sortKeyBuffer.Length(); + + MOZ_ASSERT(!keyBuffer.IsEmpty()); + + // Don't let |infoLength| overflow. + if (NS_WARN_IF(UINT32_MAX - keyBuffer.Length() < + CompressedByteCountForIndexId(info.mIndexId) + + CompressedByteCountForNumber(keyBufferLength) + + CompressedByteCountForNumber(sortKeyBufferLength))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + const uint32_t infoLength = + CompressedByteCountForIndexId(info.mIndexId) + + CompressedByteCountForNumber(keyBufferLength) + + CompressedByteCountForNumber(sortKeyBufferLength) + + keyBufferLength + + sortKeyBufferLength; + + // Don't let |blobDataLength| overflow. + if (NS_WARN_IF(UINT32_MAX - infoLength < blobDataLength)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + blobDataLength += infoLength; + } + + UniqueFreePtr blobData( + static_cast(malloc(blobDataLength))); + if (NS_WARN_IF(!blobData)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_OUT_OF_MEMORY; + } + + uint8_t* blobDataIter = blobData.get(); + + for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { + const IndexDataValue& info = aIndexValues[arrayIndex]; + const nsCString& keyBuffer = info.mKey.GetBuffer(); + const nsCString& sortKeyBuffer = info.mSortKey.GetBuffer(); + const uint32_t keyBufferLength = keyBuffer.Length(); + const uint32_t sortKeyBufferLength = sortKeyBuffer.Length(); + + WriteCompressedIndexId(info.mIndexId, info.mUnique, &blobDataIter); + WriteCompressedNumber(keyBufferLength, &blobDataIter); + + memcpy(blobDataIter, keyBuffer.get(), keyBufferLength); + blobDataIter += keyBufferLength; + + WriteCompressedNumber(sortKeyBufferLength, &blobDataIter); + + memcpy(blobDataIter, sortKeyBuffer.get(), sortKeyBufferLength); + blobDataIter += sortKeyBufferLength; + } + + MOZ_ASSERT(blobDataIter == blobData.get() + blobDataLength); + + aCompressedIndexDataValues.swap(blobData); + *aCompressedIndexDataValuesLength = uint32_t(blobDataLength); + + return NS_OK; +} + +nsresult +ReadCompressedIndexDataValuesFromBlob(const uint8_t* aBlobData, + uint32_t aBlobDataLength, + nsTArray& aIndexValues) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aBlobData); + MOZ_ASSERT(aBlobDataLength); + MOZ_ASSERT(aIndexValues.IsEmpty()); + + PROFILER_LABEL("IndexedDB", + "ReadCompressedIndexDataValuesFromBlob", + js::ProfileEntry::Category::STORAGE); + + const uint8_t* blobDataIter = aBlobData; + const uint8_t* blobDataEnd = aBlobData + aBlobDataLength; + + while (blobDataIter < blobDataEnd) { + int64_t indexId; + bool unique; + ReadCompressedIndexId(&blobDataIter, blobDataEnd, &indexId, &unique); + + if (NS_WARN_IF(blobDataIter == blobDataEnd)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_FILE_CORRUPTED; + } + + // Read key buffer length. + const uint64_t keyBufferLength = + ReadCompressedNumber(&blobDataIter, blobDataEnd); + + if (NS_WARN_IF(blobDataIter == blobDataEnd) || + NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) || + NS_WARN_IF(blobDataIter + keyBufferLength > blobDataEnd)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_FILE_CORRUPTED; + } + + nsCString keyBuffer(reinterpret_cast(blobDataIter), + uint32_t(keyBufferLength)); + blobDataIter += keyBufferLength; + + IndexDataValue idv(indexId, unique, Key(keyBuffer)); + + // Read sort key buffer length. + const uint64_t sortKeyBufferLength = + ReadCompressedNumber(&blobDataIter, blobDataEnd); + + if (sortKeyBufferLength > 0) { + if (NS_WARN_IF(blobDataIter == blobDataEnd) || + NS_WARN_IF(sortKeyBufferLength > uint64_t(UINT32_MAX)) || + NS_WARN_IF(blobDataIter + sortKeyBufferLength > blobDataEnd)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_FILE_CORRUPTED; + } + + nsCString sortKeyBuffer(reinterpret_cast(blobDataIter), + uint32_t(sortKeyBufferLength)); + blobDataIter += sortKeyBufferLength; + + idv.mSortKey = Key(sortKeyBuffer); + } + + if (NS_WARN_IF(!aIndexValues.InsertElementSorted(idv, fallible))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_OUT_OF_MEMORY; + } + } + + MOZ_ASSERT(blobDataIter == blobDataEnd); + + return NS_OK; +} + +// static +template +nsresult +ReadCompressedIndexDataValuesFromSource(T* aSource, + uint32_t aColumnIndex, + nsTArray& aIndexValues) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aSource); + MOZ_ASSERT(aIndexValues.IsEmpty()); + + int32_t columnType; + nsresult rv = aSource->GetTypeOfIndex(aColumnIndex, &columnType); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) { + return NS_OK; + } + + MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_BLOB); + + const uint8_t* blobData; + uint32_t blobDataLength; + rv = aSource->GetSharedBlob(aColumnIndex, &blobDataLength, &blobData); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!blobDataLength)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_FILE_CORRUPTED; + } + + rv = ReadCompressedIndexDataValuesFromBlob(blobData, + blobDataLength, + aIndexValues); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +ReadCompressedIndexDataValues(mozIStorageStatement* aStatement, + uint32_t aColumnIndex, + nsTArray& aIndexValues) +{ + return ReadCompressedIndexDataValuesFromSource(aStatement, + aColumnIndex, + aIndexValues); +} + +nsresult +ReadCompressedIndexDataValues(mozIStorageValueArray* aValues, + uint32_t aColumnIndex, + nsTArray& aIndexValues) +{ + return ReadCompressedIndexDataValuesFromSource(aValues, + aColumnIndex, + aIndexValues); +} + +nsresult +CreateFileTables(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "CreateFileTables", + js::ProfileEntry::Category::STORAGE); + + // Table `file` + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE file (" + "id INTEGER PRIMARY KEY, " + "refcount INTEGER NOT NULL" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_insert_trigger " + "AFTER INSERT ON object_data " + "FOR EACH ROW " + "WHEN NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(NULL, NEW.file_ids); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_update_trigger " + "AFTER UPDATE OF file_ids ON object_data " + "FOR EACH ROW " + "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_delete_trigger " + "AFTER DELETE ON object_data " + "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NULL); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER file_update_trigger " + "AFTER UPDATE ON file " + "FOR EACH ROW WHEN NEW.refcount = 0 " + "BEGIN " + "DELETE FROM file WHERE id = OLD.id; " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +CreateTables(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "CreateTables", + js::ProfileEntry::Category::STORAGE); + + // Table `database` + + // There are two reasons for having the origin column. + // First, we can ensure that we don't have collisions in the origin hash we + // use for the path because when we open the db we can make sure that the + // origins exactly match. Second, chrome code crawling through the idb + // directory can figure out the origin of every db without having to + // reverse-engineer our hash scheme. + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE database" + "( name TEXT PRIMARY KEY" + ", origin TEXT NOT NULL" + ", version INTEGER NOT NULL DEFAULT 0" + ", last_vacuum_time INTEGER NOT NULL DEFAULT 0" + ", last_analyze_time INTEGER NOT NULL DEFAULT 0" + ", last_vacuum_size INTEGER NOT NULL DEFAULT 0" + ") WITHOUT ROWID;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Table `object_store` + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store" + "( id INTEGER PRIMARY KEY" + ", auto_increment INTEGER NOT NULL DEFAULT 0" + ", name TEXT NOT NULL" + ", key_path TEXT" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Table `object_store_index` + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store_index" + "( id INTEGER PRIMARY KEY" + ", object_store_id INTEGER NOT NULL" + ", name TEXT NOT NULL" + ", key_path TEXT NOT NULL" + ", unique_index INTEGER NOT NULL" + ", multientry INTEGER NOT NULL" + ", locale TEXT" + ", is_auto_locale BOOLEAN NOT NULL" + ", FOREIGN KEY (object_store_id) " + "REFERENCES object_store(id) " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Table `object_data` + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_data" + "( object_store_id INTEGER NOT NULL" + ", key BLOB NOT NULL" + ", index_data_values BLOB DEFAULT NULL" + ", file_ids TEXT" + ", data BLOB NOT NULL" + ", PRIMARY KEY (object_store_id, key)" + ", FOREIGN KEY (object_store_id) " + "REFERENCES object_store(id) " + ") WITHOUT ROWID;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Table `index_data` + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE index_data" + "( index_id INTEGER NOT NULL" + ", value BLOB NOT NULL" + ", object_data_key BLOB NOT NULL" + ", object_store_id INTEGER NOT NULL" + ", value_locale BLOB" + ", PRIMARY KEY (index_id, value, object_data_key)" + ", FOREIGN KEY (index_id) " + "REFERENCES object_store_index(id) " + ", FOREIGN KEY (object_store_id, object_data_key) " + "REFERENCES object_data(object_store_id, key) " + ") WITHOUT ROWID;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX index_data_value_locale_index " + "ON index_data (index_id, value_locale, object_data_key, value) " + "WHERE value_locale IS NOT NULL;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Table `unique_index_data` + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE unique_index_data" + "( index_id INTEGER NOT NULL" + ", value BLOB NOT NULL" + ", object_store_id INTEGER NOT NULL" + ", object_data_key BLOB NOT NULL" + ", value_locale BLOB" + ", PRIMARY KEY (index_id, value)" + ", FOREIGN KEY (index_id) " + "REFERENCES object_store_index(id) " + ", FOREIGN KEY (object_store_id, object_data_key) " + "REFERENCES object_data(object_store_id, key) " + ") WITHOUT ROWID;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX unique_index_data_value_locale_index " + "ON unique_index_data (index_id, value_locale, object_data_key, value) " + "WHERE value_locale IS NOT NULL;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = CreateFileTables(aConnection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(kSQLiteSchemaVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom4To5", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + + // All we changed is the type of the version column, so lets try to + // convert that to an integer, and if we fail, set it to 0. + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name, version, dataVersion " + "FROM database" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsString name; + int32_t intVersion; + int64_t dataVersion; + + { + mozStorageStatementScoper scoper(stmt); + + bool hasResults; + rv = stmt->ExecuteStep(&hasResults); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (NS_WARN_IF(!hasResults)) { + return NS_ERROR_FAILURE; + } + + nsString version; + rv = stmt->GetString(1, version); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + intVersion = version.ToInteger(&rv); + if (NS_FAILED(rv)) { + intVersion = 0; + } + + rv = stmt->GetString(0, name); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->GetInt64(2, &dataVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE database" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE database (" + "name TEXT NOT NULL, " + "version INTEGER NOT NULL DEFAULT 0, " + "dataVersion INTEGER NOT NULL" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO database (name, version, dataVersion) " + "VALUES (:name, :version, :dataVersion)" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + { + mozStorageStatementScoper scoper(stmt); + + rv = stmt->BindStringParameter(0, name); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt32Parameter(1, intVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64Parameter(2, dataVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = aConnection->SetSchemaVersion(5); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom5To6", + js::ProfileEntry::Category::STORAGE); + + // First, drop all the indexes we're no longer going to use. + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX key_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX ai_key_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX value_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX ai_value_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Now, reorder the columns of object_data to put the blob data last. We do + // this by copying into a temporary table, dropping the original, then copying + // back into a newly created table. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id INTEGER PRIMARY KEY, " + "object_store_id, " + "key_value, " + "data " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, key_value, data " + "FROM object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_data (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "key_value DEFAULT NULL, " + "data BLOB NOT NULL, " + "UNIQUE (object_store_id, key_value), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_data " + "SELECT id, object_store_id, key_value, data " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // We need to add a unique constraint to our ai_object_data table. Copy all + // the data out of it using a temporary table as before. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id INTEGER PRIMARY KEY, " + "object_store_id, " + "data " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, data " + "FROM ai_object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_object_data (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "object_store_id INTEGER NOT NULL, " + "data BLOB NOT NULL, " + "UNIQUE (object_store_id, id), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO ai_object_data " + "SELECT id, object_store_id, data " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Fix up the index_data table. We're reordering the columns as well as + // changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "object_data_key NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT OR IGNORE INTO index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX index_data_object_data_id_index " + "ON index_data (object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Fix up the unique_index_data table. We're reordering the columns as well as + // changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE unique_index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "object_data_key NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "UNIQUE (index_id, value), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO unique_index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX unique_index_data_object_data_id_index " + "ON unique_index_data (object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Fix up the ai_index_data table. We're reordering the columns as well as + // changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "ai_object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, ai_object_data_id " + "FROM ai_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "ai_object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, ai_object_data_id), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT OR IGNORE INTO ai_index_data " + "SELECT index_id, value, ai_object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX ai_index_data_ai_object_data_id_index " + "ON ai_index_data (ai_object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Fix up the ai_unique_index_data table. We're reordering the columns as well + // as changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "ai_object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, ai_object_data_id " + "FROM ai_unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_unique_index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "ai_object_data_id INTEGER NOT NULL, " + "UNIQUE (index_id, value), " + "PRIMARY KEY (index_id, value, ai_object_data_id), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO ai_unique_index_data " + "SELECT index_id, value, ai_object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX ai_unique_index_data_ai_object_data_id_index " + "ON ai_unique_index_data (ai_object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(6); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom6To7", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id, " + "name, " + "key_path, " + "auto_increment" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, name, key_path, auto_increment " + "FROM object_store;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store (" + "id INTEGER PRIMARY KEY, " + "auto_increment INTEGER NOT NULL DEFAULT 0, " + "name TEXT NOT NULL, " + "key_path TEXT, " + "UNIQUE (name)" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store " + "SELECT id, auto_increment, name, nullif(key_path, '') " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(7); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom7To8", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id, " + "object_store_id, " + "name, " + "key_path, " + "unique_index, " + "object_store_autoincrement" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, name, key_path, " + "unique_index, object_store_autoincrement " + "FROM object_store_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store_index (" + "id INTEGER, " + "object_store_id INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "key_path TEXT NOT NULL, " + "unique_index INTEGER NOT NULL, " + "multientry INTEGER NOT NULL, " + "object_store_autoincrement INTERGER NOT NULL, " + "PRIMARY KEY (id), " + "UNIQUE (object_store_id, name), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store_index " + "SELECT id, object_store_id, name, key_path, " + "unique_index, 0, object_store_autoincrement " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(8); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +class CompressDataBlobsFunction final + : public mozIStorageFunction +{ +public: + NS_DECL_ISUPPORTS + +private: + ~CompressDataBlobsFunction() + { } + + NS_IMETHOD + OnFunctionCall(mozIStorageValueArray* aArguments, + nsIVariant** aResult) override + { + MOZ_ASSERT(aArguments); + MOZ_ASSERT(aResult); + + PROFILER_LABEL("IndexedDB", + "CompressDataBlobsFunction::OnFunctionCall", + js::ProfileEntry::Category::STORAGE); + + uint32_t argc; + nsresult rv = aArguments->GetNumEntries(&argc); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (argc != 1) { + NS_WARNING("Don't call me with the wrong number of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + int32_t type; + rv = aArguments->GetTypeOfIndex(0, &type); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (type != mozIStorageStatement::VALUE_TYPE_BLOB) { + NS_WARNING("Don't call me with the wrong type of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + const uint8_t* uncompressed; + uint32_t uncompressedLength; + rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); + UniqueFreePtr compressed( + static_cast(malloc(compressedLength))); + if (NS_WARN_IF(!compressed)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + snappy::RawCompress(reinterpret_cast(uncompressed), + uncompressedLength, + reinterpret_cast(compressed.get()), + &compressedLength); + + std::pair data(compressed.release(), + int(compressedLength)); + + nsCOMPtr result = new mozilla::storage::AdoptedBlobVariant(data); + + result.forget(aResult); + return NS_OK; + } +}; + +nsresult +UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom8To9_0", + js::ProfileEntry::Category::STORAGE); + + // We no longer use the dataVersion column. + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE database SET dataVersion = 0;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr compressor = new CompressDataBlobsFunction(); + + NS_NAMED_LITERAL_CSTRING(compressorName, "compress"); + + rv = aConnection->CreateFunction(compressorName, 1, compressor); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Turn off foreign key constraints before we do anything here. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE object_data SET data = compress(data);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE ai_object_data SET data = compress(data);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->RemoveFunction(compressorName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom9_0To10_0", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE object_data ADD COLUMN file_ids TEXT;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = CreateFileTables(aConnection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom10_0To11_0", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id, " + "object_store_id, " + "name, " + "key_path, " + "unique_index, " + "multientry" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, name, key_path, " + "unique_index, multientry " + "FROM object_store_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store_index (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "key_path TEXT NOT NULL, " + "unique_index INTEGER NOT NULL, " + "multientry INTEGER NOT NULL, " + "UNIQUE (object_store_id, name), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store_index " + "SELECT id, object_store_id, name, key_path, " + "unique_index, multientry " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TRIGGER object_data_insert_trigger;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " + "SELECT object_store_id, id, data, file_ids " + "FROM ai_object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_insert_trigger " + "AFTER INSERT ON object_data " + "FOR EACH ROW " + "WHEN NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(NULL, NEW.file_ids); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) " + "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id " + "FROM ai_index_data " + "INNER JOIN object_store_index ON " + "object_store_index.id = ai_index_data.index_id " + "INNER JOIN object_data ON " + "object_data.object_store_id = object_store_index.object_store_id AND " + "object_data.key_value = ai_index_data.ai_object_data_id;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) " + "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id " + "FROM ai_unique_index_data " + "INNER JOIN object_store_index ON " + "object_store_index.id = ai_unique_index_data.index_id " + "INNER JOIN object_data ON " + "object_data.object_store_id = object_store_index.object_store_id AND " + "object_data.key_value = ai_unique_index_data.ai_object_data_id;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE object_store " + "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 " + "WHERE auto_increment;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +class EncodeKeysFunction final + : public mozIStorageFunction +{ +public: + NS_DECL_ISUPPORTS + +private: + ~EncodeKeysFunction() + { } + + NS_IMETHOD + OnFunctionCall(mozIStorageValueArray* aArguments, + nsIVariant** aResult) override + { + MOZ_ASSERT(aArguments); + MOZ_ASSERT(aResult); + + PROFILER_LABEL("IndexedDB", + "EncodeKeysFunction::OnFunctionCall", + js::ProfileEntry::Category::STORAGE); + + uint32_t argc; + nsresult rv = aArguments->GetNumEntries(&argc); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (argc != 1) { + NS_WARNING("Don't call me with the wrong number of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + int32_t type; + rv = aArguments->GetTypeOfIndex(0, &type); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + Key key; + if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) { + int64_t intKey; + aArguments->GetInt64(0, &intKey); + key.SetFromInteger(intKey); + } else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) { + nsString stringKey; + aArguments->GetString(0, stringKey); + key.SetFromString(stringKey); + } else { + NS_WARNING("Don't call me with the wrong type of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + const nsCString& buffer = key.GetBuffer(); + + std::pair data(static_cast(buffer.get()), + int(buffer.Length())); + + nsCOMPtr result = new mozilla::storage::BlobVariant(data); + + result.forget(aResult); + return NS_OK; + } +}; + +nsresult +UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom11_0To12_0", + js::ProfileEntry::Category::STORAGE); + + NS_NAMED_LITERAL_CSTRING(encoderName, "encode"); + + nsCOMPtr encoder = new EncodeKeysFunction(); + + nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id INTEGER PRIMARY KEY, " + "object_store_id, " + "key_value, " + "data, " + "file_ids " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, encode(key_value), data, file_ids " + "FROM object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_data (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "key_value BLOB DEFAULT NULL, " + "file_ids TEXT, " + "data BLOB NOT NULL, " + "UNIQUE (object_store_id, key_value), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_data " + "SELECT id, object_store_id, key_value, file_ids, data " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_insert_trigger " + "AFTER INSERT ON object_data " + "FOR EACH ROW " + "WHEN NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(NULL, NEW.file_ids); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_update_trigger " + "AFTER UPDATE OF file_ids ON object_data " + "FOR EACH ROW " + "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_delete_trigger " + "AFTER DELETE ON object_data " + "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NULL); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, encode(value), encode(object_data_key), object_data_id " + "FROM index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE index_data (" + "index_id INTEGER NOT NULL, " + "value BLOB NOT NULL, " + "object_data_key BLOB NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX index_data_object_data_id_index " + "ON index_data (object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, encode(value), encode(object_data_key), object_data_id " + "FROM unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE unique_index_data (" + "index_id INTEGER NOT NULL, " + "value BLOB NOT NULL, " + "object_data_key BLOB NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "UNIQUE (index_id, value), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO unique_index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX unique_index_data_object_data_id_index " + "ON unique_index_data (object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->RemoveFunction(encoderName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection, + bool* aVacuumNeeded) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom12_0To13_0", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + +#ifdef IDB_MOBILE + int32_t defaultPageSize; + rv = aConnection->GetDefaultPageSize(&defaultPageSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Enable auto_vacuum mode and update the page size to the platform default. + nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = "); + upgradeQuery.AppendInt(defaultPageSize); + + rv = aConnection->ExecuteSimpleSQL(upgradeQuery); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + *aVacuumNeeded = true; +#endif + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + // The only change between 13 and 14 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection) +{ + // The only change between 14 and 15 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom15_0To16_0(mozIStorageConnection* aConnection) +{ + // The only change between 15 and 16 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(16, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom16_0To17_0(mozIStorageConnection* aConnection) +{ + // The only change between 16 and 17 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(17, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +class UpgradeSchemaFrom17_0To18_0Helper final +{ + class InsertIndexDataValuesFunction; + class UpgradeKeyFunction; + +public: + static nsresult + DoUpgrade(mozIStorageConnection* aConnection, const nsACString& aOrigin); + +private: + static nsresult + DoUpgradeInternal(mozIStorageConnection* aConnection, + const nsACString& aOrigin); + + UpgradeSchemaFrom17_0To18_0Helper() + { + MOZ_ASSERT_UNREACHABLE("Don't create instances of this class!"); + } + + ~UpgradeSchemaFrom17_0To18_0Helper() + { + MOZ_ASSERT_UNREACHABLE("Don't create instances of this class!"); + } +}; + +class UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction final + : public mozIStorageFunction +{ +public: + InsertIndexDataValuesFunction() + { } + + NS_DECL_ISUPPORTS + +private: + ~InsertIndexDataValuesFunction() + { } + + NS_DECL_MOZISTORAGEFUNCTION +}; + +NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper:: + InsertIndexDataValuesFunction, + mozIStorageFunction); + +NS_IMETHODIMP +UpgradeSchemaFrom17_0To18_0Helper:: +InsertIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues, + nsIVariant** _retval) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aValues); + MOZ_ASSERT(_retval); + +#ifdef DEBUG + { + uint32_t argCount; + MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount)); + MOZ_ASSERT(argCount == 4); + + int32_t valueType; + MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType)); + MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL || + valueType == mozIStorageValueArray::VALUE_TYPE_BLOB); + + MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(1, &valueType)); + MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER); + + MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(2, &valueType)); + MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER); + + MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(3, &valueType)); + MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB); + } +#endif + + // Read out the previous value. It may be NULL, in which case we'll just end + // up with an empty array. + AutoTArray indexValues; + nsresult rv = ReadCompressedIndexDataValues(aValues, 0, indexValues); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int64_t indexId; + rv = aValues->GetInt64(1, &indexId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int32_t unique; + rv = aValues->GetInt32(2, &unique); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + Key value; + rv = value.SetFromValueArray(aValues, 3); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Update the array with the new addition. + if (NS_WARN_IF(!indexValues.SetCapacity(indexValues.Length() + 1, + fallible))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_OUT_OF_MEMORY; + } + + MOZ_ALWAYS_TRUE( + indexValues.InsertElementSorted(IndexDataValue(indexId, !!unique, value), + fallible)); + + // Compress the array. + UniqueFreePtr indexValuesBlob; + uint32_t indexValuesBlobLength; + rv = MakeCompressedIndexDataValues(indexValues, + indexValuesBlob, + &indexValuesBlobLength); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // The compressed blob is the result of this function. + std::pair indexValuesBlobPair(indexValuesBlob.release(), + indexValuesBlobLength); + + nsCOMPtr result = + new storage::AdoptedBlobVariant(indexValuesBlobPair); + + result.forget(_retval); + return NS_OK; +} + +class UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction final + : public mozIStorageFunction +{ +public: + UpgradeKeyFunction() + { } + + static nsresult + CopyAndUpgradeKeyBuffer(const uint8_t* aSource, + const uint8_t* aSourceEnd, + uint8_t* aDestination) + { + return CopyAndUpgradeKeyBufferInternal(aSource, + aSourceEnd, + aDestination, + 0 /* aTagOffset */, + 0 /* aRecursionDepth */); + } + + NS_DECL_ISUPPORTS + +private: + ~UpgradeKeyFunction() + { } + + static nsresult + CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource, + const uint8_t* aSourceEnd, + uint8_t*& aDestination, + uint8_t aTagOffset, + uint8_t aRecursionDepth); + + static uint32_t + AdjustedSize(uint32_t aMaxSize, + const uint8_t* aSource, + const uint8_t* aSourceEnd) + { + MOZ_ASSERT(aMaxSize); + MOZ_ASSERT(aSource); + MOZ_ASSERT(aSourceEnd); + MOZ_ASSERT(aSource <= aSourceEnd); + + return std::min(aMaxSize, uint32_t(aSourceEnd - aSource)); + } + + NS_DECL_MOZISTORAGEFUNCTION +}; + +// static +nsresult +UpgradeSchemaFrom17_0To18_0Helper:: +UpgradeKeyFunction::CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource, + const uint8_t* aSourceEnd, + uint8_t*& aDestination, + uint8_t aTagOffset, + uint8_t aRecursionDepth) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aSource); + MOZ_ASSERT(*aSource); + MOZ_ASSERT(aSourceEnd); + MOZ_ASSERT(aSource < aSourceEnd); + MOZ_ASSERT(aDestination); + MOZ_ASSERT(aTagOffset <= Key::kMaxArrayCollapse); + + static constexpr uint8_t kOldNumberTag = 0x1; + static constexpr uint8_t kOldDateTag = 0x2; + static constexpr uint8_t kOldStringTag = 0x3; + static constexpr uint8_t kOldArrayTag = 0x4; + static constexpr uint8_t kOldMaxType = kOldArrayTag; + + if (NS_WARN_IF(aRecursionDepth > Key::kMaxRecursionDepth)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_FILE_CORRUPTED; + } + + const uint8_t sourceTag = *aSource - (aTagOffset * kOldMaxType); + MOZ_ASSERT(sourceTag); + + if (NS_WARN_IF(sourceTag > kOldMaxType * Key::kMaxArrayCollapse)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_FILE_CORRUPTED; + } + + if (sourceTag == kOldNumberTag || sourceTag == kOldDateTag) { + // Write the new tag. + *aDestination++ = + (sourceTag == kOldNumberTag ? Key::eFloat : Key::eDate) + + (aTagOffset * Key::eMaxType); + aSource++; + + // Numbers and Dates are encoded as 64-bit integers, but trailing 0 + // bytes have been removed. + const uint32_t byteCount = + AdjustedSize(sizeof(uint64_t), aSource, aSourceEnd); + + for (uint32_t count = 0; count < byteCount; count++) { + *aDestination++ = *aSource++; + } + + return NS_OK; + } + + if (sourceTag == kOldStringTag) { + // Write the new tag. + *aDestination++ = Key::eString + (aTagOffset * Key::eMaxType); + aSource++; + + while (aSource < aSourceEnd) { + const uint8_t byte = *aSource++; + *aDestination++ = byte; + + if (!byte) { + // Just copied the terminator. + break; + } + + // Maybe copy one or two extra bytes if the byte is tagged and we have + // enough source space. + if (byte & 0x80) { + const uint32_t byteCount = + AdjustedSize((byte & 0x40) ? 2 : 1, aSource, aSourceEnd); + + for (uint32_t count = 0; count < byteCount; count++) { + *aDestination++ = *aSource++; + } + } + } + + return NS_OK; + } + + if (NS_WARN_IF(sourceTag < kOldArrayTag)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_FILE_CORRUPTED; + } + + aTagOffset++; + + if (aTagOffset == Key::kMaxArrayCollapse) { + MOZ_ASSERT(sourceTag == kOldArrayTag); + + *aDestination++ = (aTagOffset * Key::eMaxType); + aSource++; + + aTagOffset = 0; + } + + while (aSource < aSourceEnd && + (*aSource - (aTagOffset * kOldMaxType)) != Key::eTerminator) { + nsresult rv = CopyAndUpgradeKeyBufferInternal(aSource, + aSourceEnd, + aDestination, + aTagOffset, + aRecursionDepth + 1); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + aTagOffset = 0; + } + + if (aSource < aSourceEnd) { + MOZ_ASSERT((*aSource - (aTagOffset * kOldMaxType)) == Key::eTerminator); + *aDestination++ = Key::eTerminator + (aTagOffset * Key::eMaxType); + aSource++; + } + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction, + mozIStorageFunction); + +NS_IMETHODIMP +UpgradeSchemaFrom17_0To18_0Helper:: +UpgradeKeyFunction::OnFunctionCall(mozIStorageValueArray* aValues, + nsIVariant** _retval) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aValues); + MOZ_ASSERT(_retval); + +#ifdef DEBUG + { + uint32_t argCount; + MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount)); + MOZ_ASSERT(argCount == 1); + + int32_t valueType; + MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType)); + MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB); + } +#endif + + // Dig the old key out of the values. + const uint8_t* blobData; + uint32_t blobDataLength; + nsresult rv = aValues->GetSharedBlob(0, &blobDataLength, &blobData); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Upgrading the key doesn't change the amount of space needed to hold it. + UniqueFreePtr upgradedBlobData( + static_cast(malloc(blobDataLength))); + if (NS_WARN_IF(!upgradedBlobData)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = CopyAndUpgradeKeyBuffer(blobData, + blobData + blobDataLength, + upgradedBlobData.get()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // The upgraded key is the result of this function. + std::pair data(upgradedBlobData.release(), + int(blobDataLength)); + + nsCOMPtr result = new mozilla::storage::AdoptedBlobVariant(data); + + result.forget(_retval); + return NS_OK; +} + +// static +nsresult +UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(mozIStorageConnection* aConnection, + const nsACString& aOrigin) +{ + MOZ_ASSERT(aConnection); + MOZ_ASSERT(!aOrigin.IsEmpty()); + + // Register the |upgrade_key| function. + RefPtr updateFunction = new UpgradeKeyFunction(); + + NS_NAMED_LITERAL_CSTRING(upgradeKeyFunctionName, "upgrade_key"); + + nsresult rv = + aConnection->CreateFunction(upgradeKeyFunctionName, 1, updateFunction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Register the |insert_idv| function. + RefPtr insertIDVFunction = + new InsertIndexDataValuesFunction(); + + NS_NAMED_LITERAL_CSTRING(insertIDVFunctionName, "insert_idv"); + + rv = aConnection->CreateFunction(insertIDVFunctionName, 4, insertIDVFunction); + if (NS_WARN_IF(NS_FAILED(rv))) { + MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(upgradeKeyFunctionName)); + return rv; + } + + rv = DoUpgradeInternal(aConnection, aOrigin); + + MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(upgradeKeyFunctionName)); + MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(insertIDVFunctionName)); + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +// static +nsresult +UpgradeSchemaFrom17_0To18_0Helper::DoUpgradeInternal( + mozIStorageConnection* aConnection, + const nsACString& aOrigin) +{ + MOZ_ASSERT(aConnection); + MOZ_ASSERT(!aOrigin.IsEmpty()); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // Drop these triggers to avoid unnecessary work during the upgrade process. + "DROP TRIGGER object_data_insert_trigger;" + "DROP TRIGGER object_data_update_trigger;" + "DROP TRIGGER object_data_delete_trigger;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // Drop these indexes before we do anything else to free disk space. + "DROP INDEX index_data_object_data_id_index;" + "DROP INDEX unique_index_data_object_data_id_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Create the new tables and triggers first. + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // This will eventually become the |database| table. + "CREATE TABLE database_upgrade " + "( name TEXT PRIMARY KEY" + ", origin TEXT NOT NULL" + ", version INTEGER NOT NULL DEFAULT 0" + ", last_vacuum_time INTEGER NOT NULL DEFAULT 0" + ", last_analyze_time INTEGER NOT NULL DEFAULT 0" + ", last_vacuum_size INTEGER NOT NULL DEFAULT 0" + ") WITHOUT ROWID;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // This will eventually become the |object_store| table. + "CREATE TABLE object_store_upgrade" + "( id INTEGER PRIMARY KEY" + ", auto_increment INTEGER NOT NULL DEFAULT 0" + ", name TEXT NOT NULL" + ", key_path TEXT" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // This will eventually become the |object_store_index| table. + "CREATE TABLE object_store_index_upgrade" + "( id INTEGER PRIMARY KEY" + ", object_store_id INTEGER NOT NULL" + ", name TEXT NOT NULL" + ", key_path TEXT NOT NULL" + ", unique_index INTEGER NOT NULL" + ", multientry INTEGER NOT NULL" + ", FOREIGN KEY (object_store_id) " + "REFERENCES object_store(id) " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // This will eventually become the |object_data| table. + "CREATE TABLE object_data_upgrade" + "( object_store_id INTEGER NOT NULL" + ", key BLOB NOT NULL" + ", index_data_values BLOB DEFAULT NULL" + ", file_ids TEXT" + ", data BLOB NOT NULL" + ", PRIMARY KEY (object_store_id, key)" + ", FOREIGN KEY (object_store_id) " + "REFERENCES object_store(id) " + ") WITHOUT ROWID;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // This will eventually become the |index_data| table. + "CREATE TABLE index_data_upgrade" + "( index_id INTEGER NOT NULL" + ", value BLOB NOT NULL" + ", object_data_key BLOB NOT NULL" + ", object_store_id INTEGER NOT NULL" + ", PRIMARY KEY (index_id, value, object_data_key)" + ", FOREIGN KEY (index_id) " + "REFERENCES object_store_index(id) " + ", FOREIGN KEY (object_store_id, object_data_key) " + "REFERENCES object_data(object_store_id, key) " + ") WITHOUT ROWID;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // This will eventually become the |unique_index_data| table. + "CREATE TABLE unique_index_data_upgrade" + "( index_id INTEGER NOT NULL" + ", value BLOB NOT NULL" + ", object_store_id INTEGER NOT NULL" + ", object_data_key BLOB NOT NULL" + ", PRIMARY KEY (index_id, value)" + ", FOREIGN KEY (index_id) " + "REFERENCES object_store_index(id) " + ", FOREIGN KEY (object_store_id, object_data_key) " + "REFERENCES object_data(object_store_id, key) " + ") WITHOUT ROWID;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // Temporarily store |index_data_values| that we build during the upgrade of + // the index tables. We will later move this to the |object_data| table. + "CREATE TEMPORARY TABLE temp_index_data_values " + "( object_store_id INTEGER NOT NULL" + ", key BLOB NOT NULL" + ", index_data_values BLOB DEFAULT NULL" + ", PRIMARY KEY (object_store_id, key)" + ") WITHOUT ROWID;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // These two triggers help build the |index_data_values| blobs. The nested + // SELECT statements help us achieve an "INSERT OR UPDATE"-like behavior. + "CREATE TEMPORARY TRIGGER unique_index_data_upgrade_insert_trigger " + "AFTER INSERT ON unique_index_data_upgrade " + "BEGIN " + "INSERT OR REPLACE INTO temp_index_data_values " + "VALUES " + "( NEW.object_store_id" + ", NEW.object_data_key" + ", insert_idv(" + "( SELECT index_data_values " + "FROM temp_index_data_values " + "WHERE object_store_id = NEW.object_store_id " + "AND key = NEW.object_data_key " + "), NEW.index_id" + ", 1" /* unique */ + ", NEW.value" + ")" + ");" + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TRIGGER index_data_upgrade_insert_trigger " + "AFTER INSERT ON index_data_upgrade " + "BEGIN " + "INSERT OR REPLACE INTO temp_index_data_values " + "VALUES " + "( NEW.object_store_id" + ", NEW.object_data_key" + ", insert_idv(" + "(" + "SELECT index_data_values " + "FROM temp_index_data_values " + "WHERE object_store_id = NEW.object_store_id " + "AND key = NEW.object_data_key " + "), NEW.index_id" + ", 0" /* not unique */ + ", NEW.value" + ")" + ");" + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Update the |unique_index_data| table to change the column order, remove the + // ON DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // Insert all the data. + "INSERT INTO unique_index_data_upgrade " + "SELECT " + "unique_index_data.index_id, " + "upgrade_key(unique_index_data.value), " + "object_data.object_store_id, " + "upgrade_key(unique_index_data.object_data_key) " + "FROM unique_index_data " + "JOIN object_data " + "ON unique_index_data.object_data_id = object_data.id;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // The trigger is no longer needed. + "DROP TRIGGER unique_index_data_upgrade_insert_trigger;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // The old table is no longer needed. + "DROP TABLE unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // Rename the table. + "ALTER TABLE unique_index_data_upgrade " + "RENAME TO unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Update the |index_data| table to change the column order, remove the ON + // DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // Insert all the data. + "INSERT INTO index_data_upgrade " + "SELECT " + "index_data.index_id, " + "upgrade_key(index_data.value), " + "upgrade_key(index_data.object_data_key), " + "object_data.object_store_id " + "FROM index_data " + "JOIN object_data " + "ON index_data.object_data_id = object_data.id;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // The trigger is no longer needed. + "DROP TRIGGER index_data_upgrade_insert_trigger;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // The old table is no longer needed. + "DROP TABLE index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // Rename the table. + "ALTER TABLE index_data_upgrade " + "RENAME TO index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Update the |object_data| table to add the |index_data_values| column, + // remove the ON DELETE CASCADE clause, and apply the WITHOUT ROWID + // optimization. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // Insert all the data. + "INSERT INTO object_data_upgrade " + "SELECT " + "object_data.object_store_id, " + "upgrade_key(object_data.key_value), " + "temp_index_data_values.index_data_values, " + "object_data.file_ids, " + "object_data.data " + "FROM object_data " + "LEFT JOIN temp_index_data_values " + "ON object_data.object_store_id = " + "temp_index_data_values.object_store_id " + "AND upgrade_key(object_data.key_value) = " + "temp_index_data_values.key;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // The temporary table is no longer needed. + "DROP TABLE temp_index_data_values;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // The old table is no longer needed. + "DROP TABLE object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // Rename the table. + "ALTER TABLE object_data_upgrade " + "RENAME TO object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Update the |object_store_index| table to remove the UNIQUE constraint and + // the ON DELETE CASCADE clause. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store_index_upgrade " + "SELECT * " + "FROM object_store_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE object_store_index_upgrade " + "RENAME TO object_store_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Update the |object_store| table to remove the UNIQUE constraint. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store_upgrade " + "SELECT * " + "FROM object_store;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE object_store_upgrade " + "RENAME TO object_store;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Update the |database| table to include the origin, vacuum information, and + // apply the WITHOUT ROWID optimization. + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO database_upgrade " + "SELECT name, :origin, version, 0, 0, 0 " + "FROM database;" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE database;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE database_upgrade " + "RENAME TO database;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + +#ifdef DEBUG + { + // Make sure there's only one entry in the |database| table. + nsCOMPtr stmt; + MOZ_ASSERT(NS_SUCCEEDED( + aConnection->CreateStatement( + NS_LITERAL_CSTRING("SELECT COUNT(*) " + "FROM database;"), + getter_AddRefs(stmt)))); + + bool hasResult; + MOZ_ASSERT(NS_SUCCEEDED(stmt->ExecuteStep(&hasResult))); + + int64_t count; + MOZ_ASSERT(NS_SUCCEEDED(stmt->GetInt64(0, &count))); + + MOZ_ASSERT(count == 1); + } +#endif + + // Recreate file table triggers. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_insert_trigger " + "AFTER INSERT ON object_data " + "WHEN NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(NULL, NEW.file_ids);" + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_update_trigger " + "AFTER UPDATE OF file_ids ON object_data " + "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NEW.file_ids);" + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_delete_trigger " + "AFTER DELETE ON object_data " + "WHEN OLD.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NULL);" + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Finally, turn on auto_vacuum mode. We use full auto_vacuum mode to reclaim + // disk space on mobile devices (at the cost of some COMMIT speed), and + // incremental auto_vacuum mode on desktop builds. + rv = aConnection->ExecuteSimpleSQL( +#ifdef IDB_MOBILE + NS_LITERAL_CSTRING("PRAGMA auto_vacuum = FULL;") +#else + NS_LITERAL_CSTRING("PRAGMA auto_vacuum = INCREMENTAL;") +#endif + ); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(18, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom17_0To18_0(mozIStorageConnection* aConnection, + const nsACString& aOrigin) +{ + MOZ_ASSERT(aConnection); + MOZ_ASSERT(!aOrigin.IsEmpty()); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom17_0To18_0", + js::ProfileEntry::Category::STORAGE); + + return UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(aConnection, aOrigin); +} + +nsresult +UpgradeSchemaFrom18_0To19_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + nsresult rv; + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom18_0To19_0", + js::ProfileEntry::Category::STORAGE); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE object_store_index " + "ADD COLUMN locale TEXT;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE object_store_index " + "ADD COLUMN is_auto_locale BOOLEAN;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE index_data " + "ADD COLUMN value_locale BLOB;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE unique_index_data " + "ADD COLUMN value_locale BLOB;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX index_data_value_locale_index " + "ON index_data (index_id, value_locale, object_data_key, value) " + "WHERE value_locale IS NOT NULL;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX unique_index_data_value_locale_index " + "ON unique_index_data (index_id, value_locale, object_data_key, value) " + "WHERE value_locale IS NOT NULL;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(19, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +#if !defined(MOZ_B2G) + +class NormalJSContext; + +class UpgradeFileIdsFunction final + : public mozIStorageFunction +{ + RefPtr mFileManager; + nsAutoPtr mContext; + +public: + UpgradeFileIdsFunction() + { + AssertIsOnIOThread(); + } + + nsresult + Init(nsIFile* aFMDirectory, + mozIStorageConnection* aConnection); + + NS_DECL_ISUPPORTS + +private: + ~UpgradeFileIdsFunction() + { + AssertIsOnIOThread(); + + if (mFileManager) { + mFileManager->Invalidate(); + } + } + + NS_IMETHOD + OnFunctionCall(mozIStorageValueArray* aArguments, + nsIVariant** aResult) override; +}; + +#endif // MOZ_B2G + +nsresult +UpgradeSchemaFrom19_0To20_0(nsIFile* aFMDirectory, + mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom19_0To20_0", + js::ProfileEntry::Category::STORAGE); + +#if defined(MOZ_B2G) + + // We don't have to do the upgrade of file ids on B2G. The old format was + // only used by the previous single process implementation and B2G was + // always multi process. This is a nice optimization since the upgrade needs + // to deserialize all structured clones which reference a stored file or + // a mutable file. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + +#else // MOZ_B2G + + nsCOMPtr stmt; + nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT count(*) " + "FROM object_data " + "WHERE file_ids IS NOT NULL" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int64_t count; + + { + mozStorageStatementScoper scoper(stmt); + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!hasResult)) { + MOZ_ASSERT(false, "This should never be possible!"); + return NS_ERROR_FAILURE; + } + + count = stmt->AsInt64(0); + if (NS_WARN_IF(count < 0)) { + MOZ_ASSERT(false, "This should never be possible!"); + return NS_ERROR_FAILURE; + } + } + + if (count == 0) { + // Nothing to upgrade. + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; + } + + RefPtr function = new UpgradeFileIdsFunction(); + + rv = function->Init(aFMDirectory, aConnection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + NS_NAMED_LITERAL_CSTRING(functionName, "upgrade"); + + rv = aConnection->CreateFunction(functionName, 2, function); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Disable update trigger. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TRIGGER object_data_update_trigger;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE object_data " + "SET file_ids = upgrade(file_ids, data) " + "WHERE file_ids IS NOT NULL;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Enable update trigger. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_update_trigger " + "AFTER UPDATE OF file_ids ON object_data " + "FOR EACH ROW " + "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->RemoveFunction(functionName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + +#endif // MOZ_B2G + + return NS_OK; +} + +class UpgradeIndexDataValuesFunction final + : public mozIStorageFunction +{ +public: + UpgradeIndexDataValuesFunction() + { + AssertIsOnIOThread(); + } + + NS_DECL_ISUPPORTS + +private: + ~UpgradeIndexDataValuesFunction() + { + AssertIsOnIOThread(); + } + + nsresult + ReadOldCompressedIDVFromBlob(const uint8_t* aBlobData, + uint32_t aBlobDataLength, + nsTArray& aIndexValues); + + NS_IMETHOD + OnFunctionCall(mozIStorageValueArray* aArguments, + nsIVariant** aResult) override; +}; + +NS_IMPL_ISUPPORTS(UpgradeIndexDataValuesFunction, mozIStorageFunction) + +nsresult +UpgradeIndexDataValuesFunction::ReadOldCompressedIDVFromBlob( + const uint8_t* aBlobData, + uint32_t aBlobDataLength, + nsTArray& aIndexValues) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aBlobData); + MOZ_ASSERT(aBlobDataLength); + MOZ_ASSERT(aIndexValues.IsEmpty()); + + const uint8_t* blobDataIter = aBlobData; + const uint8_t* blobDataEnd = aBlobData + aBlobDataLength; + + int64_t indexId; + bool unique; + bool nextIndexIdAlreadyRead = false; + + while (blobDataIter < blobDataEnd) { + if (!nextIndexIdAlreadyRead) { + ReadCompressedIndexId(&blobDataIter, blobDataEnd, &indexId, &unique); + } + nextIndexIdAlreadyRead = false; + + if (NS_WARN_IF(blobDataIter == blobDataEnd)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_FILE_CORRUPTED; + } + + // Read key buffer length. + const uint64_t keyBufferLength = + ReadCompressedNumber(&blobDataIter, blobDataEnd); + + if (NS_WARN_IF(blobDataIter == blobDataEnd) || + NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) || + NS_WARN_IF(blobDataIter + keyBufferLength > blobDataEnd)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_FILE_CORRUPTED; + } + + nsCString keyBuffer(reinterpret_cast(blobDataIter), + uint32_t(keyBufferLength)); + blobDataIter += keyBufferLength; + + IndexDataValue idv(indexId, unique, Key(keyBuffer)); + + if (blobDataIter < blobDataEnd) { + // Read either a sort key buffer length or an index id. + uint64_t maybeIndexId = ReadCompressedNumber(&blobDataIter, blobDataEnd); + + // Locale-aware indexes haven't been around long enough to have any users, + // we can safely assume all sort key buffer lengths will be zero. + if (maybeIndexId != 0) { + if (maybeIndexId % 2) { + unique = true; + maybeIndexId--; + } else { + unique = false; + } + indexId = maybeIndexId/2; + nextIndexIdAlreadyRead = true; + } + } + + if (NS_WARN_IF(!aIndexValues.InsertElementSorted(idv, fallible))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_OUT_OF_MEMORY; + } + } + + MOZ_ASSERT(blobDataIter == blobDataEnd); + + return NS_OK; +} + +NS_IMETHODIMP +UpgradeIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aArguments, + nsIVariant** aResult) +{ + MOZ_ASSERT(aArguments); + MOZ_ASSERT(aResult); + + PROFILER_LABEL("IndexedDB", + "UpgradeIndexDataValuesFunction::OnFunctionCall", + js::ProfileEntry::Category::STORAGE); + + uint32_t argc; + nsresult rv = aArguments->GetNumEntries(&argc); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (argc != 1) { + NS_WARNING("Don't call me with the wrong number of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + int32_t type; + rv = aArguments->GetTypeOfIndex(0, &type); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (type != mozIStorageStatement::VALUE_TYPE_BLOB) { + NS_WARNING("Don't call me with the wrong type of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + const uint8_t* oldBlob; + uint32_t oldBlobLength; + rv = aArguments->GetSharedBlob(0, &oldBlobLength, &oldBlob); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + AutoTArray oldIdv; + rv = ReadOldCompressedIDVFromBlob(oldBlob, oldBlobLength, oldIdv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + UniqueFreePtr newIdv; + uint32_t newIdvLength; + rv = MakeCompressedIndexDataValues(oldIdv, newIdv, &newIdvLength); + + std::pair data(newIdv.release(), newIdvLength); + + nsCOMPtr result = new storage::AdoptedBlobVariant(data); + + result.forget(aResult); + return NS_OK; +} + +nsresult +UpgradeSchemaFrom20_0To21_0(mozIStorageConnection* aConnection) +{ + // This should have been part of the 18 to 19 upgrade, where we changed the + // layout of the index_data_values blobs but didn't upgrade the existing data. + // See bug 1202788. + + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom20_0To21_0", + js::ProfileEntry::Category::STORAGE); + + RefPtr function = + new UpgradeIndexDataValuesFunction(); + + NS_NAMED_LITERAL_CSTRING(functionName, "upgrade_idv"); + + nsresult rv = aConnection->CreateFunction(functionName, 1, function); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE object_data " + "SET index_data_values = upgrade_idv(index_data_values) " + "WHERE index_data_values IS NOT NULL;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->RemoveFunction(functionName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(21, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom21_0To22_0(mozIStorageConnection* aConnection) +{ + // The only change between 21 and 22 was a different structured clone format, + // but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(22, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom22_0To23_0(mozIStorageConnection* aConnection, + const nsACString& aOrigin) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + MOZ_ASSERT(!aOrigin.IsEmpty()); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom22_0To23_0", + js::ProfileEntry::Category::STORAGE); + + nsCOMPtr stmt; + nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "UPDATE database " + "SET origin = :origin;" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(23, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom23_0To24_0(mozIStorageConnection* aConnection) +{ + // The only change between 23 and 24 was a different structured clone format, + // but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(24, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom24_0To25_0(mozIStorageConnection* aConnection) +{ + // The changes between 24 and 25 were an upgraded snappy library, a different + // structured clone format and a different file_ds format. But everything is + // backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(25, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +GetDatabaseFileURL(nsIFile* aDatabaseFile, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + uint32_t aTelemetryId, + nsIFileURL** aResult) +{ + MOZ_ASSERT(aDatabaseFile); + MOZ_ASSERT(aResult); + + nsresult rv; + + nsCOMPtr protocolHandler( + do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "file", &rv)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr fileHandler( + do_QueryInterface(protocolHandler, &rv)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr uri; + rv = fileHandler->NewFileURI(aDatabaseFile, getter_AddRefs(uri)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr fileUrl = do_QueryInterface(uri); + MOZ_ASSERT(fileUrl); + + nsAutoCString type; + PersistenceTypeToText(aPersistenceType, type); + + nsAutoCString telemetryFilenameClause; + if (aTelemetryId) { + telemetryFilenameClause.AssignLiteral("&telemetryFilename=indexedDB-"); + telemetryFilenameClause.AppendInt(aTelemetryId); + telemetryFilenameClause.AppendLiteral(".sqlite"); + } + + rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type + + NS_LITERAL_CSTRING("&group=") + aGroup + + NS_LITERAL_CSTRING("&origin=") + aOrigin + + NS_LITERAL_CSTRING("&cache=private") + + telemetryFilenameClause); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + fileUrl.forget(aResult); + return NS_OK; +} + +nsresult +SetDefaultPragmas(mozIStorageConnection* aConnection) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aConnection); + + static const char kBuiltInPragmas[] = + // We use foreign keys in DEBUG builds only because there is a performance + // cost to using them. + "PRAGMA foreign_keys = " +#ifdef DEBUG + "ON" +#else + "OFF" +#endif + ";" + + // The "INSERT OR REPLACE" statement doesn't fire the update trigger, + // instead it fires only the insert trigger. This confuses the update + // refcount function. This behavior changes with enabled recursive triggers, + // so the statement fires the delete trigger first and then the insert + // trigger. + "PRAGMA recursive_triggers = ON;" + + // We aggressively truncate the database file when idle so don't bother + // overwriting the WAL with 0 during active periods. + "PRAGMA secure_delete = OFF;" + ; + + nsresult rv = + aConnection->ExecuteSimpleSQL( + nsDependentCString(kBuiltInPragmas, + LiteralStringLength(kBuiltInPragmas))); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsAutoCString pragmaStmt; + pragmaStmt.AssignLiteral("PRAGMA synchronous = "); + + if (IndexedDatabaseManager::FullSynchronous()) { + pragmaStmt.AppendLiteral("FULL"); + } else { + pragmaStmt.AppendLiteral("NORMAL"); + } + pragmaStmt.Append(';'); + + rv = aConnection->ExecuteSimpleSQL(pragmaStmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + +#ifndef IDB_MOBILE + if (kSQLiteGrowthIncrement) { + // This is just an optimization so ignore the failure if the disk is + // currently too full. + rv = aConnection->SetGrowthIncrement(kSQLiteGrowthIncrement, + EmptyCString()); + if (rv != NS_ERROR_FILE_TOO_BIG && NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } +#endif // IDB_MOBILE + + return NS_OK; +} + +nsresult +SetJournalMode(mozIStorageConnection* aConnection) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aConnection); + + // Try enabling WAL mode. This can fail in various circumstances so we have to + // check the results here. + NS_NAMED_LITERAL_CSTRING(journalModeQueryStart, "PRAGMA journal_mode = "); + NS_NAMED_LITERAL_CSTRING(journalModeWAL, "wal"); + + nsCOMPtr stmt; + nsresult rv = + aConnection->CreateStatement(journalModeQueryStart + journalModeWAL, + getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(hasResult); + + nsCString journalMode; + rv = stmt->GetUTF8String(0, journalMode); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (journalMode.Equals(journalModeWAL)) { + // WAL mode successfully enabled. Maybe set limits on its size here. + if (kMaxWALPages >= 0) { + nsAutoCString pageCount; + pageCount.AppendInt(kMaxWALPages); + + rv = aConnection->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("PRAGMA wal_autocheckpoint = ") + pageCount); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + } else { + NS_WARNING("Failed to set WAL mode, falling back to normal journal mode."); +#ifdef IDB_MOBILE + rv = aConnection->ExecuteSimpleSQL(journalModeQueryStart + + NS_LITERAL_CSTRING("truncate")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } +#endif + } + + return NS_OK; +} + +template +struct StorageOpenTraits; + +template <> +struct StorageOpenTraits +{ + static nsresult + Open(mozIStorageService* aStorageService, + nsIFileURL* aFileURL, + mozIStorageConnection** aConnection) + { + return aStorageService->OpenDatabaseWithFileURL(aFileURL, aConnection); + } + +#ifdef DEBUG + static void + GetPath(nsIFileURL* aFileURL, nsCString& aPath) + { + MOZ_ALWAYS_SUCCEEDS(aFileURL->GetFileName(aPath)); + } +#endif +}; + +template <> +struct StorageOpenTraits +{ + static nsresult + Open(mozIStorageService* aStorageService, + nsIFile* aFile, + mozIStorageConnection** aConnection) + { + return aStorageService->OpenUnsharedDatabase(aFile, aConnection); + } + +#ifdef DEBUG + static void + GetPath(nsIFile* aFile, nsCString& aPath) + { + nsString path; + MOZ_ALWAYS_SUCCEEDS(aFile->GetPath(path)); + + aPath.AssignWithConversion(path); + } +#endif +}; + +template