From 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Tue, 27 Jun 2017 06:07:23 +0000 Subject: webkitgtk-2.16.5 --- .../WebKitPlaybackTargetAvailabilityEvent.cpp | 57 + .../WebKitPlaybackTargetAvailabilityEvent.h | 65 + .../WebKitPlaybackTargetAvailabilityEvent.idl | 36 + Source/WebCore/Modules/applepay/ApplePayLineItem.h | 44 + .../WebCore/Modules/applepay/ApplePayLineItem.idl | 39 + Source/WebCore/Modules/applepay/ApplePayPayment.h | 49 + .../WebCore/Modules/applepay/ApplePayPayment.idl | 42 + .../applepay/ApplePayPaymentAuthorizedEvent.cpp | 52 + .../applepay/ApplePayPaymentAuthorizedEvent.h | 59 + .../applepay/ApplePayPaymentAuthorizedEvent.idl | 31 + .../Modules/applepay/ApplePayPaymentContact.h | 51 + .../Modules/applepay/ApplePayPaymentContact.idl | 40 + .../Modules/applepay/ApplePayPaymentMethod.h | 48 + .../Modules/applepay/ApplePayPaymentMethod.idl | 43 + .../ApplePayPaymentMethodSelectedEvent.cpp | 52 + .../applepay/ApplePayPaymentMethodSelectedEvent.h | 59 + .../ApplePayPaymentMethodSelectedEvent.idl | 31 + .../WebCore/Modules/applepay/ApplePayPaymentPass.h | 46 + .../Modules/applepay/ApplePayPaymentPass.idl | 45 + .../Modules/applepay/ApplePayPaymentRequest.h | 65 + .../Modules/applepay/ApplePayPaymentRequest.idl | 74 + .../WebCore/Modules/applepay/ApplePaySession.cpp | 1032 ++++++++ Source/WebCore/Modules/applepay/ApplePaySession.h | 148 ++ .../WebCore/Modules/applepay/ApplePaySession.idl | 62 + .../ApplePayShippingContactSelectedEvent.cpp | 52 + .../ApplePayShippingContactSelectedEvent.h | 58 + .../ApplePayShippingContactSelectedEvent.idl | 31 + .../Modules/applepay/ApplePayShippingMethod.h | 43 + .../Modules/applepay/ApplePayShippingMethod.idl | 34 + .../ApplePayShippingMethodSelectedEvent.cpp | 77 + .../applepay/ApplePayShippingMethodSelectedEvent.h | 58 + .../ApplePayShippingMethodSelectedEvent.idl | 31 + .../applepay/ApplePayValidateMerchantEvent.cpp | 50 + .../applepay/ApplePayValidateMerchantEvent.h | 57 + .../applepay/ApplePayValidateMerchantEvent.idl | 31 + Source/WebCore/Modules/applepay/Payment.h | 63 + .../Modules/applepay/PaymentAuthorizationStatus.h | 62 + Source/WebCore/Modules/applepay/PaymentContact.h | 58 + .../Modules/applepay/PaymentCoordinator.cpp | 190 ++ .../WebCore/Modules/applepay/PaymentCoordinator.h | 79 + .../Modules/applepay/PaymentCoordinatorClient.h | 62 + Source/WebCore/Modules/applepay/PaymentHeaders.h | 33 + .../Modules/applepay/PaymentMerchantSession.h | 67 + Source/WebCore/Modules/applepay/PaymentMethod.h | 59 + Source/WebCore/Modules/applepay/PaymentRequest.cpp | 74 + Source/WebCore/Modules/applepay/PaymentRequest.h | 152 ++ .../Modules/applepay/PaymentRequestValidator.cpp | 159 ++ .../Modules/applepay/PaymentRequestValidator.h | 43 + Source/WebCore/Modules/battery/BatteryClient.h | 44 - .../WebCore/Modules/battery/BatteryController.cpp | 116 - Source/WebCore/Modules/battery/BatteryController.h | 65 - Source/WebCore/Modules/battery/BatteryManager.cpp | 112 - Source/WebCore/Modules/battery/BatteryManager.h | 87 - Source/WebCore/Modules/battery/BatteryManager.idl | 45 - Source/WebCore/Modules/battery/BatteryStatus.cpp | 58 - Source/WebCore/Modules/battery/BatteryStatus.h | 54 - .../WebCore/Modules/battery/NavigatorBattery.cpp | 75 - Source/WebCore/Modules/battery/NavigatorBattery.h | 55 - .../WebCore/Modules/battery/NavigatorBattery.idl | 24 - Source/WebCore/Modules/encryptedmedia/CDM.cpp | 663 +++++ Source/WebCore/Modules/encryptedmedia/CDM.h | 122 + .../WebCore/Modules/encryptedmedia/CDMInstance.h | 92 + Source/WebCore/Modules/encryptedmedia/CDMPrivate.h | 63 + .../Modules/encryptedmedia/InitDataRegistry.cpp | 165 ++ .../Modules/encryptedmedia/InitDataRegistry.h | 68 + .../encryptedmedia/MediaKeyMessageEvent.cpp | 52 + .../Modules/encryptedmedia/MediaKeyMessageEvent.h | 67 + .../encryptedmedia/MediaKeyMessageEvent.idl | 48 + .../encryptedmedia/MediaKeyMessageEventInit.h | 54 + .../Modules/encryptedmedia/MediaKeyMessageType.h | 44 + .../Modules/encryptedmedia/MediaKeySession.cpp | 709 ++++++ .../Modules/encryptedmedia/MediaKeySession.h | 119 + .../Modules/encryptedmedia/MediaKeySession.idl | 44 + .../Modules/encryptedmedia/MediaKeySessionType.h | 43 + .../Modules/encryptedmedia/MediaKeySessionType.idl | 35 + .../Modules/encryptedmedia/MediaKeyStatus.h | 47 + .../Modules/encryptedmedia/MediaKeyStatusMap.cpp | 112 + .../Modules/encryptedmedia/MediaKeyStatusMap.h | 81 + .../Modules/encryptedmedia/MediaKeyStatusMap.idl | 47 + .../encryptedmedia/MediaKeySystemAccess.cpp | 118 + .../Modules/encryptedmedia/MediaKeySystemAccess.h | 65 + .../encryptedmedia/MediaKeySystemAccess.idl | 37 + .../encryptedmedia/MediaKeySystemConfiguration.h | 55 + .../encryptedmedia/MediaKeySystemConfiguration.idl | 40 + .../encryptedmedia/MediaKeySystemMediaCapability.h | 44 + .../MediaKeySystemMediaCapability.idl | 35 + .../WebCore/Modules/encryptedmedia/MediaKeys.cpp | 114 + Source/WebCore/Modules/encryptedmedia/MediaKeys.h | 75 + .../WebCore/Modules/encryptedmedia/MediaKeys.idl | 36 + .../Modules/encryptedmedia/MediaKeysRequirement.h | 43 + .../encryptedmedia/MediaKeysRequirement.idl | 36 + .../Modules/encryptedmedia/MediaKeysRestrictions.h | 42 + .../Modules/encryptedmedia/NavigatorEME.cpp | 109 + .../WebCore/Modules/encryptedmedia/NavigatorEME.h | 50 + .../Modules/encryptedmedia/NavigatorEME.idl | 34 + .../Modules/encryptedmedia/legacy/LegacyCDM.cpp | 151 ++ .../Modules/encryptedmedia/legacy/LegacyCDM.h | 81 + .../encryptedmedia/legacy/LegacyCDMPrivate.h | 49 + .../legacy/LegacyCDMPrivateClearKey.cpp | 68 + .../legacy/LegacyCDMPrivateClearKey.h | 57 + .../legacy/LegacyCDMPrivateMediaPlayer.cpp | 68 + .../legacy/LegacyCDMPrivateMediaPlayer.h | 58 + .../legacy/LegacyCDMSessionClearKey.cpp | 209 ++ .../legacy/LegacyCDMSessionClearKey.h | 59 + .../legacy/WebKitMediaKeyMessageEvent.cpp | 61 + .../legacy/WebKitMediaKeyMessageEvent.h | 70 + .../legacy/WebKitMediaKeyMessageEvent.idl | 37 + .../legacy/WebKitMediaKeyNeededEvent.cpp | 58 + .../legacy/WebKitMediaKeyNeededEvent.h | 66 + .../legacy/WebKitMediaKeyNeededEvent.idl | 35 + .../legacy/WebKitMediaKeySession.cpp | 256 ++ .../encryptedmedia/legacy/WebKitMediaKeySession.h | 106 + .../legacy/WebKitMediaKeySession.idl | 41 + .../encryptedmedia/legacy/WebKitMediaKeys.cpp | 165 ++ .../encryptedmedia/legacy/WebKitMediaKeys.h | 70 + .../encryptedmedia/legacy/WebKitMediaKeys.idl | 34 + Source/WebCore/Modules/fetch/DOMWindowFetch.cpp | 51 + Source/WebCore/Modules/fetch/DOMWindowFetch.h | 48 + Source/WebCore/Modules/fetch/DOMWindowFetch.idl | 35 + Source/WebCore/Modules/fetch/DOMWindowFetch.js | 37 + Source/WebCore/Modules/fetch/FetchBody.cpp | 277 +++ Source/WebCore/Modules/fetch/FetchBody.h | 125 + Source/WebCore/Modules/fetch/FetchBody.idl | 44 + Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp | 155 ++ Source/WebCore/Modules/fetch/FetchBodyConsumer.h | 73 + Source/WebCore/Modules/fetch/FetchBodyOwner.cpp | 273 ++ Source/WebCore/Modules/fetch/FetchBodyOwner.h | 112 + Source/WebCore/Modules/fetch/FetchHeaders.cpp | 148 ++ Source/WebCore/Modules/fetch/FetchHeaders.h | 103 + Source/WebCore/Modules/fetch/FetchHeaders.idl | 49 + Source/WebCore/Modules/fetch/FetchHeaders.js | 41 + Source/WebCore/Modules/fetch/FetchInternals.js | 80 + Source/WebCore/Modules/fetch/FetchLoader.cpp | 156 ++ Source/WebCore/Modules/fetch/FetchLoader.h | 72 + Source/WebCore/Modules/fetch/FetchLoaderClient.h | 53 + Source/WebCore/Modules/fetch/FetchRequest.cpp | 241 ++ Source/WebCore/Modules/fetch/FetchRequest.h | 158 ++ Source/WebCore/Modules/fetch/FetchRequest.idl | 84 + Source/WebCore/Modules/fetch/FetchRequest.js | 50 + Source/WebCore/Modules/fetch/FetchResponse.cpp | 365 +++ Source/WebCore/Modules/fetch/FetchResponse.h | 133 + Source/WebCore/Modules/fetch/FetchResponse.idl | 77 + Source/WebCore/Modules/fetch/FetchResponse.js | 172 ++ .../WebCore/Modules/fetch/FetchResponseSource.cpp | 87 + Source/WebCore/Modules/fetch/FetchResponseSource.h | 69 + .../Modules/fetch/WorkerGlobalScopeFetch.cpp | 46 + .../WebCore/Modules/fetch/WorkerGlobalScopeFetch.h | 47 + .../Modules/fetch/WorkerGlobalScopeFetch.idl | 35 + .../Modules/fetch/WorkerGlobalScopeFetch.js | 37 + Source/WebCore/Modules/gamepad/Gamepad.cpp | 67 +- Source/WebCore/Modules/gamepad/Gamepad.h | 69 +- Source/WebCore/Modules/gamepad/Gamepad.idl | 38 +- Source/WebCore/Modules/gamepad/GamepadButton.cpp | 48 + Source/WebCore/Modules/gamepad/GamepadButton.h | 54 + Source/WebCore/Modules/gamepad/GamepadButton.idl | 34 + Source/WebCore/Modules/gamepad/GamepadEvent.cpp | 46 + Source/WebCore/Modules/gamepad/GamepadEvent.h | 67 + Source/WebCore/Modules/gamepad/GamepadEvent.idl | 38 + Source/WebCore/Modules/gamepad/GamepadList.cpp | 58 - Source/WebCore/Modules/gamepad/GamepadList.h | 59 - Source/WebCore/Modules/gamepad/GamepadList.idl | 34 - Source/WebCore/Modules/gamepad/GamepadManager.cpp | 245 ++ Source/WebCore/Modules/gamepad/GamepadManager.h | 76 + .../WebCore/Modules/gamepad/NavigatorGamepad.cpp | 122 +- Source/WebCore/Modules/gamepad/NavigatorGamepad.h | 55 +- .../WebCore/Modules/gamepad/NavigatorGamepad.idl | 36 +- .../WebCore/Modules/gamepad/deprecated/Gamepad.cpp | 61 + .../WebCore/Modules/gamepad/deprecated/Gamepad.h | 72 + .../WebCore/Modules/gamepad/deprecated/Gamepad.idl | 37 + .../Modules/gamepad/deprecated/GamepadList.cpp | 58 + .../Modules/gamepad/deprecated/GamepadList.h | 55 + .../Modules/gamepad/deprecated/GamepadList.idl | 34 + .../gamepad/deprecated/NavigatorGamepad.cpp | 76 + .../Modules/gamepad/deprecated/NavigatorGamepad.h | 56 + .../gamepad/deprecated/NavigatorGamepad.idl | 25 + Source/WebCore/Modules/geolocation/Coordinates.cpp | 40 +- Source/WebCore/Modules/geolocation/Coordinates.h | 18 +- Source/WebCore/Modules/geolocation/Coordinates.idl | 14 +- Source/WebCore/Modules/geolocation/GeoNotifier.cpp | 132 + Source/WebCore/Modules/geolocation/GeoNotifier.h | 80 + Source/WebCore/Modules/geolocation/Geolocation.cpp | 325 +-- Source/WebCore/Modules/geolocation/Geolocation.h | 93 +- Source/WebCore/Modules/geolocation/Geolocation.idl | 16 +- .../Modules/geolocation/GeolocationClient.h | 7 +- .../Modules/geolocation/GeolocationController.cpp | 84 +- .../Modules/geolocation/GeolocationController.h | 30 +- .../WebCore/Modules/geolocation/GeolocationError.h | 9 +- .../Modules/geolocation/GeolocationPosition.h | 18 +- Source/WebCore/Modules/geolocation/Geoposition.h | 22 +- Source/WebCore/Modules/geolocation/Geoposition.idl | 4 + .../Modules/geolocation/NavigatorGeolocation.cpp | 11 +- .../Modules/geolocation/NavigatorGeolocation.h | 10 +- .../WebCore/Modules/geolocation/PositionCallback.h | 5 +- .../Modules/geolocation/PositionCallback.idl | 4 +- Source/WebCore/Modules/geolocation/PositionError.h | 8 +- .../Modules/geolocation/PositionErrorCallback.h | 5 +- .../Modules/geolocation/PositionErrorCallback.idl | 4 +- .../WebCore/Modules/geolocation/PositionOptions.h | 57 +- .../Modules/geolocation/PositionOptions.idl | 30 + .../Modules/indexeddb/DOMWindowIndexedDatabase.cpp | 40 +- .../Modules/indexeddb/DOMWindowIndexedDatabase.h | 22 +- .../Modules/indexeddb/DOMWindowIndexedDatabase.idl | 7 +- .../WebCore/Modules/indexeddb/IDBActiveDOMObject.h | 98 + Source/WebCore/Modules/indexeddb/IDBAny.cpp | 218 -- Source/WebCore/Modules/indexeddb/IDBAny.h | 145 -- Source/WebCore/Modules/indexeddb/IDBAny.idl | 33 - Source/WebCore/Modules/indexeddb/IDBCallbacks.h | 83 - Source/WebCore/Modules/indexeddb/IDBCursor.cpp | 508 ++-- Source/WebCore/Modules/indexeddb/IDBCursor.h | 199 +- Source/WebCore/Modules/indexeddb/IDBCursor.idl | 18 +- .../WebCore/Modules/indexeddb/IDBCursorBackend.cpp | 109 - .../WebCore/Modules/indexeddb/IDBCursorBackend.h | 98 - .../indexeddb/IDBCursorBackendOperations.cpp | 86 - .../Modules/indexeddb/IDBCursorBackendOperations.h | 88 - .../WebCore/Modules/indexeddb/IDBCursorDirection.h | 34 + .../Modules/indexeddb/IDBCursorDirection.idl | 33 + .../Modules/indexeddb/IDBCursorWithValue.cpp | 55 +- .../WebCore/Modules/indexeddb/IDBCursorWithValue.h | 60 +- .../Modules/indexeddb/IDBCursorWithValue.idl | 4 +- Source/WebCore/Modules/indexeddb/IDBDatabase.cpp | 626 +++-- Source/WebCore/Modules/indexeddb/IDBDatabase.h | 197 +- Source/WebCore/Modules/indexeddb/IDBDatabase.idl | 29 +- .../Modules/indexeddb/IDBDatabaseBackend.cpp | 623 ----- .../WebCore/Modules/indexeddb/IDBDatabaseBackend.h | 174 -- .../Modules/indexeddb/IDBDatabaseCallbacks.h | 57 - .../Modules/indexeddb/IDBDatabaseCallbacksImpl.cpp | 82 - .../Modules/indexeddb/IDBDatabaseCallbacksImpl.h | 64 - .../WebCore/Modules/indexeddb/IDBDatabaseError.h | 16 +- .../Modules/indexeddb/IDBDatabaseException.cpp | 12 +- .../Modules/indexeddb/IDBDatabaseException.h | 11 +- .../Modules/indexeddb/IDBDatabaseIdentifier.cpp | 84 + .../Modules/indexeddb/IDBDatabaseIdentifier.h | 153 ++ .../Modules/indexeddb/IDBDatabaseMetadata.cpp | 79 - .../Modules/indexeddb/IDBDatabaseMetadata.h | 85 - .../Modules/indexeddb/IDBEventDispatcher.cpp | 28 +- .../WebCore/Modules/indexeddb/IDBEventDispatcher.h | 10 +- Source/WebCore/Modules/indexeddb/IDBFactory.cpp | 213 +- Source/WebCore/Modules/indexeddb/IDBFactory.h | 99 +- Source/WebCore/Modules/indexeddb/IDBFactory.idl | 10 +- .../indexeddb/IDBFactoryBackendInterface.cpp | 45 - .../Modules/indexeddb/IDBFactoryBackendInterface.h | 74 - .../WebCore/Modules/indexeddb/IDBGetAllResult.cpp | 118 + Source/WebCore/Modules/indexeddb/IDBGetAllResult.h | 132 + Source/WebCore/Modules/indexeddb/IDBGetResult.cpp | 62 + Source/WebCore/Modules/indexeddb/IDBGetResult.h | 119 +- Source/WebCore/Modules/indexeddb/IDBHistograms.h | 45 - Source/WebCore/Modules/indexeddb/IDBIndex.cpp | 451 ++-- Source/WebCore/Modules/indexeddb/IDBIndex.h | 161 +- Source/WebCore/Modules/indexeddb/IDBIndex.idl | 38 +- .../WebCore/Modules/indexeddb/IDBIndexMetadata.h | 68 - Source/WebCore/Modules/indexeddb/IDBKey.cpp | 115 +- Source/WebCore/Modules/indexeddb/IDBKey.h | 172 +- Source/WebCore/Modules/indexeddb/IDBKeyData.cpp | 446 +++- Source/WebCore/Modules/indexeddb/IDBKeyData.h | 271 +- Source/WebCore/Modules/indexeddb/IDBKeyPath.cpp | 183 +- Source/WebCore/Modules/indexeddb/IDBKeyPath.h | 73 +- Source/WebCore/Modules/indexeddb/IDBKeyRange.cpp | 140 +- Source/WebCore/Modules/indexeddb/IDBKeyRange.h | 70 +- Source/WebCore/Modules/indexeddb/IDBKeyRange.idl | 16 +- .../WebCore/Modules/indexeddb/IDBKeyRangeData.cpp | 79 +- Source/WebCore/Modules/indexeddb/IDBKeyRangeData.h | 69 +- .../WebCore/Modules/indexeddb/IDBObjectStore.cpp | 969 ++++---- Source/WebCore/Modules/indexeddb/IDBObjectStore.h | 220 +- .../WebCore/Modules/indexeddb/IDBObjectStore.idl | 55 +- .../Modules/indexeddb/IDBObjectStoreMetadata.h | 69 - .../WebCore/Modules/indexeddb/IDBOpenDBRequest.cpp | 268 +- .../WebCore/Modules/indexeddb/IDBOpenDBRequest.h | 86 +- .../WebCore/Modules/indexeddb/IDBOpenDBRequest.idl | 9 +- Source/WebCore/Modules/indexeddb/IDBOperation.h | 50 - .../Modules/indexeddb/IDBPendingDeleteCall.h | 56 - .../WebCore/Modules/indexeddb/IDBPendingOpenCall.h | 67 - .../indexeddb/IDBPendingTransactionMonitor.cpp | 70 - .../indexeddb/IDBPendingTransactionMonitor.h | 57 - .../Modules/indexeddb/IDBRecordIdentifier.h | 18 +- Source/WebCore/Modules/indexeddb/IDBRequest.cpp | 755 +++--- Source/WebCore/Modules/indexeddb/IDBRequest.h | 249 +- Source/WebCore/Modules/indexeddb/IDBRequest.idl | 27 +- .../indexeddb/IDBRequestCompletionEvent.cpp | 41 + .../Modules/indexeddb/IDBRequestCompletionEvent.h | 50 + .../Modules/indexeddb/IDBServerConnection.h | 99 - .../WebCore/Modules/indexeddb/IDBTransaction.cpp | 1492 +++++++++-- Source/WebCore/Modules/indexeddb/IDBTransaction.h | 375 ++- .../WebCore/Modules/indexeddb/IDBTransaction.idl | 21 +- .../Modules/indexeddb/IDBTransactionBackend.cpp | 359 --- .../Modules/indexeddb/IDBTransactionBackend.h | 128 - .../indexeddb/IDBTransactionBackendOperations.cpp | 282 --- .../indexeddb/IDBTransactionBackendOperations.h | 471 ---- .../indexeddb/IDBTransactionCoordinator.cpp | 152 -- .../Modules/indexeddb/IDBTransactionCoordinator.h | 72 - .../WebCore/Modules/indexeddb/IDBTransactionMode.h | 40 + .../Modules/indexeddb/IDBTransactionMode.idl | 32 + Source/WebCore/Modules/indexeddb/IDBValue.cpp | 91 + Source/WebCore/Modules/indexeddb/IDBValue.h | 87 + .../Modules/indexeddb/IDBVersionChangeEvent.cpp | 52 +- .../Modules/indexeddb/IDBVersionChangeEvent.h | 89 +- .../Modules/indexeddb/IDBVersionChangeEvent.idl | 8 +- Source/WebCore/Modules/indexeddb/IndexedDB.h | 68 +- .../Modules/indexeddb/PageGroupIndexedDatabase.cpp | 72 - .../Modules/indexeddb/PageGroupIndexedDatabase.h | 58 - .../indexeddb/WorkerGlobalScopeIndexedDatabase.cpp | 44 +- .../indexeddb/WorkerGlobalScopeIndexedDatabase.h | 35 +- .../indexeddb/WorkerGlobalScopeIndexedDatabase.idl | 33 +- .../indexeddb/client/IDBConnectionProxy.cpp | 562 +++++ .../Modules/indexeddb/client/IDBConnectionProxy.h | 177 ++ .../indexeddb/client/IDBConnectionToServer.cpp | 446 ++++ .../indexeddb/client/IDBConnectionToServer.h | 155 ++ .../client/IDBConnectionToServerDelegate.h | 98 + .../indexeddb/client/TransactionOperation.cpp | 53 + .../indexeddb/client/TransactionOperation.h | 270 ++ .../leveldb/IDBBackingStoreCursorLevelDB.cpp | 202 -- .../leveldb/IDBBackingStoreCursorLevelDB.h | 100 - .../indexeddb/leveldb/IDBBackingStoreLevelDB.cpp | 1931 --------------- .../indexeddb/leveldb/IDBBackingStoreLevelDB.h | 143 -- .../leveldb/IDBBackingStoreTransactionLevelDB.cpp | 86 - .../leveldb/IDBBackingStoreTransactionLevelDB.h | 71 - .../indexeddb/leveldb/IDBFactoryBackendLevelDB.cpp | 197 -- .../indexeddb/leveldb/IDBFactoryBackendLevelDB.h | 87 - .../indexeddb/leveldb/IDBIndexWriterLevelDB.cpp | 94 - .../indexeddb/leveldb/IDBIndexWriterLevelDB.h | 66 - .../Modules/indexeddb/leveldb/IDBLevelDBCoding.cpp | 1807 -------------- .../Modules/indexeddb/leveldb/IDBLevelDBCoding.h | 367 --- .../leveldb/IDBServerConnectionLevelDB.cpp | 626 ----- .../indexeddb/leveldb/IDBServerConnectionLevelDB.h | 100 - .../Modules/indexeddb/server/IDBBackingStore.h | 106 + .../indexeddb/server/IDBConnectionToClient.cpp | 192 ++ .../indexeddb/server/IDBConnectionToClient.h | 92 + .../server/IDBConnectionToClientDelegate.h | 82 + .../Modules/indexeddb/server/IDBSerialization.cpp | 420 ++++ .../Modules/indexeddb/server/IDBSerialization.h | 45 + .../WebCore/Modules/indexeddb/server/IDBServer.cpp | 682 +++++ .../WebCore/Modules/indexeddb/server/IDBServer.h | 148 ++ .../Modules/indexeddb/server/IndexValueEntry.cpp | 232 ++ .../Modules/indexeddb/server/IndexValueEntry.h | 102 + .../Modules/indexeddb/server/IndexValueStore.cpp | 419 ++++ .../Modules/indexeddb/server/IndexValueStore.h | 118 + .../server/MemoryBackingStoreTransaction.cpp | 296 +++ .../server/MemoryBackingStoreTransaction.h | 107 + .../Modules/indexeddb/server/MemoryCursor.cpp | 66 + .../Modules/indexeddb/server/MemoryCursor.h | 58 + .../indexeddb/server/MemoryIDBBackingStore.cpp | 599 +++++ .../indexeddb/server/MemoryIDBBackingStore.h | 101 + .../Modules/indexeddb/server/MemoryIndex.cpp | 277 +++ .../WebCore/Modules/indexeddb/server/MemoryIndex.h | 112 + .../Modules/indexeddb/server/MemoryIndexCursor.cpp | 228 ++ .../Modules/indexeddb/server/MemoryIndexCursor.h | 61 + .../Modules/indexeddb/server/MemoryObjectStore.cpp | 522 ++++ .../Modules/indexeddb/server/MemoryObjectStore.h | 137 ++ .../indexeddb/server/MemoryObjectStoreCursor.cpp | 356 +++ .../indexeddb/server/MemoryObjectStoreCursor.h | 74 + .../indexeddb/server/SQLiteIDBBackingStore.cpp | 2601 ++++++++++++++++++++ .../indexeddb/server/SQLiteIDBBackingStore.h | 202 ++ .../Modules/indexeddb/server/SQLiteIDBCursor.cpp | 580 +++++ .../Modules/indexeddb/server/SQLiteIDBCursor.h | 132 + .../indexeddb/server/SQLiteIDBTransaction.cpp | 233 ++ .../indexeddb/server/SQLiteIDBTransaction.h | 97 + .../indexeddb/server/ServerOpenDBRequest.cpp | 91 + .../Modules/indexeddb/server/ServerOpenDBRequest.h | 78 + .../Modules/indexeddb/server/UniqueIDBDatabase.cpp | 1908 ++++++++++++++ .../Modules/indexeddb/server/UniqueIDBDatabase.h | 281 +++ .../server/UniqueIDBDatabaseConnection.cpp | 236 ++ .../indexeddb/server/UniqueIDBDatabaseConnection.h | 102 + .../server/UniqueIDBDatabaseTransaction.cpp | 375 +++ .../server/UniqueIDBDatabaseTransaction.h | 105 + .../Modules/indexeddb/shared/IDBCursorInfo.cpp | 114 + .../Modules/indexeddb/shared/IDBCursorInfo.h | 137 ++ .../Modules/indexeddb/shared/IDBCursorRecord.h | 67 + .../Modules/indexeddb/shared/IDBDatabaseInfo.cpp | 178 ++ .../Modules/indexeddb/shared/IDBDatabaseInfo.h | 111 + .../WebCore/Modules/indexeddb/shared/IDBError.cpp | 73 + Source/WebCore/Modules/indexeddb/shared/IDBError.h | 87 + .../indexeddb/shared/IDBGetAllRecordsData.cpp | 51 + .../indexeddb/shared/IDBGetAllRecordsData.h | 88 + .../Modules/indexeddb/shared/IDBGetRecordData.cpp | 49 + .../Modules/indexeddb/shared/IDBGetRecordData.h | 74 + .../Modules/indexeddb/shared/IDBIndexInfo.cpp | 70 + .../Modules/indexeddb/shared/IDBIndexInfo.h | 103 + .../indexeddb/shared/IDBIterateCursorData.cpp | 47 + .../indexeddb/shared/IDBIterateCursorData.h | 78 + .../indexeddb/shared/IDBObjectStoreInfo.cpp | 163 ++ .../Modules/indexeddb/shared/IDBObjectStoreInfo.h | 115 + .../Modules/indexeddb/shared/IDBRequestData.cpp | 160 ++ .../Modules/indexeddb/shared/IDBRequestData.h | 175 ++ .../indexeddb/shared/IDBResourceIdentifier.cpp | 108 + .../indexeddb/shared/IDBResourceIdentifier.h | 150 ++ .../Modules/indexeddb/shared/IDBResultData.cpp | 244 ++ .../Modules/indexeddb/shared/IDBResultData.h | 231 ++ .../indexeddb/shared/IDBTransactionInfo.cpp | 121 + .../Modules/indexeddb/shared/IDBTransactionInfo.h | 130 + .../indexeddb/shared/InProcessIDBServer.cpp | 451 ++++ .../Modules/indexeddb/shared/InProcessIDBServer.h | 131 + .../WebCore/Modules/indexeddb/shared/IndexKey.cpp | 89 + Source/WebCore/Modules/indexeddb/shared/IndexKey.h | 54 + .../Modules/mediacontrols/MediaControlsHost.cpp | 294 +++ .../Modules/mediacontrols/MediaControlsHost.h | 101 + .../Modules/mediacontrols/MediaControlsHost.idl | 65 + .../Modules/mediacontrols/assets-apple-iOS.svg | 52 + .../Modules/mediacontrols/mediaControlsApple.css | 1153 +++++++++ .../Modules/mediacontrols/mediaControlsApple.js | 2509 +++++++++++++++++++ .../Modules/mediacontrols/mediaControlsBase.css | 758 ++++++ .../Modules/mediacontrols/mediaControlsBase.js | 1336 ++++++++++ .../Modules/mediacontrols/mediaControlsGtk.js | 258 ++ .../Modules/mediacontrols/mediaControlsiOS.css | 733 ++++++ .../Modules/mediacontrols/mediaControlsiOS.js | 628 +++++ .../mediasession/HTMLMediaElementMediaSession.cpp | 59 + .../mediasession/HTMLMediaElementMediaSession.h | 45 + .../mediasession/HTMLMediaElementMediaSession.idl | 32 + .../Modules/mediasession/MediaRemoteControls.cpp | 74 + .../Modules/mediasession/MediaRemoteControls.h | 76 + .../Modules/mediasession/MediaRemoteControls.idl | 36 + .../WebCore/Modules/mediasession/MediaSession.cpp | 282 +++ Source/WebCore/Modules/mediasession/MediaSession.h | 111 + .../WebCore/Modules/mediasession/MediaSession.idl | 53 + .../Modules/mediasession/MediaSessionEvents.h | 40 + .../Modules/mediasession/MediaSessionManager.cpp | 171 ++ .../Modules/mediasession/MediaSessionManager.h | 66 + .../Modules/mediasession/MediaSessionMetadata.h | 58 + .../mediasession/WebMediaSessionManager.cpp | 469 ++++ .../Modules/mediasession/WebMediaSessionManager.h | 113 + .../mediasession/WebMediaSessionManagerClient.h | 47 + .../Modules/mediasource/AudioTrackMediaSource.h | 41 + .../Modules/mediasource/AudioTrackMediaSource.idl | 4 +- .../Modules/mediasource/DOMURLMediaSource.cpp | 5 +- .../Modules/mediasource/DOMURLMediaSource.h | 9 +- .../Modules/mediasource/DOMURLMediaSource.idl | 2 +- Source/WebCore/Modules/mediasource/MediaSource.cpp | 786 ++++-- Source/WebCore/Modules/mediasource/MediaSource.h | 134 +- Source/WebCore/Modules/mediasource/MediaSource.idl | 27 +- .../Modules/mediasource/MediaSourceRegistry.cpp | 14 +- .../Modules/mediasource/MediaSourceRegistry.h | 15 +- Source/WebCore/Modules/mediasource/SampleMap.cpp | 219 +- Source/WebCore/Modules/mediasource/SampleMap.h | 120 +- .../WebCore/Modules/mediasource/SourceBuffer.cpp | 1651 ++++++++++--- Source/WebCore/Modules/mediasource/SourceBuffer.h | 198 +- .../WebCore/Modules/mediasource/SourceBuffer.idl | 41 +- .../Modules/mediasource/SourceBufferList.cpp | 31 +- .../WebCore/Modules/mediasource/SourceBufferList.h | 30 +- .../Modules/mediasource/SourceBufferList.idl | 7 +- .../Modules/mediasource/TextTrackMediaSource.h | 43 + .../Modules/mediasource/TextTrackMediaSource.idl | 4 +- .../Modules/mediasource/VideoPlaybackQuality.cpp | 4 +- .../Modules/mediasource/VideoPlaybackQuality.h | 9 +- .../Modules/mediasource/VideoPlaybackQuality.idl | 4 +- .../Modules/mediasource/VideoTrackMediaSource.h | 41 + .../Modules/mediasource/VideoTrackMediaSource.idl | 4 +- .../Modules/mediastream/AllAudioCapabilities.h | 59 - .../Modules/mediastream/AllAudioCapabilities.idl | 33 - .../Modules/mediastream/AllVideoCapabilities.h | 57 - .../Modules/mediastream/AllVideoCapabilities.idl | 38 - .../Modules/mediastream/AudioStreamTrack.cpp | 71 - .../WebCore/Modules/mediastream/AudioStreamTrack.h | 59 - .../Modules/mediastream/AudioStreamTrack.idl | 31 - .../Modules/mediastream/CapabilityRange.cpp | 92 - .../WebCore/Modules/mediastream/CapabilityRange.h | 60 - .../Modules/mediastream/CapabilityRange.idl | 33 - .../Modules/mediastream/DOMURLMediaStream.cpp | 5 +- .../Modules/mediastream/DOMURLMediaStream.h | 7 +- .../Modules/mediastream/DOMURLMediaStream.idl | 2 +- Source/WebCore/Modules/mediastream/DoubleRange.h | 40 + Source/WebCore/Modules/mediastream/DoubleRange.idl | 31 + .../mediastream/HTMLMediaElementMediaStream.cpp | 50 - .../mediastream/HTMLMediaElementMediaStream.h | 48 - .../mediastream/HTMLMediaElementMediaStream.idl | 33 - Source/WebCore/Modules/mediastream/LongRange.h | 40 + Source/WebCore/Modules/mediastream/LongRange.idl | 31 + .../Modules/mediastream/MediaConstraintsImpl.cpp | 119 +- .../Modules/mediastream/MediaConstraintsImpl.h | 54 +- .../Modules/mediastream/MediaDeviceInfo.cpp | 49 + .../WebCore/Modules/mediastream/MediaDeviceInfo.h | 60 + .../Modules/mediastream/MediaDeviceInfo.idl | 42 + .../WebCore/Modules/mediastream/MediaDevices.cpp | 108 + Source/WebCore/Modules/mediastream/MediaDevices.h | 71 + .../WebCore/Modules/mediastream/MediaDevices.idl | 46 + .../mediastream/MediaDevicesEnumerationRequest.cpp | 113 + .../mediastream/MediaDevicesEnumerationRequest.h | 71 + .../Modules/mediastream/MediaDevicesRequest.cpp | 153 ++ .../Modules/mediastream/MediaDevicesRequest.h | 65 + .../mediastream/MediaEndpointPeerConnection.cpp | 874 +++++++ .../mediastream/MediaEndpointPeerConnection.h | 132 + .../MediaEndpointSessionDescription.cpp | 116 + .../mediastream/MediaEndpointSessionDescription.h | 75 + .../Modules/mediastream/MediaSourceStates.cpp | 59 - .../Modules/mediastream/MediaSourceStates.h | 63 - .../Modules/mediastream/MediaSourceStates.idl | 44 - Source/WebCore/Modules/mediastream/MediaStream.cpp | 473 ++-- Source/WebCore/Modules/mediastream/MediaStream.h | 124 +- Source/WebCore/Modules/mediastream/MediaStream.idl | 34 +- .../mediastream/MediaStreamCapabilities.cpp | 142 -- .../Modules/mediastream/MediaStreamCapabilities.h | 69 - .../mediastream/MediaStreamCapabilities.idl | 33 - .../Modules/mediastream/MediaStreamEvent.cpp | 35 +- .../WebCore/Modules/mediastream/MediaStreamEvent.h | 29 +- .../Modules/mediastream/MediaStreamEvent.idl | 10 +- .../Modules/mediastream/MediaStreamRegistry.cpp | 52 +- .../Modules/mediastream/MediaStreamRegistry.h | 26 +- .../Modules/mediastream/MediaStreamTrack.cpp | 387 +-- .../WebCore/Modules/mediastream/MediaStreamTrack.h | 147 +- .../Modules/mediastream/MediaStreamTrack.idl | 80 +- .../Modules/mediastream/MediaStreamTrackEvent.cpp | 31 +- .../Modules/mediastream/MediaStreamTrackEvent.h | 27 +- .../Modules/mediastream/MediaStreamTrackEvent.idl | 7 +- .../mediastream/MediaStreamTrackSourcesCallback.h | 46 - .../MediaStreamTrackSourcesCallback.idl | 32 - .../mediastream/MediaStreamTrackSourcesRequest.cpp | 72 - .../mediastream/MediaStreamTrackSourcesRequest.h | 65 - .../Modules/mediastream/MediaTrackConstraint.cpp | 53 - .../Modules/mediastream/MediaTrackConstraint.h | 60 - .../Modules/mediastream/MediaTrackConstraint.idl | 31 - .../mediastream/MediaTrackConstraintSet.cpp | 53 - .../Modules/mediastream/MediaTrackConstraintSet.h | 56 - .../mediastream/MediaTrackConstraintSet.idl | 33 - .../Modules/mediastream/MediaTrackConstraints.cpp | 207 +- .../Modules/mediastream/MediaTrackConstraints.h | 100 +- .../Modules/mediastream/MediaTrackConstraints.idl | 100 +- .../mediastream/MediaTrackSupportedConstraints.h | 53 + .../mediastream/MediaTrackSupportedConstraints.idl | 46 + .../Modules/mediastream/NavigatorMediaDevices.cpp | 82 + .../Modules/mediastream/NavigatorMediaDevices.h | 61 + .../Modules/mediastream/NavigatorMediaDevices.idl | 36 + .../Modules/mediastream/NavigatorMediaStream.cpp | 71 - .../Modules/mediastream/NavigatorMediaStream.h | 50 - .../Modules/mediastream/NavigatorMediaStream.idl | 27 - .../Modules/mediastream/NavigatorUserMedia.idl | 36 + .../Modules/mediastream/NavigatorUserMedia.js | 49 + .../mediastream/NavigatorUserMediaError.cpp | 51 - .../Modules/mediastream/NavigatorUserMediaError.h | 65 - .../mediastream/NavigatorUserMediaError.idl | 33 - .../mediastream/NavigatorUserMediaErrorCallback.h | 45 - .../NavigatorUserMediaErrorCallback.idl | 30 - .../NavigatorUserMediaSuccessCallback.h | 46 - .../NavigatorUserMediaSuccessCallback.idl | 30 - .../Modules/mediastream/OverconstrainedError.h | 62 + .../Modules/mediastream/OverconstrainedError.idl | 36 + .../mediastream/OverconstrainedErrorEvent.h | 77 + .../mediastream/OverconstrainedErrorEvent.idl | 38 + .../Modules/mediastream/PeerConnectionBackend.cpp | 312 +++ .../Modules/mediastream/PeerConnectionBackend.h | 146 ++ .../WebCore/Modules/mediastream/RTCConfiguration.h | 54 + .../Modules/mediastream/RTCConfiguration.idl | 36 + .../WebCore/Modules/mediastream/RTCDTMFSender.cpp | 105 +- Source/WebCore/Modules/mediastream/RTCDTMFSender.h | 69 +- .../WebCore/Modules/mediastream/RTCDTMFSender.idl | 20 +- .../Modules/mediastream/RTCDTMFToneChangeEvent.cpp | 26 +- .../Modules/mediastream/RTCDTMFToneChangeEvent.h | 26 +- .../Modules/mediastream/RTCDTMFToneChangeEvent.idl | 12 +- .../WebCore/Modules/mediastream/RTCDataChannel.cpp | 190 +- .../WebCore/Modules/mediastream/RTCDataChannel.h | 118 +- .../WebCore/Modules/mediastream/RTCDataChannel.idl | 32 +- .../Modules/mediastream/RTCDataChannelEvent.cpp | 31 +- .../Modules/mediastream/RTCDataChannelEvent.h | 21 +- .../Modules/mediastream/RTCDataChannelEvent.idl | 2 +- .../Modules/mediastream/RTCIceCandidate.cpp | 79 +- .../WebCore/Modules/mediastream/RTCIceCandidate.h | 41 +- .../Modules/mediastream/RTCIceCandidate.idl | 21 +- .../Modules/mediastream/RTCIceCandidateEvent.cpp | 21 +- .../Modules/mediastream/RTCIceCandidateEvent.h | 15 +- .../Modules/mediastream/RTCIceCandidateEvent.idl | 2 +- Source/WebCore/Modules/mediastream/RTCIceServer.h | 44 + .../WebCore/Modules/mediastream/RTCIceServer.idl | 33 + .../WebCore/Modules/mediastream/RTCIceTransport.h | 69 + .../Modules/mediastream/RTCOfferAnswerOptions.h | 48 + .../Modules/mediastream/RTCPeerConnection.cpp | 736 ++---- .../Modules/mediastream/RTCPeerConnection.h | 175 +- .../Modules/mediastream/RTCPeerConnection.idl | 150 +- .../Modules/mediastream/RTCPeerConnection.js | 268 ++ .../mediastream/RTCPeerConnectionErrorCallback.h | 46 - .../mediastream/RTCPeerConnectionErrorCallback.idl | 31 - .../mediastream/RTCPeerConnectionInternals.js | 145 ++ .../WebCore/Modules/mediastream/RTCRtpReceiver.cpp | 45 + .../WebCore/Modules/mediastream/RTCRtpReceiver.h | 57 + .../WebCore/Modules/mediastream/RTCRtpReceiver.idl | 36 + .../WebCore/Modules/mediastream/RTCRtpSender.cpp | 87 + Source/WebCore/Modules/mediastream/RTCRtpSender.h | 75 + .../WebCore/Modules/mediastream/RTCRtpSender.idl | 38 + .../Modules/mediastream/RTCRtpSenderReceiverBase.h | 62 + .../Modules/mediastream/RTCRtpTransceiver.cpp | 110 + .../Modules/mediastream/RTCRtpTransceiver.h | 113 + .../Modules/mediastream/RTCRtpTransceiver.idl | 46 + .../Modules/mediastream/RTCSessionDescription.cpp | 74 +- .../Modules/mediastream/RTCSessionDescription.h | 40 +- .../Modules/mediastream/RTCSessionDescription.idl | 21 +- .../mediastream/RTCSessionDescriptionCallback.h | 52 - .../mediastream/RTCSessionDescriptionCallback.idl | 36 - .../RTCSessionDescriptionRequestImpl.cpp | 95 - .../mediastream/RTCSessionDescriptionRequestImpl.h | 72 - .../WebCore/Modules/mediastream/RTCStatsCallback.h | 46 - .../Modules/mediastream/RTCStatsCallback.idl | 30 - .../WebCore/Modules/mediastream/RTCStatsReport.cpp | 43 +- .../WebCore/Modules/mediastream/RTCStatsReport.h | 35 +- .../WebCore/Modules/mediastream/RTCStatsReport.idl | 12 +- .../Modules/mediastream/RTCStatsRequestImpl.cpp | 92 - .../Modules/mediastream/RTCStatsRequestImpl.h | 68 - .../Modules/mediastream/RTCStatsResponse.cpp | 69 - .../WebCore/Modules/mediastream/RTCStatsResponse.h | 63 - .../Modules/mediastream/RTCStatsResponse.idl | 31 - .../WebCore/Modules/mediastream/RTCTrackEvent.cpp | 72 + Source/WebCore/Modules/mediastream/RTCTrackEvent.h | 78 + .../WebCore/Modules/mediastream/RTCTrackEvent.idl | 47 + .../Modules/mediastream/RTCVoidRequestImpl.cpp | 92 - .../Modules/mediastream/RTCVoidRequestImpl.h | 71 - .../WebCore/Modules/mediastream/SDPProcessor.cpp | 547 ++++ Source/WebCore/Modules/mediastream/SDPProcessor.h | 78 + Source/WebCore/Modules/mediastream/SourceInfo.cpp | 64 - Source/WebCore/Modules/mediastream/SourceInfo.h | 59 - Source/WebCore/Modules/mediastream/SourceInfo.idl | 34 - .../WebCore/Modules/mediastream/UserMediaClient.h | 18 +- .../Modules/mediastream/UserMediaController.cpp | 7 +- .../Modules/mediastream/UserMediaController.h | 40 +- .../Modules/mediastream/UserMediaRequest.cpp | 264 +- .../WebCore/Modules/mediastream/UserMediaRequest.h | 83 +- .../Modules/mediastream/VideoStreamTrack.cpp | 71 - .../WebCore/Modules/mediastream/VideoStreamTrack.h | 59 - .../Modules/mediastream/VideoStreamTrack.idl | 32 - .../libwebrtc/LibWebRTCDataChannelHandler.cpp | 114 + .../libwebrtc/LibWebRTCDataChannelHandler.h | 61 + .../libwebrtc/LibWebRTCMediaEndpoint.cpp | 527 ++++ .../mediastream/libwebrtc/LibWebRTCMediaEndpoint.h | 175 ++ .../libwebrtc/LibWebRTCPeerConnectionBackend.cpp | 231 ++ .../libwebrtc/LibWebRTCPeerConnectionBackend.h | 97 + Source/WebCore/Modules/mediastream/sdp.js | 606 +++++ .../controls/airplay-button.css | 29 + .../controls/airplay-button.js | 38 + .../controls/airplay-placard.js | 39 + .../controls/background-tint.css | 48 + .../controls/background-tint.js | 34 + .../modern-media-controls/controls/button.css | 34 + .../modern-media-controls/controls/button.js | 83 + .../controls/buttons-container.css | 29 + .../controls/buttons-container.js | 78 + .../controls/controls-bar.css | 35 + .../modern-media-controls/controls/controls-bar.js | 248 ++ .../controls/forward-button.js | 38 + .../controls/fullscreen-button.js | 39 + .../modern-media-controls/controls/icon-button.css | 33 + .../modern-media-controls/controls/icon-button.js | 134 + .../modern-media-controls/controls/icon-service.js | 102 + .../controls/invalid-placard.js | 37 + .../controls/ios-inline-media-controls.css | 132 + .../controls/ios-inline-media-controls.js | 109 + .../modern-media-controls/controls/layout-item.js | 53 + .../modern-media-controls/controls/layout-node.js | 307 +++ .../macos-compact-inline-media-controls.css | 71 + .../controls/macos-fullscreen-media-controls.css | 119 + .../controls/macos-fullscreen-media-controls.js | 173 ++ .../controls/macos-inline-media-controls.css | 118 + .../controls/macos-inline-media-controls.js | 202 ++ .../controls/macos-media-controls.css | 51 + .../controls/macos-media-controls.js | 81 + .../controls/media-controls.css | 77 + .../controls/media-controls.js | 148 ++ .../modern-media-controls/controls/mute-button.js | 53 + .../modern-media-controls/controls/pip-button.js | 38 + .../modern-media-controls/controls/pip-placard.js | 38 + .../modern-media-controls/controls/placard.css | 70 + .../modern-media-controls/controls/placard.js | 48 + .../controls/play-pause-button.js | 53 + .../controls/rewind-button.js | 38 + .../modern-media-controls/controls/scheduler.js | 66 + .../modern-media-controls/controls/scrubber.js | 156 ++ .../modern-media-controls/controls/seek-button.js | 73 + .../controls/skip-back-button.js | 38 + .../modern-media-controls/controls/slider.css | 47 + .../modern-media-controls/controls/slider.js | 152 ++ .../controls/start-button.css | 58 + .../modern-media-controls/controls/start-button.js | 42 + .../controls/status-label.css | 37 + .../modern-media-controls/controls/status-label.js | 85 + .../modern-media-controls/controls/text-tracks.css | 82 + .../modern-media-controls/controls/time-control.js | 93 + .../modern-media-controls/controls/time-label.css | 33 + .../modern-media-controls/controls/time-label.js | 80 + .../controls/tracks-button.js | 38 + .../controls/tracks-panel.css | 105 + .../modern-media-controls/controls/tracks-panel.js | 305 +++ .../controls/volume-down-button.js | 38 + .../controls/volume-slider.css | 30 + .../controls/volume-slider.js | 120 + .../controls/volume-up-button.js | 38 + .../gesture-recognizers/gesture-recognizer.js | 379 +++ .../gesture-recognizers/pinch.js | 205 ++ .../gesture-recognizers/tap.js | 109 + .../images/iOS/airplay-placard@1x.png | Bin 0 -> 2945 bytes .../images/iOS/airplay-placard@2x.png | Bin 0 -> 3210 bytes .../images/iOS/airplay-placard@3x.png | Bin 0 -> 3553 bytes .../images/iOS/airplay@1x.png | Bin 0 -> 330 bytes .../images/iOS/airplay@2x.png | Bin 0 -> 362 bytes .../images/iOS/enter-fullscreen@1x.png | Bin 0 -> 426 bytes .../images/iOS/enter-fullscreen@2x.png | Bin 0 -> 653 bytes .../images/iOS/enter-fullscreen@3x.png | Bin 0 -> 958 bytes .../images/iOS/interval-skip-back@1x.png | Bin 0 -> 931 bytes .../images/iOS/interval-skip-back@2x.png | Bin 0 -> 1705 bytes .../images/iOS/interval-skip-back@3x.png | Bin 0 -> 2250 bytes .../images/iOS/invalid-placard@1x.png | Bin 0 -> 1007 bytes .../images/iOS/invalid-placard@2x.png | Bin 0 -> 1831 bytes .../images/iOS/invalid-placard@3x.png | Bin 0 -> 2403 bytes .../modern-media-controls/images/iOS/pause@1x.png | Bin 0 -> 1277 bytes .../modern-media-controls/images/iOS/pause@2x.png | Bin 0 -> 1375 bytes .../modern-media-controls/images/iOS/pause@3x.png | Bin 0 -> 1459 bytes .../modern-media-controls/images/iOS/pip-in@1x.png | Bin 0 -> 351 bytes .../modern-media-controls/images/iOS/pip-in@2x.png | Bin 0 -> 399 bytes .../modern-media-controls/images/iOS/pip-in@3x.png | Bin 0 -> 487 bytes .../images/iOS/pip-placard@1x.png | Bin 0 -> 2243 bytes .../images/iOS/pip-placard@2x.png | Bin 0 -> 4106 bytes .../images/iOS/pip-placard@3x.png | Bin 0 -> 6281 bytes .../modern-media-controls/images/iOS/play@1x.png | Bin 0 -> 546 bytes .../modern-media-controls/images/iOS/play@2x.png | Bin 0 -> 778 bytes .../modern-media-controls/images/iOS/play@3x.png | Bin 0 -> 1013 bytes .../images/iOS/slider-thumb@2x.png | Bin 0 -> 1475 bytes .../modern-media-controls/images/iOS/start@1x.png | Bin 0 -> 592 bytes .../modern-media-controls/images/iOS/start@2x.png | Bin 0 -> 1135 bytes .../modern-media-controls/images/iOS/start@3x.png | Bin 0 -> 1545 bytes .../images/macOS/airplay-fullscreen@1x.png | Bin 0 -> 294 bytes .../images/macOS/airplay-fullscreen@2x.png | Bin 0 -> 431 bytes .../images/macOS/airplay-placard@1x.png | Bin 0 -> 2945 bytes .../images/macOS/airplay-placard@2x.png | Bin 0 -> 3210 bytes .../images/macOS/airplay@1x.png | Bin 0 -> 330 bytes .../images/macOS/airplay@2x.png | Bin 0 -> 362 bytes .../images/macOS/enter-fullscreen-compact@1x.png | Bin 0 -> 263 bytes .../images/macOS/enter-fullscreen-compact@2x.png | Bin 0 -> 560 bytes .../images/macOS/enter-fullscreen@1x.png | Bin 0 -> 426 bytes .../images/macOS/enter-fullscreen@2x.png | Bin 0 -> 653 bytes .../images/macOS/exit-fullscreen@1x.png | Bin 0 -> 363 bytes .../images/macOS/exit-fullscreen@2x.png | Bin 0 -> 506 bytes .../images/macOS/forward@1x.png | Bin 0 -> 569 bytes .../images/macOS/forward@2x.png | Bin 0 -> 969 bytes .../images/macOS/interval-skip-back-compact@1x.png | Bin 0 -> 535 bytes .../images/macOS/interval-skip-back-compact@2x.png | Bin 0 -> 1304 bytes .../images/macOS/interval-skip-back@1x.png | Bin 0 -> 931 bytes .../images/macOS/interval-skip-back@2x.png | Bin 0 -> 1705 bytes .../images/macOS/invalid-placard@1x.png | Bin 0 -> 1007 bytes .../images/macOS/invalid-placard@2x.png | Bin 0 -> 1831 bytes .../images/macOS/media-selection-fullscreen@1x.png | Bin 0 -> 333 bytes .../images/macOS/media-selection-fullscreen@2x.png | Bin 0 -> 484 bytes .../images/macOS/media-selection@1x.png | Bin 0 -> 351 bytes .../images/macOS/media-selection@2x.png | Bin 0 -> 581 bytes .../images/macOS/pause-compact@1x.png | Bin 0 -> 1233 bytes .../images/macOS/pause-compact@2x.png | Bin 0 -> 1307 bytes .../images/macOS/pause-fullscreen@1x.png | Bin 0 -> 1277 bytes .../images/macOS/pause-fullscreen@2x.png | Bin 0 -> 1375 bytes .../images/macOS/pause@1x.png | Bin 0 -> 1275 bytes .../images/macOS/pause@2x.png | Bin 0 -> 1373 bytes .../images/macOS/pip-in-fullscreen@1x.png | Bin 0 -> 233 bytes .../images/macOS/pip-in-fullscreen@2x.png | Bin 0 -> 387 bytes .../images/macOS/pip-in@1x.png | Bin 0 -> 351 bytes .../images/macOS/pip-in@2x.png | Bin 0 -> 399 bytes .../images/macOS/pip-placard@1x.png | Bin 0 -> 2243 bytes .../images/macOS/pip-placard@2x.png | Bin 0 -> 4106 bytes .../images/macOS/play-compact@1x.png | Bin 0 -> 314 bytes .../images/macOS/play-compact@2x.png | Bin 0 -> 545 bytes .../images/macOS/play-fullscreen@1x.png | Bin 0 -> 546 bytes .../images/macOS/play-fullscreen@2x.png | Bin 0 -> 778 bytes .../modern-media-controls/images/macOS/play@1x.png | Bin 0 -> 546 bytes .../modern-media-controls/images/macOS/play@2x.png | Bin 0 -> 778 bytes .../images/macOS/rewind@1x.png | Bin 0 -> 595 bytes .../images/macOS/rewind@2x.png | Bin 0 -> 881 bytes .../images/macOS/scale-to-fill@1x.png | Bin 0 -> 362 bytes .../images/macOS/scale-to-fill@2x.png | Bin 0 -> 342 bytes .../images/macOS/scale-to-fit@1x.png | Bin 0 -> 205 bytes .../images/macOS/scale-to-fit@2x.png | Bin 0 -> 206 bytes .../images/macOS/start@1x.png | Bin 0 -> 592 bytes .../images/macOS/start@2x.png | Bin 0 -> 1135 bytes .../images/macOS/volume-compact@1x.png | Bin 0 -> 479 bytes .../images/macOS/volume-compact@2x.png | Bin 0 -> 1093 bytes .../images/macOS/volume-down-fullscreen@1x.png | Bin 0 -> 318 bytes .../images/macOS/volume-down-fullscreen@2x.png | Bin 0 -> 623 bytes .../images/macOS/volume-mute@1x.png | Bin 0 -> 936 bytes .../images/macOS/volume-mute@2x.png | Bin 0 -> 1545 bytes .../images/macOS/volume-up-fullscreen@1x.png | Bin 0 -> 577 bytes .../images/macOS/volume-up-fullscreen@2x.png | Bin 0 -> 1250 bytes .../images/macOS/volume@1x.png | Bin 0 -> 877 bytes .../images/macOS/volume@2x.png | Bin 0 -> 2476 bytes .../WebCore/Modules/modern-media-controls/js-files | 63 + .../WebCore/Modules/modern-media-controls/main.js | 47 + .../modern-media-controls/media/airplay-support.js | 60 + .../media/controls-visibility-support.js | 77 + .../media/fullscreen-support.js | 76 + .../media/media-controller-support.js | 101 + .../media/media-controller.js | 203 ++ .../modern-media-controls/media/mute-support.js | 52 + .../modern-media-controls/media/pip-support.js | 64 + .../modern-media-controls/media/placard-support.js | 64 + .../media/playback-support.js | 51 + .../media/scrubbing-support.js | 79 + .../media/seek-backward-support.js | 39 + .../media/seek-forward-support.js | 39 + .../modern-media-controls/media/seek-support.js | 77 + .../media/skip-back-support.js | 54 + .../modern-media-controls/media/start-support.js | 93 + .../modern-media-controls/media/status-support.js | 59 + .../media/time-labels-support.js | 51 + .../modern-media-controls/media/tracks-support.js | 150 ++ .../media/volume-down-support.js | 41 + .../modern-media-controls/media/volume-support.js | 57 + .../media/volume-up-support.js | 41 + .../NavigatorContentUtils.cpp | 153 +- .../navigatorcontentutils/NavigatorContentUtils.h | 32 +- .../NavigatorContentUtils.idl | 7 +- .../NavigatorContentUtilsClient.h | 15 +- .../notifications/DOMWindowNotifications.cpp | 42 +- .../Modules/notifications/DOMWindowNotifications.h | 24 +- .../notifications/DOMWindowNotifications.idl | 4 +- .../WebCore/Modules/notifications/Notification.cpp | 156 +- .../WebCore/Modules/notifications/Notification.h | 153 +- .../WebCore/Modules/notifications/Notification.idl | 62 +- .../Modules/notifications/NotificationCenter.cpp | 101 +- .../Modules/notifications/NotificationCenter.h | 63 +- .../Modules/notifications/NotificationCenter.idl | 11 +- .../Modules/notifications/NotificationClient.h | 24 +- .../notifications/NotificationController.cpp | 27 +- .../Modules/notifications/NotificationController.h | 24 +- .../notifications/NotificationPermissionCallback.h | 9 +- .../NotificationPermissionCallback.idl | 8 +- .../WorkerGlobalScopeNotifications.cpp | 26 +- .../notifications/WorkerGlobalScopeNotifications.h | 21 +- .../WorkerGlobalScopeNotifications.idl | 4 +- Source/WebCore/Modules/plugins/PluginReplacement.h | 38 +- .../Modules/plugins/QuickTimePluginReplacement.css | 33 + .../Modules/plugins/QuickTimePluginReplacement.h | 70 + .../Modules/plugins/QuickTimePluginReplacement.idl | 35 + .../Modules/plugins/QuickTimePluginReplacement.js | 350 +++ .../Modules/plugins/YouTubePluginReplacement.cpp | 354 +++ .../Modules/plugins/YouTubePluginReplacement.h | 63 + .../Modules/proximity/DeviceProximityClient.h | 4 +- .../proximity/DeviceProximityController.cpp | 9 +- .../Modules/proximity/DeviceProximityController.h | 12 +- .../Modules/proximity/DeviceProximityEvent.cpp | 10 +- .../Modules/proximity/DeviceProximityEvent.h | 40 +- .../Modules/proximity/DeviceProximityEvent.idl | 13 +- Source/WebCore/Modules/quota/DOMWindowQuota.cpp | 8 +- Source/WebCore/Modules/quota/DOMWindowQuota.h | 7 +- Source/WebCore/Modules/quota/DOMWindowQuota.idl | 4 +- .../Modules/quota/NavigatorStorageQuota.cpp | 5 +- .../WebCore/Modules/quota/NavigatorStorageQuota.h | 7 +- .../WebCore/Modules/quota/StorageErrorCallback.cpp | 16 +- .../WebCore/Modules/quota/StorageErrorCallback.h | 15 +- .../WebCore/Modules/quota/StorageErrorCallback.idl | 4 +- Source/WebCore/Modules/quota/StorageInfo.cpp | 12 +- Source/WebCore/Modules/quota/StorageInfo.h | 14 +- Source/WebCore/Modules/quota/StorageInfo.idl | 4 +- Source/WebCore/Modules/quota/StorageQuota.h | 14 +- Source/WebCore/Modules/quota/StorageQuota.idl | 4 +- .../WebCore/Modules/quota/StorageQuotaCallback.h | 7 +- .../WebCore/Modules/quota/StorageQuotaCallback.idl | 4 +- .../WebCore/Modules/quota/StorageUsageCallback.h | 7 +- .../WebCore/Modules/quota/StorageUsageCallback.idl | 4 +- .../Modules/quota/WorkerNavigatorStorageQuota.cpp | 5 +- .../Modules/quota/WorkerNavigatorStorageQuota.h | 7 +- .../Modules/speech/DOMWindowSpeechSynthesis.cpp | 81 + .../Modules/speech/DOMWindowSpeechSynthesis.h | 55 + .../Modules/speech/DOMWindowSpeechSynthesis.idl | 31 + Source/WebCore/Modules/speech/SpeechSynthesis.cpp | 242 ++ Source/WebCore/Modules/speech/SpeechSynthesis.h | 100 + Source/WebCore/Modules/speech/SpeechSynthesis.idl | 39 + .../Modules/speech/SpeechSynthesisEvent.cpp | 48 + .../WebCore/Modules/speech/SpeechSynthesisEvent.h | 54 + .../Modules/speech/SpeechSynthesisEvent.idl | 32 + .../Modules/speech/SpeechSynthesisUtterance.cpp | 70 + .../Modules/speech/SpeechSynthesisUtterance.h | 84 + .../Modules/speech/SpeechSynthesisUtterance.idl | 45 + .../Modules/speech/SpeechSynthesisVoice.cpp | 45 + .../WebCore/Modules/speech/SpeechSynthesisVoice.h | 57 + .../Modules/speech/SpeechSynthesisVoice.idl | 35 + .../Modules/streams/ByteLengthQueuingStrategy.idl | 37 + .../Modules/streams/ByteLengthQueuingStrategy.js | 46 + .../Modules/streams/CountQueuingStrategy.idl | 37 + .../Modules/streams/CountQueuingStrategy.js | 45 + .../streams/ReadableByteStreamController.idl | 43 + .../streams/ReadableByteStreamController.js | 95 + .../Modules/streams/ReadableByteStreamInternals.js | 330 +++ Source/WebCore/Modules/streams/ReadableStream.idl | 45 + Source/WebCore/Modules/streams/ReadableStream.js | 229 ++ .../streams/ReadableStreamDefaultController.idl | 42 + .../streams/ReadableStreamDefaultController.js | 82 + .../streams/ReadableStreamDefaultReader.idl | 42 + .../Modules/streams/ReadableStreamDefaultReader.js | 77 + .../Modules/streams/ReadableStreamInternals.js | 503 ++++ .../WebCore/Modules/streams/ReadableStreamSource.h | 121 + .../Modules/streams/ReadableStreamSource.idl | 40 + Source/WebCore/Modules/streams/StreamInternals.js | 129 + Source/WebCore/Modules/streams/WritableStream.idl | 42 + Source/WebCore/Modules/streams/WritableStream.js | 189 ++ .../Modules/streams/WritableStreamInternals.js | 135 + .../Modules/vibration/NavigatorVibration.cpp | 60 + .../WebCore/Modules/vibration/NavigatorVibration.h | 46 + .../Modules/vibration/NavigatorVibration.idl | 26 + Source/WebCore/Modules/vibration/Vibration.cpp | 130 + Source/WebCore/Modules/vibration/Vibration.h | 60 + Source/WebCore/Modules/vibration/VibrationClient.h | 38 + Source/WebCore/Modules/webaudio/AnalyserNode.cpp | 35 +- Source/WebCore/Modules/webaudio/AnalyserNode.h | 40 +- Source/WebCore/Modules/webaudio/AnalyserNode.idl | 14 +- .../WebCore/Modules/webaudio/AsyncAudioDecoder.cpp | 44 +- .../WebCore/Modules/webaudio/AsyncAudioDecoder.h | 20 +- .../Modules/webaudio/AudioBasicInspectorNode.cpp | 28 +- .../Modules/webaudio/AudioBasicInspectorNode.h | 21 +- .../Modules/webaudio/AudioBasicProcessorNode.cpp | 4 +- .../Modules/webaudio/AudioBasicProcessorNode.h | 24 +- Source/WebCore/Modules/webaudio/AudioBuffer.cpp | 93 +- Source/WebCore/Modules/webaudio/AudioBuffer.h | 34 +- Source/WebCore/Modules/webaudio/AudioBuffer.idl | 11 +- .../WebCore/Modules/webaudio/AudioBufferCallback.h | 7 +- .../Modules/webaudio/AudioBufferCallback.idl | 4 +- .../Modules/webaudio/AudioBufferSourceNode.cpp | 277 ++- .../Modules/webaudio/AudioBufferSourceNode.h | 54 +- .../Modules/webaudio/AudioBufferSourceNode.idl | 22 +- Source/WebCore/Modules/webaudio/AudioContext.cpp | 712 +++--- Source/WebCore/Modules/webaudio/AudioContext.h | 251 +- Source/WebCore/Modules/webaudio/AudioContext.idl | 66 +- .../Modules/webaudio/AudioDestinationNode.cpp | 62 +- .../Modules/webaudio/AudioDestinationNode.h | 66 +- Source/WebCore/Modules/webaudio/AudioListener.cpp | 2 +- Source/WebCore/Modules/webaudio/AudioListener.h | 15 +- Source/WebCore/Modules/webaudio/AudioListener.idl | 12 +- Source/WebCore/Modules/webaudio/AudioNode.cpp | 205 +- Source/WebCore/Modules/webaudio/AudioNode.h | 41 +- Source/WebCore/Modules/webaudio/AudioNode.idl | 25 +- Source/WebCore/Modules/webaudio/AudioNodeInput.cpp | 24 +- Source/WebCore/Modules/webaudio/AudioNodeInput.h | 10 +- .../WebCore/Modules/webaudio/AudioNodeOutput.cpp | 45 +- Source/WebCore/Modules/webaudio/AudioNodeOutput.h | 8 +- Source/WebCore/Modules/webaudio/AudioParam.cpp | 22 +- Source/WebCore/Modules/webaudio/AudioParam.h | 20 +- Source/WebCore/Modules/webaudio/AudioParam.idl | 25 +- .../Modules/webaudio/AudioParamTimeline.cpp | 28 +- .../WebCore/Modules/webaudio/AudioParamTimeline.h | 18 +- .../Modules/webaudio/AudioProcessingEvent.cpp | 17 +- .../Modules/webaudio/AudioProcessingEvent.h | 23 +- .../Modules/webaudio/AudioProcessingEvent.idl | 1 + .../Modules/webaudio/AudioScheduledSourceNode.cpp | 113 +- .../Modules/webaudio/AudioScheduledSourceNode.h | 51 +- .../Modules/webaudio/AudioSummingJunction.cpp | 19 +- .../Modules/webaudio/AudioSummingJunction.h | 11 +- .../WebCore/Modules/webaudio/BiquadDSPKernel.cpp | 16 +- Source/WebCore/Modules/webaudio/BiquadDSPKernel.h | 13 +- .../WebCore/Modules/webaudio/BiquadFilterNode.cpp | 73 +- Source/WebCore/Modules/webaudio/BiquadFilterNode.h | 34 +- .../WebCore/Modules/webaudio/BiquadFilterNode.idl | 32 +- .../WebCore/Modules/webaudio/BiquadProcessor.cpp | 11 +- Source/WebCore/Modules/webaudio/BiquadProcessor.h | 39 +- .../WebCore/Modules/webaudio/ChannelMergerNode.cpp | 10 +- .../WebCore/Modules/webaudio/ChannelMergerNode.h | 22 +- .../WebCore/Modules/webaudio/ChannelMergerNode.idl | 2 +- .../Modules/webaudio/ChannelSplitterNode.cpp | 6 +- .../WebCore/Modules/webaudio/ChannelSplitterNode.h | 18 +- Source/WebCore/Modules/webaudio/ConvolverNode.cpp | 43 +- Source/WebCore/Modules/webaudio/ConvolverNode.h | 41 +- Source/WebCore/Modules/webaudio/ConvolverNode.idl | 2 +- .../webaudio/DefaultAudioDestinationNode.cpp | 58 +- .../Modules/webaudio/DefaultAudioDestinationNode.h | 41 +- Source/WebCore/Modules/webaudio/DelayDSPKernel.h | 13 +- Source/WebCore/Modules/webaudio/DelayNode.cpp | 17 +- Source/WebCore/Modules/webaudio/DelayNode.h | 19 +- Source/WebCore/Modules/webaudio/DelayProcessor.cpp | 2 +- Source/WebCore/Modules/webaudio/DelayProcessor.h | 9 +- .../Modules/webaudio/DynamicsCompressorNode.cpp | 2 +- .../Modules/webaudio/DynamicsCompressorNode.h | 23 +- Source/WebCore/Modules/webaudio/GainNode.cpp | 4 +- Source/WebCore/Modules/webaudio/GainNode.h | 22 +- .../webaudio/MediaElementAudioSourceNode.cpp | 18 +- .../Modules/webaudio/MediaElementAudioSourceNode.h | 30 +- .../webaudio/MediaStreamAudioDestinationNode.cpp | 22 +- .../webaudio/MediaStreamAudioDestinationNode.h | 22 +- .../Modules/webaudio/MediaStreamAudioSource.cpp | 46 +- .../Modules/webaudio/MediaStreamAudioSource.h | 32 +- .../webaudio/MediaStreamAudioSourceNode.cpp | 95 +- .../Modules/webaudio/MediaStreamAudioSourceNode.h | 42 +- .../webaudio/OfflineAudioCompletionEvent.cpp | 18 +- .../Modules/webaudio/OfflineAudioCompletionEvent.h | 16 +- .../Modules/webaudio/OfflineAudioContext.cpp | 34 +- .../WebCore/Modules/webaudio/OfflineAudioContext.h | 11 +- .../Modules/webaudio/OfflineAudioContext.idl | 6 +- .../webaudio/OfflineAudioDestinationNode.cpp | 38 +- .../Modules/webaudio/OfflineAudioDestinationNode.h | 23 +- Source/WebCore/Modules/webaudio/OscillatorNode.cpp | 114 +- Source/WebCore/Modules/webaudio/OscillatorNode.h | 54 +- Source/WebCore/Modules/webaudio/OscillatorNode.idl | 31 +- Source/WebCore/Modules/webaudio/PannerNode.cpp | 135 +- Source/WebCore/Modules/webaudio/PannerNode.h | 65 +- Source/WebCore/Modules/webaudio/PannerNode.idl | 49 +- Source/WebCore/Modules/webaudio/PeriodicWave.cpp | 58 +- Source/WebCore/Modules/webaudio/PeriodicWave.h | 27 +- .../WebCore/Modules/webaudio/RealtimeAnalyser.cpp | 3 +- Source/WebCore/Modules/webaudio/RealtimeAnalyser.h | 7 +- .../Modules/webaudio/ScriptProcessorNode.cpp | 75 +- .../WebCore/Modules/webaudio/ScriptProcessorNode.h | 30 +- .../Modules/webaudio/ScriptProcessorNode.idl | 2 +- .../WebCore/Modules/webaudio/WaveShaperDSPKernel.h | 15 +- Source/WebCore/Modules/webaudio/WaveShaperNode.cpp | 56 +- Source/WebCore/Modules/webaudio/WaveShaperNode.h | 23 +- Source/WebCore/Modules/webaudio/WaveShaperNode.idl | 2 +- .../Modules/webaudio/WaveShaperProcessor.cpp | 12 +- .../WebCore/Modules/webaudio/WaveShaperProcessor.h | 13 +- .../Modules/webdatabase/AbstractDatabaseServer.h | 89 - .../Modules/webdatabase/AbstractSQLStatement.h | 51 - .../webdatabase/AbstractSQLStatementBackend.h | 49 - .../Modules/webdatabase/AbstractSQLTransaction.h | 55 - .../webdatabase/AbstractSQLTransactionBackend.h | 61 - .../Modules/webdatabase/ChangeVersionData.h | 9 +- .../Modules/webdatabase/ChangeVersionWrapper.cpp | 40 +- .../Modules/webdatabase/ChangeVersionWrapper.h | 22 +- .../Modules/webdatabase/DOMWindowWebDatabase.cpp | 46 +- .../Modules/webdatabase/DOMWindowWebDatabase.h | 28 +- .../Modules/webdatabase/DOMWindowWebDatabase.idl | 11 +- Source/WebCore/Modules/webdatabase/Database.cpp | 761 +++++- Source/WebCore/Modules/webdatabase/Database.h | 161 +- Source/WebCore/Modules/webdatabase/Database.idl | 12 +- .../Modules/webdatabase/DatabaseAuthorizer.cpp | 51 +- .../Modules/webdatabase/DatabaseAuthorizer.h | 13 +- .../Modules/webdatabase/DatabaseBackend.cpp | 177 -- .../WebCore/Modules/webdatabase/DatabaseBackend.h | 87 - .../Modules/webdatabase/DatabaseBackendBase.cpp | 626 ----- .../Modules/webdatabase/DatabaseBackendBase.h | 147 -- .../Modules/webdatabase/DatabaseBackendContext.cpp | 55 - .../Modules/webdatabase/DatabaseBackendContext.h | 58 - .../Modules/webdatabase/DatabaseBackendSync.cpp | 62 - .../Modules/webdatabase/DatabaseBackendSync.h | 60 - .../WebCore/Modules/webdatabase/DatabaseBase.cpp | 54 - Source/WebCore/Modules/webdatabase/DatabaseBase.h | 52 - .../Modules/webdatabase/DatabaseBasicTypes.h | 45 - .../WebCore/Modules/webdatabase/DatabaseCallback.h | 13 +- .../Modules/webdatabase/DatabaseCallback.idl | 9 +- .../Modules/webdatabase/DatabaseContext.cpp | 130 +- .../WebCore/Modules/webdatabase/DatabaseContext.h | 63 +- .../WebCore/Modules/webdatabase/DatabaseDetails.h | 11 +- Source/WebCore/Modules/webdatabase/DatabaseError.h | 46 - .../Modules/webdatabase/DatabaseManager.cpp | 452 +--- .../WebCore/Modules/webdatabase/DatabaseManager.h | 131 +- .../Modules/webdatabase/DatabaseManagerClient.h | 20 +- .../WebCore/Modules/webdatabase/DatabaseServer.cpp | 163 -- .../WebCore/Modules/webdatabase/DatabaseServer.h | 78 - .../WebCore/Modules/webdatabase/DatabaseSync.cpp | 195 -- Source/WebCore/Modules/webdatabase/DatabaseSync.h | 89 - .../WebCore/Modules/webdatabase/DatabaseSync.idl | 42 - .../WebCore/Modules/webdatabase/DatabaseTask.cpp | 104 +- Source/WebCore/Modules/webdatabase/DatabaseTask.h | 127 +- .../WebCore/Modules/webdatabase/DatabaseThread.cpp | 177 +- .../WebCore/Modules/webdatabase/DatabaseThread.h | 56 +- .../Modules/webdatabase/DatabaseTracker.cpp | 918 ++++--- .../WebCore/Modules/webdatabase/DatabaseTracker.h | 182 +- Source/WebCore/Modules/webdatabase/OriginLock.cpp | 5 - Source/WebCore/Modules/webdatabase/OriginLock.h | 15 +- .../Modules/webdatabase/SQLCallbackWrapper.h | 72 +- Source/WebCore/Modules/webdatabase/SQLError.h | 19 +- Source/WebCore/Modules/webdatabase/SQLError.idl | 5 +- .../WebCore/Modules/webdatabase/SQLException.cpp | 11 +- Source/WebCore/Modules/webdatabase/SQLException.h | 13 +- .../WebCore/Modules/webdatabase/SQLException.idl | 2 - .../WebCore/Modules/webdatabase/SQLResultSet.cpp | 42 +- Source/WebCore/Modules/webdatabase/SQLResultSet.h | 38 +- .../WebCore/Modules/webdatabase/SQLResultSet.idl | 13 +- .../Modules/webdatabase/SQLResultSetRowList.cpp | 21 +- .../Modules/webdatabase/SQLResultSetRowList.h | 20 +- .../Modules/webdatabase/SQLResultSetRowList.idl | 9 +- .../WebCore/Modules/webdatabase/SQLStatement.cpp | 202 +- Source/WebCore/Modules/webdatabase/SQLStatement.h | 55 +- .../Modules/webdatabase/SQLStatementBackend.cpp | 238 -- .../Modules/webdatabase/SQLStatementBackend.h | 88 - .../Modules/webdatabase/SQLStatementCallback.h | 12 +- .../Modules/webdatabase/SQLStatementCallback.idl | 8 +- .../webdatabase/SQLStatementErrorCallback.h | 13 +- .../webdatabase/SQLStatementErrorCallback.idl | 8 +- .../Modules/webdatabase/SQLStatementSync.cpp | 137 -- .../WebCore/Modules/webdatabase/SQLStatementSync.h | 63 - .../WebCore/Modules/webdatabase/SQLTransaction.cpp | 576 ++++- .../WebCore/Modules/webdatabase/SQLTransaction.h | 132 +- .../WebCore/Modules/webdatabase/SQLTransaction.idl | 12 +- .../Modules/webdatabase/SQLTransactionBackend.cpp | 423 +--- .../Modules/webdatabase/SQLTransactionBackend.h | 115 +- .../webdatabase/SQLTransactionBackendSync.cpp | 237 -- .../webdatabase/SQLTransactionBackendSync.h | 86 - .../Modules/webdatabase/SQLTransactionCallback.h | 13 +- .../Modules/webdatabase/SQLTransactionCallback.idl | 8 +- .../Modules/webdatabase/SQLTransactionClient.cpp | 62 - .../Modules/webdatabase/SQLTransactionClient.h | 57 - .../webdatabase/SQLTransactionCoordinator.cpp | 50 +- .../webdatabase/SQLTransactionCoordinator.h | 21 +- .../webdatabase/SQLTransactionErrorCallback.h | 13 +- .../webdatabase/SQLTransactionErrorCallback.idl | 8 +- .../Modules/webdatabase/SQLTransactionState.h | 9 +- .../webdatabase/SQLTransactionStateMachine.cpp | 4 - .../webdatabase/SQLTransactionStateMachine.h | 32 +- .../Modules/webdatabase/SQLTransactionSync.cpp | 61 - .../Modules/webdatabase/SQLTransactionSync.h | 56 - .../Modules/webdatabase/SQLTransactionSync.idl | 38 - .../webdatabase/SQLTransactionSyncCallback.h | 53 - .../webdatabase/SQLTransactionSyncCallback.idl | 35 - .../webdatabase/WorkerGlobalScopeWebDatabase.cpp | 76 - .../webdatabase/WorkerGlobalScopeWebDatabase.h | 59 - .../webdatabase/WorkerGlobalScopeWebDatabase.idl | 34 - .../Modules/webdriver/NavigatorWebDriver.cpp | 78 + .../WebCore/Modules/webdriver/NavigatorWebDriver.h | 50 + .../Modules/webdriver/NavigatorWebDriver.idl | 28 + Source/WebCore/Modules/websockets/CloseEvent.h | 47 +- Source/WebCore/Modules/websockets/CloseEvent.idl | 15 +- .../websockets/ThreadableWebSocketChannel.cpp | 22 +- .../websockets/ThreadableWebSocketChannel.h | 17 +- .../ThreadableWebSocketChannelClientWrapper.cpp | 153 +- .../ThreadableWebSocketChannelClientWrapper.h | 38 +- Source/WebCore/Modules/websockets/WebSocket.cpp | 416 ++-- Source/WebCore/Modules/websockets/WebSocket.h | 125 +- Source/WebCore/Modules/websockets/WebSocket.idl | 56 +- .../Modules/websockets/WebSocketChannel.cpp | 277 ++- .../WebCore/Modules/websockets/WebSocketChannel.h | 115 +- .../Modules/websockets/WebSocketChannelClient.h | 41 +- .../Modules/websockets/WebSocketDeflateFramer.cpp | 55 +- .../Modules/websockets/WebSocketDeflateFramer.h | 35 +- .../Modules/websockets/WebSocketDeflater.cpp | 14 +- .../WebCore/Modules/websockets/WebSocketDeflater.h | 22 +- .../websockets/WebSocketExtensionDispatcher.cpp | 30 +- .../websockets/WebSocketExtensionDispatcher.h | 13 +- .../Modules/websockets/WebSocketExtensionParser.h | 5 +- .../websockets/WebSocketExtensionProcessor.h | 7 +- Source/WebCore/Modules/websockets/WebSocketFrame.h | 7 +- .../Modules/websockets/WebSocketHandshake.cpp | 249 +- .../Modules/websockets/WebSocketHandshake.h | 18 +- .../WorkerThreadableWebSocketChannel.cpp | 474 ++-- .../websockets/WorkerThreadableWebSocketChannel.h | 101 +- 1116 files changed, 79567 insertions(+), 32564 deletions(-) create mode 100644 Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.cpp create mode 100644 Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.h create mode 100644 Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayLineItem.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayLineItem.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayPayment.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayPayment.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.cpp create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentContact.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentContact.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentMethod.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentMethod.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.cpp create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentPass.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentPass.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentRequest.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayPaymentRequest.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePaySession.cpp create mode 100644 Source/WebCore/Modules/applepay/ApplePaySession.h create mode 100644 Source/WebCore/Modules/applepay/ApplePaySession.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.cpp create mode 100644 Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayShippingMethod.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayShippingMethod.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.cpp create mode 100644 Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.idl create mode 100644 Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.cpp create mode 100644 Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.h create mode 100644 Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.idl create mode 100644 Source/WebCore/Modules/applepay/Payment.h create mode 100644 Source/WebCore/Modules/applepay/PaymentAuthorizationStatus.h create mode 100644 Source/WebCore/Modules/applepay/PaymentContact.h create mode 100644 Source/WebCore/Modules/applepay/PaymentCoordinator.cpp create mode 100644 Source/WebCore/Modules/applepay/PaymentCoordinator.h create mode 100644 Source/WebCore/Modules/applepay/PaymentCoordinatorClient.h create mode 100644 Source/WebCore/Modules/applepay/PaymentHeaders.h create mode 100644 Source/WebCore/Modules/applepay/PaymentMerchantSession.h create mode 100644 Source/WebCore/Modules/applepay/PaymentMethod.h create mode 100644 Source/WebCore/Modules/applepay/PaymentRequest.cpp create mode 100644 Source/WebCore/Modules/applepay/PaymentRequest.h create mode 100644 Source/WebCore/Modules/applepay/PaymentRequestValidator.cpp create mode 100644 Source/WebCore/Modules/applepay/PaymentRequestValidator.h delete mode 100644 Source/WebCore/Modules/battery/BatteryClient.h delete mode 100644 Source/WebCore/Modules/battery/BatteryController.cpp delete mode 100644 Source/WebCore/Modules/battery/BatteryController.h delete mode 100644 Source/WebCore/Modules/battery/BatteryManager.cpp delete mode 100644 Source/WebCore/Modules/battery/BatteryManager.h delete mode 100644 Source/WebCore/Modules/battery/BatteryManager.idl delete mode 100644 Source/WebCore/Modules/battery/BatteryStatus.cpp delete mode 100644 Source/WebCore/Modules/battery/BatteryStatus.h delete mode 100644 Source/WebCore/Modules/battery/NavigatorBattery.cpp delete mode 100644 Source/WebCore/Modules/battery/NavigatorBattery.h delete mode 100644 Source/WebCore/Modules/battery/NavigatorBattery.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/CDM.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/CDM.h create mode 100644 Source/WebCore/Modules/encryptedmedia/CDMInstance.h create mode 100644 Source/WebCore/Modules/encryptedmedia/CDMPrivate.h create mode 100644 Source/WebCore/Modules/encryptedmedia/InitDataRegistry.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/InitDataRegistry.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEventInit.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeyMessageType.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySession.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySession.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySessionType.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySessionType.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeyStatus.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySystemConfiguration.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySystemConfiguration.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySystemMediaCapability.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeySystemMediaCapability.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeys.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeys.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeys.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeysRequirement.h create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeysRequirement.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/MediaKeysRestrictions.h create mode 100644 Source/WebCore/Modules/encryptedmedia/NavigatorEME.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/NavigatorEME.h create mode 100644 Source/WebCore/Modules/encryptedmedia/NavigatorEME.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDM.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDM.h create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivate.h create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateClearKey.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateClearKey.h create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateMediaPlayer.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateMediaPlayer.h create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMSessionClearKey.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMSessionClearKey.h create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.h create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.h create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.h create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.idl create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.cpp create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.h create mode 100644 Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.idl create mode 100644 Source/WebCore/Modules/fetch/DOMWindowFetch.cpp create mode 100644 Source/WebCore/Modules/fetch/DOMWindowFetch.h create mode 100644 Source/WebCore/Modules/fetch/DOMWindowFetch.idl create mode 100644 Source/WebCore/Modules/fetch/DOMWindowFetch.js create mode 100644 Source/WebCore/Modules/fetch/FetchBody.cpp create mode 100644 Source/WebCore/Modules/fetch/FetchBody.h create mode 100644 Source/WebCore/Modules/fetch/FetchBody.idl create mode 100644 Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp create mode 100644 Source/WebCore/Modules/fetch/FetchBodyConsumer.h create mode 100644 Source/WebCore/Modules/fetch/FetchBodyOwner.cpp create mode 100644 Source/WebCore/Modules/fetch/FetchBodyOwner.h create mode 100644 Source/WebCore/Modules/fetch/FetchHeaders.cpp create mode 100644 Source/WebCore/Modules/fetch/FetchHeaders.h create mode 100644 Source/WebCore/Modules/fetch/FetchHeaders.idl create mode 100644 Source/WebCore/Modules/fetch/FetchHeaders.js create mode 100644 Source/WebCore/Modules/fetch/FetchInternals.js create mode 100644 Source/WebCore/Modules/fetch/FetchLoader.cpp create mode 100644 Source/WebCore/Modules/fetch/FetchLoader.h create mode 100644 Source/WebCore/Modules/fetch/FetchLoaderClient.h create mode 100644 Source/WebCore/Modules/fetch/FetchRequest.cpp create mode 100644 Source/WebCore/Modules/fetch/FetchRequest.h create mode 100644 Source/WebCore/Modules/fetch/FetchRequest.idl create mode 100644 Source/WebCore/Modules/fetch/FetchRequest.js create mode 100644 Source/WebCore/Modules/fetch/FetchResponse.cpp create mode 100644 Source/WebCore/Modules/fetch/FetchResponse.h create mode 100644 Source/WebCore/Modules/fetch/FetchResponse.idl create mode 100644 Source/WebCore/Modules/fetch/FetchResponse.js create mode 100644 Source/WebCore/Modules/fetch/FetchResponseSource.cpp create mode 100644 Source/WebCore/Modules/fetch/FetchResponseSource.h create mode 100644 Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.cpp create mode 100644 Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.h create mode 100644 Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.idl create mode 100644 Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.js create mode 100644 Source/WebCore/Modules/gamepad/GamepadButton.cpp create mode 100644 Source/WebCore/Modules/gamepad/GamepadButton.h create mode 100644 Source/WebCore/Modules/gamepad/GamepadButton.idl create mode 100644 Source/WebCore/Modules/gamepad/GamepadEvent.cpp create mode 100644 Source/WebCore/Modules/gamepad/GamepadEvent.h create mode 100644 Source/WebCore/Modules/gamepad/GamepadEvent.idl delete mode 100644 Source/WebCore/Modules/gamepad/GamepadList.cpp delete mode 100644 Source/WebCore/Modules/gamepad/GamepadList.h delete mode 100644 Source/WebCore/Modules/gamepad/GamepadList.idl create mode 100644 Source/WebCore/Modules/gamepad/GamepadManager.cpp create mode 100644 Source/WebCore/Modules/gamepad/GamepadManager.h create mode 100644 Source/WebCore/Modules/gamepad/deprecated/Gamepad.cpp create mode 100644 Source/WebCore/Modules/gamepad/deprecated/Gamepad.h create mode 100644 Source/WebCore/Modules/gamepad/deprecated/Gamepad.idl create mode 100644 Source/WebCore/Modules/gamepad/deprecated/GamepadList.cpp create mode 100644 Source/WebCore/Modules/gamepad/deprecated/GamepadList.h create mode 100644 Source/WebCore/Modules/gamepad/deprecated/GamepadList.idl create mode 100644 Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.cpp create mode 100644 Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.h create mode 100644 Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.idl create mode 100644 Source/WebCore/Modules/geolocation/GeoNotifier.cpp create mode 100644 Source/WebCore/Modules/geolocation/GeoNotifier.h create mode 100644 Source/WebCore/Modules/geolocation/PositionOptions.idl create mode 100644 Source/WebCore/Modules/indexeddb/IDBActiveDOMObject.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBAny.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBAny.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBAny.idl delete mode 100644 Source/WebCore/Modules/indexeddb/IDBCallbacks.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBCursorBackend.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBCursorBackend.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.h create mode 100644 Source/WebCore/Modules/indexeddb/IDBCursorDirection.h create mode 100644 Source/WebCore/Modules/indexeddb/IDBCursorDirection.idl delete mode 100644 Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacks.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.h create mode 100644 Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.cpp create mode 100644 Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.h create mode 100644 Source/WebCore/Modules/indexeddb/IDBGetAllResult.cpp create mode 100644 Source/WebCore/Modules/indexeddb/IDBGetAllResult.h create mode 100644 Source/WebCore/Modules/indexeddb/IDBGetResult.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBHistograms.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBIndexMetadata.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBObjectStoreMetadata.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBOperation.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBPendingDeleteCall.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBPendingOpenCall.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.h create mode 100644 Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.cpp create mode 100644 Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBServerConnection.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBTransactionBackend.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBTransactionBackend.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.h delete mode 100644 Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.h create mode 100644 Source/WebCore/Modules/indexeddb/IDBTransactionMode.h create mode 100644 Source/WebCore/Modules/indexeddb/IDBTransactionMode.idl create mode 100644 Source/WebCore/Modules/indexeddb/IDBValue.cpp create mode 100644 Source/WebCore/Modules/indexeddb/IDBValue.h delete mode 100644 Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.h create mode 100644 Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.cpp create mode 100644 Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.h create mode 100644 Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.cpp create mode 100644 Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.h create mode 100644 Source/WebCore/Modules/indexeddb/client/IDBConnectionToServerDelegate.h create mode 100644 Source/WebCore/Modules/indexeddb/client/TransactionOperation.cpp create mode 100644 Source/WebCore/Modules/indexeddb/client/TransactionOperation.h delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.h delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.h delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.h delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.h delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.h delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.cpp delete mode 100644 Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.h create mode 100644 Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h create mode 100644 Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.h create mode 100644 Source/WebCore/Modules/indexeddb/server/IDBConnectionToClientDelegate.h create mode 100644 Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/IDBSerialization.h create mode 100644 Source/WebCore/Modules/indexeddb/server/IDBServer.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/IDBServer.h create mode 100644 Source/WebCore/Modules/indexeddb/server/IndexValueEntry.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/IndexValueEntry.h create mode 100644 Source/WebCore/Modules/indexeddb/server/IndexValueStore.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/IndexValueStore.h create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.h create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryCursor.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryCursor.h create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryIndex.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryIndex.h create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.h create mode 100644 Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h create mode 100644 Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h create mode 100644 Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h create mode 100644 Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.h create mode 100644 Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h create mode 100644 Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.h create mode 100644 Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp create mode 100644 Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBCursorRecord.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBError.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBError.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBRequestData.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBRequestData.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBResultData.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBResultData.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h create mode 100644 Source/WebCore/Modules/indexeddb/shared/IndexKey.cpp create mode 100644 Source/WebCore/Modules/indexeddb/shared/IndexKey.h create mode 100644 Source/WebCore/Modules/mediacontrols/MediaControlsHost.cpp create mode 100644 Source/WebCore/Modules/mediacontrols/MediaControlsHost.h create mode 100644 Source/WebCore/Modules/mediacontrols/MediaControlsHost.idl create mode 100644 Source/WebCore/Modules/mediacontrols/assets-apple-iOS.svg create mode 100644 Source/WebCore/Modules/mediacontrols/mediaControlsApple.css create mode 100644 Source/WebCore/Modules/mediacontrols/mediaControlsApple.js create mode 100644 Source/WebCore/Modules/mediacontrols/mediaControlsBase.css create mode 100644 Source/WebCore/Modules/mediacontrols/mediaControlsBase.js create mode 100644 Source/WebCore/Modules/mediacontrols/mediaControlsGtk.js create mode 100644 Source/WebCore/Modules/mediacontrols/mediaControlsiOS.css create mode 100644 Source/WebCore/Modules/mediacontrols/mediaControlsiOS.js create mode 100644 Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.cpp create mode 100644 Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.h create mode 100644 Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.idl create mode 100644 Source/WebCore/Modules/mediasession/MediaRemoteControls.cpp create mode 100644 Source/WebCore/Modules/mediasession/MediaRemoteControls.h create mode 100644 Source/WebCore/Modules/mediasession/MediaRemoteControls.idl create mode 100644 Source/WebCore/Modules/mediasession/MediaSession.cpp create mode 100644 Source/WebCore/Modules/mediasession/MediaSession.h create mode 100644 Source/WebCore/Modules/mediasession/MediaSession.idl create mode 100644 Source/WebCore/Modules/mediasession/MediaSessionEvents.h create mode 100644 Source/WebCore/Modules/mediasession/MediaSessionManager.cpp create mode 100644 Source/WebCore/Modules/mediasession/MediaSessionManager.h create mode 100644 Source/WebCore/Modules/mediasession/MediaSessionMetadata.h create mode 100644 Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp create mode 100644 Source/WebCore/Modules/mediasession/WebMediaSessionManager.h create mode 100644 Source/WebCore/Modules/mediasession/WebMediaSessionManagerClient.h create mode 100644 Source/WebCore/Modules/mediasource/AudioTrackMediaSource.h create mode 100644 Source/WebCore/Modules/mediasource/TextTrackMediaSource.h create mode 100644 Source/WebCore/Modules/mediasource/VideoTrackMediaSource.h delete mode 100644 Source/WebCore/Modules/mediastream/AllAudioCapabilities.h delete mode 100644 Source/WebCore/Modules/mediastream/AllAudioCapabilities.idl delete mode 100644 Source/WebCore/Modules/mediastream/AllVideoCapabilities.h delete mode 100644 Source/WebCore/Modules/mediastream/AllVideoCapabilities.idl delete mode 100644 Source/WebCore/Modules/mediastream/AudioStreamTrack.cpp delete mode 100644 Source/WebCore/Modules/mediastream/AudioStreamTrack.h delete mode 100644 Source/WebCore/Modules/mediastream/AudioStreamTrack.idl delete mode 100644 Source/WebCore/Modules/mediastream/CapabilityRange.cpp delete mode 100644 Source/WebCore/Modules/mediastream/CapabilityRange.h delete mode 100644 Source/WebCore/Modules/mediastream/CapabilityRange.idl create mode 100644 Source/WebCore/Modules/mediastream/DoubleRange.h create mode 100644 Source/WebCore/Modules/mediastream/DoubleRange.idl delete mode 100644 Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.cpp delete mode 100644 Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.h delete mode 100644 Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.idl create mode 100644 Source/WebCore/Modules/mediastream/LongRange.h create mode 100644 Source/WebCore/Modules/mediastream/LongRange.idl create mode 100644 Source/WebCore/Modules/mediastream/MediaDeviceInfo.cpp create mode 100644 Source/WebCore/Modules/mediastream/MediaDeviceInfo.h create mode 100644 Source/WebCore/Modules/mediastream/MediaDeviceInfo.idl create mode 100644 Source/WebCore/Modules/mediastream/MediaDevices.cpp create mode 100644 Source/WebCore/Modules/mediastream/MediaDevices.h create mode 100644 Source/WebCore/Modules/mediastream/MediaDevices.idl create mode 100644 Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.cpp create mode 100644 Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.h create mode 100644 Source/WebCore/Modules/mediastream/MediaDevicesRequest.cpp create mode 100644 Source/WebCore/Modules/mediastream/MediaDevicesRequest.h create mode 100644 Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.cpp create mode 100644 Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.h create mode 100644 Source/WebCore/Modules/mediastream/MediaEndpointSessionDescription.cpp create mode 100644 Source/WebCore/Modules/mediastream/MediaEndpointSessionDescription.h delete mode 100644 Source/WebCore/Modules/mediastream/MediaSourceStates.cpp delete mode 100644 Source/WebCore/Modules/mediastream/MediaSourceStates.h delete mode 100644 Source/WebCore/Modules/mediastream/MediaSourceStates.idl delete mode 100644 Source/WebCore/Modules/mediastream/MediaStreamCapabilities.cpp delete mode 100644 Source/WebCore/Modules/mediastream/MediaStreamCapabilities.h delete mode 100644 Source/WebCore/Modules/mediastream/MediaStreamCapabilities.idl delete mode 100644 Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesCallback.h delete mode 100644 Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesCallback.idl delete mode 100644 Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesRequest.cpp delete mode 100644 Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesRequest.h delete mode 100644 Source/WebCore/Modules/mediastream/MediaTrackConstraint.cpp delete mode 100644 Source/WebCore/Modules/mediastream/MediaTrackConstraint.h delete mode 100644 Source/WebCore/Modules/mediastream/MediaTrackConstraint.idl delete mode 100644 Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.cpp delete mode 100644 Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.h delete mode 100644 Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.idl create mode 100644 Source/WebCore/Modules/mediastream/MediaTrackSupportedConstraints.h create mode 100644 Source/WebCore/Modules/mediastream/MediaTrackSupportedConstraints.idl create mode 100644 Source/WebCore/Modules/mediastream/NavigatorMediaDevices.cpp create mode 100644 Source/WebCore/Modules/mediastream/NavigatorMediaDevices.h create mode 100644 Source/WebCore/Modules/mediastream/NavigatorMediaDevices.idl delete mode 100644 Source/WebCore/Modules/mediastream/NavigatorMediaStream.cpp delete mode 100644 Source/WebCore/Modules/mediastream/NavigatorMediaStream.h delete mode 100644 Source/WebCore/Modules/mediastream/NavigatorMediaStream.idl create mode 100644 Source/WebCore/Modules/mediastream/NavigatorUserMedia.idl create mode 100644 Source/WebCore/Modules/mediastream/NavigatorUserMedia.js delete mode 100644 Source/WebCore/Modules/mediastream/NavigatorUserMediaError.cpp delete mode 100644 Source/WebCore/Modules/mediastream/NavigatorUserMediaError.h delete mode 100644 Source/WebCore/Modules/mediastream/NavigatorUserMediaError.idl delete mode 100644 Source/WebCore/Modules/mediastream/NavigatorUserMediaErrorCallback.h delete mode 100644 Source/WebCore/Modules/mediastream/NavigatorUserMediaErrorCallback.idl delete mode 100644 Source/WebCore/Modules/mediastream/NavigatorUserMediaSuccessCallback.h delete mode 100644 Source/WebCore/Modules/mediastream/NavigatorUserMediaSuccessCallback.idl create mode 100644 Source/WebCore/Modules/mediastream/OverconstrainedError.h create mode 100644 Source/WebCore/Modules/mediastream/OverconstrainedError.idl create mode 100644 Source/WebCore/Modules/mediastream/OverconstrainedErrorEvent.h create mode 100644 Source/WebCore/Modules/mediastream/OverconstrainedErrorEvent.idl create mode 100644 Source/WebCore/Modules/mediastream/PeerConnectionBackend.cpp create mode 100644 Source/WebCore/Modules/mediastream/PeerConnectionBackend.h create mode 100644 Source/WebCore/Modules/mediastream/RTCConfiguration.h create mode 100644 Source/WebCore/Modules/mediastream/RTCConfiguration.idl create mode 100644 Source/WebCore/Modules/mediastream/RTCIceServer.h create mode 100644 Source/WebCore/Modules/mediastream/RTCIceServer.idl create mode 100644 Source/WebCore/Modules/mediastream/RTCIceTransport.h create mode 100644 Source/WebCore/Modules/mediastream/RTCOfferAnswerOptions.h create mode 100644 Source/WebCore/Modules/mediastream/RTCPeerConnection.js delete mode 100644 Source/WebCore/Modules/mediastream/RTCPeerConnectionErrorCallback.h delete mode 100644 Source/WebCore/Modules/mediastream/RTCPeerConnectionErrorCallback.idl create mode 100644 Source/WebCore/Modules/mediastream/RTCPeerConnectionInternals.js create mode 100644 Source/WebCore/Modules/mediastream/RTCRtpReceiver.cpp create mode 100644 Source/WebCore/Modules/mediastream/RTCRtpReceiver.h create mode 100644 Source/WebCore/Modules/mediastream/RTCRtpReceiver.idl create mode 100644 Source/WebCore/Modules/mediastream/RTCRtpSender.cpp create mode 100644 Source/WebCore/Modules/mediastream/RTCRtpSender.h create mode 100644 Source/WebCore/Modules/mediastream/RTCRtpSender.idl create mode 100644 Source/WebCore/Modules/mediastream/RTCRtpSenderReceiverBase.h create mode 100644 Source/WebCore/Modules/mediastream/RTCRtpTransceiver.cpp create mode 100644 Source/WebCore/Modules/mediastream/RTCRtpTransceiver.h create mode 100644 Source/WebCore/Modules/mediastream/RTCRtpTransceiver.idl delete mode 100644 Source/WebCore/Modules/mediastream/RTCSessionDescriptionCallback.h delete mode 100644 Source/WebCore/Modules/mediastream/RTCSessionDescriptionCallback.idl delete mode 100644 Source/WebCore/Modules/mediastream/RTCSessionDescriptionRequestImpl.cpp delete mode 100644 Source/WebCore/Modules/mediastream/RTCSessionDescriptionRequestImpl.h delete mode 100644 Source/WebCore/Modules/mediastream/RTCStatsCallback.h delete mode 100644 Source/WebCore/Modules/mediastream/RTCStatsCallback.idl delete mode 100644 Source/WebCore/Modules/mediastream/RTCStatsRequestImpl.cpp delete mode 100644 Source/WebCore/Modules/mediastream/RTCStatsRequestImpl.h delete mode 100644 Source/WebCore/Modules/mediastream/RTCStatsResponse.cpp delete mode 100644 Source/WebCore/Modules/mediastream/RTCStatsResponse.h delete mode 100644 Source/WebCore/Modules/mediastream/RTCStatsResponse.idl create mode 100644 Source/WebCore/Modules/mediastream/RTCTrackEvent.cpp create mode 100644 Source/WebCore/Modules/mediastream/RTCTrackEvent.h create mode 100644 Source/WebCore/Modules/mediastream/RTCTrackEvent.idl delete mode 100644 Source/WebCore/Modules/mediastream/RTCVoidRequestImpl.cpp delete mode 100644 Source/WebCore/Modules/mediastream/RTCVoidRequestImpl.h create mode 100644 Source/WebCore/Modules/mediastream/SDPProcessor.cpp create mode 100644 Source/WebCore/Modules/mediastream/SDPProcessor.h delete mode 100644 Source/WebCore/Modules/mediastream/SourceInfo.cpp delete mode 100644 Source/WebCore/Modules/mediastream/SourceInfo.h delete mode 100644 Source/WebCore/Modules/mediastream/SourceInfo.idl delete mode 100644 Source/WebCore/Modules/mediastream/VideoStreamTrack.cpp delete mode 100644 Source/WebCore/Modules/mediastream/VideoStreamTrack.h delete mode 100644 Source/WebCore/Modules/mediastream/VideoStreamTrack.idl create mode 100644 Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.cpp create mode 100644 Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.h create mode 100644 Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp create mode 100644 Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h create mode 100644 Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp create mode 100644 Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h create mode 100644 Source/WebCore/Modules/mediastream/sdp.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/airplay-button.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/airplay-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/airplay-placard.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/background-tint.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/background-tint.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/button.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/controls-bar.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/controls-bar.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/forward-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/fullscreen-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/icon-button.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/icon-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/icon-service.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/invalid-placard.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/layout-item.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/layout-node.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/macos-compact-inline-media-controls.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/media-controls.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/media-controls.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/mute-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/pip-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/pip-placard.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/placard.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/placard.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/play-pause-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/rewind-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/scheduler.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/scrubber.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/seek-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/skip-back-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/slider.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/slider.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/start-button.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/start-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/status-label.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/status-label.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/text-tracks.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/time-control.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/time-label.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/time-label.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/tracks-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/volume-down-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/volume-slider.css create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/volume-slider.js create mode 100644 Source/WebCore/Modules/modern-media-controls/controls/volume-up-button.js create mode 100644 Source/WebCore/Modules/modern-media-controls/gesture-recognizers/gesture-recognizer.js create mode 100644 Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js create mode 100644 Source/WebCore/Modules/modern-media-controls/gesture-recognizers/tap.js create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@3x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@3x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@3x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@3x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/pause@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/pause@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/pause@3x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@3x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@3x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/play@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/play@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/play@3x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/slider-thumb@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/start@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/start@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/iOS/start@3x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/forward@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/forward@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pause@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pause@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/play@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/play@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/start@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/start@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/volume@1x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/images/macOS/volume@2x.png create mode 100644 Source/WebCore/Modules/modern-media-controls/js-files create mode 100644 Source/WebCore/Modules/modern-media-controls/main.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/airplay-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/controls-visibility-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/fullscreen-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/media-controller-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/media-controller.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/mute-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/pip-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/placard-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/playback-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/scrubbing-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/seek-backward-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/seek-forward-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/seek-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/skip-back-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/start-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/status-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/time-labels-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/tracks-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/volume-down-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/volume-support.js create mode 100644 Source/WebCore/Modules/modern-media-controls/media/volume-up-support.js create mode 100644 Source/WebCore/Modules/plugins/QuickTimePluginReplacement.css create mode 100644 Source/WebCore/Modules/plugins/QuickTimePluginReplacement.h create mode 100644 Source/WebCore/Modules/plugins/QuickTimePluginReplacement.idl create mode 100644 Source/WebCore/Modules/plugins/QuickTimePluginReplacement.js create mode 100644 Source/WebCore/Modules/plugins/YouTubePluginReplacement.cpp create mode 100644 Source/WebCore/Modules/plugins/YouTubePluginReplacement.h create mode 100644 Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.cpp create mode 100644 Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.h create mode 100644 Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.idl create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesis.cpp create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesis.h create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesis.idl create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisEvent.cpp create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisEvent.h create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisEvent.idl create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisUtterance.cpp create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisUtterance.h create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisUtterance.idl create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisVoice.cpp create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisVoice.h create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisVoice.idl create mode 100644 Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.idl create mode 100644 Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.js create mode 100644 Source/WebCore/Modules/streams/CountQueuingStrategy.idl create mode 100644 Source/WebCore/Modules/streams/CountQueuingStrategy.js create mode 100644 Source/WebCore/Modules/streams/ReadableByteStreamController.idl create mode 100644 Source/WebCore/Modules/streams/ReadableByteStreamController.js create mode 100644 Source/WebCore/Modules/streams/ReadableByteStreamInternals.js create mode 100644 Source/WebCore/Modules/streams/ReadableStream.idl create mode 100644 Source/WebCore/Modules/streams/ReadableStream.js create mode 100644 Source/WebCore/Modules/streams/ReadableStreamDefaultController.idl create mode 100644 Source/WebCore/Modules/streams/ReadableStreamDefaultController.js create mode 100644 Source/WebCore/Modules/streams/ReadableStreamDefaultReader.idl create mode 100644 Source/WebCore/Modules/streams/ReadableStreamDefaultReader.js create mode 100644 Source/WebCore/Modules/streams/ReadableStreamInternals.js create mode 100644 Source/WebCore/Modules/streams/ReadableStreamSource.h create mode 100644 Source/WebCore/Modules/streams/ReadableStreamSource.idl create mode 100644 Source/WebCore/Modules/streams/StreamInternals.js create mode 100644 Source/WebCore/Modules/streams/WritableStream.idl create mode 100644 Source/WebCore/Modules/streams/WritableStream.js create mode 100644 Source/WebCore/Modules/streams/WritableStreamInternals.js create mode 100644 Source/WebCore/Modules/vibration/NavigatorVibration.cpp create mode 100644 Source/WebCore/Modules/vibration/NavigatorVibration.h create mode 100644 Source/WebCore/Modules/vibration/NavigatorVibration.idl create mode 100644 Source/WebCore/Modules/vibration/Vibration.cpp create mode 100644 Source/WebCore/Modules/vibration/Vibration.h create mode 100644 Source/WebCore/Modules/vibration/VibrationClient.h delete mode 100644 Source/WebCore/Modules/webdatabase/AbstractDatabaseServer.h delete mode 100644 Source/WebCore/Modules/webdatabase/AbstractSQLStatement.h delete mode 100644 Source/WebCore/Modules/webdatabase/AbstractSQLStatementBackend.h delete mode 100644 Source/WebCore/Modules/webdatabase/AbstractSQLTransaction.h delete mode 100644 Source/WebCore/Modules/webdatabase/AbstractSQLTransactionBackend.h delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseBackend.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseBackend.h delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseBackendBase.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseBackendBase.h delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseBackendContext.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseBackendContext.h delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseBackendSync.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseBackendSync.h delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseBase.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseBase.h delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseBasicTypes.h delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseError.h delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseServer.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseServer.h delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseSync.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseSync.h delete mode 100644 Source/WebCore/Modules/webdatabase/DatabaseSync.idl delete mode 100644 Source/WebCore/Modules/webdatabase/SQLStatementBackend.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/SQLStatementBackend.h delete mode 100644 Source/WebCore/Modules/webdatabase/SQLStatementSync.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/SQLStatementSync.h delete mode 100644 Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.h delete mode 100644 Source/WebCore/Modules/webdatabase/SQLTransactionClient.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/SQLTransactionClient.h delete mode 100644 Source/WebCore/Modules/webdatabase/SQLTransactionSync.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/SQLTransactionSync.h delete mode 100644 Source/WebCore/Modules/webdatabase/SQLTransactionSync.idl delete mode 100644 Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.h delete mode 100644 Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.idl delete mode 100644 Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.cpp delete mode 100644 Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.h delete mode 100644 Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.idl create mode 100644 Source/WebCore/Modules/webdriver/NavigatorWebDriver.cpp create mode 100644 Source/WebCore/Modules/webdriver/NavigatorWebDriver.h create mode 100644 Source/WebCore/Modules/webdriver/NavigatorWebDriver.idl (limited to 'Source/WebCore/Modules') diff --git a/Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.cpp b/Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.cpp new file mode 100644 index 000000000..1090d9437 --- /dev/null +++ b/Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebKitPlaybackTargetAvailabilityEvent.h" + +#include + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + +namespace WebCore { + +static const AtomicString& stringForPlaybackTargetAvailability(bool available) +{ + static NeverDestroyed availableString("available", AtomicString::ConstructFromLiteral); + static NeverDestroyed notAvailableString("not-available", AtomicString::ConstructFromLiteral); + + return available ? availableString : notAvailableString; +} + +WebKitPlaybackTargetAvailabilityEvent::WebKitPlaybackTargetAvailabilityEvent(const AtomicString& eventType, bool available) + : Event(eventType, false, false) + , m_availability(stringForPlaybackTargetAvailability(available)) +{ +} + +WebKitPlaybackTargetAvailabilityEvent::WebKitPlaybackTargetAvailabilityEvent(const AtomicString& eventType, const Init& initializer, IsTrusted isTrusted) + : Event(eventType, initializer, isTrusted) + , m_availability(initializer.availability) +{ +} + +} // namespace WebCore + +#endif // ENABLE(WIRELESS_PLAYBACK_TARGET) diff --git a/Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.h b/Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.h new file mode 100644 index 000000000..acbe7d1df --- /dev/null +++ b/Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + +#include "Event.h" + +namespace WebCore { + +class WebKitPlaybackTargetAvailabilityEvent : public Event { +public: + ~WebKitPlaybackTargetAvailabilityEvent() { } + + static Ref create(const AtomicString& eventType, bool available) + { + return adoptRef(*new WebKitPlaybackTargetAvailabilityEvent(eventType, available)); + } + + struct Init : EventInit { + String availability; + }; + + static Ref create(const AtomicString& eventType, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) + { + return adoptRef(*new WebKitPlaybackTargetAvailabilityEvent(eventType, initializer, isTrusted)); + } + + String availability() const { return m_availability; } + + EventInterface eventInterface() const override { return WebKitPlaybackTargetAvailabilityEventInterfaceType; } + +private: + explicit WebKitPlaybackTargetAvailabilityEvent(const AtomicString& eventType, bool available); + WebKitPlaybackTargetAvailabilityEvent(const AtomicString& eventType, const Init&, IsTrusted); + + String m_availability; +}; + +} // namespace WebCore + +#endif // ENABLE(WIRELESS_PLAYBACK_TARGET) diff --git a/Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.idl b/Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.idl new file mode 100644 index 000000000..10b88b2a7 --- /dev/null +++ b/Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +[ + Conditional=WIRELESS_PLAYBACK_TARGET, + Constructor(DOMString type, optional WebKitPlaybackTargetAvailabilityEventInit eventInitDict), +] interface WebKitPlaybackTargetAvailabilityEvent : Event { + readonly attribute DOMString availability; +}; + +dictionary WebKitPlaybackTargetAvailabilityEventInit : EventInit { + DOMString availability = ""; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayLineItem.h b/Source/WebCore/Modules/applepay/ApplePayLineItem.h new file mode 100644 index 000000000..e3ec6f605 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayLineItem.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "PaymentRequest.h" + +namespace WebCore { + +struct ApplePayLineItem { + using Type = PaymentRequest::LineItem::Type; + + Type type { Type::Final }; + String label; + String amount; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayLineItem.idl b/Source/WebCore/Modules/applepay/ApplePayLineItem.idl new file mode 100644 index 000000000..2c58037ec --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayLineItem.idl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, +] enum ApplePayLineItemType { + "pending", + "final" +}; + +[ + Conditional=APPLE_PAY, +] dictionary ApplePayLineItem { + ApplePayLineItemType type = "final"; + DOMString label; + DOMString amount; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayPayment.h b/Source/WebCore/Modules/applepay/ApplePayPayment.h new file mode 100644 index 000000000..31f4cef82 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPayment.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "ApplePayPaymentContact.h" +#include "ApplePayPaymentMethod.h" + +namespace WebCore { + +struct ApplePayPayment { + struct Token { + ApplePayPaymentMethod paymentMethod; + String transactionIdentifier; + String paymentData; + }; + + Token token; + std::optional billingContact; + std::optional shippingContact; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayPayment.idl b/Source/WebCore/Modules/applepay/ApplePayPayment.idl new file mode 100644 index 000000000..25bb0e96b --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPayment.idl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, + JSGenerateToJSObject +] dictionary ApplePayPayment { + required ApplePayPaymentToken token; + ApplePayPaymentContact billingContact; + ApplePayPaymentContact shippingContact; +}; + +[ + Conditional=APPLE_PAY, + JSGenerateToJSObject +] dictionary ApplePayPaymentToken { + required ApplePayPaymentMethod paymentMethod; + DOMString transactionIdentifier; + JSON paymentData; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.cpp b/Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.cpp new file mode 100644 index 000000000..42b7d5dc8 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ApplePayPaymentAuthorizedEvent.h" + +#if ENABLE(APPLE_PAY) + +#include "Payment.h" + +namespace WebCore { + +ApplePayPaymentAuthorizedEvent::ApplePayPaymentAuthorizedEvent(const AtomicString& type, const Payment& payment) + : Event(type, false, false) + , m_payment(payment.toApplePayPayment()) +{ +} + +ApplePayPaymentAuthorizedEvent::~ApplePayPaymentAuthorizedEvent() +{ +} + +EventInterface ApplePayPaymentAuthorizedEvent::eventInterface() const +{ + return ApplePayPaymentAuthorizedEventInterfaceType; +} + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.h b/Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.h new file mode 100644 index 000000000..d07a24160 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "ApplePayPayment.h" +#include "Event.h" + +namespace WebCore { + +class Payment; + +class ApplePayPaymentAuthorizedEvent final : public Event { +public: + static Ref create(const AtomicString& type, const Payment& payment) + { + return adoptRef(*new ApplePayPaymentAuthorizedEvent(type, payment)); + } + + virtual ~ApplePayPaymentAuthorizedEvent(); + + const ApplePayPayment& payment() const { return m_payment; } + +private: + ApplePayPaymentAuthorizedEvent(const AtomicString& type, const Payment&); + + // Event. + EventInterface eventInterface() const override; + + const ApplePayPayment m_payment; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.idl b/Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.idl new file mode 100644 index 000000000..eeeb32a5f --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, + NoInterfaceObject, +] interface ApplePayPaymentAuthorizedEvent : Event { + [CachedAttribute] readonly attribute ApplePayPayment payment; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentContact.h b/Source/WebCore/Modules/applepay/ApplePayPaymentContact.h new file mode 100644 index 000000000..1e389d440 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentContact.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include +#include +#include + +namespace WebCore { + +struct ApplePayPaymentContact { + String phoneNumber; + String emailAddress; + String givenName; + String familyName; + std::optional> addressLines; + String locality; + String postalCode; + String administrativeArea; + String country; + String countryCode; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentContact.idl b/Source/WebCore/Modules/applepay/ApplePayPaymentContact.idl new file mode 100644 index 000000000..62be98b16 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentContact.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, + JSGenerateToJSObject +] dictionary ApplePayPaymentContact { + DOMString phoneNumber; + DOMString emailAddress; + DOMString givenName; + DOMString familyName; + sequence addressLines; + DOMString locality; + DOMString postalCode; + DOMString administrativeArea; + DOMString country; + DOMString countryCode; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentMethod.h b/Source/WebCore/Modules/applepay/ApplePayPaymentMethod.h new file mode 100644 index 000000000..7bf917c93 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentMethod.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "ApplePayPaymentPass.h" +#include + +namespace WebCore { + +enum class ApplePayPaymentMethodType { Debit, Credit, Prepaid, Store }; + +struct ApplePayPaymentMethod { + using Type = ApplePayPaymentMethodType; + + String displayName; + String network; + std::optional type; + std::optional paymentPass; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentMethod.idl b/Source/WebCore/Modules/applepay/ApplePayPaymentMethod.idl new file mode 100644 index 000000000..82c4ec21a --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentMethod.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, +] enum ApplePayPaymentMethodType { + "debit", + "credit", + "prepaid", + "store" +}; + +[ + Conditional=APPLE_PAY, + JSGenerateToJSObject +] dictionary ApplePayPaymentMethod { + DOMString displayName; + DOMString network; + ApplePayPaymentMethodType type; + ApplePayPaymentPass paymentPass; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.cpp b/Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.cpp new file mode 100644 index 000000000..4c38c5f3d --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ApplePayPaymentMethodSelectedEvent.h" + +#if ENABLE(APPLE_PAY) + +#include "PaymentMethod.h" + +namespace WebCore { + +ApplePayPaymentMethodSelectedEvent::ApplePayPaymentMethodSelectedEvent(const AtomicString& type, const PaymentMethod& paymentMethod) + : Event(type, false, false) + , m_paymentMethod(paymentMethod.toApplePayPaymentMethod()) +{ +} + +ApplePayPaymentMethodSelectedEvent::~ApplePayPaymentMethodSelectedEvent() +{ +} + +EventInterface ApplePayPaymentMethodSelectedEvent::eventInterface() const +{ + return ApplePayPaymentMethodSelectedEventInterfaceType; +} + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.h b/Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.h new file mode 100644 index 000000000..d51832407 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "ApplePayPaymentMethod.h" +#include "Event.h" + +namespace WebCore { + +class PaymentMethod; + +class ApplePayPaymentMethodSelectedEvent final : public Event { +public: + static Ref create(const AtomicString& type, const PaymentMethod& paymentMethod) + { + return adoptRef(*new ApplePayPaymentMethodSelectedEvent(type, paymentMethod)); + } + + virtual ~ApplePayPaymentMethodSelectedEvent(); + + const ApplePayPaymentMethod& paymentMethod() { return m_paymentMethod; } + +private: + ApplePayPaymentMethodSelectedEvent(const AtomicString& type, const PaymentMethod&); + + // Event. + EventInterface eventInterface() const override; + + const ApplePayPaymentMethod m_paymentMethod; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.idl b/Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.idl new file mode 100644 index 000000000..06fcb0847 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, + NoInterfaceObject, +] interface ApplePayPaymentMethodSelectedEvent : Event { + [CachedAttribute] readonly attribute ApplePayPaymentMethod paymentMethod; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentPass.h b/Source/WebCore/Modules/applepay/ApplePayPaymentPass.h new file mode 100644 index 000000000..2f058af7b --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentPass.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include + +namespace WebCore { + +struct ApplePayPaymentPass { + enum class ActivationState { Activated, RequiresActivation, Activating, Suspended, Deactivated }; + + String primaryAccountIdentifier; + String primaryAccountNumberSuffix; + String deviceAccountIdentifier; + String deviceAccountNumberSuffix; + ActivationState activationState; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentPass.idl b/Source/WebCore/Modules/applepay/ApplePayPaymentPass.idl new file mode 100644 index 000000000..43f6d7882 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentPass.idl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, +] enum ApplePayPaymentPassActivationState { + "activated", + "requiresActivation", + "activating", + "suspended", + "deactivated" +}; + +[ + Conditional=APPLE_PAY, + JSGenerateToJSObject +] dictionary ApplePayPaymentPass { + required DOMString primaryAccountIdentifier; + required DOMString primaryAccountNumberSuffix; + DOMString deviceAccountIdentifier; + DOMString deviceAccountNumberSuffix; + required ApplePayPaymentPassActivationState activationState; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentRequest.h b/Source/WebCore/Modules/applepay/ApplePayPaymentRequest.h new file mode 100644 index 000000000..1203ad8b5 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentRequest.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "ApplePayLineItem.h" +#include "ApplePayPaymentContact.h" +#include "ApplePayShippingMethod.h" +#include "PaymentRequest.h" + +namespace WebCore { + +struct ApplePayPaymentRequest { + enum class MerchantCapability { Supports3DS, SupportsEMV, SupportsCredit, SupportsDebit }; + enum class ContactField { Email, Name, Phone, PostalAddress }; + + using ShippingType = PaymentRequest::ShippingType; + + Vector merchantCapabilities; + Vector supportedNetworks; + String countryCode; + String currencyCode; + + std::optional> requiredBillingContactFields; + std::optional billingContact; + + std::optional> requiredShippingContactFields; + std::optional shippingContact; + + ShippingType shippingType { ShippingType::Shipping }; + std::optional> shippingMethods; + + ApplePayLineItem total; + std::optional> lineItems; + + String applicationData; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayPaymentRequest.idl b/Source/WebCore/Modules/applepay/ApplePayPaymentRequest.idl new file mode 100644 index 000000000..322bf0f2e --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayPaymentRequest.idl @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, +] enum ApplePayMerchantCapability { + "supports3DS", + "supportsEMV", + "supportsCredit", + "supportsDebit" +}; + +[ + Conditional=APPLE_PAY, +] enum ApplePayContactField { + "email", + "name", + "phone", + "postalAddress" +}; + +[ + Conditional=APPLE_PAY, +] enum ApplePayShippingType { + "shipping", + "delivery", + "storePickup", + "servicePickup" +}; + +[ + Conditional=APPLE_PAY, +] dictionary ApplePayPaymentRequest { + required ApplePayLineItem total; + sequence lineItems; + + required sequence merchantCapabilities; + required sequence supportedNetworks; // FIXME: Should this be an sequence of enums? + required DOMString countryCode; + required DOMString currencyCode; + + sequence requiredBillingContactFields; + ApplePayPaymentContact billingContact; + + sequence requiredShippingContactFields; + ApplePayPaymentContact shippingContact; + + ApplePayShippingType shippingType = "shipping"; + sequence shippingMethods; + + DOMString applicationData; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePaySession.cpp b/Source/WebCore/Modules/applepay/ApplePaySession.cpp new file mode 100644 index 000000000..e5225062a --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePaySession.cpp @@ -0,0 +1,1032 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ApplePaySession.h" + +#if ENABLE(APPLE_PAY) + +#include "ApplePayLineItem.h" +#include "ApplePayPaymentAuthorizedEvent.h" +#include "ApplePayPaymentMethodSelectedEvent.h" +#include "ApplePayPaymentRequest.h" +#include "ApplePayShippingContactSelectedEvent.h" +#include "ApplePayShippingMethod.h" +#include "ApplePayShippingMethodSelectedEvent.h" +#include "ApplePayValidateMerchantEvent.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "EventNames.h" +#include "JSDOMPromise.h" +#include "LinkIconCollector.h" +#include "LinkIconType.h" +#include "MainFrame.h" +#include "Page.h" +#include "PageConsoleClient.h" +#include "PaymentAuthorizationStatus.h" +#include "PaymentContact.h" +#include "PaymentCoordinator.h" +#include "PaymentMerchantSession.h" +#include "PaymentMethod.h" +#include "PaymentRequestValidator.h" +#include "ScriptController.h" +#include "SecurityOrigin.h" +#include "Settings.h" + +namespace WebCore { + +static bool parseDigit(UChar digit, bool isNegative, int64_t& amount) +{ + if (!isASCIIDigit(digit)) + return false; + + int64_t digitValue = (digit - '0'); + + const int64_t maxMultiplier = std::numeric_limits::max() / 10; + + // Check overflow. + if (amount > maxMultiplier || (amount == maxMultiplier && digitValue > (std::numeric_limits::max() % 10) + isNegative)) + return false; + + amount = amount * 10 + digitValue; + return true; +} + +// The amount follows the regular expression -?[0-9]+(\.[0-9][0-9])?. +static std::optional parseAmount(const String& amountString) +{ + int64_t amount = 0; + + bool isNegative = false; + + enum class State { + Start, + Sign, + Digit, + Dot, + DotDigit, + End, + }; + + State state = State::Start; + + for (unsigned i = 0; i < amountString.length(); ++i) { + UChar c = amountString[i]; + + switch (state) { + case State::Start: + if (c == '-') { + isNegative = true; + state = State::Sign; + break; + } + + if (!parseDigit(c, isNegative, amount)) + return std::nullopt; + state = State::Digit; + break; + + case State::Sign: + if (!parseDigit(c, isNegative, amount)) + return std::nullopt; + state = State::Digit; + break; + + case State::Digit: + if (c == '.') { + state = State::Dot; + break; + } + + if (!parseDigit(c, isNegative, amount)) + return std::nullopt; + break; + + case State::Dot: + if (!parseDigit(c, isNegative, amount)) + return std::nullopt; + + state = State::DotDigit; + break; + + case State::DotDigit: + if (!parseDigit(c, isNegative, amount)) + return std::nullopt; + + state = State::End; + break; + + case State::End: + return std::nullopt; + } + } + + if (state != State::Digit && state != State::DotDigit && state != State::End) + return std::nullopt; + + if (state == State::DotDigit) { + // There was a single digit after the decimal point. + // FIXME: Handle this overflowing. + amount *= 10; + } else if (state == State::Digit) { + // There was no decimal point. + // FIXME: Handle this overflowing. + amount *= 100; + } + + if (isNegative) + amount = -amount; + + return amount; +} + +static ExceptionOr convertAndValidateTotal(ApplePayLineItem&& lineItem) +{ + auto amount = parseAmount(lineItem.amount); + if (!amount) + return Exception { TypeError, makeString("\"" + lineItem.amount, "\" is not a valid amount.") }; + + PaymentRequest::LineItem result; + result.amount = *amount; + result.type = lineItem.type; + result.label = lineItem.label; + + return WTFMove(result); +} + +static ExceptionOr convertAndValidate(ApplePayLineItem&& lineItem) +{ + PaymentRequest::LineItem result; + + // It is OK for pending types to not have an amount. + if (lineItem.type != PaymentRequest::LineItem::Type::Pending) { + auto amount = parseAmount(lineItem.amount); + if (!amount) + return Exception { TypeError, makeString("\"" + lineItem.amount, "\" is not a valid amount.") }; + + result.amount = *amount; + } + + result.type = lineItem.type; + result.label = lineItem.label; + + return WTFMove(result); +} + +static ExceptionOr> convertAndValidate(std::optional>&& lineItems) +{ + Vector result; + if (!lineItems) + return WTFMove(result); + + result.reserveInitialCapacity(lineItems->size()); + + for (auto lineItem : lineItems.value()) { + auto convertedLineItem = convertAndValidate(WTFMove(lineItem)); + if (convertedLineItem.hasException()) + return convertedLineItem.releaseException(); + result.uncheckedAppend(convertedLineItem.releaseReturnValue()); + } + + return WTFMove(result); +} + +static ExceptionOr convertAndValidate(Vector&& merchantCapabilities) +{ + if (merchantCapabilities.isEmpty()) + return Exception { TypeError, "At least one merchant capability must be provided." }; + + PaymentRequest::MerchantCapabilities result; + + for (auto& merchantCapability : merchantCapabilities) { + switch (merchantCapability) { + case ApplePayPaymentRequest::MerchantCapability::Supports3DS: + result.supports3DS = true; + break; + case ApplePayPaymentRequest::MerchantCapability::SupportsEMV: + result.supportsEMV = true; + break; + case ApplePayPaymentRequest::MerchantCapability::SupportsCredit: + result.supportsCredit = true; + break; + case ApplePayPaymentRequest::MerchantCapability::SupportsDebit: + result.supportsDebit = true; + break; + } + } + + return WTFMove(result); +} + +static ExceptionOr> convertAndValidate(unsigned version, Vector&& supportedNetworks) +{ + if (supportedNetworks.isEmpty()) + return Exception { TypeError, "At least one supported network must be provided." }; + + for (auto& supportedNetwork : supportedNetworks) { + if (!PaymentRequest::isValidSupportedNetwork(version, supportedNetwork)) + return Exception { TypeError, makeString("\"" + supportedNetwork, "\" is not a valid payment network.") }; + } + + return WTFMove(supportedNetworks); +} + +static ExceptionOr convertAndValidate(Vector&& contactFields) +{ + if (contactFields.isEmpty()) + return Exception { TypeError, "At least one contact field must be provided." }; + + PaymentRequest::ContactFields result; + + for (auto& contactField : contactFields) { + switch (contactField) { + case ApplePayPaymentRequest::ContactField::Email: + result.email = true; + break; + case ApplePayPaymentRequest::ContactField::Name: + result.name = true; + break; + case ApplePayPaymentRequest::ContactField::Phone: + result.phone = true; + break; + case ApplePayPaymentRequest::ContactField::PostalAddress: + result.postalAddress = true; + break; + } + } + + return WTFMove(result); +} + +static ExceptionOr convertAndValidate(ApplePayShippingMethod&& shippingMethod) +{ + auto amount = parseAmount(shippingMethod.amount); + if (!amount) + return Exception { TypeError, makeString("\"" + shippingMethod.amount, "\" is not a valid amount.") }; + + PaymentRequest::ShippingMethod result; + result.amount = *amount; + result.label = shippingMethod.label; + result.detail = shippingMethod.detail; + result.identifier = shippingMethod.identifier; + + return WTFMove(result); +} + +static ExceptionOr> convertAndValidate(Vector&& shippingMethods) +{ + if (shippingMethods.isEmpty()) + return Exception { TypeError, "At least one shipping method must be provided." }; + + Vector result; + result.reserveInitialCapacity(shippingMethods.size()); + + for (auto& shippingMethod : shippingMethods) { + auto convertedShippingMethod = convertAndValidate(WTFMove(shippingMethod)); + if (convertedShippingMethod.hasException()) + return convertedShippingMethod.releaseException(); + result.uncheckedAppend(convertedShippingMethod.releaseReturnValue()); + } + + return WTFMove(result); +} + +static ExceptionOr convertAndValidate(unsigned version, ApplePayPaymentRequest&& paymentRequest) +{ + PaymentRequest result; + + auto total = convertAndValidateTotal(WTFMove(paymentRequest.total)); + if (total.hasException()) + return total.releaseException(); + result.setTotal(total.releaseReturnValue()); + + auto lineItems = convertAndValidate(WTFMove(paymentRequest.lineItems)); + if (lineItems.hasException()) + return lineItems.releaseException(); + result.setLineItems(lineItems.releaseReturnValue()); + + result.setCountryCode(paymentRequest.countryCode); + result.setCurrencyCode(paymentRequest.currencyCode); + + auto merchantCapabilities = convertAndValidate(WTFMove(paymentRequest.merchantCapabilities)); + if (merchantCapabilities.hasException()) + return merchantCapabilities.releaseException(); + result.setMerchantCapabilities(merchantCapabilities.releaseReturnValue()); + + auto supportedNetworks = convertAndValidate(version, WTFMove(paymentRequest.supportedNetworks)); + if (supportedNetworks.hasException()) + return supportedNetworks.releaseException(); + result.setSupportedNetworks(supportedNetworks.releaseReturnValue()); + + if (paymentRequest.requiredBillingContactFields) { + auto requiredBillingContactFields = convertAndValidate(WTFMove(*paymentRequest.requiredBillingContactFields)); + if (requiredBillingContactFields.hasException()) + return requiredBillingContactFields.releaseException(); + result.setRequiredBillingContactFields(requiredBillingContactFields.releaseReturnValue()); + } + + if (paymentRequest.billingContact) + result.setBillingContact(PaymentContact::fromApplePayPaymentContact(paymentRequest.billingContact.value())); + + if (paymentRequest.requiredShippingContactFields) { + auto requiredShippingContactFields = convertAndValidate(WTFMove(*paymentRequest.requiredShippingContactFields)); + if (requiredShippingContactFields.hasException()) + return requiredShippingContactFields.releaseException(); + result.setRequiredShippingContactFields(requiredShippingContactFields.releaseReturnValue()); + } + + if (paymentRequest.shippingContact) + result.setShippingContact(PaymentContact::fromApplePayPaymentContact(paymentRequest.shippingContact.value())); + + result.setShippingType(paymentRequest.shippingType); + + if (paymentRequest.shippingMethods) { + auto shippingMethods = convertAndValidate(WTFMove(*paymentRequest.shippingMethods)); + if (shippingMethods.hasException()) + return shippingMethods.releaseException(); + result.setShippingMethods(shippingMethods.releaseReturnValue()); + } + + result.setApplicationData(paymentRequest.applicationData); + + // FIXME: Merge this validation into the validation we are doing above. + auto validatedPaymentRequest = PaymentRequestValidator::validate(result); + if (validatedPaymentRequest.hasException()) + return validatedPaymentRequest.releaseException(); + + return WTFMove(result); +} + +static bool isSecure(DocumentLoader& documentLoader) +{ + if (!documentLoader.response().url().protocolIs("https")) + return false; + + if (!documentLoader.response().certificateInfo() || documentLoader.response().certificateInfo()->containsNonRootSHA1SignedCertificate()) + return false; + + return true; +} + +static ExceptionOr canCallApplePaySessionAPIs(Document& document) +{ + if (!isSecure(*document.loader())) + return Exception { INVALID_ACCESS_ERR, "Trying to call an ApplePaySession API from an insecure document." }; + + auto& topDocument = document.topDocument(); + if (&document != &topDocument) { + auto& topOrigin = topDocument.topOrigin(); + + if (!document.securityOrigin().isSameSchemeHostPort(topOrigin)) + return Exception { INVALID_ACCESS_ERR, "Trying to call an ApplePaySession API from a document with an different security origin than its top-level frame." }; + + for (auto* ancestorDocument = document.parentDocument(); ancestorDocument != &topDocument; ancestorDocument = ancestorDocument->parentDocument()) { + if (!isSecure(*ancestorDocument->loader())) + return Exception { INVALID_ACCESS_ERR, "Trying to call an ApplePaySession API from a document with an insecure parent frame." }; + + if (!ancestorDocument->securityOrigin().isSameSchemeHostPort(topOrigin)) + return Exception { INVALID_ACCESS_ERR, "Trying to call an ApplePaySession API from a document with an different security origin than its top-level frame." }; + } + } + + return { }; +} + +ExceptionOr> ApplePaySession::create(Document& document, unsigned version, ApplePayPaymentRequest&& paymentRequest) +{ + auto canCall = canCallApplePaySessionAPIs(document); + if (canCall.hasException()) + return canCall.releaseException(); + + if (!ScriptController::processingUserGesture()) + return Exception { INVALID_ACCESS_ERR, "Must create a new ApplePaySession from a user gesture handler." }; + + auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator(); + + if (!version || !paymentCoordinator.supportsVersion(version)) + return Exception { INVALID_ACCESS_ERR, makeString("\"" + String::number(version), "\" is not a supported version.") }; + + auto convertedPaymentRequest = convertAndValidate(version, WTFMove(paymentRequest)); + if (convertedPaymentRequest.hasException()) + return convertedPaymentRequest.releaseException(); + + return adoptRef(*new ApplePaySession(document, convertedPaymentRequest.releaseReturnValue())); +} + +ApplePaySession::ApplePaySession(Document& document, PaymentRequest&& paymentRequest) + : ActiveDOMObject(&document) + , m_paymentRequest(WTFMove(paymentRequest)) +{ + suspendIfNeeded(); +} + +ApplePaySession::~ApplePaySession() +{ +} + +ExceptionOr ApplePaySession::supportsVersion(ScriptExecutionContext& scriptExecutionContext, unsigned version) +{ + if (!version) + return Exception { INVALID_ACCESS_ERR }; + + auto& document = downcast(scriptExecutionContext); + + auto canCall = canCallApplePaySessionAPIs(document); + if (canCall.hasException()) + return canCall.releaseException(); + + return document.frame()->mainFrame().paymentCoordinator().supportsVersion(version); +} + +static bool shouldDiscloseApplePayCapability(Document& document) +{ + auto* page = document.page(); + if (!page || page->usesEphemeralSession()) + return false; + + return document.settings().applePayCapabilityDisclosureAllowed(); +} + +ExceptionOr ApplePaySession::canMakePayments(ScriptExecutionContext& scriptExecutionContext) +{ + auto& document = downcast(scriptExecutionContext); + + auto canCall = canCallApplePaySessionAPIs(document); + if (canCall.hasException()) + return canCall.releaseException(); + + return document.frame()->mainFrame().paymentCoordinator().canMakePayments(); +} + +ExceptionOr ApplePaySession::canMakePaymentsWithActiveCard(ScriptExecutionContext& scriptExecutionContext, const String& merchantIdentifier, Ref&& passedPromise) +{ + auto& document = downcast(scriptExecutionContext); + + auto canCall = canCallApplePaySessionAPIs(document); + if (canCall.hasException()) + return canCall.releaseException(); + + RefPtr promise(WTFMove(passedPromise)); + if (!shouldDiscloseApplePayCapability(document)) { + auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator(); + bool canMakePayments = paymentCoordinator.canMakePayments(); + + RunLoop::main().dispatch([promise, canMakePayments]() mutable { + promise->resolve(canMakePayments); + }); + return { }; + } + + auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator(); + + paymentCoordinator.canMakePaymentsWithActiveCard(merchantIdentifier, document.domain(), [promise](bool canMakePayments) mutable { + promise->resolve(canMakePayments); + }); + return { }; +} + +ExceptionOr ApplePaySession::openPaymentSetup(ScriptExecutionContext& scriptExecutionContext, const String& merchantIdentifier, Ref&& passedPromise) +{ + auto& document = downcast(scriptExecutionContext); + + auto canCall = canCallApplePaySessionAPIs(document); + if (canCall.hasException()) + return canCall.releaseException(); + + if (!ScriptController::processingUserGesture()) + return Exception { INVALID_ACCESS_ERR, "Must call ApplePaySession.openPaymentSetup from a user gesture handler." }; + + RefPtr promise(WTFMove(passedPromise)); + auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator(); + + paymentCoordinator.openPaymentSetup(merchantIdentifier, document.domain(), [promise](bool result) mutable { + promise->resolve(result); + }); + + return { }; +} + +ExceptionOr ApplePaySession::begin() +{ + if (!canBegin()) + return Exception { INVALID_ACCESS_ERR, "Payment session is already active." }; + + if (paymentCoordinator().hasActiveSession()) + return Exception { INVALID_ACCESS_ERR, "Page already has an active payment session." }; + + auto& document = *downcast(scriptExecutionContext()); + + Vector linkIconURLs; + for (auto& icon : LinkIconCollector { document }.iconsOfTypes({ LinkIconType::TouchIcon, LinkIconType::TouchPrecomposedIcon })) + linkIconURLs.append(icon.url); + + if (!paymentCoordinator().beginPaymentSession(*this, document.url(), linkIconURLs, m_paymentRequest)) + return Exception { INVALID_ACCESS_ERR, "There is already has an active payment session." }; + + m_state = State::Active; + + setPendingActivity(this); + + return { }; +} + +ExceptionOr ApplePaySession::abort() +{ + if (!canAbort()) + return Exception { INVALID_ACCESS_ERR }; + + m_state = State::Aborted; + paymentCoordinator().abortPaymentSession(); + + didReachFinalState(); + + return { }; +} + +ExceptionOr ApplePaySession::completeMerchantValidation(JSC::ExecState& state, JSC::JSValue merchantSessionValue) +{ + if (!canCompleteMerchantValidation()) + return Exception { INVALID_ACCESS_ERR }; + + if (!merchantSessionValue.isObject()) + return Exception { TypeError }; + + auto& document = *downcast(scriptExecutionContext()); + auto& window = *document.domWindow(); + + String errorMessage; + auto merchantSession = PaymentMerchantSession::fromJS(state, asObject(merchantSessionValue), errorMessage); + if (!merchantSession) { + window.printErrorMessage(errorMessage); + return Exception { INVALID_ACCESS_ERR }; + } + + m_merchantValidationState = MerchantValidationState::ValidationComplete; + paymentCoordinator().completeMerchantValidation(*merchantSession); + + return { }; +} + +static std::optional toPaymentAuthorizationStatus(unsigned short status) +{ + switch (status) { + case ApplePaySession::STATUS_SUCCESS: + return PaymentAuthorizationStatus::Success; + + case ApplePaySession::STATUS_FAILURE: + return PaymentAuthorizationStatus::Failure; + + case ApplePaySession::STATUS_INVALID_BILLING_POSTAL_ADDRESS: + return PaymentAuthorizationStatus::InvalidBillingPostalAddress; + + case ApplePaySession::STATUS_INVALID_SHIPPING_POSTAL_ADDRESS: + return PaymentAuthorizationStatus::InvalidShippingPostalAddress; + + case ApplePaySession::STATUS_INVALID_SHIPPING_CONTACT: + return PaymentAuthorizationStatus::InvalidShippingContact; + + case ApplePaySession::STATUS_PIN_REQUIRED: + return PaymentAuthorizationStatus::PINRequired; + + case ApplePaySession::STATUS_PIN_INCORRECT: + return PaymentAuthorizationStatus::PINIncorrect; + + case ApplePaySession::STATUS_PIN_LOCKOUT: + return PaymentAuthorizationStatus::PINLockout; + + default: + return std::nullopt; + } +} + +ExceptionOr ApplePaySession::completeShippingMethodSelection(unsigned short status, ApplePayLineItem&& newTotal, Vector&& newLineItems) +{ + if (!canCompleteShippingMethodSelection()) + return Exception { INVALID_ACCESS_ERR }; + + auto authorizationStatus = toPaymentAuthorizationStatus(status); + if (!authorizationStatus) + return Exception { INVALID_ACCESS_ERR }; + + auto convertedNewTotal = convertAndValidateTotal(WTFMove(newTotal)); + if (convertedNewTotal.hasException()) + return convertedNewTotal.releaseException(); + + PaymentRequest::TotalAndLineItems totalAndLineItems; + totalAndLineItems.total = convertedNewTotal.releaseReturnValue(); + + // FIXME: Merge this validation into the validation we are doing above. + auto validatedTotal = PaymentRequestValidator::validateTotal(totalAndLineItems.total); + if (validatedTotal.hasException()) + return validatedTotal.releaseException(); + + auto convertedNewLineItems = convertAndValidate(WTFMove(newLineItems)); + if (convertedNewLineItems.hasException()) + return convertedNewLineItems.releaseException(); + + totalAndLineItems.lineItems = convertedNewLineItems.releaseReturnValue(); + + m_state = State::Active; + paymentCoordinator().completeShippingMethodSelection(*authorizationStatus, totalAndLineItems); + + return { }; +} + +ExceptionOr ApplePaySession::completeShippingContactSelection(unsigned short status, Vector&& newShippingMethods, ApplePayLineItem&& newTotal, Vector&& newLineItems) +{ + if (!canCompleteShippingContactSelection()) + return Exception { INVALID_ACCESS_ERR }; + + auto authorizationStatus = toPaymentAuthorizationStatus(status); + if (!authorizationStatus) + return Exception { INVALID_ACCESS_ERR }; + + auto convertedNewShippingMethods = convertAndValidate(WTFMove(newShippingMethods)); + if (convertedNewShippingMethods.hasException()) + return convertedNewShippingMethods.releaseException(); + + auto convertedNewTotal = convertAndValidateTotal(WTFMove(newTotal)); + if (convertedNewTotal.hasException()) + return convertedNewTotal.releaseException(); + + PaymentRequest::TotalAndLineItems totalAndLineItems; + totalAndLineItems.total = convertedNewTotal.releaseReturnValue(); + + // FIXME: Merge this validation into the validation we are doing above. + auto validatedTotal = PaymentRequestValidator::validateTotal(totalAndLineItems.total); + if (validatedTotal.hasException()) + return validatedTotal.releaseException(); + + auto convertedNewLineItems = convertAndValidate(WTFMove(newLineItems)); + if (convertedNewLineItems.hasException()) + return convertedNewLineItems.releaseException(); + + totalAndLineItems.lineItems = convertedNewLineItems.releaseReturnValue(); + + m_state = State::Active; + paymentCoordinator().completeShippingContactSelection(*authorizationStatus, convertedNewShippingMethods.releaseReturnValue(), totalAndLineItems); + + return { }; +} + +ExceptionOr ApplePaySession::completePaymentMethodSelection(ApplePayLineItem&& newTotal, Vector&& newLineItems) +{ + if (!canCompletePaymentMethodSelection()) + return Exception { INVALID_ACCESS_ERR }; + + auto convertedNewTotal = convertAndValidateTotal(WTFMove(newTotal)); + if (convertedNewTotal.hasException()) + return convertedNewTotal.releaseException(); + + PaymentRequest::TotalAndLineItems totalAndLineItems; + totalAndLineItems.total = convertedNewTotal.releaseReturnValue(); + + // FIXME: Merge this validation into the validation we are doing above. + auto validatedTotal = PaymentRequestValidator::validateTotal(totalAndLineItems.total); + if (validatedTotal.hasException()) + return validatedTotal.releaseException(); + + auto convertedNewLineItems = convertAndValidate(WTFMove(newLineItems)); + if (convertedNewLineItems.hasException()) + return convertedNewLineItems.releaseException(); + + totalAndLineItems.lineItems = convertedNewLineItems.releaseReturnValue(); + + m_state = State::Active; + paymentCoordinator().completePaymentMethodSelection(totalAndLineItems); + + return { }; +} + +ExceptionOr ApplePaySession::completePayment(unsigned short status) +{ + if (!canCompletePayment()) + return Exception { INVALID_ACCESS_ERR }; + + auto authorizationStatus = toPaymentAuthorizationStatus(status); + if (!authorizationStatus) + return Exception { INVALID_ACCESS_ERR }; + + paymentCoordinator().completePaymentSession(*authorizationStatus); + + if (!isFinalStateStatus(*authorizationStatus)) { + m_state = State::Active; + return { }; + } + + m_state = State::Completed; + unsetPendingActivity(this); + return { }; +} + +void ApplePaySession::validateMerchant(const URL& validationURL) +{ + if (m_state == State::Aborted) { + // ApplePaySession::abort has been called. + return; + } + + ASSERT(m_merchantValidationState == MerchantValidationState::Idle); + ASSERT(m_state == State::Active); + + if (validationURL.isNull()) { + // Something went wrong when getting the validation URL. + // FIXME: Maybe we should send an error event here instead? + return; + } + + m_merchantValidationState = MerchantValidationState::ValidatingMerchant; + + auto event = ApplePayValidateMerchantEvent::create(eventNames().validatemerchantEvent, validationURL); + dispatchEvent(event.get()); +} + +void ApplePaySession::didAuthorizePayment(const Payment& payment) +{ + ASSERT(m_state == State::Active); + + m_state = State::Authorized; + + auto event = ApplePayPaymentAuthorizedEvent::create(eventNames().paymentauthorizedEvent, payment); + dispatchEvent(event.get()); +} + +void ApplePaySession::didSelectShippingMethod(const PaymentRequest::ShippingMethod& shippingMethod) +{ + ASSERT(m_state == State::Active); + + if (!hasEventListeners(eventNames().shippingmethodselectedEvent)) { + paymentCoordinator().completeShippingMethodSelection(PaymentAuthorizationStatus::Success, { }); + return; + } + + m_state = State::ShippingMethodSelected; + auto event = ApplePayShippingMethodSelectedEvent::create(eventNames().shippingmethodselectedEvent, shippingMethod); + dispatchEvent(event.get()); +} + +void ApplePaySession::didSelectShippingContact(const PaymentContact& shippingContact) +{ + ASSERT(m_state == State::Active); + + if (!hasEventListeners(eventNames().shippingcontactselectedEvent)) { + paymentCoordinator().completeShippingContactSelection(PaymentAuthorizationStatus::Success, { }, { }); + return; + } + + m_state = State::ShippingContactSelected; + auto event = ApplePayShippingContactSelectedEvent::create(eventNames().shippingcontactselectedEvent, shippingContact); + dispatchEvent(event.get()); +} + +void ApplePaySession::didSelectPaymentMethod(const PaymentMethod& paymentMethod) +{ + ASSERT(m_state == State::Active); + + if (!hasEventListeners(eventNames().paymentmethodselectedEvent)) { + paymentCoordinator().completePaymentMethodSelection({ }); + return; + } + + m_state = State::PaymentMethodSelected; + auto event = ApplePayPaymentMethodSelectedEvent::create(eventNames().paymentmethodselectedEvent, paymentMethod); + dispatchEvent(event.get()); +} + +void ApplePaySession::didCancelPayment() +{ + ASSERT(canCancel()); + + m_state = State::Canceled; + + auto event = Event::create(eventNames().cancelEvent, false, false); + dispatchEvent(event.get()); + + didReachFinalState(); +} + +const char* ApplePaySession::activeDOMObjectName() const +{ + return "ApplePaySession"; +} + +bool ApplePaySession::canSuspendForDocumentSuspension() const +{ + switch (m_state) { + case State::Idle: + case State::Aborted: + case State::Completed: + case State::Canceled: + return true; + + case State::Active: + case State::Authorized: + case State::ShippingMethodSelected: + case State::ShippingContactSelected: + case State::PaymentMethodSelected: + return false; + } +} + +void ApplePaySession::stop() +{ + if (!canAbort()) + return; + + m_state = State::Aborted; + paymentCoordinator().abortPaymentSession(); + + didReachFinalState(); +} + +PaymentCoordinator& ApplePaySession::paymentCoordinator() const +{ + return downcast(*scriptExecutionContext()).frame()->mainFrame().paymentCoordinator(); +} + +bool ApplePaySession::canBegin() const +{ + switch (m_state) { + case State::Idle: + return true; + + case State::Active: + case State::Aborted: + case State::Authorized: + case State::Completed: + case State::Canceled: + case State::ShippingMethodSelected: + case State::ShippingContactSelected: + case State::PaymentMethodSelected: + return false; + } +} + +bool ApplePaySession::canAbort() const +{ + switch (m_state) { + case State::Idle: + case State::Aborted: + case State::Completed: + case State::Canceled: + return false; + + case State::Active: + case State::Authorized: + case State::ShippingMethodSelected: + case State::ShippingContactSelected: + case State::PaymentMethodSelected: + return true; + } +} + +bool ApplePaySession::canCancel() const +{ + switch (m_state) { + case State::Idle: + case State::Aborted: + case State::Completed: + case State::Canceled: + return false; + + case State::Active: + case State::Authorized: + case State::ShippingMethodSelected: + case State::ShippingContactSelected: + case State::PaymentMethodSelected: + return true; + } +} + +bool ApplePaySession::canCompleteMerchantValidation() const +{ + if (m_state != State::Active) + return false; + + if (m_merchantValidationState != MerchantValidationState::ValidatingMerchant) + return false; + + return true; +} + +bool ApplePaySession::canCompleteShippingMethodSelection() const +{ + switch (m_state) { + case State::Idle: + case State::Aborted: + case State::Active: + case State::Completed: + case State::Canceled: + case State::Authorized: + case State::PaymentMethodSelected: + case State::ShippingContactSelected: + return false; + + case State::ShippingMethodSelected: + return true; + } +} + +bool ApplePaySession::canCompleteShippingContactSelection() const +{ + switch (m_state) { + case State::Idle: + case State::Aborted: + case State::Active: + case State::Completed: + case State::Canceled: + case State::Authorized: + case State::PaymentMethodSelected: + case State::ShippingMethodSelected: + return false; + + case State::ShippingContactSelected: + return true; + } +} + +bool ApplePaySession::canCompletePaymentMethodSelection() const +{ + switch (m_state) { + case State::Idle: + case State::Aborted: + case State::Active: + case State::Completed: + case State::Canceled: + case State::Authorized: + case State::ShippingMethodSelected: + case State::ShippingContactSelected: + return false; + + case State::PaymentMethodSelected: + return true; + } +} + +bool ApplePaySession::canCompletePayment() const +{ + switch (m_state) { + case State::Idle: + case State::Aborted: + case State::Active: + case State::Completed: + case State::Canceled: + case State::ShippingMethodSelected: + case State::ShippingContactSelected: + case State::PaymentMethodSelected: + return false; + + case State::Authorized: + return true; + } +} + +bool ApplePaySession::isFinalState() const +{ + switch (m_state) { + case State::Idle: + case State::Active: + case State::ShippingMethodSelected: + case State::ShippingContactSelected: + case State::PaymentMethodSelected: + case State::Authorized: + return false; + + case State::Completed: + case State::Aborted: + case State::Canceled: + return true; + } +} + +void ApplePaySession::didReachFinalState() +{ + ASSERT(isFinalState()); + unsetPendingActivity(this); +} + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePaySession.h b/Source/WebCore/Modules/applepay/ApplePaySession.h new file mode 100644 index 000000000..ef759ded0 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePaySession.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "ActiveDOMObject.h" +#include "ApplePayPaymentRequest.h" +#include "EventTarget.h" +#include "ExceptionOr.h" +#include +#include + +namespace JSC { +class ExecState; +class JSValue; +} + +namespace WebCore { + +class DeferredPromise; +class Document; +class Payment; +class PaymentContact; +class PaymentCoordinator; +class PaymentMethod; +class URL; + +struct ApplePayLineItem; +struct ApplePayPaymentRequest; +struct ApplePayShippingMethod; + +class ApplePaySession final : public RefCounted, public ActiveDOMObject, public EventTargetWithInlineData { +public: + static ExceptionOr> create(Document&, unsigned version, ApplePayPaymentRequest&&); + virtual ~ApplePaySession(); + + static const unsigned short STATUS_SUCCESS = 0; + static const unsigned short STATUS_FAILURE = 1; + static const unsigned short STATUS_INVALID_BILLING_POSTAL_ADDRESS = 2; + static const unsigned short STATUS_INVALID_SHIPPING_POSTAL_ADDRESS = 3; + static const unsigned short STATUS_INVALID_SHIPPING_CONTACT = 4; + static const unsigned short STATUS_PIN_REQUIRED = 5; + static const unsigned short STATUS_PIN_INCORRECT = 6; + static const unsigned short STATUS_PIN_LOCKOUT = 7; + + static ExceptionOr supportsVersion(ScriptExecutionContext&, unsigned version); + static ExceptionOr canMakePayments(ScriptExecutionContext&); + static ExceptionOr canMakePaymentsWithActiveCard(ScriptExecutionContext&, const String& merchantIdentifier, Ref&&); + static ExceptionOr openPaymentSetup(ScriptExecutionContext&, const String& merchantIdentifier, Ref&&); + + ExceptionOr begin(); + ExceptionOr abort(); + ExceptionOr completeMerchantValidation(JSC::ExecState&, JSC::JSValue merchantSession); + ExceptionOr completeShippingMethodSelection(unsigned short status, ApplePayLineItem&& newTotal, Vector&& newLineItems); + ExceptionOr completeShippingContactSelection(unsigned short status, Vector&& newShippingMethods, ApplePayLineItem&& newTotal, Vector&& newLineItems); + ExceptionOr completePaymentMethodSelection(ApplePayLineItem&& newTotal, Vector&& newLineItems); + ExceptionOr completePayment(unsigned short status); + + const PaymentRequest& paymentRequest() const { return m_paymentRequest; } + + void validateMerchant(const URL&); + void didAuthorizePayment(const Payment&); + void didSelectShippingMethod(const PaymentRequest::ShippingMethod&); + void didSelectShippingContact(const PaymentContact&); + void didSelectPaymentMethod(const PaymentMethod&); + void didCancelPayment(); + + using RefCounted::ref; + using RefCounted::deref; + +private: + ApplePaySession(Document&, PaymentRequest&&); + + // ActiveDOMObject. + const char* activeDOMObjectName() const override; + bool canSuspendForDocumentSuspension() const override; + void stop() override; + + // EventTargetWithInlineData. + EventTargetInterface eventTargetInterface() const override { return ApplePaySessionEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const override { return ActiveDOMObject::scriptExecutionContext(); } + void refEventTarget() override { ref(); } + void derefEventTarget() override { deref(); } + + PaymentCoordinator& paymentCoordinator() const; + + bool canBegin() const; + bool canAbort() const; + bool canCancel() const; + bool canCompleteMerchantValidation() const; + bool canCompleteShippingMethodSelection() const; + bool canCompleteShippingContactSelection() const; + bool canCompletePaymentMethodSelection() const; + bool canCompletePayment() const; + + bool isFinalState() const; + void didReachFinalState(); + + enum class State { + Idle, + + Active, + ShippingMethodSelected, + ShippingContactSelected, + PaymentMethodSelected, + Authorized, + Completed, + + Aborted, + Canceled, + } m_state { State::Idle }; + + enum class MerchantValidationState { + Idle, + ValidatingMerchant, + ValidationComplete, + } m_merchantValidationState { MerchantValidationState::Idle }; + + const PaymentRequest m_paymentRequest; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePaySession.idl b/Source/WebCore/Modules/applepay/ApplePaySession.idl new file mode 100644 index 000000000..802f3c3f0 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePaySession.idl @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + ActiveDOMObject, + Conditional=APPLE_PAY, + Constructor(unsigned long version, ApplePayPaymentRequest paymentRequest), + ConstructorCallWith=Document, + ConstructorMayThrowException, + EnabledBySetting=ApplePay, +] interface ApplePaySession : EventTarget { + const unsigned short STATUS_SUCCESS = 0; + const unsigned short STATUS_FAILURE = 1; + const unsigned short STATUS_INVALID_BILLING_POSTAL_ADDRESS = 2; + const unsigned short STATUS_INVALID_SHIPPING_POSTAL_ADDRESS = 3; + const unsigned short STATUS_INVALID_SHIPPING_CONTACT = 4; + const unsigned short STATUS_PIN_REQUIRED = 5; + const unsigned short STATUS_PIN_INCORRECT = 6; + const unsigned short STATUS_PIN_LOCKOUT = 7; + + [CallWith=ScriptExecutionContext, MayThrowException] static boolean supportsVersion(unsigned long version); + [CallWith=ScriptExecutionContext, MayThrowException] static boolean canMakePayments(); + [CallWith=ScriptExecutionContext, MayThrowException] static Promise canMakePaymentsWithActiveCard(DOMString merchantIdentifier); + [CallWith=ScriptExecutionContext, MayThrowException] static Promise openPaymentSetup(DOMString merchantIdentifier); + + [MayThrowException] void begin(); + [MayThrowException] void abort(); + [MayThrowException, CallWith=ScriptState] void completeMerchantValidation(any merchantSession); + [MayThrowException] void completeShippingMethodSelection(unsigned short status, ApplePayLineItem newTotal, sequence newLineItems); + [MayThrowException] void completeShippingContactSelection(unsigned short status, sequence newShippingMethods, ApplePayLineItem newTotal, sequence newLineItems); + [MayThrowException] void completePaymentMethodSelection(ApplePayLineItem newTotal, sequence newLineItems); + [MayThrowException] void completePayment(unsigned short status); + + attribute EventHandler onvalidatemerchant; + attribute EventHandler onpaymentmethodselected; + attribute EventHandler onpaymentauthorized; + attribute EventHandler onshippingmethodselected; + attribute EventHandler onshippingcontactselected; + attribute EventHandler oncancel; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.cpp b/Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.cpp new file mode 100644 index 000000000..41a8447c4 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ApplePayShippingContactSelectedEvent.h" + +#if ENABLE(APPLE_PAY) + +#include "PaymentContact.h" + +namespace WebCore { + +ApplePayShippingContactSelectedEvent::ApplePayShippingContactSelectedEvent(const AtomicString& type, const PaymentContact& shippingContact) + : Event(type, false, false) + , m_shippingContact(shippingContact.toApplePayPaymentContact()) +{ +} + +ApplePayShippingContactSelectedEvent::~ApplePayShippingContactSelectedEvent() +{ +} + +EventInterface ApplePayShippingContactSelectedEvent::eventInterface() const +{ + return ApplePayShippingContactSelectedEventInterfaceType; +} + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.h b/Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.h new file mode 100644 index 000000000..2e7e6f7ef --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "ApplePayPaymentContact.h" +#include "Event.h" + +namespace WebCore { + +class PaymentContact; + +class ApplePayShippingContactSelectedEvent final : public Event { +public: + static Ref create(const AtomicString& type, const PaymentContact& shippingContact) + { + return adoptRef(*new ApplePayShippingContactSelectedEvent(type, shippingContact)); + } + + virtual ~ApplePayShippingContactSelectedEvent(); + + const ApplePayPaymentContact& shippingContact() const { return m_shippingContact; } + +private: + ApplePayShippingContactSelectedEvent(const AtomicString& type, const PaymentContact&); + + // Event. + EventInterface eventInterface() const override; + + const ApplePayPaymentContact m_shippingContact; +}; + +} +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.idl b/Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.idl new file mode 100644 index 000000000..7ff4dfd21 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, + NoInterfaceObject, +] interface ApplePayShippingContactSelectedEvent : Event { + [CachedAttribute] readonly attribute ApplePayPaymentContact shippingContact; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayShippingMethod.h b/Source/WebCore/Modules/applepay/ApplePayShippingMethod.h new file mode 100644 index 000000000..593521af4 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayShippingMethod.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include + +namespace WebCore { + +struct ApplePayShippingMethod { + String label; + String detail; + String amount; + String identifier; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayShippingMethod.idl b/Source/WebCore/Modules/applepay/ApplePayShippingMethod.idl new file mode 100644 index 000000000..36f3af754 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayShippingMethod.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, + JSGenerateToJSObject +] dictionary ApplePayShippingMethod { + required DOMString label; + required DOMString detail; + required DOMString amount; + required DOMString identifier; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.cpp b/Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.cpp new file mode 100644 index 000000000..6c4fff9f3 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ApplePayShippingMethodSelectedEvent.h" + +#if ENABLE(APPLE_PAY) + +#include + +namespace WebCore { + +static inline String convert(int64_t amount) +{ + StringBuilder amountString; + amountString.appendNumber(amount / 100); + amountString.append('.'); + + unsigned decimals = amount % 100; + if (decimals < 10) + amountString.append('0'); + amountString.appendNumber(decimals); + + return amountString.toString(); +} + +static inline ApplePayShippingMethod convert(const PaymentRequest::ShippingMethod& shippingMethod) +{ + ApplePayShippingMethod convertedMethod; + convertedMethod.label = shippingMethod.label; + convertedMethod.detail = shippingMethod.detail; + convertedMethod.identifier = shippingMethod.identifier; + convertedMethod.amount = convert(shippingMethod.amount); + + return convertedMethod; +} + +ApplePayShippingMethodSelectedEvent::ApplePayShippingMethodSelectedEvent(const AtomicString& type, const PaymentRequest::ShippingMethod& shippingMethod) + : Event(type, false, false) + , m_shippingMethod(convert(shippingMethod)) +{ +} + +ApplePayShippingMethodSelectedEvent::~ApplePayShippingMethodSelectedEvent() +{ +} + +EventInterface ApplePayShippingMethodSelectedEvent::eventInterface() const +{ + return ApplePayShippingMethodSelectedEventInterfaceType; +} + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.h b/Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.h new file mode 100644 index 000000000..0693487de --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "ApplePayShippingMethod.h" +#include "Event.h" +#include "PaymentRequest.h" + +namespace WebCore { + +class ApplePayShippingMethodSelectedEvent final : public Event { +public: + static Ref create(const AtomicString& type, const PaymentRequest::ShippingMethod& shippingMethod) + { + return adoptRef(*new ApplePayShippingMethodSelectedEvent(type, shippingMethod)); + } + + virtual ~ApplePayShippingMethodSelectedEvent(); + + const ApplePayShippingMethod& shippingMethod() const { return m_shippingMethod; } + +private: + ApplePayShippingMethodSelectedEvent(const AtomicString& type, const PaymentRequest::ShippingMethod&); + + // Event. + EventInterface eventInterface() const override; + + const ApplePayShippingMethod m_shippingMethod; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.idl b/Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.idl new file mode 100644 index 000000000..935a17927 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, + NoInterfaceObject, +] interface ApplePayShippingMethodSelectedEvent : Event { + [CachedAttribute] readonly attribute ApplePayShippingMethod shippingMethod; +}; diff --git a/Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.cpp b/Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.cpp new file mode 100644 index 000000000..c6c863748 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ApplePayValidateMerchantEvent.h" + +#if ENABLE(APPLE_PAY) + +namespace WebCore { + +ApplePayValidateMerchantEvent::ApplePayValidateMerchantEvent(const AtomicString& type, const URL& validationURL) + : Event(type, false, false) + , m_validationURL(validationURL) +{ +} + +ApplePayValidateMerchantEvent::~ApplePayValidateMerchantEvent() +{ +} + +EventInterface ApplePayValidateMerchantEvent::eventInterface() const +{ + return ApplePayValidateMerchantEventInterfaceType; +} + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.h b/Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.h new file mode 100644 index 000000000..451559831 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "Event.h" +#include "URL.h" + +namespace WebCore { + +class ApplePayValidateMerchantEvent final : public Event { +public: + static Ref create(const AtomicString& type, const URL& validationURL) + { + return adoptRef(*new ApplePayValidateMerchantEvent(type, validationURL)); + } + + virtual ~ApplePayValidateMerchantEvent(); + + const String& validationURL() const { return m_validationURL.string(); } + +private: + ApplePayValidateMerchantEvent(const AtomicString& type, const URL& validationURL); + + // Event. + EventInterface eventInterface() const override; + + const URL m_validationURL; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.idl b/Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.idl new file mode 100644 index 000000000..3b2a85bf1 --- /dev/null +++ b/Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=APPLE_PAY, + NoInterfaceObject, +] interface ApplePayValidateMerchantEvent : Event { + readonly attribute DOMString validationURL; +}; diff --git a/Source/WebCore/Modules/applepay/Payment.h b/Source/WebCore/Modules/applepay/Payment.h new file mode 100644 index 000000000..f8b7da25e --- /dev/null +++ b/Source/WebCore/Modules/applepay/Payment.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include + +OBJC_CLASS PKPayment; + +namespace WebCore { + +struct ApplePayPayment; + +class Payment { +public: + Payment() + { + } + + explicit Payment(PKPayment *pkPayment) + : m_pkPayment(pkPayment) + { + } + + ~Payment() + { + } + + ApplePayPayment toApplePayPayment() const; + + PKPayment *pkPayment() const { return m_pkPayment.get(); } + +private: + RetainPtr m_pkPayment; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/PaymentAuthorizationStatus.h b/Source/WebCore/Modules/applepay/PaymentAuthorizationStatus.h new file mode 100644 index 000000000..97c77842a --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentAuthorizationStatus.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +namespace WebCore { + +enum class PaymentAuthorizationStatus { + Success, + Failure, + InvalidBillingPostalAddress, + InvalidShippingPostalAddress, + InvalidShippingContact, + PINRequired, + PINIncorrect, + PINLockout, +}; + +static inline bool isFinalStateStatus(PaymentAuthorizationStatus status) +{ + switch (status) { + case PaymentAuthorizationStatus::Success: + case PaymentAuthorizationStatus::Failure: + return true; + + case PaymentAuthorizationStatus::InvalidBillingPostalAddress: + case PaymentAuthorizationStatus::InvalidShippingPostalAddress: + case PaymentAuthorizationStatus::InvalidShippingContact: + case PaymentAuthorizationStatus::PINRequired: + case PaymentAuthorizationStatus::PINIncorrect: + case PaymentAuthorizationStatus::PINLockout: + return false; + } +} + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/PaymentContact.h b/Source/WebCore/Modules/applepay/PaymentContact.h new file mode 100644 index 000000000..a8e3d0140 --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentContact.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include +#include + +OBJC_CLASS PKContact; + +namespace WebCore { + +struct ApplePayPaymentContact; + +class PaymentContact { +public: + PaymentContact() = default; + explicit PaymentContact(PKContact *pkContact) + : m_pkContact(pkContact) + { + } + + static PaymentContact fromApplePayPaymentContact(const ApplePayPaymentContact&); + ApplePayPaymentContact toApplePayPaymentContact() const; + + PKContact *pkContact() const { return m_pkContact.get(); } + +private: + RetainPtr m_pkContact; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/PaymentCoordinator.cpp b/Source/WebCore/Modules/applepay/PaymentCoordinator.cpp new file mode 100644 index 000000000..154a6d05c --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentCoordinator.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PaymentCoordinator.h" + +#if ENABLE(APPLE_PAY) + +#include "ApplePaySession.h" +#include "PaymentAuthorizationStatus.h" +#include "PaymentCoordinatorClient.h" +#include "URL.h" + +namespace WebCore { + +PaymentCoordinator::PaymentCoordinator(PaymentCoordinatorClient& client) + : m_client(client) +{ +} + +PaymentCoordinator::~PaymentCoordinator() +{ + m_client.paymentCoordinatorDestroyed(); +} + +bool PaymentCoordinator::supportsVersion(unsigned version) +{ + return m_client.supportsVersion(version); +} + +bool PaymentCoordinator::canMakePayments() +{ + return m_client.canMakePayments(); +} + +void PaymentCoordinator::canMakePaymentsWithActiveCard(const String& merchantIdentifier, const String& domainName, std::function completionHandler) +{ + m_client.canMakePaymentsWithActiveCard(merchantIdentifier, domainName, WTFMove(completionHandler)); +} + +void PaymentCoordinator::openPaymentSetup(const String& merchantIdentifier, const String& domainName, std::function completionHandler) +{ + m_client.openPaymentSetup(merchantIdentifier, domainName, WTFMove(completionHandler)); +} + +bool PaymentCoordinator::beginPaymentSession(ApplePaySession& paymentSession, const URL& originatingURL, const Vector& linkIconURLs, const PaymentRequest& paymentRequest) +{ + ASSERT(!m_activeSession); + + if (!m_client.showPaymentUI(originatingURL, linkIconURLs, paymentRequest)) + return false; + + m_activeSession = &paymentSession; + return true; +} + +void PaymentCoordinator::completeMerchantValidation(const PaymentMerchantSession& paymentMerchantSession) +{ + ASSERT(m_activeSession); + + m_client.completeMerchantValidation(paymentMerchantSession); +} + +void PaymentCoordinator::completeShippingMethodSelection(PaymentAuthorizationStatus status, std::optional newTotalAndItems) +{ + ASSERT(m_activeSession); + + m_client.completeShippingMethodSelection(status, WTFMove(newTotalAndItems)); +} + +void PaymentCoordinator::completeShippingContactSelection(PaymentAuthorizationStatus status, const Vector& newShippingMethods, std::optional newTotalAndItems) +{ + ASSERT(m_activeSession); + + m_client.completeShippingContactSelection(status, newShippingMethods, WTFMove(newTotalAndItems)); +} + +void PaymentCoordinator::completePaymentMethodSelection(std::optional newTotalAndItems) +{ + ASSERT(m_activeSession); + + m_client.completePaymentMethodSelection(WTFMove(newTotalAndItems)); +} + +void PaymentCoordinator::completePaymentSession(PaymentAuthorizationStatus status) +{ + ASSERT(m_activeSession); + + m_client.completePaymentSession(status); + + if (!isFinalStateStatus(status)) + return; + + m_activeSession = nullptr; +} + +void PaymentCoordinator::abortPaymentSession() +{ + ASSERT(m_activeSession); + + m_client.abortPaymentSession(); + m_activeSession = nullptr; +} + +void PaymentCoordinator::validateMerchant(const URL& validationURL) +{ + if (!m_activeSession) { + // It's possible that the payment has been aborted already. + return; + } + + m_activeSession->validateMerchant(validationURL); +} + +void PaymentCoordinator::didAuthorizePayment(const Payment& payment) +{ + if (!m_activeSession) { + // It's possible that the payment has been aborted already. + return; + } + + m_activeSession->didAuthorizePayment(payment); +} + +void PaymentCoordinator::didSelectPaymentMethod(const PaymentMethod& paymentMethod) +{ + if (!m_activeSession) { + // It's possible that the payment has been aborted already. + return; + } + + m_activeSession->didSelectPaymentMethod(paymentMethod); +} + +void PaymentCoordinator::didSelectShippingMethod(const PaymentRequest::ShippingMethod& shippingMethod) +{ + if (!m_activeSession) { + // It's possible that the payment has been aborted already. + return; + } + + m_activeSession->didSelectShippingMethod(shippingMethod); +} + +void PaymentCoordinator::didSelectShippingContact(const PaymentContact& shippingContact) +{ + if (!m_activeSession) { + // It's possible that the payment has been aborted already. + return; + } + + m_activeSession->didSelectShippingContact(shippingContact); +} + +void PaymentCoordinator::didCancelPayment() +{ + if (!m_activeSession) { + // It's possible that the payment has been aborted already. + return; + } + + m_activeSession->didCancelPayment(); + m_activeSession = nullptr; +} + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/PaymentCoordinator.h b/Source/WebCore/Modules/applepay/PaymentCoordinator.h new file mode 100644 index 000000000..500ae16d1 --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentCoordinator.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "PaymentRequest.h" +#include + +namespace WebCore { + +class ApplePaySession; +class Payment; +class PaymentCoordinatorClient; +class PaymentContact; +class PaymentMerchantSession; +class PaymentMethod; +class URL; +enum class PaymentAuthorizationStatus; + +class PaymentCoordinator { +public: + explicit PaymentCoordinator(PaymentCoordinatorClient&); + ~PaymentCoordinator(); + + bool supportsVersion(unsigned version); + bool canMakePayments(); + void canMakePaymentsWithActiveCard(const String& merchantIdentifier, const String& domainName, std::function completionHandler); + void openPaymentSetup(const String& merchantIdentifier, const String& domainName, std::function completionHandler); + + bool hasActiveSession() const { return m_activeSession; } + + bool beginPaymentSession(ApplePaySession&, const URL& originatingURL, const Vector& linkIconURLs, const PaymentRequest&); + void completeMerchantValidation(const PaymentMerchantSession&); + void completeShippingMethodSelection(PaymentAuthorizationStatus, std::optional newItems); + void completeShippingContactSelection(PaymentAuthorizationStatus, const Vector& newShippingMethods, std::optional newItems); + void completePaymentMethodSelection(std::optional newItems); + void completePaymentSession(PaymentAuthorizationStatus); + void abortPaymentSession(); + + WEBCORE_EXPORT void validateMerchant(const URL& validationURL); + WEBCORE_EXPORT void didAuthorizePayment(const Payment&); + WEBCORE_EXPORT void didSelectPaymentMethod(const PaymentMethod&); + WEBCORE_EXPORT void didSelectShippingMethod(const PaymentRequest::ShippingMethod&); + WEBCORE_EXPORT void didSelectShippingContact(const PaymentContact&); + WEBCORE_EXPORT void didCancelPayment(); + +private: + PaymentCoordinatorClient& m_client; + + RefPtr m_activeSession; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/PaymentCoordinatorClient.h b/Source/WebCore/Modules/applepay/PaymentCoordinatorClient.h new file mode 100644 index 000000000..3d6ecc073 --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentCoordinatorClient.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "PaymentRequest.h" +#include +#include + +namespace WebCore { + +class PaymentMerchantSession; +class URL; +enum class PaymentAuthorizationStatus; + +class PaymentCoordinatorClient { +public: + virtual bool supportsVersion(unsigned version) = 0; + virtual bool canMakePayments() = 0; + virtual void canMakePaymentsWithActiveCard(const String& merchantIdentifier, const String& domainName, std::function completionHandler) = 0; + virtual void openPaymentSetup(const String& merchantIdentifier, const String& domainName, std::function completionHandler) = 0; + + virtual bool showPaymentUI(const URL& originatingURL, const Vector& linkIconURLs, const PaymentRequest&) = 0; + virtual void completeMerchantValidation(const PaymentMerchantSession&) = 0; + virtual void completeShippingMethodSelection(PaymentAuthorizationStatus, std::optional newTotalAndItems) = 0; + virtual void completeShippingContactSelection(PaymentAuthorizationStatus, const Vector& newShippingMethods, std::optional newTotalAndItems) = 0; + virtual void completePaymentMethodSelection(std::optional newTotalAndItems) = 0; + virtual void completePaymentSession(PaymentAuthorizationStatus) = 0; + virtual void abortPaymentSession() = 0; + virtual void paymentCoordinatorDestroyed() = 0; + +protected: + virtual ~PaymentCoordinatorClient() { } +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/PaymentHeaders.h b/Source/WebCore/Modules/applepay/PaymentHeaders.h new file mode 100644 index 000000000..99bcb8bc2 --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentHeaders.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Payment.h" +#include "PaymentContact.h" +#include "PaymentMerchantSession.h" +#include "PaymentMethod.h" +#include "PaymentRequest.h" + diff --git a/Source/WebCore/Modules/applepay/PaymentMerchantSession.h b/Source/WebCore/Modules/applepay/PaymentMerchantSession.h new file mode 100644 index 000000000..a5c6dddce --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentMerchantSession.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include +#include + +namespace JSC { +class ExecState; +class JSValue; +} + +OBJC_CLASS PKPaymentMerchantSession; + +namespace WebCore { + +class PaymentMerchantSession { +public: + PaymentMerchantSession() + { + } + + explicit PaymentMerchantSession(PKPaymentMerchantSession *pkPaymentMerchantSession) + : m_pkPaymentMerchantSession(pkPaymentMerchantSession) + { + } + + ~PaymentMerchantSession() + { + } + + static std::optional fromJS(JSC::ExecState&, JSC::JSValue, String& errorMessage); + + PKPaymentMerchantSession *pkPaymentMerchantSession() const { return m_pkPaymentMerchantSession.get(); } + +private: + RetainPtr m_pkPaymentMerchantSession; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/PaymentMethod.h b/Source/WebCore/Modules/applepay/PaymentMethod.h new file mode 100644 index 000000000..69a3770c7 --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentMethod.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include + +OBJC_CLASS PKPaymentMethod; + +namespace WebCore { + +struct ApplePayPaymentMethod; + +class PaymentMethod { +public: + PaymentMethod() + { + } + + explicit PaymentMethod(PKPaymentMethod *pkPaymentMethod) + : m_pkPaymentMethod(pkPaymentMethod) + { + } + + ApplePayPaymentMethod toApplePayPaymentMethod() const; + + PKPaymentMethod *pkPaymentMethod() const { return m_pkPaymentMethod.get(); } + +private: + RetainPtr m_pkPaymentMethod; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/PaymentRequest.cpp b/Source/WebCore/Modules/applepay/PaymentRequest.cpp new file mode 100644 index 000000000..06e5244ae --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentRequest.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PaymentRequest.h" + +#if ENABLE(APPLE_PAY) + +#include "SoftLinking.h" + +namespace WebCore { + +PaymentRequest::PaymentRequest() +{ +} + +PaymentRequest::~PaymentRequest() +{ +} + +#if USE(APPLE_INTERNAL_SDK) && __has_include() +#include +#else +static inline bool isAdditionalValidSupportedNetwork(unsigned, const String&) +{ + return false; +} +#endif + +bool PaymentRequest::isValidSupportedNetwork(unsigned version, const String& supportedNetwork) +{ + if (supportedNetwork == "amex") + return true; + if (supportedNetwork == "chinaUnionPay") + return true; + if (supportedNetwork == "discover") + return true; + if (supportedNetwork == "interac") + return true; + if (supportedNetwork == "masterCard") + return true; + if (supportedNetwork == "privateLabel") + return true; + if (supportedNetwork == "visa") + return true; + + return isAdditionalValidSupportedNetwork(version, supportedNetwork); +} + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/PaymentRequest.h b/Source/WebCore/Modules/applepay/PaymentRequest.h new file mode 100644 index 000000000..ec3ee1faf --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentRequest.h @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "PaymentContact.h" +#include +#include +#include + +namespace WebCore { + +class PaymentRequest { +public: + WEBCORE_EXPORT PaymentRequest(); + WEBCORE_EXPORT ~PaymentRequest(); + + const String& countryCode() const { return m_countryCode; } + void setCountryCode(const String& countryCode) { m_countryCode = countryCode; } + + const String& currencyCode() const { return m_currencyCode; } + void setCurrencyCode(const String& currencyCode) { m_currencyCode = currencyCode; } + + struct ContactFields { + bool postalAddress { false }; + bool phone { false }; + bool email { false }; + bool name { false }; + }; + + const ContactFields& requiredBillingContactFields() const { return m_requiredBillingContactFields; } + void setRequiredBillingContactFields(const ContactFields& requiredBillingContactFields) { m_requiredBillingContactFields = requiredBillingContactFields; } + + const PaymentContact& billingContact() const { return m_billingContact; } + void setBillingContact(const PaymentContact& billingContact) { m_billingContact = billingContact; } + + const ContactFields& requiredShippingContactFields() const { return m_requiredShippingContactFields; } + void setRequiredShippingContactFields(const ContactFields& requiredShippingContactFields) { m_requiredShippingContactFields = requiredShippingContactFields; } + + const PaymentContact& shippingContact() const { return m_shippingContact; } + void setShippingContact(const PaymentContact& shippingContact) { m_shippingContact = shippingContact; } + + static bool isValidSupportedNetwork(unsigned version, const String&); + + const Vector& supportedNetworks() const { return m_supportedNetworks; } + void setSupportedNetworks(const Vector& supportedNetworks) { m_supportedNetworks = supportedNetworks; } + + struct MerchantCapabilities { + bool supports3DS { false }; + bool supportsEMV { false }; + bool supportsCredit { false }; + bool supportsDebit { false }; + }; + + const MerchantCapabilities& merchantCapabilities() const { return m_merchantCapabilities; } + void setMerchantCapabilities(const MerchantCapabilities& merchantCapabilities) { m_merchantCapabilities = merchantCapabilities; } + + struct LineItem { + enum class Type { + Pending, + Final, + } type { Type::Final }; + + // Stored as a fixed point decimal number with two decimals: + // 1.23 -> 123. + // 0.01 -> 1. + std::optional amount; + String label; + }; + + enum class ShippingType { + Shipping, + Delivery, + StorePickup, + ServicePickup, + }; + ShippingType shippingType() const { return m_shippingType; } + void setShippingType(ShippingType shippingType) { m_shippingType = shippingType; } + + struct ShippingMethod { + String label; + String detail; + int64_t amount; + + String identifier; + }; + const Vector& shippingMethods() const { return m_shippingMethods; } + void setShippingMethods(const Vector& shippingMethods) { m_shippingMethods = shippingMethods; } + + const Vector& lineItems() const { return m_lineItems; } + void setLineItems(const Vector& lineItems) { m_lineItems = lineItems; } + + const LineItem& total() const { return m_total; }; + void setTotal(const LineItem& total) { m_total = total; } + + struct TotalAndLineItems { + PaymentRequest::LineItem total; + Vector lineItems; + }; + + const String& applicationData() const { return m_applicationData; } + void setApplicationData(const String& applicationData) { m_applicationData = applicationData; } + +private: + String m_countryCode; + String m_currencyCode; + + ContactFields m_requiredBillingContactFields; + PaymentContact m_billingContact; + + ContactFields m_requiredShippingContactFields; + PaymentContact m_shippingContact; + + Vector m_supportedNetworks; + MerchantCapabilities m_merchantCapabilities; + + ShippingType m_shippingType { ShippingType::Shipping }; + Vector m_shippingMethods; + + Vector m_lineItems; + LineItem m_total; + + String m_applicationData; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/PaymentRequestValidator.cpp b/Source/WebCore/Modules/applepay/PaymentRequestValidator.cpp new file mode 100644 index 000000000..9245841bf --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentRequestValidator.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PaymentRequestValidator.h" + +#if ENABLE(APPLE_PAY) + +#include "ExceptionCode.h" +#include "PaymentRequest.h" +#include +#include + +namespace WebCore { + +static ExceptionOr validateCountryCode(const String&); +static ExceptionOr validateCurrencyCode(const String&); +static ExceptionOr validateMerchantCapabilities(const PaymentRequest::MerchantCapabilities&); +static ExceptionOr validateSupportedNetworks(const Vector&); +static ExceptionOr validateShippingMethods(const Vector&); +static ExceptionOr validateShippingMethod(const PaymentRequest::ShippingMethod&); + + +ExceptionOr PaymentRequestValidator::validate(const PaymentRequest& paymentRequest) +{ + auto validatedCountryCode = validateCountryCode(paymentRequest.countryCode()); + if (validatedCountryCode.hasException()) + return validatedCountryCode.releaseException(); + + auto validatedCurrencyCode = validateCurrencyCode(paymentRequest.currencyCode()); + if (validatedCurrencyCode.hasException()) + return validatedCurrencyCode.releaseException(); + + auto validatedSupportedNetworks = validateSupportedNetworks(paymentRequest.supportedNetworks()); + if (validatedSupportedNetworks.hasException()) + return validatedSupportedNetworks.releaseException(); + + auto validatedMerchantCapabilities = validateMerchantCapabilities(paymentRequest.merchantCapabilities()); + if (validatedMerchantCapabilities.hasException()) + return validatedMerchantCapabilities.releaseException(); + + auto validatedTotal = validateTotal(paymentRequest.total()); + if (validatedTotal.hasException()) + return validatedTotal.releaseException(); + + auto validatedShippingMethods = validateShippingMethods(paymentRequest.shippingMethods()); + if (validatedShippingMethods.hasException()) + return validatedShippingMethods.releaseException(); + + return { }; +} + +ExceptionOr PaymentRequestValidator::validateTotal(const PaymentRequest::LineItem& total) +{ + if (!total.label) + return Exception { TypeError, "Missing total label." }; + + if (!total.amount) + return Exception { TypeError, "Missing total amount." }; + + if (*total.amount <= 0) + return Exception { TypeError, "Total amount must be greater than zero." }; + + if (*total.amount > 10000000000) + return Exception { TypeError, "Total amount is too big." }; + + return { }; +} + +static ExceptionOr validateCountryCode(const String& countryCode) +{ + if (!countryCode) + return Exception { TypeError, "Missing country code." }; + + for (auto *countryCodePtr = uloc_getISOCountries(); *countryCodePtr; ++countryCodePtr) { + if (countryCode == *countryCodePtr) + return { }; + } + + return Exception { TypeError, makeString("\"" + countryCode, "\" is not a valid country code.") }; +} + +static ExceptionOr validateCurrencyCode(const String& currencyCode) +{ + if (!currencyCode) + return Exception { TypeError, "Missing currency code." }; + + UErrorCode errorCode = U_ZERO_ERROR; + auto currencyCodes = std::unique_ptr(ucurr_openISOCurrencies(UCURR_ALL, &errorCode), uenum_close); + + int32_t length; + while (auto *currencyCodePtr = uenum_next(currencyCodes.get(), &length, &errorCode)) { + if (currencyCodePtr == currencyCode) + return { }; + } + + return Exception { TypeError, makeString("\"" + currencyCode, "\" is not a valid currency code.") }; +} + +static ExceptionOr validateMerchantCapabilities(const PaymentRequest::MerchantCapabilities& merchantCapabilities) +{ + if (!merchantCapabilities.supports3DS && !merchantCapabilities.supportsEMV && !merchantCapabilities.supportsCredit && !merchantCapabilities.supportsDebit) + return Exception { TypeError, "Missing merchant capabilities." }; + + return { }; +} + +static ExceptionOr validateSupportedNetworks(const Vector& supportedNetworks) +{ + if (supportedNetworks.isEmpty()) + return Exception { TypeError, "Missing supported networks." }; + + return { }; +} + +static ExceptionOr validateShippingMethod(const PaymentRequest::ShippingMethod& shippingMethod) +{ + if (shippingMethod.amount < 0) + return Exception { TypeError, "Shipping method amount must be greater than or equal to zero." }; + + return { }; +} + +static ExceptionOr validateShippingMethods(const Vector& shippingMethods) +{ + for (const auto& shippingMethod : shippingMethods) { + auto validatedShippingMethod = validateShippingMethod(shippingMethod); + if (validatedShippingMethod.hasException()) + return validatedShippingMethod.releaseException(); + } + + return { }; +} + +} + +#endif diff --git a/Source/WebCore/Modules/applepay/PaymentRequestValidator.h b/Source/WebCore/Modules/applepay/PaymentRequestValidator.h new file mode 100644 index 000000000..bcd618595 --- /dev/null +++ b/Source/WebCore/Modules/applepay/PaymentRequestValidator.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(APPLE_PAY) + +#include "ExceptionOr.h" +#include "PaymentRequest.h" + +namespace WebCore { + +class PaymentRequestValidator { +public: + static ExceptionOr validate(const PaymentRequest&); + static ExceptionOr validateTotal(const PaymentRequest::LineItem&); +}; + +} + +#endif diff --git a/Source/WebCore/Modules/battery/BatteryClient.h b/Source/WebCore/Modules/battery/BatteryClient.h deleted file mode 100644 index 2c87a44bb..000000000 --- a/Source/WebCore/Modules/battery/BatteryClient.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef BatteryClient_h -#define BatteryClient_h - -#if ENABLE(BATTERY_STATUS) - -namespace WebCore { - -class Page; - -class BatteryClient { -public: - virtual ~BatteryClient() { } - - virtual void startUpdating() = 0; - virtual void stopUpdating() = 0; - virtual void batteryControllerDestroyed() = 0; -}; - -void provideBatteryTo(Page*, BatteryClient*); - -} - -#endif // BATTERY_STATUS -#endif // BatteryClient_h - diff --git a/Source/WebCore/Modules/battery/BatteryController.cpp b/Source/WebCore/Modules/battery/BatteryController.cpp deleted file mode 100644 index f10883a14..000000000 --- a/Source/WebCore/Modules/battery/BatteryController.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * Copyright (C) 2012 Google Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "BatteryController.h" - -#if ENABLE(BATTERY_STATUS) - -#include "BatteryClient.h" -#include "BatteryStatus.h" -#include "Event.h" - -namespace WebCore { - -BatteryController::BatteryController(BatteryClient* client) - : m_client(client) -{ - ASSERT(m_client); -} - -BatteryController::~BatteryController() -{ - for (ListenerVector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) - (*it)->batteryControllerDestroyed(); - m_client->batteryControllerDestroyed(); -} - -PassOwnPtr BatteryController::create(BatteryClient* client) -{ - return adoptPtr(new BatteryController(client)); -} - -void BatteryController::addListener(BatteryManager* batteryManager) -{ - m_listeners.append(batteryManager); - m_client->startUpdating(); - - if (m_batteryStatus) - batteryManager->updateBatteryStatus(m_batteryStatus); -} - -void BatteryController::removeListener(BatteryManager* batteryManager) -{ - size_t pos = m_listeners.find(batteryManager); - if (pos == WTF::notFound) - return; - m_listeners.remove(pos); - if (m_listeners.isEmpty()) - m_client->stopUpdating(); -} - -void BatteryController::updateBatteryStatus(PassRefPtr batteryStatus) -{ - RefPtr status = batteryStatus; - if (m_batteryStatus) { - if (m_batteryStatus->charging() != status->charging()) - didChangeBatteryStatus(WebCore::eventNames().chargingchangeEvent, status); - else if (status->charging() && m_batteryStatus->chargingTime() != status->chargingTime()) - didChangeBatteryStatus(WebCore::eventNames().chargingtimechangeEvent, status); - else if (!status->charging() && m_batteryStatus->dischargingTime() != status->dischargingTime()) - didChangeBatteryStatus(WebCore::eventNames().dischargingtimechangeEvent, status); - - if (m_batteryStatus->level() != status->level()) - didChangeBatteryStatus(WebCore::eventNames().levelchangeEvent, status); - } else { - for (ListenerVector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) - (*it)->updateBatteryStatus(status); - } - - m_batteryStatus = status.release(); -} - -void BatteryController::didChangeBatteryStatus(const AtomicString& eventType, PassRefPtr batteryStatus) -{ - RefPtr event = Event::create(eventType, false, false); - RefPtr battery = batteryStatus; - for (ListenerVector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) - (*it)->didChangeBatteryStatus(event, battery); -} - -const char* BatteryController::supplementName() -{ - return "BatteryController"; -} - -bool BatteryController::isActive(Page* page) -{ - return static_cast(BatteryController::from(page)); -} - -void provideBatteryTo(Page* page, BatteryClient* client) -{ - Supplement::provideTo(page, BatteryController::supplementName(), BatteryController::create(client)); -} - -} - -#endif // BATTERY_STATUS - diff --git a/Source/WebCore/Modules/battery/BatteryController.h b/Source/WebCore/Modules/battery/BatteryController.h deleted file mode 100644 index e43a092b8..000000000 --- a/Source/WebCore/Modules/battery/BatteryController.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * Copyright (C) 2012 Google Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef BatteryController_h -#define BatteryController_h - -#if ENABLE(BATTERY_STATUS) - -#include "BatteryManager.h" -#include "Page.h" - -namespace WebCore { - -class BatteryClient; - -class BatteryController : public Supplement { -public: - ~BatteryController(); - - static PassOwnPtr create(BatteryClient*); - - void addListener(BatteryManager*); - void removeListener(BatteryManager*); - void updateBatteryStatus(PassRefPtr); - void didChangeBatteryStatus(const AtomicString& eventType, PassRefPtr); - - BatteryClient* client() const { return m_client; } - - static const char* supplementName(); - static BatteryController* from(Page* page) { return static_cast(Supplement::from(page, supplementName())); } - static bool isActive(Page*); - -private: - typedef Vector ListenerVector; - - explicit BatteryController(BatteryClient*); - - BatteryClient* m_client; - ListenerVector m_listeners; - - RefPtr m_batteryStatus; -}; - -} - -#endif // BATTERY_STATUS -#endif // BatteryController_h - diff --git a/Source/WebCore/Modules/battery/BatteryManager.cpp b/Source/WebCore/Modules/battery/BatteryManager.cpp deleted file mode 100644 index f637b524f..000000000 --- a/Source/WebCore/Modules/battery/BatteryManager.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "BatteryManager.h" - -#if ENABLE(BATTERY_STATUS) - -#include "BatteryController.h" -#include "BatteryStatus.h" -#include "Document.h" -#include "Event.h" -#include "Frame.h" -#include "Navigator.h" -#include - -namespace WebCore { - -PassRefPtr BatteryManager::create(Navigator* navigator) -{ - RefPtr batteryManager(adoptRef(new BatteryManager(navigator))); - batteryManager->suspendIfNeeded(); - return batteryManager.release(); -} - -BatteryManager::~BatteryManager() -{ -} - -BatteryManager::BatteryManager(Navigator* navigator) - : ActiveDOMObject(navigator->frame()->document()) - , m_batteryController(BatteryController::from(navigator->frame()->page())) - , m_batteryStatus(0) -{ - m_batteryController->addListener(this); -} - -bool BatteryManager::charging() -{ - return m_batteryStatus ? m_batteryStatus->charging() : true; -} - -double BatteryManager::chargingTime() -{ - if (!m_batteryStatus || !m_batteryStatus->charging()) - return std::numeric_limits::infinity(); - - return m_batteryStatus->chargingTime(); -} - -double BatteryManager::dischargingTime() -{ - if (!m_batteryStatus || m_batteryStatus->charging()) - return std::numeric_limits::infinity(); - - return m_batteryStatus->dischargingTime(); -} - -double BatteryManager::level() -{ - return m_batteryStatus ? m_batteryStatus->level() : 1; -} - -void BatteryManager::didChangeBatteryStatus(PassRefPtr event, PassRefPtr batteryStatus) -{ - updateBatteryStatus(batteryStatus); - dispatchEvent(event); -} - -void BatteryManager::updateBatteryStatus(PassRefPtr batteryStatus) -{ - m_batteryStatus = batteryStatus; -} - -void BatteryManager::suspend(ReasonForSuspension) -{ - if (m_batteryController) - m_batteryController->removeListener(this); -} - -void BatteryManager::resume() -{ - if (m_batteryController) - m_batteryController->addListener(this); -} - -void BatteryManager::stop() -{ - if (m_batteryController) - m_batteryController->removeListener(this); -} - -} // namespace WebCore - -#endif // BATTERY_STATUS - diff --git a/Source/WebCore/Modules/battery/BatteryManager.h b/Source/WebCore/Modules/battery/BatteryManager.h deleted file mode 100644 index 93047a51d..000000000 --- a/Source/WebCore/Modules/battery/BatteryManager.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef BatteryManager_h -#define BatteryManager_h - -#if ENABLE(BATTERY_STATUS) - -#include "ActiveDOMObject.h" -#include "BatteryStatus.h" -#include "EventTarget.h" - -namespace WebCore { - -class BatteryController; -class Navigator; -class ScriptExecutionContext; - -class BatteryManager : public ActiveDOMObject, public RefCounted, public EventTarget { -public: - virtual ~BatteryManager(); - static PassRefPtr create(Navigator*); - - // EventTarget implementation. - virtual EventTargetInterface eventTargetInterface() const { return BatteryManagerEventTargetInterfaceType; } - virtual ScriptExecutionContext* scriptExecutionContext() const { return ActiveDOMObject::scriptExecutionContext(); } - - bool charging(); - double chargingTime(); - double dischargingTime(); - double level(); - - DEFINE_ATTRIBUTE_EVENT_LISTENER(chargingchange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(chargingtimechange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dischargingtimechange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(levelchange); - - void didChangeBatteryStatus(PassRefPtr, PassRefPtr); - void updateBatteryStatus(PassRefPtr); - void batteryControllerDestroyed() { m_batteryController = 0; } - - using RefCounted::ref; - using RefCounted::deref; - - // ActiveDOMObject implementation. - virtual bool canSuspend() const { return true; } - virtual void suspend(ReasonForSuspension); - virtual void resume(); - virtual void stop(); - -protected: - virtual EventTargetData* eventTargetData() { return &m_eventTargetData; } - virtual EventTargetData& ensureEventTargetData() { return m_eventTargetData; } - -private: - explicit BatteryManager(Navigator*); - - // EventTarget implementation. - virtual void refEventTarget() { ref(); } - virtual void derefEventTarget() { deref(); } - - BatteryController* m_batteryController; - EventTargetData m_eventTargetData; - RefPtr m_batteryStatus; -}; - -} - -#endif // BATTERY_STATUS -#endif // BatteryManager_h - diff --git a/Source/WebCore/Modules/battery/BatteryManager.idl b/Source/WebCore/Modules/battery/BatteryManager.idl deleted file mode 100644 index be8b74358..000000000 --- a/Source/WebCore/Modules/battery/BatteryManager.idl +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -// http://dev.w3.org/2009/dap/system-info/battery-status.html -[ - NoInterfaceObject, - Conditional=BATTERY_STATUS, - ActiveDOMObject, - EventTarget, -] interface BatteryManager { - readonly attribute boolean charging; - readonly attribute double chargingTime; - readonly attribute double dischargingTime; - readonly attribute double level; - - attribute EventListener onchargingchange; - attribute EventListener onchargingtimechange; - attribute EventListener ondischargingtimechange; - attribute EventListener onlevelchange; - - // EventTarget interface - void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event event); -}; diff --git a/Source/WebCore/Modules/battery/BatteryStatus.cpp b/Source/WebCore/Modules/battery/BatteryStatus.cpp deleted file mode 100644 index 00f530bf8..000000000 --- a/Source/WebCore/Modules/battery/BatteryStatus.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "BatteryStatus.h" - -#if ENABLE(BATTERY_STATUS) - -#include - -namespace WebCore { - -PassRefPtr BatteryStatus::create() -{ - return adoptRef(new BatteryStatus); -} - -PassRefPtr BatteryStatus::create(bool charging, double chargingTime, double dischargingTime, double level) -{ - return adoptRef(new BatteryStatus(charging, chargingTime, dischargingTime, level)); -} - -BatteryStatus::BatteryStatus() - : m_charging(true) - , m_chargingTime(0) - , m_dischargingTime(std::numeric_limits::infinity()) - , m_level(1) -{ -} - -BatteryStatus::BatteryStatus(bool charging, double chargingTime, double dischargingTime, double level) - : m_charging(charging) - , m_chargingTime(chargingTime) - , m_dischargingTime(dischargingTime) - , m_level(level) -{ -} - -} // namespace WebCore - -#endif // BATTERY_STATUS - diff --git a/Source/WebCore/Modules/battery/BatteryStatus.h b/Source/WebCore/Modules/battery/BatteryStatus.h deleted file mode 100644 index 2c451ab4e..000000000 --- a/Source/WebCore/Modules/battery/BatteryStatus.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef BatteryStatus_h -#define BatteryStatus_h - -#if ENABLE(BATTERY_STATUS) - -#include -#include - -namespace WebCore { - -class BatteryStatus : public RefCounted { -public: - static PassRefPtr create(); - static PassRefPtr create(bool charging, double chargingTime, double dischargingTime, double level); - - bool charging() const { return m_charging; } - double chargingTime() const { return m_chargingTime; } - double dischargingTime() const { return m_dischargingTime; } - double level() const { return m_level; } - -private: - BatteryStatus(); - BatteryStatus(bool charging, double chargingTime, double dischargingTime, double level); - - bool m_charging; - double m_chargingTime; - double m_dischargingTime; - double m_level; -}; - -} // namespace WebCore - -#endif // BATTERY_STATUS -#endif // BatteryStatus_h - diff --git a/Source/WebCore/Modules/battery/NavigatorBattery.cpp b/Source/WebCore/Modules/battery/NavigatorBattery.cpp deleted file mode 100644 index 6ae0c9d98..000000000 --- a/Source/WebCore/Modules/battery/NavigatorBattery.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "NavigatorBattery.h" - -#if ENABLE(BATTERY_STATUS) - -#include "BatteryController.h" -#include "BatteryManager.h" -#include "Navigator.h" -#include "ScriptExecutionContext.h" - -namespace WebCore { - -NavigatorBattery::NavigatorBattery() -{ -} - -NavigatorBattery::~NavigatorBattery() -{ -} - -BatteryManager* NavigatorBattery::webkitBattery(Navigator* navigator) -{ - if (!navigator->frame()) - return 0; - - NavigatorBattery* navigatorBattery = NavigatorBattery::from(navigator); - if (!navigatorBattery->m_batteryManager) - navigatorBattery->m_batteryManager = BatteryManager::create(navigator); - return navigatorBattery->m_batteryManager.get(); -} - -const char* NavigatorBattery::supplementName() -{ - return "NavigatorBattery"; -} - -NavigatorBattery* NavigatorBattery::from(Navigator* navigator) -{ - NavigatorBattery* supplement = static_cast(Supplement::from(navigator, supplementName())); - if (!supplement) { - supplement = new NavigatorBattery(); - provideTo(navigator, supplementName(), adoptPtr(supplement)); - } - return supplement; -} - -BatteryManager* NavigatorBattery::batteryManager() -{ - return m_batteryManager.get(); -} - -} // namespace WebCore - -#endif // ENABLE(BATTERY_STATUS) - - diff --git a/Source/WebCore/Modules/battery/NavigatorBattery.h b/Source/WebCore/Modules/battery/NavigatorBattery.h deleted file mode 100644 index af7114f42..000000000 --- a/Source/WebCore/Modules/battery/NavigatorBattery.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef NavigatorBattery_h -#define NavigatorBattery_h - -#if ENABLE(BATTERY_STATUS) - -#include "Supplementable.h" - -namespace WebCore { - -class BatteryManager; -class Navigator; -class ScriptExecutionContext; - -class NavigatorBattery : public Supplement { -public: - virtual ~NavigatorBattery(); - - static NavigatorBattery* from(Navigator*); - - static BatteryManager* webkitBattery(Navigator*); - BatteryManager* batteryManager(); - - private: - NavigatorBattery(); - static const char* supplementName(); - - RefPtr m_batteryManager; -}; - -} // namespace WebCore - -#endif // ENABLE(BATTERY_STATUS) - -#endif // NavigatorBattery_h - - diff --git a/Source/WebCore/Modules/battery/NavigatorBattery.idl b/Source/WebCore/Modules/battery/NavigatorBattery.idl deleted file mode 100644 index f69199fd2..000000000 --- a/Source/WebCore/Modules/battery/NavigatorBattery.idl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -[ - Conditional=BATTERY_STATUS, -] partial interface Navigator { - readonly attribute BatteryManager webkitBattery; -}; diff --git a/Source/WebCore/Modules/encryptedmedia/CDM.cpp b/Source/WebCore/Modules/encryptedmedia/CDM.cpp new file mode 100644 index 000000000..7588b0628 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/CDM.cpp @@ -0,0 +1,663 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CDM.h" + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "CDMPrivate.h" +#include "Document.h" +#include "InitDataRegistry.h" +#include "MediaKeysRestrictions.h" +#include "MediaPlayer.h" +#include "NotImplemented.h" +#include "ParsedContentType.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include + +namespace WebCore { + +static Vector& cdmFactories() +{ + static NeverDestroyed> factories; + return factories; +} + +static std::unique_ptr createCDMPrivateForKeySystem(const String& keySystem, CDM& cdm) +{ + for (auto* factory : cdmFactories()) { + if (factory->supportsKeySystem(keySystem)) + return factory->createCDM(cdm); + } + ASSERT_NOT_REACHED(); + return nullptr; +} + +void CDM::registerCDMFactory(CDMFactory& factory) +{ + ASSERT(!cdmFactories().contains(&factory)); + cdmFactories().append(&factory); +} + +void CDM::unregisterCDMFactory(CDMFactory& factory) +{ + ASSERT(cdmFactories().contains(&factory)); + cdmFactories().removeAll(&factory); +} + +bool CDM::supportsKeySystem(const String& keySystem) +{ + for (auto* factory : cdmFactories()) { + if (factory->supportsKeySystem(keySystem)) + return true; + } + return false; +} + +Ref CDM::create(Document& document, const String& keySystem) +{ + return adoptRef(*new CDM(document, keySystem)); +} + +CDM::CDM(Document& document, const String& keySystem) + : ContextDestructionObserver(&document) + , m_keySystem(keySystem) + , m_private(createCDMPrivateForKeySystem(keySystem, *this)) + , m_weakPtrFactory(this) +{ + ASSERT(supportsKeySystem(keySystem)); + for (auto* factory : cdmFactories()) { + if (!factory->supportsKeySystem(keySystem)) + continue; + m_private = factory->createCDM(*this); + } +} + +CDM::~CDM() = default; + +void CDM::getSupportedConfiguration(MediaKeySystemConfiguration&& candidateConfiguration, SupportedConfigurationCallback&& callback) +{ + // https://w3c.github.io/encrypted-media/#get-supported-configuration + // W3C Editor's Draft 09 November 2016 + + // 3.1.1.1 Get Supported Configuration + // Given a Key Systems implementation implementation, MediaKeySystemConfiguration candidate configuration, and origin, + // this algorithm returns a supported configuration or NotSupported as appropriate. + + // 1. Let supported configuration be ConsentDenied. + // 2. Initialize restrictions to indicate that no configurations have had user consent denied. + MediaKeysRestrictions restrictions { }; + doSupportedConfigurationStep(WTFMove(candidateConfiguration), WTFMove(restrictions), WTFMove(callback)); +} + +void CDM::doSupportedConfigurationStep(MediaKeySystemConfiguration&& candidateConfiguration, MediaKeysRestrictions&& restrictions, SupportedConfigurationCallback&& callback) +{ + // https://w3c.github.io/encrypted-media/#get-supported-configuration + // W3C Editor's Draft 09 November 2016, ctd. + + // 3.1.1.1 Get Supported Configuration + // 3. Repeat the following step while supported configuration is ConsentDenied: + // 3.1. Let supported configuration and, if provided, restrictions be the result of executing the + // Get Supported Configuration and Consent algorithm with implementation, candidate configuration, + // restrictions and origin. + auto optionalConfiguration = getSupportedConfiguration(candidateConfiguration, restrictions); + if (!optionalConfiguration) { + callback(std::nullopt); + return; + } + + auto consentCallback = [weakThis = createWeakPtr(), callback = WTFMove(callback)] (ConsentStatus status, MediaKeySystemConfiguration&& configuration, MediaKeysRestrictions&& restrictions) mutable { + if (!weakThis) { + callback(std::nullopt); + return; + } + // 3.1.1.2 Get Supported Configuration and Consent, ctd. + // 22. Let consent status and updated restrictions be the result of running the Get Consent Status algorithm on accumulated configuration, + // restrictions and origin and follow the steps for the value of consent status from the following list: + switch (status) { + case ConsentStatus::ConsentDenied: + // ↳ ConsentDenied: + // Return ConsentDenied and updated restrictions. + weakThis->doSupportedConfigurationStep(WTFMove(configuration), WTFMove(restrictions), WTFMove(callback)); + return; + + case ConsentStatus::InformUser: + // ↳ InformUser + // Inform the user that accumulated configuration is in use in the origin including, specifically, the information that + // Distinctive Identifier(s) and/or Distinctive Permanent Identifier(s) as appropriate will be used if the + // distinctiveIdentifier member of accumulated configuration is "required". Continue to the next step. + // NOTE: Implement. + break; + + case ConsentStatus::Allowed: + // ↳ Allowed: + // Continue to the next step. + break; + } + // 23. Return accumulated configuration. + callback(WTFMove(configuration)); + }; + getConsentStatus(WTFMove(optionalConfiguration.value()), WTFMove(restrictions), consentCallback); +} + +bool CDM::isPersistentType(MediaKeySessionType sessionType) +{ + // https://w3c.github.io/encrypted-media/#is-persistent-session-type + // W3C Editor's Draft 09 November 2016 + + // 5.1.1. Is persistent session type? + // 1. Let the session type be the specified MediaKeySessionType value. + // 2. Follow the steps for the value of session type from the following list: + switch (sessionType) { + case MediaKeySessionType::Temporary: + // ↳ "temporary" + return false; + case MediaKeySessionType::PersistentLicense: + case MediaKeySessionType::PersistentUsageRecord: + // ↳ "persistent-license" + return true; + } +} + +std::optional CDM::getSupportedConfiguration(const MediaKeySystemConfiguration& candidateConfiguration, MediaKeysRestrictions& restrictions) +{ + // https://w3c.github.io/encrypted-media/#get-supported-configuration-and-consent + // W3C Editor's Draft 09 November 2016 + + ASSERT(m_private); + if (!m_private) + return std::nullopt; + + // 3.1.1.2 Get Supported Configuration and Consent + // Given a Key Systems implementation implementation, MediaKeySystemConfiguration candidate configuration, + // restrictions and origin, this algorithm returns a supported configuration, NotSupported, or ConsentDenied + // as appropriate and, in the ConsentDenied case, restrictions. + + // 1. Let accumulated configuration be a new MediaKeySystemConfiguration dictionary. + MediaKeySystemConfiguration accumulatedConfiguration { }; + + // 2. Set the label member of accumulated configuration to equal the label member of candidate configuration. + accumulatedConfiguration.label = candidateConfiguration.label; + + // 3. If the initDataTypes member of candidate configuration is non-empty, run the following steps: + if (!candidateConfiguration.initDataTypes.isEmpty()) { + // 3.1. Let supported types be an empty sequence of DOMStrings. + Vector supportedTypes; + + // 3.2. For each value in candidate configuration's initDataTypes member: + for (auto initDataType : candidateConfiguration.initDataTypes) { + // 3.2.1. Let initDataType be the value. + // 3.2.2. If the implementation supports generating requests based on initDataType, add initDataType + // to supported types. String comparison is case-sensitive. The empty string is never supported. + if (initDataType.isEmpty()) + continue; + + if (m_private && m_private->supportsInitDataType(initDataType)) + supportedTypes.append(initDataType); + } + + // 3.3. If supported types is empty, return NotSupported. + if (supportedTypes.isEmpty()) + return std::nullopt; + + // 3.4. Set the initDataTypes member of accumulated configuration to supported types. + accumulatedConfiguration.initDataTypes = WTFMove(supportedTypes); + } + + // 4. Let distinctive identifier requirement be the value of candidate configuration's distinctiveIdentifier member. + MediaKeysRequirement distinctiveIdentifierRequirement = candidateConfiguration.distinctiveIdentifier; + + // 5. If distinctive identifier requirement is "optional" and Distinctive Identifiers are not allowed according to + // restrictions, set distinctive identifier requirement to "not-allowed". + if (distinctiveIdentifierRequirement == MediaKeysRequirement::Optional && restrictions.distinctiveIdentifierDenied) + distinctiveIdentifierRequirement = MediaKeysRequirement::NotAllowed; + + // 6. Follow the steps for distinctive identifier requirement from the following list: + switch (distinctiveIdentifierRequirement) { + case MediaKeysRequirement::Required: + // ↳ "required" + // If the implementation does not support use of Distinctive Identifier(s) in combination + // with accumulated configuration and restrictions, return NotSupported. + if (m_private->distinctiveIdentifiersRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::NotAllowed) + return std::nullopt; + break; + + case MediaKeysRequirement::Optional: + // ↳ "optional" + // Continue with the following steps. + break; + + case MediaKeysRequirement::NotAllowed: + // ↳ "not-allowed" + // If the implementation requires use Distinctive Identifier(s) or Distinctive Permanent Identifier(s) + // in combination with accumulated configuration and restrictions, return NotSupported. + if (m_private->distinctiveIdentifiersRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required) + return std::nullopt; + break; + } + + // 7. Set the distinctiveIdentifier member of accumulated configuration to equal distinctive identifier requirement. + accumulatedConfiguration.distinctiveIdentifier = distinctiveIdentifierRequirement; + + // 8. Let persistent state requirement be equal to the value of candidate configuration's persistentState member. + MediaKeysRequirement persistentStateRequirement = candidateConfiguration.persistentState; + + // 9. If persistent state requirement is "optional" and persisting state is not allowed according to restrictions, + // set persistent state requirement to "not-allowed". + if (persistentStateRequirement == MediaKeysRequirement::Optional && restrictions.persistentStateDenied) + persistentStateRequirement = MediaKeysRequirement::NotAllowed; + + // 10. Follow the steps for persistent state requirement from the following list: + switch (persistentStateRequirement) { + case MediaKeysRequirement::Required: + // ↳ "required" + // If the implementation does not support persisting state in combination with accumulated configuration + // and restrictions, return NotSupported. + if (m_private->persistentStateRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::NotAllowed) + return std::nullopt; + break; + + case MediaKeysRequirement::Optional: + // ↳ "optional" + // Continue with the following steps. + break; + + case MediaKeysRequirement::NotAllowed: + // ↳ "not-allowed" + // If the implementation requires persisting state in combination with accumulated configuration + // and restrictions, return NotSupported + if (m_private->persistentStateRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required) + return std::nullopt; + break; + } + + // 11. Set the persistentState member of accumulated configuration to equal the value of persistent state requirement. + accumulatedConfiguration.persistentState = persistentStateRequirement; + + // 12. Follow the steps for the first matching condition from the following list: + Vector sessionTypes; + + if (!candidateConfiguration.sessionTypes.isEmpty()) { + // ↳ If the sessionTypes member is present [WebIDL] in candidate configuration + // Let session types be candidate configuration's sessionTypes member. + sessionTypes = candidateConfiguration.sessionTypes; + } else { + // ↳ Otherwise + // Let session types be [ "temporary" ]. + sessionTypes = { MediaKeySessionType::Temporary }; + } + + // 13. For each value in session types: + for (auto& sessionType : sessionTypes) { + // 13.1. Let session type be the value. + // 13.2. If accumulated configuration's persistentState value is "not-allowed" and the + // Is persistent session type? algorithm returns true for session type return NotSupported. + if (accumulatedConfiguration.persistentState == MediaKeysRequirement::NotAllowed && isPersistentType(sessionType)) + return std::nullopt; + + // 13.3. If the implementation does not support session type in combination with accumulated configuration + // and restrictions for other reasons, return NotSupported. + if (!m_private->supportsSessionTypeWithConfiguration(sessionType, accumulatedConfiguration)) + return std::nullopt; + + // 13.4 If accumulated configuration's persistentState value is "optional" and the result of running the Is + // persistent session type? algorithm on session type is true, change accumulated configuration's persistentState + // value to "required". + if (accumulatedConfiguration.persistentState == MediaKeysRequirement::Optional && isPersistentType(sessionType)) + accumulatedConfiguration.persistentState = MediaKeysRequirement::Required; + } + + // 14. Set the sessionTypes member of accumulated configuration to session types. + accumulatedConfiguration.sessionTypes = sessionTypes; + + // 15. If the videoCapabilities and audioCapabilities members in candidate configuration are both empty, return NotSupported. + if (candidateConfiguration.videoCapabilities.isEmpty() && candidateConfiguration.audioCapabilities.isEmpty()) + return std::nullopt; + + // 16. ↳ If the videoCapabilities member in candidate configuration is non-empty: + if (!candidateConfiguration.videoCapabilities.isEmpty()) { + // 16.1. Let video capabilities be the result of executing the Get Supported Capabilities for Audio/Video Type algorithm on + // Video, candidate configuration's videoCapabilities member, accumulated configuration, and restrictions. + auto videoCapabilities = getSupportedCapabilitiesForAudioVideoType(AudioVideoType::Video, candidateConfiguration.videoCapabilities, accumulatedConfiguration, restrictions); + + // 16.2. If video capabilities is null, return NotSupported. + if (!videoCapabilities) + return std::nullopt; + + // 16.3 Set the videoCapabilities member of accumulated configuration to video capabilities. + accumulatedConfiguration.videoCapabilities = WTFMove(videoCapabilities.value()); + } else { + // 16. ↳ Otherwise: + // Set the videoCapabilities member of accumulated configuration to an empty sequence. + accumulatedConfiguration.videoCapabilities = { }; + } + + // 17. ↳ If the audioCapabilities member in candidate configuration is non-empty: + if (!candidateConfiguration.audioCapabilities.isEmpty()) { + // 17.1. Let audio capabilities be the result of executing the Get Supported Capabilities for Audio/Video Type algorithm on + // Audio, candidate configuration's audioCapabilities member, accumulated configuration, and restrictions. + auto audioCapabilities = getSupportedCapabilitiesForAudioVideoType(AudioVideoType::Audio, candidateConfiguration.audioCapabilities, accumulatedConfiguration, restrictions); + + // 17.2. If audio capabilities is null, return NotSupported. + if (!audioCapabilities) + return std::nullopt; + + // 17.3 Set the audioCapabilities member of accumulated configuration to audio capabilities. + accumulatedConfiguration.audioCapabilities = WTFMove(audioCapabilities.value()); + } else { + // 17. ↳ Otherwise: + // Set the audioCapabilities member of accumulated configuration to an empty sequence. + accumulatedConfiguration.audioCapabilities = { }; + } + + // 18. If accumulated configuration's distinctiveIdentifier value is "optional", follow the steps for the first matching + // condition from the following list: + if (accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Optional) { + // ↳ If the implementation requires use Distinctive Identifier(s) or Distinctive Permanent Identifier(s) for any of the + // combinations in accumulated configuration + if (m_private->distinctiveIdentifiersRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required) { + // Change accumulated configuration's distinctiveIdentifier value to "required". + accumulatedConfiguration.distinctiveIdentifier = MediaKeysRequirement::Required; + } else { + // ↳ Otherwise + // Change accumulated configuration's distinctiveIdentifier value to "not-allowed". + accumulatedConfiguration.distinctiveIdentifier = MediaKeysRequirement::NotAllowed; + } + } + + // 19. If accumulated configuration's persistentState value is "optional", follow the steps for the first matching + // condition from the following list: + if (accumulatedConfiguration.persistentState == MediaKeysRequirement::Optional) { + // ↳ If the implementation requires persisting state for any of the combinations in accumulated configuration + if (m_private->persistentStateRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required) { + // Change accumulated configuration's persistentState value to "required". + accumulatedConfiguration.persistentState = MediaKeysRequirement::Required; + } else { + // ↳ Otherwise + // Change accumulated configuration's persistentState value to "not-allowed". + accumulatedConfiguration.persistentState = MediaKeysRequirement::NotAllowed; + } + } + + // 20. If implementation in the configuration specified by the combination of the values in accumulated configuration + // is not supported or not allowed in the origin, return NotSupported. + if (!m_private->supportsConfiguration(accumulatedConfiguration)) + return std::nullopt; + + Document* document = downcast(m_scriptExecutionContext); + if (!document) + return std::nullopt; + + SecurityOrigin& origin = document->securityOrigin(); + SecurityOrigin& topOrigin = document->topOrigin(); + + if ((accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Required || accumulatedConfiguration.persistentState == MediaKeysRequirement::Required) && !origin.canAccessLocalStorage(&topOrigin)) + return std::nullopt; + + return WTFMove(accumulatedConfiguration); + // NOTE: Continued in getConsentStatus(). +} + +std::optional> CDM::getSupportedCapabilitiesForAudioVideoType(CDM::AudioVideoType type, const Vector& requestedCapabilities, const MediaKeySystemConfiguration& partialConfiguration, MediaKeysRestrictions& restrictions) +{ + // https://w3c.github.io/encrypted-media/#get-supported-capabilities-for-audio-video-type + // W3C Editor's Draft 09 November 2016 + + ASSERT(m_private); + if (!m_private) + return std::nullopt; + + // 3.1.1.3 Get Supported Capabilities for Audio/Video Type + + // Given an audio/video type, MediaKeySystemMediaCapability sequence requested media capabilities, MediaKeySystemConfiguration + // partial configuration, and restrictions, this algorithm returns a sequence of supported MediaKeySystemMediaCapability values + // for this audio/video type or null as appropriate. + + // 1. Let local accumulated configuration be a local copy of partial configuration. + MediaKeySystemConfiguration accumulatedConfiguration = partialConfiguration; + + // 2. Let supported media capabilities be an empty sequence of MediaKeySystemMediaCapability dictionaries. + Vector supportedMediaCapabilities { }; + + // 3. For each requested media capability in requested media capabilities: + for (auto& requestedCapability : requestedCapabilities) { + // 3.1. Let content type be requested media capability's contentType member. + // 3.2. Let robustness be requested media capability's robustness member. + String robustness = requestedCapability.robustness; + + // 3.3. If content type is the empty string, return null. + if (requestedCapability.contentType.isEmpty()) + return std::nullopt; + + // 3.4. If content type is an invalid or unrecognized MIME type, continue to the next iteration. + if (!isValidContentType(requestedCapability.contentType)) + continue; + + // 3.5. Let container be the container type specified by content type. + ParsedContentType contentType { requestedCapability.contentType }; + String container = contentType.mimeType(); + + // 3.6. If the user agent does not support container, continue to the next iteration. The case-sensitivity + // of string comparisons is determined by the appropriate RFC. + // 3.7. Let parameters be the RFC 6381 [RFC6381] parameters, if any, specified by content type. + // 3.8. If the user agent does not recognize one or more parameters, continue to the next iteration. + // 3.9. Let media types be the set of codecs and codec constraints specified by parameters. The case-sensitivity + // of string comparisons is determined by the appropriate RFC or other specification. + String codecs = contentType.parameterValueForName("codecs"); + if (contentType.parameterCount() > (codecs.isEmpty() ? 0 : 1)) + continue; + + // 3.10. If media types is empty: + if (codecs.isEmpty()) { + // ↳ If container normatively implies a specific set of codecs and codec constraints: + // ↳ Otherwise: + notImplemented(); + } + + // 3.11. If content type is not strictly a audio/video type, continue to the next iteration. + // 3.12. If robustness is not the empty string and contains an unrecognized value or a value not supported by + // implementation, continue to the next iteration. String comparison is case-sensitive. + if (!robustness.isEmpty() && !m_private->supportsRobustness(robustness)) + continue; + + // 3.13. If the user agent and implementation definitely support playback of encrypted media data for the + // combination of container, media types, robustness and local accumulated configuration in combination + // with restrictions: + MediaEngineSupportParameters parameters; + parameters.type = contentType.mimeType(); + parameters.codecs = codecs; + if (!MediaPlayer::supportsType(parameters, nullptr)) { + // Try with Media Source: + parameters.isMediaSource = true; + if (!MediaPlayer::supportsType(parameters, nullptr)) + continue; + } + + if (!m_private->supportsConfigurationWithRestrictions(accumulatedConfiguration, restrictions)) + continue; + + // 3.13.1. Add requested media capability to supported media capabilities. + supportedMediaCapabilities.append(requestedCapability); + + // 3.13.2. ↳ If audio/video type is Video: + // Add requested media capability to the videoCapabilities member of local accumulated configuration. + if (type == AudioVideoType::Video) + accumulatedConfiguration.videoCapabilities.append(requestedCapability); + // 3.13.2. ↳ If audio/video type is Audio: + // Add requested media capability to the audioCapabilities member of local accumulated configuration. + else + accumulatedConfiguration.audioCapabilities.append(requestedCapability); + } + + // 4. If supported media capabilities is empty, return null. + if (supportedMediaCapabilities.isEmpty()) + return std::nullopt; + + // 5. Return supported media capabilities. + return supportedMediaCapabilities; +} + +void CDM::getConsentStatus(MediaKeySystemConfiguration&& accumulatedConfiguration, MediaKeysRestrictions&& restrictions, ConsentStatusCallback&& callback) +{ + // https://w3c.github.io/encrypted-media/#get-supported-configuration-and-consent + // W3C Editor's Draft 09 November 2016 + if (!m_scriptExecutionContext) { + callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + // NOTE: In the future, these checks belowe will involve asking the page client, possibly across a process boundary. + // They will by necessity be asynchronous with callbacks. For now, imply this behavior by performing it in an async task. + + m_scriptExecutionContext->postTask([this, weakThis = createWeakPtr(), accumulatedConfiguration = WTFMove(accumulatedConfiguration), restrictions = WTFMove(restrictions), callback = WTFMove(callback)] (ScriptExecutionContext&) mutable { + if (!weakThis || !m_private) { + callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + Document* document = downcast(m_scriptExecutionContext); + if (!document) { + callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + SecurityOrigin& origin = document->securityOrigin(); + SecurityOrigin& topOrigin = document->topOrigin(); + + // 3.1.1.2 Get Supported Configuration and Consent, ctd. + // 21. If accumulated configuration's distinctiveIdentifier value is "required" and the Distinctive Identifier(s) associated + // with accumulated configuration are not unique per origin and profile and clearable: + if (accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Required && !m_private->distinctiveIdentifiersAreUniquePerOriginAndClearable(accumulatedConfiguration)) { + // 21.1. Update restrictions to reflect that all configurations described by accumulated configuration do not have user consent. + restrictions.distinctiveIdentifierDenied = true; + callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + // https://w3c.github.io/encrypted-media/#get-consent-status + // 3.1.1.4 Get Consent Status + // Given an accumulated configuration, restrictions and origin, this algorithm returns the consent status for accumulated + // configuration and origin as one of ConsentDenied, InformUser or Allowed, together with an updated value for restrictions + // in the ConsentDenied case. + + // 1. If there is persisted denial for origin indicating that accumulated configuration is not allowed, run the following steps: + // 1.1. Update restrictions to reflect the configurations for which consent has been denied. + // 1.2. Return ConsentDenied and restrictions. + // 2. If there is persisted consent for origin indicating accumulated configuration is allowed, return Allowed. + // NOTE: persisted denial / consent unimplemented. + + // 3. If any of the following are true: + // ↳ The distinctiveIdentifier member of accumulated configuration is not "not-allowed" and the combination of the User Agent, + // implementation and accumulated configuration does not follow all the recommendations of Allow Persistent Data to Be Cleared + // with respect to Distinctive Identifier(s). + // NOTE: assume that implementations follow all recommendations. + + // ↳ The user agent requires explicit user consent for the accumulated configuration for other reasons. + // NOTE: assume the user agent does not require explicit user consent. + + // 3.1. Request user consent to use accumulated configuration in the origin and wait for the user response. + // The consent must include consent to use a Distinctive Identifier(s) and/or Distinctive Permanent Identifier(s) as appropriate + // if accumulated configuration's distinctiveIdentifier member is "required". + // 3.2. If consent was denied, run the following steps: + // 3.2.1. Update restrictions to reflect the configurations for which consent was denied. + // 3.2.1. Return ConsentDenied and restrictions. + // NOTE: assume implied consent if the combination of origin and topOrigin allows it. + if (accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Required && !origin.canAccessLocalStorage(&topOrigin)) { + restrictions.distinctiveIdentifierDenied = true; + callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + // 4. If the distinctiveIdentifier member of accumulated configuration is not "not-allowed", return InformUser. + if (accumulatedConfiguration.distinctiveIdentifier != MediaKeysRequirement::NotAllowed) { + callback(ConsentStatus::InformUser, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + // 5. If the user agent requires informing the user for the accumulated configuration for other reasons, return InformUser. + // NOTE: assume the user agent does not require informing the user. + + // 6. Return Allowed. + callback(ConsentStatus::Allowed, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + }); +} + +void CDM::loadAndInitialize() +{ + if (m_private) + m_private->loadAndInitialize(); +} + +RefPtr CDM::createInstance() +{ + if (!m_private) + return nullptr; + return m_private->createInstance(); +} + +bool CDM::supportsServerCertificates() const +{ + return m_private && m_private->supportsServerCertificates(); +} + +bool CDM::supportsSessions() const +{ + return m_private && m_private->supportsSessions(); +} + +bool CDM::supportsInitDataType(const AtomicString& initDataType) const +{ + return m_private && m_private->supportsInitDataType(initDataType); +} + +RefPtr CDM::sanitizeInitData(const AtomicString& initDataType, const SharedBuffer& initData) +{ + return InitDataRegistry::shared().sanitizeInitData(initDataType, initData); +} + +bool CDM::supportsInitData(const AtomicString& initDataType, const SharedBuffer& initData) +{ + return m_private && m_private->supportsInitData(initDataType, initData); +} + +RefPtr CDM::sanitizeResponse(const SharedBuffer& response) +{ + if (!m_private) + return nullptr; + return m_private->sanitizeResponse(response); +} + +std::optional CDM::sanitizeSessionId(const String& sessionId) +{ + if (!m_private) + return std::nullopt; + return m_private->sanitizeSessionId(sessionId); +} + +} + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/CDM.h b/Source/WebCore/Modules/encryptedmedia/CDM.h new file mode 100644 index 000000000..c43ee747d --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/CDM.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "ContextDestructionObserver.h" +#include "MediaKeySystemConfiguration.h" +#include "SharedBuffer.h" +#include +#include +#include +#include +#include +#include + +namespace WebCore { + +class CDM; +class CDMInstance; +class CDMPrivate; +class Document; +class ScriptExecutionContext; +class SharedBuffer; + +struct MediaKeysRestrictions; + +class CDMFactory { +public: + virtual ~CDMFactory() { }; + virtual std::unique_ptr createCDM(CDM&) = 0; + virtual bool supportsKeySystem(const String&) = 0; +}; + +class CDM : public RefCounted, private ContextDestructionObserver { +public: + WEBCORE_EXPORT static void registerCDMFactory(CDMFactory&); + WEBCORE_EXPORT static void unregisterCDMFactory(CDMFactory&); + + static bool supportsKeySystem(const String&); + static bool isPersistentType(MediaKeySessionType); + + static Ref create(Document&, const String& keySystem); + ~CDM(); + + using SupportedConfigurationCallback = std::function)>; + void getSupportedConfiguration(MediaKeySystemConfiguration&& candidateConfiguration, SupportedConfigurationCallback&&); + + const String& keySystem() const { return m_keySystem; } + + void loadAndInitialize(); + RefPtr createInstance(); + bool supportsServerCertificates() const; + bool supportsSessions() const; + bool supportsInitDataType(const AtomicString&) const; + + RefPtr sanitizeInitData(const AtomicString& initDataType, const SharedBuffer&); + bool supportsInitData(const AtomicString& initDataType, const SharedBuffer&); + + RefPtr sanitizeResponse(const SharedBuffer&); + + std::optional sanitizeSessionId(const String& sessionId); + +private: + CDM(Document&, const String& keySystem); + + enum class ConfigurationStatus { + Supported, + NotSupported, + ConsentDenied, + }; + + enum class ConsentStatus { + ConsentDenied, + InformUser, + Allowed, + }; + + enum class AudioVideoType { + Audio, + Video, + }; + + void doSupportedConfigurationStep(MediaKeySystemConfiguration&& candidateConfiguration, MediaKeysRestrictions&&, SupportedConfigurationCallback&&); + std::optional getSupportedConfiguration(const MediaKeySystemConfiguration& candidateConfiguration, MediaKeysRestrictions&); + std::optional> getSupportedCapabilitiesForAudioVideoType(AudioVideoType, const Vector& requestedCapabilities, const MediaKeySystemConfiguration& partialConfiguration, MediaKeysRestrictions&); + + WeakPtr createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); } + + using ConsentStatusCallback = std::function; + void getConsentStatus(MediaKeySystemConfiguration&& accumulatedConfiguration, MediaKeysRestrictions&&, ConsentStatusCallback&&); + String m_keySystem; + std::unique_ptr m_private; + WeakPtrFactory m_weakPtrFactory; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/CDMInstance.h b/Source/WebCore/Modules/encryptedmedia/CDMInstance.h new file mode 100644 index 000000000..d4d84d86e --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/CDMInstance.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "MediaKeyMessageType.h" +#include "MediaKeySessionType.h" +#include "MediaKeyStatus.h" +#include +#include +#include +#include +#include + +namespace WebCore { + +struct MediaKeySystemConfiguration; +class SharedBuffer; + +class CDMInstance : public RefCounted { +public: + virtual ~CDMInstance() { } + + enum SuccessValue { + Failed, + Succeeded, + }; + + using LicenseType = MediaKeySessionType; + using KeyStatus = MediaKeyStatus; + using MessageType = MediaKeyMessageType; + + virtual SuccessValue initializeWithConfiguration(const MediaKeySystemConfiguration&) = 0; + virtual SuccessValue setDistinctiveIdentifiersAllowed(bool) = 0; + virtual SuccessValue setPersistentStateAllowed(bool) = 0; + virtual SuccessValue setServerCertificate(Ref&&) = 0; + + using LicenseCallback = Function&& message, const String& sessionId, bool needsIndividualization, SuccessValue succeeded)>; + virtual void requestLicense(LicenseType, const AtomicString& initDataType, Ref&& initData, LicenseCallback) = 0; + + using KeyStatusVector = Vector, KeyStatus>>; + using Message = std::pair>; + using LicenseUpdateCallback = Function&& changedKeys, std::optional&& changedExpiration, std::optional&& message, SuccessValue succeeded)>; + virtual void updateLicense(const String& sessionId, LicenseType, const SharedBuffer& response, LicenseUpdateCallback) = 0; + + enum class SessionLoadFailure { + None, + NoSessionData, + MismatchedSessionType, + QuotaExceeded, + Other, + }; + + using LoadSessionCallback = Function&&, std::optional&&, std::optional&&, SuccessValue, SessionLoadFailure)>; + virtual void loadSession(LicenseType, const String& sessionId, const String& origin, LoadSessionCallback) = 0; + + using CloseSessionCallback = Function; + virtual void closeSession(const String& sessionId, CloseSessionCallback) = 0; + + using RemoveSessionDataCallback = Function>&&, SuccessValue)>; + virtual void removeSessionData(const String& sessionId, LicenseType, RemoveSessionDataCallback) = 0; + + virtual void storeRecordOfKeyUsage(const String& sessionId) = 0; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/CDMPrivate.h b/Source/WebCore/Modules/encryptedmedia/CDMPrivate.h new file mode 100644 index 000000000..44f27d86e --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/CDMPrivate.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "CDMInstance.h" +#include "MediaKeySessionType.h" +#include "MediaKeysRequirement.h" +#include + +namespace WebCore { + +struct MediaKeySystemConfiguration; +struct MediaKeysRestrictions; + +class CDMPrivate { +public: + virtual ~CDMPrivate() { } + + virtual bool supportsInitDataType(const AtomicString&) const = 0; + virtual bool supportsConfiguration(const MediaKeySystemConfiguration&) const = 0; + virtual bool supportsConfigurationWithRestrictions(const MediaKeySystemConfiguration&, const MediaKeysRestrictions&) const = 0; + virtual bool supportsSessionTypeWithConfiguration(MediaKeySessionType&, const MediaKeySystemConfiguration&) const = 0; + virtual bool supportsRobustness(const String&) const = 0; + virtual MediaKeysRequirement distinctiveIdentifiersRequirement(const MediaKeySystemConfiguration&, const MediaKeysRestrictions&) const = 0; + virtual MediaKeysRequirement persistentStateRequirement(const MediaKeySystemConfiguration&, const MediaKeysRestrictions&) const = 0; + virtual bool distinctiveIdentifiersAreUniquePerOriginAndClearable(const MediaKeySystemConfiguration&) const = 0; + virtual RefPtr createInstance() = 0; + virtual void loadAndInitialize() = 0; + virtual bool supportsServerCertificates() const = 0; + virtual bool supportsSessions() const = 0; + virtual bool supportsInitData(const AtomicString&, const SharedBuffer&) const = 0; + virtual RefPtr sanitizeResponse(const SharedBuffer&) const = 0; + virtual std::optional sanitizeSessionId(const String&) const = 0; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.cpp b/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.cpp new file mode 100644 index 000000000..f5b4758e0 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InitDataRegistry.h" + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "NotImplemented.h" +#include "SharedBuffer.h" +#include "inspector/InspectorValues.h" +#include +#include + +using namespace Inspector; + +namespace WebCore { + +static Vector> extractKeyIDsKeyids(const SharedBuffer& buffer) +{ + // 1. Format + // https://w3c.github.io/encrypted-media/format-registry/initdata/keyids.html#format + String json { buffer.data(), buffer.size() }; + + RefPtr value; + if (!InspectorValue::parseJSON(json, value)) + return { }; + + RefPtr object; + if (!value->asObject(object)) + return { }; + + RefPtr kidsArray; + if (!object->getArray("kids", kidsArray)) + return { }; + + Vector> keyIDs; + for (auto& value : *kidsArray) { + String keyID; + if (!value->asString(keyID)) + continue; + + Vector keyIDData; + if (!WTF::base64URLDecode(keyID, { keyIDData })) + continue; + + Ref keyIDBuffer = SharedBuffer::adoptVector(keyIDData); + keyIDs.append(WTFMove(keyIDBuffer)); + } + + return keyIDs; +} + +static RefPtr sanitizeKeyids(const SharedBuffer& buffer) +{ + // 1. Format + // https://w3c.github.io/encrypted-media/format-registry/initdata/keyids.html#format + Vector> keyIDBuffer = extractKeyIDsKeyids(buffer); + if (keyIDBuffer.isEmpty()) + return nullptr; + + auto object = InspectorObject::create(); + auto kidsArray = InspectorArray::create(); + for (auto& buffer : keyIDBuffer) + kidsArray->pushString(WTF::base64URLEncode(buffer->data(), buffer->size())); + object->setArray("kids", WTFMove(kidsArray)); + + CString jsonData = object->toJSONString().utf8(); + return SharedBuffer::create(jsonData.data(), jsonData.length()); +} + +static RefPtr sanitizeCenc(const SharedBuffer& buffer) +{ + // 4. Common SystemID and PSSH Box Format + // https://w3c.github.io/encrypted-media/format-registry/initdata/cenc.html#common-system + notImplemented(); + return buffer.copy(); +} + +static Vector> extractKeyIDsCenc(const SharedBuffer&) +{ + // 4. Common SystemID and PSSH Box Format + // https://w3c.github.io/encrypted-media/format-registry/initdata/cenc.html#common-system + notImplemented(); + return { }; +} + +static RefPtr sanitizeWebM(const SharedBuffer& buffer) +{ + // 1. Format + // https://w3c.github.io/encrypted-media/format-registry/initdata/webm.html#format + notImplemented(); + return buffer.copy(); +} + +static Vector> extractKeyIDsWebM(const SharedBuffer&) +{ + // 1. Format + // https://w3c.github.io/encrypted-media/format-registry/initdata/webm.html#format + notImplemented(); + return { }; +} + +InitDataRegistry& InitDataRegistry::shared() +{ + static NeverDestroyed registry; + return registry.get(); +} + +InitDataRegistry::InitDataRegistry() +{ + registerInitDataType("keyids", { &sanitizeKeyids, &extractKeyIDsKeyids }); + registerInitDataType("cenc", { &sanitizeCenc, &extractKeyIDsCenc }); + registerInitDataType("webm", { &sanitizeWebM, &extractKeyIDsWebM }); +} + +InitDataRegistry::~InitDataRegistry() = default; + +RefPtr InitDataRegistry::sanitizeInitData(const AtomicString& initDataType, const SharedBuffer& buffer) +{ + auto iter = m_types.find(initDataType); + if (iter == m_types.end() || !iter->value.sanitizeInitData) + return nullptr; + return iter->value.sanitizeInitData(buffer); +} + +Vector> InitDataRegistry::extractKeyIDs(const AtomicString& initDataType, const SharedBuffer& buffer) +{ + auto iter = m_types.find(initDataType); + if (iter == m_types.end() || !iter->value.sanitizeInitData) + return { }; + return iter->value.extractKeyIDs(buffer); +} + +void InitDataRegistry::registerInitDataType(const AtomicString& initDataType, InitDataTypeCallbacks&& callbacks) +{ + ASSERT(!m_types.contains(initDataType)); + m_types.set(initDataType, WTFMove(callbacks)); +} + +} + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.h b/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.h new file mode 100644 index 000000000..39bfa0453 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include +#include +#include +#include +#include +#include +#include + +namespace WebCore { + +class SharedBuffer; + +class InitDataRegistry { +public: + WEBCORE_EXPORT static InitDataRegistry& shared(); + friend class NeverDestroyed; + + RefPtr sanitizeInitData(const AtomicString& initDataType, const SharedBuffer&); + WEBCORE_EXPORT Vector> extractKeyIDs(const AtomicString& initDataType, const SharedBuffer&); + + struct InitDataTypeCallbacks { + using SanitizeInitDataCallback = Function(const SharedBuffer&)>; + using ExtractKeyIDsCallback = Function>(const SharedBuffer&)>; + + SanitizeInitDataCallback sanitizeInitData; + ExtractKeyIDsCallback extractKeyIDs; + }; + void registerInitDataType(const AtomicString& initDataType, InitDataTypeCallbacks&&); + +private: + InitDataRegistry(); + ~InitDataRegistry(); + + HashMap m_types; +}; + +} + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.cpp b/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.cpp new file mode 100644 index 000000000..2b39ec4bf --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MediaKeyMessageEvent.h" + +#if ENABLE(ENCRYPTED_MEDIA) + +namespace WebCore { + +MediaKeyMessageEvent::MediaKeyMessageEvent(const AtomicString& type, const MediaKeyMessageEvent::Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) + , m_messageType(initializer.messageType) + , m_message(initializer.message) +{ +} + +MediaKeyMessageEvent::~MediaKeyMessageEvent() = default; + +EventInterface MediaKeyMessageEvent::eventInterface() const +{ + return MediaKeyMessageEventInterfaceType; +} + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.h b/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.h new file mode 100644 index 000000000..a633c51a6 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "Event.h" +#include "MediaKeyMessageEventInit.h" +#include "MediaKeyMessageType.h" +#include + +namespace WebCore { + +class MediaKeyMessageEvent final : public Event { +public: + using Type = MediaKeyMessageType; + using Init = MediaKeyMessageEventInit; + + virtual ~MediaKeyMessageEvent(); + + static Ref create(const AtomicString& type, const MediaKeyMessageEventInit& initializer, IsTrusted isTrusted = IsTrusted::No) + { + return adoptRef(*new MediaKeyMessageEvent(type, initializer, isTrusted)); + } + + Type messageType() const { return m_messageType; } + RefPtr message() const { return m_message; } + +private: + MediaKeyMessageEvent(const AtomicString&, const MediaKeyMessageEventInit&, IsTrusted); + + // Event + EventInterface eventInterface() const override; + + MediaKeyMessageType m_messageType; + RefPtr m_message; +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.idl b/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.idl new file mode 100644 index 000000000..2cfbc6a53 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.idl @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum MediaKeyMessageType { + "license-request", + "license-renewal", + "license-release", + "individualization-request" +}; + +dictionary MediaKeyMessageEventInit : EventInit { + required MediaKeyMessageType messageType; + required ArrayBuffer message; +}; + +[ + Conditional=ENCRYPTED_MEDIA, + Constructor(DOMString type, MediaKeyMessageEventInit eventInitDict), + EnabledAtRuntime=EncryptedMediaAPI +] interface MediaKeyMessageEvent : Event { + readonly attribute MediaKeyMessageType messageType; + readonly attribute ArrayBuffer message; +}; diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEventInit.h b/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEventInit.h new file mode 100644 index 000000000..0bf130e4e --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEventInit.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "Event.h" +#include "MediaKeyMessageType.h" +#include + +namespace WebCore { + +struct MediaKeyMessageEventInit : EventInit { + MediaKeyMessageEventInit() = default; + + MediaKeyMessageEventInit(MediaKeyMessageType messageType, RefPtr&& message) + : EventInit() + , messageType(messageType) + , message(WTFMove(message)) + { } + + MediaKeyMessageType messageType; + RefPtr message; +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageType.h b/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageType.h new file mode 100644 index 000000000..da7361eaa --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeyMessageType.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +namespace WebCore { + +enum class MediaKeyMessageType { + LicenseRequest, + LicenseRenewal, + LicenseRelease, + IndividualizationRequest +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp b/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp new file mode 100644 index 000000000..220f6dbed --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp @@ -0,0 +1,709 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MediaKeySession.h" + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "CDM.h" +#include "CDMInstance.h" +#include "Document.h" +#include "EventNames.h" +#include "MediaKeyMessageEvent.h" +#include "MediaKeyMessageType.h" +#include "MediaKeyStatusMap.h" +#include "NotImplemented.h" +#include "SecurityOrigin.h" +#include "SharedBuffer.h" +#include + +namespace WebCore { + +Ref MediaKeySession::create(ScriptExecutionContext& context, MediaKeySessionType sessionType, bool useDistinctiveIdentifier, Ref&& implementation, Ref&& instance) +{ + auto session = adoptRef(*new MediaKeySession(context, sessionType, useDistinctiveIdentifier, WTFMove(implementation), WTFMove(instance))); + session->suspendIfNeeded(); + return session; +} + +MediaKeySession::MediaKeySession(ScriptExecutionContext& context, MediaKeySessionType sessionType, bool useDistinctiveIdentifier, Ref&& implementation, Ref&& instance) + : ActiveDOMObject(&context) + , m_expiration(std::numeric_limits::quiet_NaN()) + , m_keyStatuses(MediaKeyStatusMap::create(*this)) + , m_useDistinctiveIdentifier(useDistinctiveIdentifier) + , m_sessionType(sessionType) + , m_implementation(WTFMove(implementation)) + , m_instance(WTFMove(instance)) + , m_eventQueue(*this) + , m_weakPtrFactory(this) +{ + // https://w3c.github.io/encrypted-media/#dom-mediakeys-setservercertificate + // W3C Editor's Draft 09 November 2016 + // createSession(), ctd. + + // 3.1. Let the sessionId attribute be the empty string. + // 3.2. Let the expiration attribute be NaN. + // 3.3. Let the closed attribute be a new promise. + // 3.4. Let key status be a new empty MediaKeyStatusMap object, and initialize it as follows: + // 3.4.1. Let the size attribute be 0. + // 3.5. Let the session type value be sessionType. + // 3.6. Let the uninitialized value be true. + // 3.7. Let the callable value be false. + // 3.8. Let the use distinctive identifier value be this object's use distinctive identifier value. + // 3.9. Let the cdm implementation value be this object's cdm implementation. + // 3.10. Let the cdm instance value be this object's cdm instance. + + UNUSED_PARAM(m_callable); + UNUSED_PARAM(m_sessionType); + UNUSED_PARAM(m_useDistinctiveIdentifier); + UNUSED_PARAM(m_closed); + UNUSED_PARAM(m_uninitialized); +} + +MediaKeySession::~MediaKeySession() +{ + m_keyStatuses->detachSession(); +} + +const String& MediaKeySession::sessionId() const +{ + return m_sessionId; +} + +double MediaKeySession::expiration() const +{ + return m_expiration; +} + +Ref MediaKeySession::keyStatuses() const +{ + return m_keyStatuses.copyRef(); +} + +void MediaKeySession::generateRequest(const AtomicString& initDataType, const BufferSource& initData, Ref&& promise) +{ + // https://w3c.github.io/encrypted-media/#dom-mediakeysession-generaterequest + // W3C Editor's Draft 09 November 2016 + + // When this method is invoked, the user agent must run the following steps: + // 1. If this object is closed, return a promise rejected with an InvalidStateError. + // 2. If this object's uninitialized value is false, return a promise rejected with an InvalidStateError. + if (m_closed || !m_uninitialized) { + promise->reject(INVALID_STATE_ERR); + return; + } + + // 3. Let this object's uninitialized value be false. + m_uninitialized = false; + + // 4. If initDataType is the empty string, return a promise rejected with a newly created TypeError. + // 5. If initData is an empty array, return a promise rejected with a newly created TypeError. + if (initDataType.isEmpty() || !initData.length()) { + promise->reject(TypeError); + return; + } + + // 6. If the Key System implementation represented by this object's cdm implementation value does not support + // initDataType as an Initialization Data Type, return a promise rejected with a NotSupportedError. String + // comparison is case-sensitive. + if (!m_implementation->supportsInitDataType(initDataType)) { + promise->reject(NOT_SUPPORTED_ERR); + return; + } + + // 7. Let init data be a copy of the contents of the initData parameter. + // 8. Let session type be this object's session type. + // 9. Let promise be a new promise. + // 10. Run the following steps in parallel: + m_taskQueue.enqueueTask([this, initData = SharedBuffer::create(initData.data(), initData.length()), initDataType, promise = WTFMove(promise)] () mutable { + // 10.1. If the init data is not valid for initDataType, reject promise with a newly created TypeError. + // 10.2. Let sanitized init data be a validated and sanitized version of init data. + RefPtr sanitizedInitData = m_implementation->sanitizeInitData(initDataType, initData); + + // 10.3. If the preceding step failed, reject promise with a newly created TypeError. + if (!sanitizedInitData) { + promise->reject(TypeError); + return; + } + + // 10.4. If sanitized init data is empty, reject promise with a NotSupportedError. + if (sanitizedInitData->isEmpty()) { + promise->reject(NOT_SUPPORTED_ERR); + return; + } + + // 10.5. Let session id be the empty string. + // 10.6. Let message be null. + // 10.7. Let message type be null. + // 10.8. Let cdm be the CDM instance represented by this object's cdm instance value. + // 10.9. Use the cdm to execute the following steps: + // 10.9.1. If the sanitized init data is not supported by the cdm, reject promise with a NotSupportedError. + if (!m_implementation->supportsInitData(initDataType, *sanitizedInitData)) { + promise->reject(NOT_SUPPORTED_ERR); + return; + } + + // 10.9.2 Follow the steps for the value of session type from the following list: + // ↳ "temporary" + // Let requested license type be a temporary non-persistable license. + // ↳ "persistent-license" + // Let requested license type be a persistable license. + // ↳ "persistent-usage-record" + // 1. Initialize this object's record of key usage as follows. + // Set the list of key IDs known to the session to an empty list. + // Set the first decrypt time to null. + // Set the latest decrypt time to null. + // 2. Let requested license type be a non-persistable license that will + // persist a record of key usage. + + if (m_sessionType == MediaKeySessionType::PersistentUsageRecord) { + m_recordOfKeyUsage.clear(); + m_firstDecryptTime = 0; + m_latestDecryptTime = 0; + } + + m_instance->requestLicense(m_sessionType, initDataType, WTFMove(initData), [this, weakThis = m_weakPtrFactory.createWeakPtr(), promise = WTFMove(promise)] (Ref&& message, const String& sessionId, bool needsIndividualization, CDMInstance::SuccessValue succeeded) mutable { + if (!weakThis) + return; + + // 10.9.3. Let session id be a unique Session ID string. + + MediaKeyMessageType messageType; + if (!needsIndividualization) { + // 10.9.4. If a license request for the requested license type can be generated based on the sanitized init data: + // 10.9.4.1. Let message be a license request for the requested license type generated based on the sanitized init data interpreted per initDataType. + // 10.9.4.2. Let message type be "license-request". + messageType = MediaKeyMessageType::LicenseRequest; + } else { + // 10.9.5. Otherwise: + // 10.9.5.1. Let message be the request that needs to be processed before a license request request for the requested license + // type can be generated based on the sanitized init data. + // 10.9.5.2. Let message type reflect the type of message, either "license-request" or "individualization-request". + messageType = MediaKeyMessageType::IndividualizationRequest; + } + + // 10.10. Queue a task to run the following steps: + m_taskQueue.enqueueTask([this, promise = WTFMove(promise), message = WTFMove(message), messageType, sessionId, succeeded] () mutable { + // 10.10.1. If any of the preceding steps failed, reject promise with a new DOMException whose name is the appropriate error name. + if (succeeded == CDMInstance::SuccessValue::Failed) { + promise->reject(NOT_SUPPORTED_ERR); + return; + } + // 10.10.2. Set the sessionId attribute to session id. + m_sessionId = sessionId; + + // 10.9.3. Let this object's callable value be true. + m_callable = true; + + // 10.9.3. Run the Queue a "message" Event algorithm on the session, providing message type and message. + enqueueMessage(messageType, message); + + // 10.9.3. Resolve promise. + promise->resolve(); + }); + }); + }); + + // 11. Return promise. +} + +void MediaKeySession::load(const String& sessionId, Ref&& promise) +{ + // https://w3c.github.io/encrypted-media/#dom-mediakeysession-load + // W3C Editor's Draft 09 November 2016 + + // 1. If this object is closed, return a promise rejected with an InvalidStateError. + // 2. If this object's uninitialized value is false, return a promise rejected with an InvalidStateError. + if (m_closed || !m_uninitialized) { + promise->reject(INVALID_STATE_ERR); + return; + } + + // 3. Let this object's uninitialized value be false. + m_uninitialized = false; + + // 4. If sessionId is the empty string, return a promise rejected with a newly created TypeError. + // 5. If the result of running the Is persistent session type? algorithm on this object's session type is false, return a promise rejected with a newly created TypeError. + if (sessionId.isEmpty() || m_sessionType == MediaKeySessionType::Temporary) { + promise->reject(TypeError); + return; + } + + // 6. Let origin be the origin of this object's Document. + // This is retrieved in the following task. + + // 7. Let promise be a new promise. + // 8. Run the following steps in parallel: + m_taskQueue.enqueueTask([this, sessionId, promise = WTFMove(promise)] () mutable { + // 8.1. Let sanitized session ID be a validated and/or sanitized version of sessionId. + // 8.2. If the preceding step failed, or if sanitized session ID is empty, reject promise with a newly created TypeError. + std::optional sanitizedSessionId = m_implementation->sanitizeSessionId(sessionId); + if (!sanitizedSessionId || sanitizedSessionId->isEmpty()) { + promise->reject(TypeError); + return; + } + + // 8.3. If there is a MediaKeySession object that is not closed in this object's Document whose sessionId attribute is sanitized session ID, reject promise with a QuotaExceededError. + // FIXME: This needs a global MediaKeySession tracker. + + String origin; + if (auto* document = downcast(scriptExecutionContext())) + origin = document->securityOrigin().toString(); + + // 8.4. Let expiration time be NaN. + // 8.5. Let message be null. + // 8.6. Let message type be null. + // 8.7. Let cdm be the CDM instance represented by this object's cdm instance value. + // 8.8. Use the cdm to execute the following steps: + m_instance->loadSession(m_sessionType, *sanitizedSessionId, origin, [this, weakThis = m_weakPtrFactory.createWeakPtr(), promise = WTFMove(promise), sanitizedSessionId = *sanitizedSessionId] (std::optional&& knownKeys, std::optional&& expiration, std::optional&& message, CDMInstance::SuccessValue succeeded, CDMInstance::SessionLoadFailure failure) mutable { + // 8.8.1. If there is no data stored for the sanitized session ID in the origin, resolve promise with false and abort these steps. + // 8.8.2. If the stored session's session type is not the same as the current MediaKeySession session type, reject promise with a newly created TypeError. + // 8.8.3. Let session data be the data stored for the sanitized session ID in the origin. This must not include data from other origin(s) or that is not associated with an origin. + // 8.8.4. If there is a MediaKeySession object that is not closed in any Document and that represents the session data, reject promise with a QuotaExceededError. + // 8.8.5. Load the session data. + // 8.8.6. If the session data indicates an expiration time for the session, let expiration time be the expiration time in milliseconds since 01 January 1970 UTC. + // 8.8.7. If the CDM needs to send a message: + // 8.8.7.1. Let message be a message generated by the CDM based on the session data. + // 8.8.7.2. Let message type be the appropriate MediaKeyMessageType for the message. + // NOTE: Steps 8.8.1. through 8.8.7. should be implemented in CDMInstance. + + if (succeeded == CDMInstance::SuccessValue::Failed) { + switch (failure) { + case CDMInstance::SessionLoadFailure::NoSessionData: + promise->resolve(false); + return; + case CDMInstance::SessionLoadFailure::MismatchedSessionType: + promise->reject(TypeError); + return; + case CDMInstance::SessionLoadFailure::QuotaExceeded: + promise->reject(QUOTA_EXCEEDED_ERR); + return; + case CDMInstance::SessionLoadFailure::None: + case CDMInstance::SessionLoadFailure::Other: + // In any other case, the session load failure will cause a rejection in the following task. + break; + } + } + + // 8.9. Queue a task to run the following steps: + m_taskQueue.enqueueTask([this, knownKeys = WTFMove(knownKeys), expiration = WTFMove(expiration), message = WTFMove(message), sanitizedSessionId, succeeded, promise = WTFMove(promise)] () mutable { + // 8.9.1. If any of the preceding steps failed, reject promise with a the appropriate error name. + if (succeeded == CDMInstance::SuccessValue::Failed) { + promise->reject(NOT_SUPPORTED_ERR); + return; + } + + // 8.9.2. Set the sessionId attribute to sanitized session ID. + // 8.9.3. Let this object's callable value be true. + m_sessionId = sanitizedSessionId; + m_callable = true; + + // 8.9.4. If the loaded session contains information about any keys (there are known keys), run the Update Key Statuses algorithm on the session, providing each key's key ID along with the appropriate MediaKeyStatus. + if (knownKeys) + updateKeyStatuses(WTFMove(*knownKeys)); + + // 8.9.5. Run the Update Expiration algorithm on the session, providing expiration time. + // This must be run, and NaN is the default value if the CDM instance doesn't provide one. + updateExpiration(expiration.value_or(std::numeric_limits::quiet_NaN())); + + // 8.9.6. If message is not null, run the Queue a "message" Event algorithm on the session, providing message type and message. + if (message) + enqueueMessage(message->first, WTFMove(message->second)); + + // 8.9.7. Resolve promise with true. + promise->resolve(true); + }); + }); + }); + + // 9. Return promise. +} + +void MediaKeySession::update(const BufferSource& response, Ref&& promise) +{ + // https://w3c.github.io/encrypted-media/#dom-mediakeysession-update + // W3C Editor's Draft 09 November 2016 + + // When this method is invoked, the user agent must run the following steps: + // 1. If this object is closed, return a promise rejected with an InvalidStateError. + // 2. If this object's callable value is false, return a promise rejected with an InvalidStateError. + if (m_closed || !m_callable) { + promise->reject(INVALID_STATE_ERR); + return; + } + + // 3. If response is an empty array, return a promise rejected with a newly created TypeError. + if (!response.length()) { + promise->reject(TypeError); + return; + } + + // 4. Let response copy be a copy of the contents of the response parameter. + // 5. Let promise be a new promise. + // 6. Run the following steps in parallel: + m_taskQueue.enqueueTask([this, response = SharedBuffer::create(response.data(), response.length()), promise = WTFMove(promise)] () mutable { + // 6.1. Let sanitized response be a validated and/or sanitized version of response copy. + RefPtr sanitizedResponse = m_implementation->sanitizeResponse(response); + + // 6.2. If the preceding step failed, or if sanitized response is empty, reject promise with a newly created TypeError. + if (!sanitizedResponse || sanitizedResponse->isEmpty()) { + promise->reject(TypeError); + return; + } + + // 6.3. Let message be null. + // 6.4. Let message type be null. + // 6.5. Let session closed be false. + // 6.6. Let cdm be the CDM instance represented by this object's cdm instance value. + // 6.7. Use the cdm to execute the following steps: + m_instance->updateLicense(m_sessionId, m_sessionType, *sanitizedResponse, [this, weakThis = m_weakPtrFactory.createWeakPtr(), promise = WTFMove(promise)] (bool sessionWasClosed, std::optional&& changedKeys, std::optional&& changedExpiration, std::optional&& message, CDMInstance::SuccessValue succeeded) mutable { + if (!weakThis) + return; + + // 6.7.1. If the format of sanitized response is invalid in any way, reject promise with a newly created TypeError. + // 6.7.2. Process sanitized response, following the stipulation for the first matching condition from the following list: + // ↳ If sanitized response contains a license or key(s) + // Process sanitized response, following the stipulation for the first matching condition from the following list: + // ↳ If sessionType is "temporary" and sanitized response does not specify that session data, including any license, key(s), or similar session data it contains, should be stored + // Process sanitized response, not storing any session data. + // ↳ If sessionType is "persistent-license" and sanitized response contains a persistable license + // Process sanitized response, storing the license/key(s) and related session data contained in sanitized response. Such data must be stored such that only the origin of this object's Document can access it. + // ↳ If sessionType is "persistent-usage-record" and sanitized response contains a non-persistable license + // Run the following steps: + // 6.7.2.3.1. Process sanitized response, not storing any session data. + // 6.7.2.3.2. If processing sanitized response results in the addition of keys to the set of known keys, add the key IDs of these keys to this object's record of key usage. + // ↳ Otherwise + // Reject promise with a newly created TypeError. + // ↳ If sanitized response contains a record of license destruction acknowledgement and sessionType is "persistent-license" + // Run the following steps: + // 6.7.2.1. Close the key session and clear all stored session data associated with this object, including the sessionId and record of license destruction. + // 6.7.2.2. Set session closed to true. + // ↳ Otherwise + // Process sanitized response, not storing any session data. + // NOTE: Steps 6.7.1. and 6.7.2. should be implemented in CDMInstance. + + if (succeeded == CDMInstance::SuccessValue::Failed) { + promise->reject(TypeError); + return; + } + + // 6.7.3. If a message needs to be sent to the server, execute the following steps: + // 6.7.3.1. Let message be that message. + // 6.7.3.2. Let message type be the appropriate MediaKeyMessageType for the message. + // 6.8. Queue a task to run the following steps: + m_taskQueue.enqueueTask([this, sessionWasClosed, changedKeys = WTFMove(changedKeys), changedExpiration = WTFMove(changedExpiration), message = WTFMove(message), promise = WTFMove(promise)] () mutable { + // 6.8.1. + if (sessionWasClosed) { + // ↳ If session closed is true: + // Run the Session Closed algorithm on this object. + sessionClosed(); + } else { + // ↳ Otherwise: + // Run the following steps: + // 6.8.1.1. If the set of keys known to the CDM for this object changed or the status of any key(s) changed, run the Update Key Statuses + // algorithm on the session, providing each known key's key ID along with the appropriate MediaKeyStatus. Should additional + // processing be necessary to determine with certainty the status of a key, use "status-pending". Once the additional processing + // for one or more keys has completed, run the Update Key Statuses algorithm again with the actual status(es). + if (changedKeys) + updateKeyStatuses(WTFMove(*changedKeys)); + + // 6.8.1.2. If the expiration time for the session changed, run the Update Expiration algorithm on the session, providing the new expiration time. + if (changedExpiration) + updateExpiration(*changedExpiration); + + // 6.8.1.3. If any of the preceding steps failed, reject promise with a new DOMException whose name is the appropriate error name. + // FIXME: At this point the implementations of preceding steps can't fail. + + // 6.8.1.4. If message is not null, run the Queue a "message" Event algorithm on the session, providing message type and message. + if (message) { + MediaKeyMessageType messageType; + switch (message->first) { + case CDMInstance::MessageType::LicenseRequest: + messageType = MediaKeyMessageType::LicenseRequest; + break; + case CDMInstance::MessageType::LicenseRenewal: + messageType = MediaKeyMessageType::LicenseRenewal; + break; + case CDMInstance::MessageType::LicenseRelease: + messageType = MediaKeyMessageType::LicenseRelease; + break; + case CDMInstance::MessageType::IndividualizationRequest: + messageType = MediaKeyMessageType::IndividualizationRequest; + break; + } + + enqueueMessage(messageType, WTFMove(message->second)); + } + } + + // 6.8.2. Resolve promise. + promise->resolve(); + }); + }); + }); + + // 7. Return promise. +} + +void MediaKeySession::close(Ref&& promise) +{ + // https://w3c.github.io/encrypted-media/#dom-mediakeysession-close + // W3C Editor's Draft 09 November 2016 + + // 1. Let session be the associated MediaKeySession object. + // 2. If session is closed, return a resolved promise. + if (m_closed) { + promise->resolve(); + return; + } + + // 3. If session's callable value is false, return a promise rejected with an InvalidStateError. + if (!m_callable) { + promise->reject(INVALID_STATE_ERR); + return; + } + + // 4. Let promise be a new promise. + // 5. Run the following steps in parallel: + m_taskQueue.enqueueTask([this, promise = WTFMove(promise)] () mutable { + // 5.1. Let cdm be the CDM instance represented by session's cdm instance value. + // 5.2. Use cdm to close the key session associated with session. + m_instance->closeSession(m_sessionId, [this, weakThis = m_weakPtrFactory.createWeakPtr(), promise = WTFMove(promise)] () mutable { + if (!weakThis) + return; + + // 5.3. Queue a task to run the following steps: + m_taskQueue.enqueueTask([this, promise = WTFMove(promise)] () mutable { + // 5.3.1. Run the Session Closed algorithm on the session. + sessionClosed(); + + // 5.3.2. Resolve promise. + promise->resolve(); + }); + }); + }); + + // 6. Return promise. +} + +void MediaKeySession::remove(Ref&& promise) +{ + // https://w3c.github.io/encrypted-media/#dom-mediakeysession-remove + // W3C Editor's Draft 09 November 2016 + + // 1. If this object is closed, return a promise rejected with an InvalidStateError. + // 2. If this object's callable value is false, return a promise rejected with an InvalidStateError. + if (m_closed || !m_callable) { + promise->reject(INVALID_STATE_ERR); + return; + } + + // 3. Let promise be a new promise. + // 4. Run the following steps in parallel: + m_taskQueue.enqueueTask([this, promise = WTFMove(promise)] () mutable { + // 4.1. Let cdm be the CDM instance represented by this object's cdm instance value. + // 4.2. Let message be null. + // 4.3. Let message type be null. + + // 4.4. Use the cdm to execute the following steps: + m_instance->removeSessionData(m_sessionId, m_sessionType, [this, weakThis = m_weakPtrFactory.createWeakPtr(), promise = WTFMove(promise)] (CDMInstance::KeyStatusVector&& keys, std::optional>&& message, CDMInstance::SuccessValue succeeded) mutable { + if (!weakThis) + return; + + // 4.4.1. If any license(s) and/or key(s) are associated with the session: + // 4.4.1.1. Destroy the license(s) and/or key(s) associated with the session. + // 4.4.1.2. Follow the steps for the value of this object's session type from the following list: + // ↳ "temporary" + // 4.4.1.2.1.1 Continue with the following steps. + // ↳ "persistent-license" + // 4.4.1.2.2.1. Let record of license destruction be a record of license destruction for the license represented by this object. + // 4.4.1.2.2.2. Store the record of license destruction. + // 4.4.1.2.2.3. Let message be a message containing or reflecting the record of license destruction. + // ↳ "persistent-usage-record" + // 4.4.1.2.3.1. Store this object's record of key usage. + // 4.4.1.2.3.2. Let message be a message containing or reflecting this object's record of key usage. + // NOTE: Step 4.4.1. should be implemented in CDMInstance. + + // 4.5. Queue a task to run the following steps: + m_taskQueue.enqueueTask([this, keys = WTFMove(keys), message = WTFMove(message), succeeded, promise = WTFMove(promise)] () mutable { + // 4.5.1. Run the Update Key Statuses algorithm on the session, providing all key ID(s) in the session along with the "released" MediaKeyStatus value for each. + updateKeyStatuses(WTFMove(keys)); + + // 4.5.2. Run the Update Expiration algorithm on the session, providing NaN. + updateExpiration(std::numeric_limits::quiet_NaN()); + + // 4.5.3. If any of the preceding steps failed, reject promise with a new DOMException whose name is the appropriate error name. + if (succeeded == CDMInstance::SuccessValue::Failed) { + promise->reject(NOT_SUPPORTED_ERR); + return; + } + + // 4.5.4. Let message type be "license-release". + // 4.5.5. If message is not null, run the Queue a "message" Event algorithm on the session, providing message type and message. + if (message) + enqueueMessage(MediaKeyMessageType::LicenseRelease, *message); + + // 4.5.6. Resolve promise. + promise->resolve(); + }); + }); + }); + + // 5. Return promise. +} + +void MediaKeySession::registerClosedPromise(ClosedPromise&& promise) +{ + ASSERT(!m_closedPromise); + if (m_closed) { + promise.resolve(); + return; + } + m_closedPromise = WTFMove(promise); +} + +void MediaKeySession::enqueueMessage(MediaKeyMessageType messageType, const SharedBuffer& message) +{ + // 6.4.1 Queue a "message" Event + // https://w3c.github.io/encrypted-media/#queue-message + // W3C Editor's Draft 09 November 2016 + + // The following steps are run: + // 1. Let the session be the specified MediaKeySession object. + // 2. Queue a task to create an event named message that does not bubble and is not cancellable using the MediaKeyMessageEvent + // interface with its type attribute set to message and its isTrusted attribute initialized to true, and dispatch it at the + // session. + auto messageEvent = MediaKeyMessageEvent::create(eventNames().messageEvent, {messageType, message.createArrayBuffer()}, Event::IsTrusted::Yes); + m_eventQueue.enqueueEvent(WTFMove(messageEvent)); +} + +void MediaKeySession::updateKeyStatuses(CDMInstance::KeyStatusVector&& inputStatuses) +{ + // https://w3c.github.io/encrypted-media/#update-key-statuses + // W3C Editor's Draft 09 November 2016 + + // 1. Let the session be the associated MediaKeySession object. + // 2. Let the input statuses be the sequence of pairs key ID and associated MediaKeyStatus pairs. + // 3. Let the statuses be session's keyStatuses attribute. + // 4. Run the following steps to replace the contents of statuses: + // 4.1. Empty statuses. + // 4.2. For each pair in input statuses. + // 4.2.1. Let pair be the pair. + // 4.2.2. Insert an entry for pair's key ID into statuses with the value of pair's MediaKeyStatus value. + + static auto toMediaKeyStatus = [] (CDMInstance::KeyStatus status) -> MediaKeyStatus { + switch (status) { + case CDMInstance::KeyStatus::Usable: + return MediaKeyStatus::Usable; + case CDMInstance::KeyStatus::Expired: + return MediaKeyStatus::Expired; + case CDMInstance::KeyStatus::Released: + return MediaKeyStatus::Released; + case CDMInstance::KeyStatus::OutputRestricted: + return MediaKeyStatus::OutputRestricted; + case CDMInstance::KeyStatus::OutputDownscaled: + return MediaKeyStatus::OutputDownscaled; + case CDMInstance::KeyStatus::StatusPending: + return MediaKeyStatus::StatusPending; + case CDMInstance::KeyStatus::InternalError: + return MediaKeyStatus::InternalError; + }; + }; + + m_statuses.clear(); + m_statuses.reserveCapacity(inputStatuses.size()); + for (auto& status : inputStatuses) + m_statuses.uncheckedAppend({ WTFMove(status.first), toMediaKeyStatus(status.second) }); + + // 5. Queue a task to fire a simple event named keystatuseschange at the session. + m_eventQueue.enqueueEvent(Event::create(eventNames().keystatuseschangeEvent, false, false)); + + // 6. Queue a task to run the Attempt to Resume Playback If Necessary algorithm on each of the media element(s) whose mediaKeys attribute is the MediaKeys object that created the session. + // FIXME: Implement. +} + +void MediaKeySession::updateExpiration(double) +{ + notImplemented(); +} + +void MediaKeySession::sessionClosed() +{ + // https://w3c.github.io/encrypted-media/#session-closed + // W3C Editor's Draft 09 November 2016 + + // 1. Let session be the associated MediaKeySession object. + // 2. If session's session type is "persistent-usage-record", execute the following steps in parallel: + if (m_sessionType == MediaKeySessionType::PersistentUsageRecord) { + // 2.1. Let cdm be the CDM instance represented by session's cdm instance value. + // 2.2. Use cdm to store session's record of key usage, if it exists. + m_instance->storeRecordOfKeyUsage(m_sessionId); + } + + // 3. Run the Update Key Statuses algorithm on the session, providing an empty sequence. + updateKeyStatuses({ }); + + // 4. Run the Update Expiration algorithm on the session, providing NaN. + updateExpiration(std::numeric_limits::quiet_NaN()); + + // Let's consider the session closed before any promise on the 'closed' attribute is resolved. + m_closed = true; + + // 5. Let promise be the closed attribute of the session. + // 6. Resolve promise. + if (m_closedPromise) + m_closedPromise->resolve(); +} + +bool MediaKeySession::hasPendingActivity() const +{ + notImplemented(); + return false; +} + +const char* MediaKeySession::activeDOMObjectName() const +{ + notImplemented(); + return "MediaKeySession"; +} + +bool MediaKeySession::canSuspendForDocumentSuspension() const +{ + notImplemented(); + return false; +} + +void MediaKeySession::stop() +{ + notImplemented(); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySession.h b/Source/WebCore/Modules/encryptedmedia/MediaKeySession.h new file mode 100644 index 000000000..0ce875d89 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySession.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "ActiveDOMObject.h" +#include "CDMInstance.h" +#include "EventTarget.h" +#include "GenericEventQueue.h" +#include "GenericTaskQueue.h" +#include "JSDOMPromise.h" +#include "MediaKeyMessageType.h" +#include "MediaKeySessionType.h" +#include "MediaKeyStatus.h" +#include +#include +#include +#include + +namespace WebCore { + +class BufferSource; +class CDM; +class MediaKeyStatusMap; +class MediaKeys; +class SharedBuffer; + +class MediaKeySession final : public RefCounted, public EventTargetWithInlineData, public ActiveDOMObject { +public: + static Ref create(ScriptExecutionContext&, MediaKeySessionType, bool useDistinctiveIdentifier, Ref&&, Ref&&); + virtual ~MediaKeySession(); + + using RefCounted::ref; + using RefCounted::deref; + + const String& sessionId() const; + double expiration() const; + Ref keyStatuses() const; + + void generateRequest(const AtomicString&, const BufferSource&, Ref&&); + void load(const String&, Ref&&); + void update(const BufferSource&, Ref&&); + void close(Ref&&); + void remove(Ref&&); + + using ClosedPromise = DOMPromise; + void registerClosedPromise(ClosedPromise&&); + + const Vector, MediaKeyStatus>>& statuses() const { return m_statuses; } + +private: + MediaKeySession(ScriptExecutionContext&, MediaKeySessionType, bool useDistinctiveIdentifier, Ref&&, Ref&&); + void enqueueMessage(MediaKeyMessageType, const SharedBuffer&); + void updateKeyStatuses(CDMInstance::KeyStatusVector&&); + void updateExpiration(double); + void sessionClosed(); + + // EventTarget + EventTargetInterface eventTargetInterface() const override { return MediaKeySessionEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const override { return ActiveDOMObject::scriptExecutionContext(); } + void refEventTarget() override { ref(); } + void derefEventTarget() override { deref(); } + + // ActiveDOMObject + bool hasPendingActivity() const override; + const char* activeDOMObjectName() const override; + bool canSuspendForDocumentSuspension() const override; + void stop() override; + + String m_sessionId; + double m_expiration; + std::optional m_closedPromise; + Ref m_keyStatuses; + bool m_closed { false }; + bool m_uninitialized { true }; + bool m_callable { false }; + bool m_useDistinctiveIdentifier; + MediaKeySessionType m_sessionType; + Ref m_implementation; + Ref m_instance; + GenericEventQueue m_eventQueue; + GenericTaskQueue m_taskQueue; + Vector> m_recordOfKeyUsage; + double m_firstDecryptTime { 0 }; + double m_latestDecryptTime { 0 }; + Vector, MediaKeyStatus>> m_statuses; + WeakPtrFactory m_weakPtrFactory; +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySession.idl b/Source/WebCore/Modules/encryptedmedia/MediaKeySession.idl new file mode 100644 index 000000000..52223a8b8 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySession.idl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=ENCRYPTED_MEDIA, + EnabledAtRuntime=EncryptedMediaAPI +] interface MediaKeySession : EventTarget { + readonly attribute DOMString sessionId; + readonly attribute unrestricted double expiration; + [CustomGetter, CachedAttribute] readonly attribute Promise closed; + readonly attribute MediaKeyStatusMap keyStatuses; + attribute EventHandler onkeystatuseschange; + attribute EventHandler onmessage; + Promise generateRequest(DOMString initDataType, BufferSource initData); + Promise load(DOMString sessionId); + Promise update(BufferSource response); + Promise close(); + Promise remove(); +}; diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySessionType.h b/Source/WebCore/Modules/encryptedmedia/MediaKeySessionType.h new file mode 100644 index 000000000..f31c87c1a --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySessionType.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +namespace WebCore { + +enum class MediaKeySessionType { + Temporary, + PersistentUsageRecord, + PersistentLicense +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySessionType.idl b/Source/WebCore/Modules/encryptedmedia/MediaKeySessionType.idl new file mode 100644 index 000000000..a92e8f6ad --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySessionType.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=ENCRYPTED_MEDIA, + ImplementedAs=MediaKeySessionType, + ExportMacro=WEBCORE_TESTSUPPORT_EXPORT +] +enum MediaKeySessionType { + "temporary", + "persistent-usage-record", + "persistent-license" +}; diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeyStatus.h b/Source/WebCore/Modules/encryptedmedia/MediaKeyStatus.h new file mode 100644 index 000000000..c3cf4c897 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeyStatus.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +namespace WebCore { + +enum class MediaKeyStatus { + Usable, + Expired, + Released, + OutputRestricted, + OutputDownscaled, + StatusPending, + InternalError +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.cpp b/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.cpp new file mode 100644 index 000000000..fc0451eed --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MediaKeyStatusMap.h" + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "JSMediaKeyStatusMap.h" +#include "MediaKeySession.h" +#include "SharedBuffer.h" + +namespace WebCore { + +MediaKeyStatusMap::MediaKeyStatusMap(const MediaKeySession& session) + : m_session(&session) +{ +} + +MediaKeyStatusMap::~MediaKeyStatusMap() = default; + +void MediaKeyStatusMap::detachSession() +{ + m_session = nullptr; +} + +unsigned long MediaKeyStatusMap::size() +{ + if (!m_session) + return 0; + return m_session->statuses().size(); +} + +static bool keyIdsMatch(const SharedBuffer& a, const BufferSource& b) +{ + auto length = a.size(); + if (!length || length != b.length()) + return false; + return !std::memcmp(a.data(), b.data(), length); +} + +bool MediaKeyStatusMap::has(const BufferSource& keyId) +{ + if (!m_session) + return false; + + auto& statuses = m_session->statuses(); + return std::any_of(statuses.begin(), statuses.end(), + [&keyId] (auto& it) { return keyIdsMatch(it.first, keyId); }); +} + +JSC::JSValue MediaKeyStatusMap::get(JSC::ExecState& state, const BufferSource& keyId) +{ + if (!m_session) + return JSC::jsUndefined(); + + auto& statuses = m_session->statuses(); + auto it = std::find_if(statuses.begin(), statuses.end(), + [&keyId] (auto& it) { return keyIdsMatch(it.first, keyId); }); + + if (it == statuses.end()) + return JSC::jsUndefined(); + return convertEnumerationToJS(state, it->second); +} + +MediaKeyStatusMap::Iterator::Iterator(MediaKeyStatusMap& map) + : m_map(map) +{ +} + +std::optional> MediaKeyStatusMap::Iterator::next() +{ + if (!m_map->m_session) + return std::nullopt; + + auto& statuses = m_map->m_session->statuses(); + if (m_index >= statuses.size()) + return std::nullopt; + + auto& pair = statuses[m_index++]; + auto buffer = ArrayBuffer::create(pair.first->data(), pair.first->size()); + return WTF::KeyValuePair { RefPtr(WTFMove(buffer)), pair.second }; +} + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.h b/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.h new file mode 100644 index 000000000..34c5ed795 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "BufferSource.h" +#include "MediaKeyStatus.h" +#include +#include +#include +#include + +namespace WebCore { + +class MediaKeySession; +class SharedBuffer; + +class MediaKeyStatusMap : public RefCounted { +public: + using Status = MediaKeyStatus; + + static Ref create(const MediaKeySession& session) + { + return adoptRef(*new MediaKeyStatusMap(session)); + } + + virtual ~MediaKeyStatusMap(); + + void detachSession(); + + unsigned long size(); + bool has(const BufferSource&); + JSC::JSValue get(JSC::ExecState&, const BufferSource&); + + class Iterator { + public: + explicit Iterator(MediaKeyStatusMap&); + std::optional> next(); + + private: + Ref m_map; + size_t m_index { 0 }; + }; + Iterator createIterator() { return Iterator(*this); } + +private: + MediaKeyStatusMap(const MediaKeySession&); + + const MediaKeySession* m_session; +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.idl b/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.idl new file mode 100644 index 000000000..b3215ae83 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.idl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum MediaKeyStatus { + "usable", + "expired", + "released", + "output-restricted", + "output-downscaled", + "status-pending", + "internal-error" +}; + +[ + Conditional=ENCRYPTED_MEDIA, + EnabledAtRuntime=EncryptedMediaAPI +] interface MediaKeyStatusMap { + iterable; + readonly attribute unsigned long size; + boolean has(BufferSource keyId); + [CallWith=ScriptState] any get(BufferSource keyId); +}; diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.cpp b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.cpp new file mode 100644 index 000000000..d84a48d06 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MediaKeySystemAccess.h" + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "CDM.h" +#include "CDMInstance.h" +#include "JSMediaKeys.h" +#include "MediaKeySystemConfiguration.h" +#include "MediaKeys.h" + +namespace WebCore { + +Ref MediaKeySystemAccess::create(const String& keySystem, MediaKeySystemConfiguration&& configuration, Ref&& implementation) +{ + return adoptRef(*new MediaKeySystemAccess(keySystem, WTFMove(configuration), WTFMove(implementation))); +} + +MediaKeySystemAccess::MediaKeySystemAccess(const String& keySystem, MediaKeySystemConfiguration&& configuration, Ref&& implementation) + : m_keySystem(keySystem) + , m_configuration(new MediaKeySystemConfiguration(WTFMove(configuration))) + , m_implementation(WTFMove(implementation)) +{ +} + +MediaKeySystemAccess::~MediaKeySystemAccess() = default; + +void MediaKeySystemAccess::createMediaKeys(Ref&& promise) +{ + // https://w3c.github.io/encrypted-media/#dom-mediakeysystemaccess-createmediakeys + // W3C Editor's Draft 09 November 2016 + + // When this method is invoked, the user agent must run the following steps: + // 1. Let promise be a new promise. + // 2. Run the following steps in parallel: + m_taskQueue.enqueueTask([this, promise = WTFMove(promise)] () mutable { + // 2.1. Let configuration be the value of this object's configuration value. + // 2.2. Let use distinctive identifier be true if the value of configuration's distinctiveIdentifier member is "required" and false otherwise. + bool useDistinctiveIdentifier = m_configuration->distinctiveIdentifier == MediaKeysRequirement::Required; + + // 2.3. Let persistent state allowed be true if the value of configuration's persistentState member is "required" and false otherwise. + bool persistentStateAllowed = m_configuration->persistentState == MediaKeysRequirement::Required; + + // 2.4. Load and initialize the Key System implementation represented by this object's cdm implementation value if necessary. + m_implementation->loadAndInitialize(); + + // 2.5. Let instance be a new instance of the Key System implementation represented by this object's cdm implementation value. + auto instance = m_implementation->createInstance(); + if (!instance) { + promise->reject(INVALID_STATE_ERR); + return; + } + + // 2.6. Initialize instance to enable, disable and/or select Key System features using configuration. + if (instance->initializeWithConfiguration(*m_configuration) == CDMInstance::Failed) { + promise->reject(NotAllowedError); + return; + } + + // 2.7. If use distinctive identifier is false, prevent instance from using Distinctive Identifier(s) and Distinctive Permanent Identifier(s). + if (!useDistinctiveIdentifier && instance->setDistinctiveIdentifiersAllowed(false) == CDMInstance::Failed) { + promise->reject(NotAllowedError); + return; + } + + // 2.8. If persistent state allowed is false, prevent instance from persisting any state related to the application or origin of this object's Document. + if (!persistentStateAllowed && instance->setPersistentStateAllowed(false) == CDMInstance::Failed) { + promise->reject(NotAllowedError); + return; + } + + // 2.9. If any of the preceding steps failed, reject promise with a new DOMException whose name is the appropriate error name. + // 2.10. Let media keys be a new MediaKeys object, and initialize it as follows: + // 2.10.1. Let the use distinctive identifier value be use distinctive identifier. + // 2.10.2. Let the persistent state allowed value be persistent state allowed. + // 2.10.3. Let the supported session types value be be the value of configuration's sessionTypes member. + // 2.10.4. Let the cdm implementation value be this object's cdm implementation value. + // 2.10.5. Let the cdm instance value be instance. + auto mediaKeys = MediaKeys::create(useDistinctiveIdentifier, persistentStateAllowed, m_configuration->sessionTypes, m_implementation.copyRef(), instance.releaseNonNull()); + + // 2.11. Resolve promise with media keys. + promise->resolveWithNewlyCreated>(WTFMove(mediaKeys)); + }); + + // 3. Return promise. +} + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.h b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.h new file mode 100644 index 000000000..a113e2601 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "GenericTaskQueue.h" +#include "JSDOMPromise.h" +#include +#include + +namespace WebCore { + +class CDM; +class MediaKeys; + +struct MediaKeySystemConfiguration; + +class MediaKeySystemAccess : public RefCounted { +public: + static Ref create(const String& keySystem, MediaKeySystemConfiguration&&, Ref&&); + ~MediaKeySystemAccess(); + + const String& keySystem() const { return m_keySystem; } + const MediaKeySystemConfiguration& getConfiguration() const { return *m_configuration; } + void createMediaKeys(Ref&&); + +private: + MediaKeySystemAccess(const String& keySystem, MediaKeySystemConfiguration&&, Ref&&); + + String m_keySystem; + std::unique_ptr m_configuration; + Ref m_implementation; + GenericTaskQueue m_taskQueue; +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.idl b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.idl new file mode 100644 index 000000000..30a825e15 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.idl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=ENCRYPTED_MEDIA, + EnabledAtRuntime=EncryptedMediaAPI, + ImplementationLacksVTable, +] interface MediaKeySystemAccess { + readonly attribute DOMString keySystem; + MediaKeySystemConfiguration getConfiguration(); + Promise createMediaKeys(); +}; diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySystemConfiguration.h b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemConfiguration.h new file mode 100644 index 000000000..7f358fd9d --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemConfiguration.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "MediaKeySessionType.h" +#include "MediaKeySystemMediaCapability.h" +#include "MediaKeysRequirement.h" +#include +#include + +namespace WebCore { + +struct MediaKeySystemConfiguration { + using KeysRequirement = MediaKeysRequirement; + + String label; + Vector initDataTypes; + Vector audioCapabilities; + Vector videoCapabilities; + MediaKeysRequirement distinctiveIdentifier; + MediaKeysRequirement persistentState; + Vector sessionTypes; +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySystemConfiguration.idl b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemConfiguration.idl new file mode 100644 index 000000000..466cc800f --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemConfiguration.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=ENCRYPTED_MEDIA, + JSGenerateToJSObject +] dictionary MediaKeySystemConfiguration { + DOMString label = ""; + sequence initDataTypes = []; + sequence audioCapabilities = []; + sequence videoCapabilities = []; + MediaKeysRequirement distinctiveIdentifier = "optional"; + MediaKeysRequirement persistentState = "optional"; + sequence sessionTypes; +}; diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySystemMediaCapability.h b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemMediaCapability.h new file mode 100644 index 000000000..ba7200542 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemMediaCapability.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include + +namespace WebCore { + +struct MediaKeySystemMediaCapability { + String contentType; + String robustness; +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySystemMediaCapability.idl b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemMediaCapability.idl new file mode 100644 index 000000000..3204794da --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemMediaCapability.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=ENCRYPTED_MEDIA, + JSGenerateToJSObject, +] dictionary MediaKeySystemMediaCapability { + DOMString contentType = ""; + DOMString robustness = ""; +}; diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeys.cpp b/Source/WebCore/Modules/encryptedmedia/MediaKeys.cpp new file mode 100644 index 000000000..c8bc5ae29 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeys.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MediaKeys.h" + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "CDM.h" +#include "CDMInstance.h" +#include "MediaKeySession.h" +#include "SharedBuffer.h" + +namespace WebCore { + +MediaKeys::MediaKeys(bool useDistinctiveIdentifier, bool persistentStateAllowed, const Vector& supportedSessionTypes, Ref&& implementation, Ref&& instance) + : m_useDistinctiveIdentifier(useDistinctiveIdentifier) + , m_persistentStateAllowed(persistentStateAllowed) + , m_supportedSessionTypes(supportedSessionTypes) + , m_implementation(WTFMove(implementation)) + , m_instance(WTFMove(instance)) +{ +} + +MediaKeys::~MediaKeys() = default; + +ExceptionOr> MediaKeys::createSession(ScriptExecutionContext& context, MediaKeySessionType sessionType) +{ + // https://w3c.github.io/encrypted-media/#dom-mediakeys-setservercertificate + // W3C Editor's Draft 09 November 2016 + + // When this method is invoked, the user agent must run the following steps: + // 1. If this object's supported session types value does not contain sessionType, throw [WebIDL] a NotSupportedError. + if (!m_supportedSessionTypes.contains(sessionType)) + return Exception(NOT_SUPPORTED_ERR); + + // 2. If the implementation does not support MediaKeySession operations in the current state, throw [WebIDL] an InvalidStateError. + if (!m_implementation->supportsSessions()) + return Exception(INVALID_STATE_ERR); + + // 3. Let session be a new MediaKeySession object, and initialize it as follows: + // NOTE: Continued in MediaKeySession. + // 4. Return session. + return MediaKeySession::create(context, sessionType, m_useDistinctiveIdentifier, m_implementation.copyRef(), m_instance.copyRef()); +} + +void MediaKeys::setServerCertificate(const BufferSource& serverCertificate, Ref&& promise) +{ + // https://w3c.github.io/encrypted-media/#dom-mediakeys-setservercertificate + // W3C Editor's Draft 09 November 2016 + + // When this method is invoked, the user agent must run the following steps: + // 1. If the Key System implementation represented by this object's cdm implementation value does not support + // server certificates, return a promise resolved with false. + if (!m_implementation->supportsServerCertificates()) { + promise->resolve(false); + return; + } + + // 2. If serverCertificate is an empty array, return a promise rejected with a new a newly created TypeError. + if (!serverCertificate.length()) { + promise->reject(TypeError); + return; + } + + // 3. Let certificate be a copy of the contents of the serverCertificate parameter. + auto certificate = SharedBuffer::create(serverCertificate.data(), serverCertificate.length()); + + // 4. Let promise be a new promise. + // 5. Run the following steps in parallel: + + m_taskQueue.enqueueTask([this, certificate = WTFMove(certificate), promise = WTFMove(promise)] () mutable { + // 5.1. Use this object's cdm instance to process certificate. + if (m_instance->setServerCertificate(WTFMove(certificate)) == CDMInstance::Failed) { + // 5.2. If the preceding step failed, resolve promise with a new DOMException whose name is the appropriate error name. + promise->reject(INVALID_STATE_ERR); + return; + } + + // 5.1. Resolve promise with true. + promise->resolve(true); + }); + + // 6. Return promise. +} + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeys.h b/Source/WebCore/Modules/encryptedmedia/MediaKeys.h new file mode 100644 index 000000000..bd80bd2c9 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeys.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "ExceptionOr.h" +#include "GenericTaskQueue.h" +#include "JSDOMPromise.h" +#include "MediaKeySessionType.h" +#include +#include + +namespace WebCore { + +class CDM; +class CDMInstance; +class BufferSource; +class MediaKeySession; + +class MediaKeys : public RefCounted { +public: + using KeySessionType = MediaKeySessionType; + + static Ref create(bool useDistinctiveIdentifier, bool persistentStateAllowed, const Vector& supportedSessionTypes, Ref&& implementation, Ref&& instance) + { + return adoptRef(*new MediaKeys(useDistinctiveIdentifier, persistentStateAllowed, supportedSessionTypes, WTFMove(implementation), WTFMove(instance))); + } + + ~MediaKeys(); + + ExceptionOr> createSession(ScriptExecutionContext&, MediaKeySessionType); + + void setServerCertificate(const BufferSource&, Ref&&); + +protected: + MediaKeys(bool useDistinctiveIdentifier, bool persistentStateAllowed, const Vector&, Ref&&, Ref&&); + + bool m_useDistinctiveIdentifier; + bool m_persistentStateAllowed; + Vector m_supportedSessionTypes; + Ref m_implementation; + Ref m_instance; + GenericTaskQueue m_taskQueue; +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeys.idl b/Source/WebCore/Modules/encryptedmedia/MediaKeys.idl new file mode 100644 index 000000000..781850420 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeys.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=ENCRYPTED_MEDIA, + EnabledAtRuntime=EncryptedMediaAPI, + ImplementationLacksVTable, +] interface MediaKeys { + [CallWith=ScriptExecutionContext, MayThrowException] MediaKeySession createSession(optional MediaKeySessionType sessionType = "temporary"); + Promise setServerCertificate(BufferSource serverCertificate); +}; diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeysRequirement.h b/Source/WebCore/Modules/encryptedmedia/MediaKeysRequirement.h new file mode 100644 index 000000000..c6af6b4fe --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeysRequirement.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +namespace WebCore { + +enum class MediaKeysRequirement { + Required, + Optional, + NotAllowed +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeysRequirement.idl b/Source/WebCore/Modules/encryptedmedia/MediaKeysRequirement.idl new file mode 100644 index 000000000..a30897918 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeysRequirement.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=ENCRYPTED_MEDIA, + ImplementedAs=MediaKeysRequirement, + ExportMacro=WEBCORE_TESTSUPPORT_EXPORT +] +enum MediaKeysRequirement { + "required", + "optional", + "not-allowed" +}; + diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeysRestrictions.h b/Source/WebCore/Modules/encryptedmedia/MediaKeysRestrictions.h new file mode 100644 index 000000000..0be8567f6 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/MediaKeysRestrictions.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include + +namespace WebCore { + +struct MediaKeysRestrictions { + bool distinctiveIdentifierDenied { false }; + bool persistentStateDenied { false }; + HashSet deniedSessionTypes; +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/NavigatorEME.cpp b/Source/WebCore/Modules/encryptedmedia/NavigatorEME.cpp new file mode 100644 index 000000000..edc9a0c7b --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/NavigatorEME.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "NavigatorEME.h" + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "CDM.h" +#include "Document.h" +#include "JSMediaKeySystemAccess.h" + +namespace WebCore { + +static void tryNextSupportedConfiguration(RefPtr&& implementation, Vector&& supportedConfigurations, RefPtr&&); + +void NavigatorEME::requestMediaKeySystemAccess(Navigator&, Document& document, const String& keySystem, Vector&& supportedConfigurations, Ref&& promise) +{ + // https://w3c.github.io/encrypted-media/#dom-navigator-requestmediakeysystemaccess + // W3C Editor's Draft 09 November 2016 + + // When this method is invoked, the user agent must run the following steps: + // 1. If keySystem is the empty string, return a promise rejected with a newly created TypeError. + // 2. If supportedConfigurations is empty, return a promise rejected with a newly created TypeError. + if (keySystem.isEmpty() || supportedConfigurations.isEmpty()) { + promise->reject(TypeError); + return; + } + + document.postTask([keySystem, supportedConfigurations = WTFMove(supportedConfigurations), promise = WTFMove(promise), &document] (ScriptExecutionContext&) mutable { + // 3. Let document be the calling context's Document. + // 4. Let origin be the origin of document. + // 5. Let promise be a new promise. + // 6. Run the following steps in parallel: + // 6.1. If keySystem is not one of the Key Systems supported by the user agent, reject promise with a NotSupportedError. + // String comparison is case-sensitive. + if (!CDM::supportsKeySystem(keySystem)) { + promise->reject(NOT_SUPPORTED_ERR); + return; + } + + // 6.2. Let implementation be the implementation of keySystem. + RefPtr implementation = CDM::create(document, keySystem); + tryNextSupportedConfiguration(WTFMove(implementation), WTFMove(supportedConfigurations), WTFMove(promise)); + }); +} + +static void tryNextSupportedConfiguration(RefPtr&& implementation, Vector&& supportedConfigurations, RefPtr&& promise) +{ + // 6.3. For each value in supportedConfigurations: + if (!supportedConfigurations.isEmpty()) { + // 6.3.1. Let candidate configuration be the value. + // 6.3.2. Let supported configuration be the result of executing the Get Supported Configuration + // algorithm on implementation, candidate configuration, and origin. + MediaKeySystemConfiguration candidateCofiguration = WTFMove(supportedConfigurations.first()); + supportedConfigurations.remove(0); + + CDM::SupportedConfigurationCallback callback = [implementation = implementation, supportedConfigurations = WTFMove(supportedConfigurations), promise] (std::optional supportedConfiguration) mutable { + // 6.3.3. If supported configuration is not NotSupported, run the following steps: + if (supportedConfiguration) { + // 6.3.3.1. Let access be a new MediaKeySystemAccess object, and initialize it as follows: + // 6.3.3.1.1. Set the keySystem attribute to keySystem. + // 6.3.3.1.2. Let the configuration value be supported configuration. + // 6.3.3.1.3. Let the cdm implementation value be implementation. + auto access = MediaKeySystemAccess::create(implementation->keySystem(), WTFMove(supportedConfiguration.value()), implementation.releaseNonNull()); + // 6.3.3.2. Resolve promise with access and abort the parallel steps of this algorithm. + promise->resolveWithNewlyCreated>(WTFMove(access)); + return; + } + + tryNextSupportedConfiguration(WTFMove(implementation), WTFMove(supportedConfigurations), WTFMove(promise)); + }; + implementation->getSupportedConfiguration(WTFMove(candidateCofiguration), WTFMove(callback)); + return; + } + + + // 6.4. Reject promise with a NotSupportedError. + promise->reject(NOT_SUPPORTED_ERR); +} + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/NavigatorEME.h b/Source/WebCore/Modules/encryptedmedia/NavigatorEME.h new file mode 100644 index 000000000..4c204e941 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/NavigatorEME.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "JSDOMPromise.h" +#include "MediaKeySystemConfiguration.h" +#include "Supplementable.h" + +namespace WebCore { + +class MediaKeySystemAccess; +class Navigator; +class Page; + +class NavigatorEME final : public Supplement { +public: + static void requestMediaKeySystemAccess(Navigator&, Document&, const String&, Vector&&, Ref&&); +}; + +} // namespace WebCore + +#endif // ENABLE(ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/NavigatorEME.idl b/Source/WebCore/Modules/encryptedmedia/NavigatorEME.idl new file mode 100644 index 000000000..99df263b2 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/NavigatorEME.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=ENCRYPTED_MEDIA, + EnabledAtRuntime=EncryptedMediaAPI +] partial interface Navigator { + [CallWith=Document] Promise requestMediaKeySystemAccess(DOMString keySystem, sequence supportedConfiguration); +}; diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDM.cpp b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDM.cpp new file mode 100644 index 000000000..4e333f856 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDM.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include "LegacyCDM.h" + +#include "LegacyCDMPrivateClearKey.h" +#include "LegacyCDMPrivateMediaPlayer.h" +#include "LegacyCDMSession.h" +#include "MediaPlayer.h" +#include "WebKitMediaKeys.h" +#include +#include + +#if PLATFORM(MAC) && ENABLE(MEDIA_SOURCE) +#include "CDMPrivateMediaSourceAVFObjC.h" +#endif + +namespace WebCore { + +struct CDMFactory { + WTF_MAKE_NONCOPYABLE(CDMFactory); WTF_MAKE_FAST_ALLOCATED; +public: + CDMFactory(CreateCDM constructor, CDMSupportsKeySystem supportsKeySystem, CDMSupportsKeySystemAndMimeType supportsKeySystemAndMimeType) + : constructor(constructor) + , supportsKeySystem(supportsKeySystem) + , supportsKeySystemAndMimeType(supportsKeySystemAndMimeType) + { + } + + CreateCDM constructor; + CDMSupportsKeySystem supportsKeySystem; + CDMSupportsKeySystemAndMimeType supportsKeySystemAndMimeType; +}; + +static Vector& installedCDMFactories() +{ + static NeverDestroyed> cdms; + static bool queriedCDMs = false; + if (!queriedCDMs) { + queriedCDMs = true; + + cdms.get().append(new CDMFactory([](CDM* cdm) { return std::make_unique(cdm); }, + CDMPrivateClearKey::supportsKeySystem, CDMPrivateClearKey::supportsKeySystemAndMimeType)); + + // FIXME: initialize specific UA CDMs. http://webkit.org/b/109318, http://webkit.org/b/109320 + cdms.get().append(new CDMFactory([](CDM* cdm) { return std::make_unique(cdm); }, + CDMPrivateMediaPlayer::supportsKeySystem, CDMPrivateMediaPlayer::supportsKeySystemAndMimeType)); + +#if PLATFORM(MAC) && ENABLE(MEDIA_SOURCE) + cdms.get().append(new CDMFactory([](CDM* cdm) { return std::make_unique(cdm); }, + CDMPrivateMediaSourceAVFObjC::supportsKeySystem, CDMPrivateMediaSourceAVFObjC::supportsKeySystemAndMimeType)); +#endif + } + + return cdms; +} + +void CDM::registerCDMFactory(CreateCDM constructor, CDMSupportsKeySystem supportsKeySystem, CDMSupportsKeySystemAndMimeType supportsKeySystemAndMimeType) +{ + installedCDMFactories().append(new CDMFactory(constructor, supportsKeySystem, supportsKeySystemAndMimeType)); +} + +static CDMFactory* CDMFactoryForKeySystem(const String& keySystem) +{ + for (auto& factory : installedCDMFactories()) { + if (factory->supportsKeySystem(keySystem)) + return factory; + } + return 0; +} + +bool CDM::supportsKeySystem(const String& keySystem) +{ + return CDMFactoryForKeySystem(keySystem); +} + +bool CDM::keySystemSupportsMimeType(const String& keySystem, const String& mimeType) +{ + if (CDMFactory* factory = CDMFactoryForKeySystem(keySystem)) + return factory->supportsKeySystemAndMimeType(keySystem, mimeType); + return false; +} + +std::unique_ptr CDM::create(const String& keySystem) +{ + if (!supportsKeySystem(keySystem)) + return nullptr; + + return std::make_unique(keySystem); +} + +CDM::CDM(const String& keySystem) + : m_keySystem(keySystem) + , m_client(nullptr) +{ + m_private = CDMFactoryForKeySystem(keySystem)->constructor(this); +} + +CDM::~CDM() +{ +} + +bool CDM::supportsMIMEType(const String& mimeType) const +{ + return m_private->supportsMIMEType(mimeType); +} + +std::unique_ptr CDM::createSession(CDMSessionClient& client) +{ + auto session = m_private->createSession(&client); + if (mediaPlayer()) + mediaPlayer()->setCDMSession(session.get()); + return session; +} + +MediaPlayer* CDM::mediaPlayer() const +{ + if (!m_client) + return 0; + return m_client->cdmMediaPlayer(this); +} + +} + +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDM.h b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDM.h new file mode 100644 index 000000000..2a00b7e0f --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDM.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include "LegacyCDMSession.h" +#include +#include +#include + +namespace WebCore { + +class CDM; +class CDMPrivateInterface; +class MediaPlayer; + +typedef std::function (CDM*)> CreateCDM; +typedef bool (*CDMSupportsKeySystem)(const String&); +typedef bool (*CDMSupportsKeySystemAndMimeType)(const String&, const String&); + +class CDMClient { +public: + virtual ~CDMClient() { } + + virtual MediaPlayer* cdmMediaPlayer(const CDM*) const = 0; +}; + +class CDM { +public: + explicit CDM(const String& keySystem); + + enum CDMErrorCode { NoError, UnknownError, ClientError, ServiceError, OutputError, HardwareChangeError, DomainError }; + static bool supportsKeySystem(const String&); + static bool keySystemSupportsMimeType(const String& keySystem, const String& mimeType); + static std::unique_ptr create(const String& keySystem); + WEBCORE_EXPORT static void registerCDMFactory(CreateCDM, CDMSupportsKeySystem, CDMSupportsKeySystemAndMimeType); + ~CDM(); + + bool supportsMIMEType(const String&) const; + std::unique_ptr createSession(CDMSessionClient&); + + const String& keySystem() const { return m_keySystem; } + + CDMClient* client() const { return m_client; } + void setClient(CDMClient* client) { m_client = client; } + + MediaPlayer* mediaPlayer() const; + +private: + String m_keySystem; + std::unique_ptr m_private; + CDMClient* m_client; +}; + +} + +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivate.h b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivate.h new file mode 100644 index 000000000..de46b063b --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivate.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include + +namespace WebCore { + +class CDMSession; +class CDMSessionClient; + +class CDMPrivateInterface { +public: + CDMPrivateInterface() { } + virtual ~CDMPrivateInterface() { } + + virtual bool supportsMIMEType(const String&) = 0; + + virtual std::unique_ptr createSession(CDMSessionClient*) = 0; +}; + +} // namespace WebCore + +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateClearKey.cpp b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateClearKey.cpp new file mode 100644 index 000000000..c5582c429 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateClearKey.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LegacyCDMPrivateClearKey.h" + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include "LegacyCDM.h" +#include "LegacyCDMSessionClearKey.h" +#include "ContentType.h" +#include "MediaPlayer.h" + +namespace WebCore { + +bool CDMPrivateClearKey::supportsKeySystem(const String& keySystem) +{ + if (!equalLettersIgnoringASCIICase(keySystem, "org.w3c.clearkey")) + return false; + + // The MediaPlayer must also support the key system: + return MediaPlayer::supportsKeySystem(keySystem, emptyString()); +} + +bool CDMPrivateClearKey::supportsKeySystemAndMimeType(const String& keySystem, const String& mimeType) +{ + if (!equalLettersIgnoringASCIICase(keySystem, "org.w3c.clearkey")) + return false; + + // The MediaPlayer must also support the key system: + return MediaPlayer::supportsKeySystem(keySystem, mimeType); +} + +bool CDMPrivateClearKey::supportsMIMEType(const String& mimeType) +{ + return MediaPlayer::supportsKeySystem(m_cdm->keySystem(), mimeType); +} + +std::unique_ptr CDMPrivateClearKey::createSession(CDMSessionClient* client) +{ + return std::make_unique(client); +} + +} + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateClearKey.h b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateClearKey.h new file mode 100644 index 000000000..a674e7a40 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateClearKey.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include "LegacyCDMPrivate.h" + +namespace WebCore { + +class CDM; + +class CDMPrivateClearKey : public CDMPrivateInterface { +public: + explicit CDMPrivateClearKey(CDM* cdm) + : m_cdm(cdm) + { + } + + virtual ~CDMPrivateClearKey() { } + + static bool supportsKeySystem(const String&); + static bool supportsKeySystemAndMimeType(const String& keySystem, const String& mimeType); + + bool supportsMIMEType(const String& mimeType) override; + std::unique_ptr createSession(CDMSessionClient*) override; + +protected: + CDM* m_cdm; +}; + +} // namespace WebCore + +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateMediaPlayer.cpp b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateMediaPlayer.cpp new file mode 100644 index 000000000..f46fba264 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateMediaPlayer.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LegacyCDMPrivateMediaPlayer.h" + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include "LegacyCDM.h" +#include "LegacyCDMSession.h" +#include "ContentType.h" +#include "MediaPlayer.h" + +#if PLATFORM(IOS) +#include "SoftLinking.h" +#endif + +namespace WebCore { + +bool CDMPrivateMediaPlayer::supportsKeySystem(const String& keySystem) +{ + return MediaPlayer::supportsKeySystem(keySystem, emptyString()); +} + +bool CDMPrivateMediaPlayer::supportsKeySystemAndMimeType(const String& keySystem, const String& mimeType) +{ + return MediaPlayer::supportsKeySystem(keySystem, mimeType); +} + +bool CDMPrivateMediaPlayer::supportsMIMEType(const String& mimeType) +{ + return MediaPlayer::supportsKeySystem(m_cdm->keySystem(), mimeType); +} + +std::unique_ptr CDMPrivateMediaPlayer::createSession(CDMSessionClient* client) +{ + MediaPlayer* mediaPlayer = m_cdm->mediaPlayer(); + if (!mediaPlayer) + return nullptr; + + return mediaPlayer->createSession(m_cdm->keySystem(), client); +} + +} + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateMediaPlayer.h b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateMediaPlayer.h new file mode 100644 index 000000000..6facef6ae --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateMediaPlayer.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "LegacyCDMPrivate.h" + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +namespace WebCore { + +class CDM; + +class CDMPrivateMediaPlayer : public CDMPrivateInterface { +public: + explicit CDMPrivateMediaPlayer(CDM* cdm) + : m_cdm(cdm) + { } + + static bool supportsKeySystem(const String&); + static bool supportsKeySystemAndMimeType(const String& keySystem, const String& mimeType); + + virtual ~CDMPrivateMediaPlayer() { } + + bool supportsMIMEType(const String& mimeType) override; + std::unique_ptr createSession(CDMSessionClient*) override; + + CDM* cdm() const { return m_cdm; } + +protected: + CDM* m_cdm; +}; + +} // namespace WebCore + +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMSessionClearKey.cpp b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMSessionClearKey.cpp new file mode 100644 index 000000000..e08a368bc --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMSessionClearKey.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LegacyCDMSessionClearKey.h" + +#include "JSMainThreadExecState.h" +#include "Logging.h" +#include "TextEncoding.h" +#include "UUID.h" +#include "WebKitMediaKeyError.h" +#include +#include +#include +#include +#include +#include + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +using namespace JSC; + +namespace WebCore { + +static VM& clearKeyVM() +{ + static NeverDestroyed> vm; + if (!vm.get()) + vm.get() = VM::create(); + + return *vm.get(); +} + +CDMSessionClearKey::CDMSessionClearKey(CDMSessionClient* client) + : m_client(client) + , m_sessionId(createCanonicalUUIDString()) +{ +} + +CDMSessionClearKey::~CDMSessionClearKey() +{ +} + +RefPtr CDMSessionClearKey::generateKeyRequest(const String& mimeType, Uint8Array* initData, String& destinationURL, unsigned short& errorCode, uint32_t& systemCode) +{ + UNUSED_PARAM(mimeType); + UNUSED_PARAM(destinationURL); + UNUSED_PARAM(systemCode); + + if (!initData) { + errorCode = WebKitMediaKeyError::MEDIA_KEYERR_CLIENT; + return nullptr; + } + m_initData = initData; + + bool sawError = false; + String keyID = UTF8Encoding().decode(reinterpret_cast_ptr(m_initData->baseAddress()), m_initData->byteLength(), true, sawError); + if (sawError) { + errorCode = WebKitMediaKeyError::MEDIA_KEYERR_CLIENT; + return nullptr; + } + + return initData; +} + +void CDMSessionClearKey::releaseKeys() +{ + m_cachedKeys.clear(); +} + +bool CDMSessionClearKey::update(Uint8Array* rawKeysData, RefPtr& nextMessage, unsigned short& errorCode, uint32_t& systemCode) +{ + UNUSED_PARAM(nextMessage); + UNUSED_PARAM(systemCode); + ASSERT(rawKeysData); + + do { + auto rawKeysString = String::fromUTF8(rawKeysData->data(), rawKeysData->length()); + if (rawKeysString.isEmpty()) { + LOG(Media, "CDMSessionClearKey::update(%p) - failed: empty message", this); + continue; + } + + auto& vm = clearKeyVM(); + JSLockHolder lock(vm); + auto scope = DECLARE_THROW_SCOPE(vm); + auto* globalObject = JSGlobalObject::create(vm, JSGlobalObject::createStructure(vm, jsNull())); + auto& state = *globalObject->globalExec(); + + auto keysDataValue = JSONParse(&state, rawKeysString); + if (scope.exception() || !keysDataValue.isObject()) { + LOG(Media, "CDMSessionClearKey::update(%p) - failed: invalid JSON", this); + break; + } + + auto keysArrayValue = asObject(keysDataValue)->get(&state, Identifier::fromString(&state, "keys")); + if (scope.exception() || !isJSArray(keysArrayValue)) { + LOG(Media, "CDMSessionClearKey::update(%p) - failed: keys array missing or empty", this); + break; + } + + auto keysArray = asArray(keysArrayValue); + auto length = keysArray->length(); + if (!length) { + LOG(Media, "CDMSessionClearKey::update(%p) - failed: keys array missing or empty", this); + break; + } + + bool foundValidKey = false; + for (unsigned i = 0; i < length; ++i) { + auto keyValue = keysArray->getIndex(&state, i); + + if (scope.exception() || !keyValue.isObject()) { + LOG(Media, "CDMSessionClearKey::update(%p) - failed: null keyDictionary", this); + continue; + } + + auto keyObject = asObject(keyValue); + + auto getStringProperty = [&scope, &state, &keyObject](const char* name) -> String { + auto value = keyObject->get(&state, Identifier::fromString(&state, name)); + if (scope.exception() || !value.isString()) + return { }; + + auto string = asString(value)->value(&state); + if (scope.exception()) + return { }; + + return string; + }; + + auto algorithm = getStringProperty("alg"); + if (!equalLettersIgnoringASCIICase(algorithm, "a128kw")) { + LOG(Media, "CDMSessionClearKey::update(%p) - failed: algorithm unsupported", this); + continue; + } + + auto keyType = getStringProperty("kty"); + if (!equalLettersIgnoringASCIICase(keyType, "oct")) { + LOG(Media, "CDMSessionClearKey::update(%p) - failed: keyType unsupported", this); + continue; + } + + auto keyId = getStringProperty("kid"); + if (keyId.isEmpty()) { + LOG(Media, "CDMSessionClearKey::update(%p) - failed: keyId missing or empty", this); + continue; + } + + auto rawKeyData = getStringProperty("k"); + if (rawKeyData.isEmpty()) { + LOG(Media, "CDMSessionClearKey::update(%p) - failed: key missing or empty", this); + continue; + } + + Vector keyData; + if (!base64Decode(rawKeyData, keyData) || keyData.isEmpty()) { + LOG(Media, "CDMSessionClearKey::update(%p) - failed: unable to base64 decode key", this); + continue; + } + + m_cachedKeys.set(keyId, WTFMove(keyData)); + foundValidKey = true; + } + + if (foundValidKey) + return true; + + } while (false); + + errorCode = WebKitMediaKeyError::MEDIA_KEYERR_CLIENT; + return false; +} + +RefPtr CDMSessionClearKey::cachedKeyForKeyID(const String& keyId) const +{ + if (!m_cachedKeys.contains(keyId)) + return nullptr; + + auto keyData = m_cachedKeys.get(keyId); + RefPtr keyDataArray = Uint8Array::create(keyData.data(), keyData.size()); + return keyDataArray->unsharedBuffer(); +} + +} + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMSessionClearKey.h b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMSessionClearKey.h new file mode 100644 index 000000000..e1e735a40 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMSessionClearKey.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "LegacyCDMSession.h" +#include +#include + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +namespace WebCore { + +class CDMSessionClearKey : public CDMSession { +public: + CDMSessionClearKey(CDMSessionClient*); + virtual ~CDMSessionClearKey(); + + // CDMSessionPrivate + CDMSessionType type() override { return CDMSessionTypeClearKey; } + void setClient(CDMSessionClient* client) override { m_client = client; } + const String& sessionId() const override { return m_sessionId; } + RefPtr generateKeyRequest(const String& mimeType, Uint8Array*, String&, unsigned short&, uint32_t&) override; + void releaseKeys() override; + bool update(Uint8Array*, RefPtr&, unsigned short&, uint32_t&) override; + RefPtr cachedKeyForKeyID(const String&) const override; + +protected: + CDMSessionClient* m_client; + RefPtr m_initData; + HashMap> m_cachedKeys; + String m_sessionId; +}; + +} // namespace WebCore + +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.cpp b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.cpp new file mode 100644 index 000000000..2efadf15c --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebKitMediaKeyMessageEvent.h" + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include + +namespace WebCore { + +WebKitMediaKeyMessageEvent::WebKitMediaKeyMessageEvent(const AtomicString& type, Uint8Array* message, const String& destinationURL) + : Event(type, false, false) + , m_message(message) + , m_destinationURL(destinationURL) +{ +} + + +WebKitMediaKeyMessageEvent::WebKitMediaKeyMessageEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) + , m_message(initializer.message) + , m_destinationURL(initializer.destinationURL) +{ +} + +WebKitMediaKeyMessageEvent::~WebKitMediaKeyMessageEvent() +{ +} + +EventInterface WebKitMediaKeyMessageEvent::eventInterface() const +{ + return WebKitMediaKeyMessageEventInterfaceType; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.h b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.h new file mode 100644 index 000000000..51946f213 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include "Event.h" +#include "WebKitMediaKeyError.h" + +namespace WebCore { + +class WebKitMediaKeyMessageEvent : public Event { +public: + virtual ~WebKitMediaKeyMessageEvent(); + + static Ref create(const AtomicString& type, Uint8Array* message, const String& destinationURL) + { + return adoptRef(*new WebKitMediaKeyMessageEvent(type, message, destinationURL)); + } + + struct Init : EventInit { + RefPtr message; + String destinationURL; + }; + + static Ref create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) + { + return adoptRef(*new WebKitMediaKeyMessageEvent(type, initializer, isTrusted)); + } + + EventInterface eventInterface() const override; + + Uint8Array* message() const { return m_message.get(); } + String destinationURL() const { return m_destinationURL; } + +private: + WebKitMediaKeyMessageEvent(const AtomicString& type, Uint8Array* message, const String& destinationURL); + WebKitMediaKeyMessageEvent(const AtomicString& type, const Init&, IsTrusted); + + RefPtr m_message; + String m_destinationURL; +}; + +} // namespace WebCore + +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.idl b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.idl new file mode 100644 index 000000000..09609128f --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.idl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=LEGACY_ENCRYPTED_MEDIA, + Constructor(DOMString type, optional WebKitMediaKeyMessageEventInit eventInitDict), +] interface WebKitMediaKeyMessageEvent : Event { + readonly attribute Uint8Array message; + readonly attribute DOMString destinationURL; +}; + +dictionary WebKitMediaKeyMessageEventInit : EventInit { + Uint8Array? message = null; + DOMString destinationURL = ""; +}; diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.cpp b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.cpp new file mode 100644 index 000000000..e538c1cce --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebKitMediaKeyNeededEvent.h" + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include + +namespace WebCore { + +WebKitMediaKeyNeededEvent::WebKitMediaKeyNeededEvent(const AtomicString& type, Uint8Array* initData) + : Event(type, false, false) + , m_initData(initData) +{ +} + +WebKitMediaKeyNeededEvent::WebKitMediaKeyNeededEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) + , m_initData(initializer.initData) +{ +} + +WebKitMediaKeyNeededEvent::~WebKitMediaKeyNeededEvent() +{ +} + +EventInterface WebKitMediaKeyNeededEvent::eventInterface() const +{ + return WebKitMediaKeyNeededEventInterfaceType; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.h b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.h new file mode 100644 index 000000000..de487d5df --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include "Event.h" +#include "WebKitMediaKeyError.h" + +namespace WebCore { + +class WebKitMediaKeyNeededEvent : public Event { +public: + virtual ~WebKitMediaKeyNeededEvent(); + + static Ref create(const AtomicString& type, Uint8Array* initData) + { + return adoptRef(*new WebKitMediaKeyNeededEvent(type, initData)); + } + + struct Init : EventInit { + RefPtr initData; + }; + + static Ref create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) + { + return adoptRef(*new WebKitMediaKeyNeededEvent(type, initializer, isTrusted)); + } + + EventInterface eventInterface() const override; + + Uint8Array* initData() const { return m_initData.get(); } + +private: + WebKitMediaKeyNeededEvent(const AtomicString& type, Uint8Array* initData); + WebKitMediaKeyNeededEvent(const AtomicString& type, const Init&, IsTrusted); + + RefPtr m_initData; +}; + +} // namespace WebCore + +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.idl b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.idl new file mode 100644 index 000000000..823646e8c --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=LEGACY_ENCRYPTED_MEDIA, + Constructor(DOMString type, optional WebKitMediaKeyNeededEventInit eventInitDict), +] interface WebKitMediaKeyNeededEvent : Event { + readonly attribute Uint8Array initData; +}; + +dictionary WebKitMediaKeyNeededEventInit : EventInit { + Uint8Array? initData = null; +}; diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp new file mode 100644 index 000000000..7e9e68cd2 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebKitMediaKeySession.h" + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include "Document.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "FileSystem.h" +#include "SecurityOriginData.h" +#include "Settings.h" +#include "WebKitMediaKeyError.h" +#include "WebKitMediaKeyMessageEvent.h" +#include "WebKitMediaKeys.h" + +namespace WebCore { + +Ref WebKitMediaKeySession::create(ScriptExecutionContext& context, WebKitMediaKeys& keys, const String& keySystem) +{ + auto session = adoptRef(*new WebKitMediaKeySession(context, keys, keySystem)); + session->suspendIfNeeded(); + return session; +} + +WebKitMediaKeySession::WebKitMediaKeySession(ScriptExecutionContext& context, WebKitMediaKeys& keys, const String& keySystem) + : ActiveDOMObject(&context) + , m_keys(&keys) + , m_keySystem(keySystem) + , m_asyncEventQueue(*this) + , m_session(keys.cdm().createSession(*this)) + , m_keyRequestTimer(*this, &WebKitMediaKeySession::keyRequestTimerFired) + , m_addKeyTimer(*this, &WebKitMediaKeySession::addKeyTimerFired) +{ +} + +WebKitMediaKeySession::~WebKitMediaKeySession() +{ + if (m_session) + m_session->setClient(nullptr); + + m_asyncEventQueue.cancelAllEvents(); +} + +void WebKitMediaKeySession::close() +{ + if (m_session) + m_session->releaseKeys(); +} + +RefPtr WebKitMediaKeySession::cachedKeyForKeyId(const String& keyId) const +{ + return m_session ? m_session->cachedKeyForKeyID(keyId) : nullptr; +} + +const String& WebKitMediaKeySession::sessionId() const +{ + return m_session->sessionId(); +} + +void WebKitMediaKeySession::generateKeyRequest(const String& mimeType, Ref&& initData) +{ + m_pendingKeyRequests.append({ mimeType, WTFMove(initData) }); + m_keyRequestTimer.startOneShot(0); +} + +void WebKitMediaKeySession::keyRequestTimerFired() +{ + ASSERT(m_pendingKeyRequests.size()); + if (!m_session) + return; + + while (!m_pendingKeyRequests.isEmpty()) { + auto request = m_pendingKeyRequests.takeFirst(); + + // NOTE: Continued from step 5 in MediaKeys::createSession(). + // The user agent will asynchronously execute the following steps in the task: + + // 1. Let cdm be the cdm loaded in the MediaKeys constructor. + // 2. Let destinationURL be null. + String destinationURL; + WebKitMediaKeyError::Code errorCode = 0; + uint32_t systemCode = 0; + + // 3. Use cdm to generate a key request and follow the steps for the first matching condition from the following list: + + auto keyRequest = m_session->generateKeyRequest(request.mimeType, request.initData.ptr(), destinationURL, errorCode, systemCode); + + // Otherwise [if a request is not successfully generated]: + if (errorCode) { + // 3.1. Create a new MediaKeyError object with the following attributes: + // code = the appropriate MediaKeyError code + // systemCode = a Key System-specific value, if provided, and 0 otherwise + // 3.2. Set the MediaKeySession object's error attribute to the error object created in the previous step. + // 3.3. queue a task to fire a simple event named keyerror at the MediaKeySession object. + sendError(errorCode, systemCode); + // 3.4. Abort the task. + continue; + } + + // 4. queue a task to fire a simple event named keymessage at the new object + // The event is of type MediaKeyMessageEvent and has: + // message = key request + // destinationURL = destinationURL + if (keyRequest) + sendMessage(keyRequest.get(), destinationURL); + } +} + +ExceptionOr WebKitMediaKeySession::update(Ref&& key) +{ + // From : + // The addKey(key) method must run the following steps: + // 1. If the first or second argument [sic] is an empty array, throw an INVALID_ACCESS_ERR. + // NOTE: the reference to a "second argument" is a spec bug. + if (!key->length()) + return Exception { INVALID_ACCESS_ERR }; + + // 2. Schedule a task to handle the call, providing key. + m_pendingKeys.append(WTFMove(key)); + m_addKeyTimer.startOneShot(0); + + return { }; +} + +void WebKitMediaKeySession::addKeyTimerFired() +{ + ASSERT(m_pendingKeys.size()); + if (!m_session) + return; + + while (!m_pendingKeys.isEmpty()) { + auto pendingKey = m_pendingKeys.takeFirst(); + unsigned short errorCode = 0; + uint32_t systemCode = 0; + + // NOTE: Continued from step 2. of MediaKeySession::update() + // 2.1. Let cdm be the cdm loaded in the MediaKeys constructor. + // NOTE: This is m_session. + // 2.2. Let 'did store key' be false. + bool didStoreKey = false; + // 2.3. Let 'next message' be null. + RefPtr nextMessage; + // 2.4. Use cdm to handle key. + didStoreKey = m_session->update(pendingKey.ptr(), nextMessage, errorCode, systemCode); + // 2.5. If did store key is true and the media element is waiting for a key, queue a task to attempt to resume playback. + // TODO: Find and restart the media element + + // 2.6. If next message is not null, queue a task to fire a simple event named keymessage at the MediaKeySession object. + // The event is of type MediaKeyMessageEvent and has: + // message = next message + // destinationURL = null + if (nextMessage) + sendMessage(nextMessage.get(), emptyString()); + + // 2.7. If did store key is true, queue a task to fire a simple event named keyadded at the MediaKeySession object. + if (didStoreKey) { + auto keyaddedEvent = Event::create(eventNames().webkitkeyaddedEvent, false, false); + keyaddedEvent->setTarget(this); + m_asyncEventQueue.enqueueEvent(WTFMove(keyaddedEvent)); + + ASSERT(m_keys); + m_keys->keyAdded(); + } + + // 2.8. If any of the preceding steps in the task failed + if (errorCode) { + // 2.8.1. Create a new MediaKeyError object with the following attributes: + // code = the appropriate MediaKeyError code + // systemCode = a Key System-specific value, if provided, and 0 otherwise + // 2.8.2. Set the MediaKeySession object's error attribute to the error object created in the previous step. + // 2.8.3. queue a task to fire a simple event named keyerror at the MediaKeySession object. + sendError(errorCode, systemCode); + // 2.8.4. Abort the task. + // NOTE: no-op + } + } +} + +void WebKitMediaKeySession::sendMessage(Uint8Array* message, String destinationURL) +{ + auto event = WebKitMediaKeyMessageEvent::create(eventNames().webkitkeymessageEvent, message, destinationURL); + event->setTarget(this); + m_asyncEventQueue.enqueueEvent(WTFMove(event)); +} + +void WebKitMediaKeySession::sendError(MediaKeyErrorCode errorCode, uint32_t systemCode) +{ + m_error = WebKitMediaKeyError::create(errorCode, systemCode); + + auto keyerrorEvent = Event::create(eventNames().webkitkeyerrorEvent, false, false); + keyerrorEvent->setTarget(this); + m_asyncEventQueue.enqueueEvent(WTFMove(keyerrorEvent)); +} + +String WebKitMediaKeySession::mediaKeysStorageDirectory() const +{ + auto* document = downcast(scriptExecutionContext()); + if (!document) + return emptyString(); + + auto storageDirectory = document->settings().mediaKeysStorageDirectory(); + if (storageDirectory.isEmpty()) + return emptyString(); + + return pathByAppendingComponent(storageDirectory, SecurityOriginData::fromSecurityOrigin(document->securityOrigin()).databaseIdentifier()); +} + +bool WebKitMediaKeySession::hasPendingActivity() const +{ + return (m_keys && m_session) || m_asyncEventQueue.hasPendingEvents(); +} + +void WebKitMediaKeySession::stop() +{ + close(); +} + +const char* WebKitMediaKeySession::activeDOMObjectName() const +{ + return "WebKitMediaKeySession"; +} + +bool WebKitMediaKeySession::canSuspendForDocumentSuspension() const +{ + // FIXME: We should try and do better here. + return false; +} + +} + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.h b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.h new file mode 100644 index 000000000..c466c3c6d --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include "ActiveDOMObject.h" +#include "LegacyCDMSession.h" +#include "EventTarget.h" +#include "ExceptionOr.h" +#include "GenericEventQueue.h" +#include "Timer.h" +#include +#include + +namespace WebCore { + +class WebKitMediaKeyError; +class WebKitMediaKeys; + +class WebKitMediaKeySession final : public RefCounted, public EventTargetWithInlineData, private ActiveDOMObject, private CDMSessionClient { +public: + static Ref create(ScriptExecutionContext&, WebKitMediaKeys&, const String& keySystem); + ~WebKitMediaKeySession(); + + WebKitMediaKeyError* error() { return m_error.get(); } + const String& keySystem() const { return m_keySystem; } + const String& sessionId() const; + ExceptionOr update(Ref&& key); + void close(); + + CDMSession* session() { return m_session.get(); } + + void detachKeys() { m_keys = nullptr; } + + void generateKeyRequest(const String& mimeType, Ref&& initData); + RefPtr cachedKeyForKeyId(const String& keyId) const; + + using RefCounted::ref; + using RefCounted::deref; + + bool hasPendingActivity() const final; + +private: + WebKitMediaKeySession(ScriptExecutionContext&, WebKitMediaKeys&, const String& keySystem); + void keyRequestTimerFired(); + void addKeyTimerFired(); + + void sendMessage(Uint8Array*, String destinationURL) final; + void sendError(MediaKeyErrorCode, uint32_t systemCode) final; + String mediaKeysStorageDirectory() const final; + + void refEventTarget() final { ref(); } + void derefEventTarget() final { deref(); } + + void stop() final; + bool canSuspendForDocumentSuspension() const final; + const char* activeDOMObjectName() const final; + + EventTargetInterface eventTargetInterface() const final { return WebKitMediaKeySessionEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); } + + WebKitMediaKeys* m_keys; + String m_keySystem; + String m_sessionId; + RefPtr m_error; + GenericEventQueue m_asyncEventQueue; + std::unique_ptr m_session; + + struct PendingKeyRequest { + String mimeType; + Ref initData; + }; + Deque m_pendingKeyRequests; + Timer m_keyRequestTimer; + + Deque> m_pendingKeys; + Timer m_addKeyTimer; +}; + +} + +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.idl b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.idl new file mode 100644 index 000000000..b9ffb2c61 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.idl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + ActiveDOMObject, + Conditional=LEGACY_ENCRYPTED_MEDIA, +] interface WebKitMediaKeySession : EventTarget { + readonly attribute WebKitMediaKeyError error; + + readonly attribute DOMString keySystem; + readonly attribute DOMString sessionId; + + [MayThrowException] void update(Uint8Array key); + void close(); + + attribute EventHandler onwebkitkeyadded; + attribute EventHandler onwebkitkeyerror; + attribute EventHandler onwebkitkeymessage; +}; diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.cpp b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.cpp new file mode 100644 index 000000000..fcfbb0a74 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebKitMediaKeys.h" + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include "ExceptionCode.h" +#include "HTMLMediaElement.h" +#include "WebKitMediaKeySession.h" + +namespace WebCore { + +ExceptionOr> WebKitMediaKeys::create(const String& keySystem) +{ + // From : + // The MediaKeys(keySystem) constructor must run the following steps: + + // 1. If keySystem is null or an empty string, throw an INVALID_ACCESS_ERR exception and abort these steps. + if (keySystem.isEmpty()) + return Exception { INVALID_ACCESS_ERR }; + + // 2. If keySystem is not one of the user agent's supported Key Systems, throw a NOT_SUPPORTED_ERR and abort these steps. + if (!CDM::supportsKeySystem(keySystem)) + return Exception { NOT_SUPPORTED_ERR }; + + // 3. Let cdm be the content decryption module corresponding to keySystem. + // 4. Load cdm if necessary. + auto cdm = CDM::create(keySystem); + + // 5. Create a new MediaKeys object. + // 5.1 Let the keySystem attribute be keySystem. + // 6. Return the new object to the caller. + return adoptRef(*new WebKitMediaKeys(keySystem, WTFMove(cdm))); +} + +WebKitMediaKeys::WebKitMediaKeys(const String& keySystem, std::unique_ptr&& cdm) + : m_keySystem(keySystem) + , m_cdm(WTFMove(cdm)) +{ + m_cdm->setClient(this); +} + +WebKitMediaKeys::~WebKitMediaKeys() +{ + // From : + // When destroying a MediaKeys object, follow the steps in close(). + for (auto& session : m_sessions) { + session->close(); + session->detachKeys(); + } +} + +ExceptionOr> WebKitMediaKeys::createSession(ScriptExecutionContext& context, const String& type, Ref&& initData) +{ + // From : + // The createSession(type, initData) method must run the following steps: + // Note: The contents of initData are container-specific Initialization Data. + + // 1. If contentType is null or an empty string, throw an INVALID_ACCESS_ERR exception and abort these steps. + if (type.isEmpty()) + return Exception { INVALID_ACCESS_ERR }; + + // 2. If initData is an empty array, throw an INVALID_ACCESS_ERR exception and abort these steps. + if (!initData->length()) + return Exception { INVALID_ACCESS_ERR }; + + // 3. If type contains a MIME type that is not supported or is not supported by the keySystem, throw + // a NOT_SUPPORTED_ERR exception and abort these steps. + if (!m_cdm->supportsMIMEType(type)) + return Exception { NOT_SUPPORTED_ERR }; + + // 4. Create a new MediaKeySession object. + // 4.1 Let the keySystem attribute be keySystem. + // 4.2 Let the sessionId attribute be a unique Session ID string. It may be generated by cdm. + auto session = WebKitMediaKeySession::create(context, *this, m_keySystem); + + m_sessions.append(session.copyRef()); + + // 5. Schedule a task to initialize the session, providing contentType, initData, and the new object. + session->generateKeyRequest(type, WTFMove(initData)); + + // 6. Return the new object to the caller. + return WTFMove(session); +} + +bool WebKitMediaKeys::isTypeSupported(const String& keySystem, const String& mimeType) +{ + // 1. If keySystem contains an unrecognized or unsupported Key System, return false and abort these steps. + // Key system string comparison is case-sensitive. + if (keySystem.isEmpty() || !CDM::supportsKeySystem(keySystem)) + return false; + + // 2. If type is null or an empty string, return true and abort these steps. + if (mimeType.isEmpty()) + return true; + + // 3. If the Key System specified by keySystem does not support decrypting the container and/or codec + // specified by type, return false and abort these steps. + if (!CDM::keySystemSupportsMimeType(keySystem, mimeType)) + return false; + + // 4. Return true; + return true; +} + +void WebKitMediaKeys::setMediaElement(HTMLMediaElement* element) +{ + if (m_mediaElement && m_mediaElement->player()) + m_mediaElement->player()->setCDMSession(nullptr); + + m_mediaElement = element; + + if (m_mediaElement && m_mediaElement->player() && !m_sessions.isEmpty()) + m_mediaElement->player()->setCDMSession(m_sessions.last()->session()); +} + +MediaPlayer* WebKitMediaKeys::cdmMediaPlayer(const CDM*) const +{ + if (!m_mediaElement) + return nullptr; + return m_mediaElement->player(); +} + +void WebKitMediaKeys::keyAdded() +{ + if (m_mediaElement) + m_mediaElement->keyAdded(); +} + +RefPtr WebKitMediaKeys::cachedKeyForKeyId(const String& keyId) const +{ + for (auto& session : m_sessions) { + if (auto key = session->cachedKeyForKeyId(keyId)) + return key; + } + return nullptr; +} + +} + +#endif diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.h b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.h new file mode 100644 index 000000000..64c835a1e --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include "LegacyCDM.h" +#include "ExceptionOr.h" +#include +#include + +namespace WebCore { + +class HTMLMediaElement; +class ScriptExecutionContext; +class WebKitMediaKeySession; + +class WebKitMediaKeys final : public RefCounted, private CDMClient { +public: + static ExceptionOr> create(const String& keySystem); + virtual ~WebKitMediaKeys(); + + ExceptionOr> createSession(ScriptExecutionContext&, const String& mimeType, Ref&& initData); + static bool isTypeSupported(const String& keySystem, const String& mimeType); + const String& keySystem() const { return m_keySystem; } + + CDM& cdm() { ASSERT(m_cdm); return *m_cdm; } + + void setMediaElement(HTMLMediaElement*); + + void keyAdded(); + RefPtr cachedKeyForKeyId(const String& keyId) const; + +private: + MediaPlayer* cdmMediaPlayer(const CDM*) const final; + + WebKitMediaKeys(const String& keySystem, std::unique_ptr&&); + + Vector> m_sessions; + HTMLMediaElement* m_mediaElement { nullptr }; + String m_keySystem; + std::unique_ptr m_cdm; +}; + +} + +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) diff --git a/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.idl b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.idl new file mode 100644 index 000000000..dc6494feb --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=LEGACY_ENCRYPTED_MEDIA, + Constructor(DOMString keySystem), + ConstructorMayThrowException, +] interface WebKitMediaKeys { + [CallWith=ScriptExecutionContext, MayThrowException] WebKitMediaKeySession createSession(DOMString type, Uint8Array initData); + static boolean isTypeSupported(DOMString keySystem, optional DOMString type); + readonly attribute DOMString keySystem; +}; diff --git a/Source/WebCore/Modules/fetch/DOMWindowFetch.cpp b/Source/WebCore/Modules/fetch/DOMWindowFetch.cpp new file mode 100644 index 000000000..c59d3656a --- /dev/null +++ b/Source/WebCore/Modules/fetch/DOMWindowFetch.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 Canon Inc. + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DOMWindowFetch.h" + +#if ENABLE(FETCH_API) + +#include "DOMWindow.h" +#include "Document.h" +#include "FetchResponse.h" + +namespace WebCore { + +void DOMWindowFetch::fetch(DOMWindow& window, FetchRequest& request, Ref&& promise) +{ + auto* document = window.document(); + if (!document) + return; + FetchResponse::fetch(*document, request, WTFMove(promise)); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/DOMWindowFetch.h b/Source/WebCore/Modules/fetch/DOMWindowFetch.h new file mode 100644 index 000000000..b3d15757a --- /dev/null +++ b/Source/WebCore/Modules/fetch/DOMWindowFetch.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "JSDOMPromise.h" +#include + +namespace WebCore { + +class DOMWindow; +class FetchRequest; + +class DOMWindowFetch { +public: + static void fetch(DOMWindow&, FetchRequest&, Ref&&); +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/DOMWindowFetch.idl b/Source/WebCore/Modules/fetch/DOMWindowFetch.idl new file mode 100644 index 000000000..2c94ed393 --- /dev/null +++ b/Source/WebCore/Modules/fetch/DOMWindowFetch.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=FETCH_API, + EnabledAtRuntime=FetchAPI, +] partial interface DOMWindow { + [JSBuiltin] Promise fetch(any input, optional RequestInit init); + [PrivateIdentifier, ImplementedAs=fetch] Promise fetchRequest(FetchRequest request); +}; diff --git a/Source/WebCore/Modules/fetch/DOMWindowFetch.js b/Source/WebCore/Modules/fetch/DOMWindowFetch.js new file mode 100644 index 000000000..ef6d075ea --- /dev/null +++ b/Source/WebCore/Modules/fetch/DOMWindowFetch.js @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) + +function fetch(input, init) +{ + "use strict"; + + try { + return @fetchRequest(new @Request(input, init)); + } catch (e) { + return @Promise.@reject(e); + } +} diff --git a/Source/WebCore/Modules/fetch/FetchBody.cpp b/Source/WebCore/Modules/fetch/FetchBody.cpp new file mode 100644 index 000000000..8eb878cf0 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBody.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchBody.h" + +#if ENABLE(FETCH_API) + +#include "Document.h" +#include "FetchBodyOwner.h" +#include "FetchHeaders.h" +#include "FetchResponseSource.h" +#include "HTTPHeaderValues.h" +#include "HTTPParsers.h" +#include "JSBlob.h" +#include "JSDOMFormData.h" +#include "JSReadableStream.h" +#include "JSURLSearchParams.h" +#include "ReadableStreamSource.h" +#include + +namespace WebCore { + +std::optional FetchBody::extract(ScriptExecutionContext& context, JSC::ExecState& state, JSC::JSValue value, String& contentType) +{ + JSC::VM& vm = state.vm(); + if (value.inherits(vm, JSBlob::info())) { + auto& blob = *JSBlob::toWrapped(vm, value); + contentType = blob.type(); + return FetchBody(blob); + } + if (value.inherits(vm, JSDOMFormData::info())) { + ASSERT(!context.isWorkerGlobalScope()); + auto& domFormData = *JSDOMFormData::toWrapped(vm, value); + auto formData = FormData::createMultiPart(domFormData, domFormData.encoding(), &static_cast(context)); + contentType = makeString("multipart/form-data; boundary=", formData->boundary().data()); + return FetchBody(WTFMove(formData)); + } + if (value.isString()) { + contentType = HTTPHeaderValues::textPlainContentType(); + return FetchBody(String { asString(value)->value(&state) }); + } + if (value.inherits(vm, JSURLSearchParams::info())) { + contentType = HTTPHeaderValues::formURLEncodedContentType(); + return FetchBody(*JSURLSearchParams::toWrapped(vm, value)); + } + if (value.inherits(vm, JSReadableStream::info())) { + FetchBody body; + body.m_isReadableStream = true; + return WTFMove(body); + } + if (value.inherits(vm, JSC::JSArrayBuffer::info())) { + ArrayBuffer* data = toUnsharedArrayBuffer(vm, value); + ASSERT(data); + return FetchBody(*data); + } + if (value.inherits(vm, JSC::JSArrayBufferView::info())) + return FetchBody(toUnsharedArrayBufferView(vm, value).releaseConstNonNull()); + + return std::nullopt; +} + +void FetchBody::arrayBuffer(FetchBodyOwner& owner, Ref&& promise) +{ + m_consumer.setType(FetchBodyConsumer::Type::ArrayBuffer); + consume(owner, WTFMove(promise)); +} + +void FetchBody::blob(FetchBodyOwner& owner, Ref&& promise, const String& contentType) +{ + m_consumer.setType(FetchBodyConsumer::Type::Blob); + m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(contentType))); + consume(owner, WTFMove(promise)); +} + +void FetchBody::json(FetchBodyOwner& owner, Ref&& promise) +{ + if (isText()) { + fulfillPromiseWithJSON(WTFMove(promise), textBody()); + return; + } + m_consumer.setType(FetchBodyConsumer::Type::JSON); + consume(owner, WTFMove(promise)); +} + +void FetchBody::text(FetchBodyOwner& owner, Ref&& promise) +{ + if (isText()) { + promise->resolve(textBody()); + return; + } + m_consumer.setType(FetchBodyConsumer::Type::Text); + consume(owner, WTFMove(promise)); +} + +void FetchBody::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Ref&& promise) +{ + m_consumer.setType(type); + m_consumePromise = WTFMove(promise); +} + +void FetchBody::consume(FetchBodyOwner& owner, Ref&& promise) +{ + if (isArrayBuffer()) { + consumeArrayBuffer(WTFMove(promise)); + return; + } + if (isArrayBufferView()) { + consumeArrayBufferView(WTFMove(promise)); + return; + } + if (isText()) { + consumeText(WTFMove(promise), textBody()); + return; + } + if (isURLSearchParams()) { + consumeText(WTFMove(promise), urlSearchParamsBody().toString()); + return; + } + if (isBlob()) { + consumeBlob(owner, WTFMove(promise)); + return; + } + if (isFormData()) { + // FIXME: Support consuming FormData. + promise->reject(); + return; + } + m_consumer.resolve(WTFMove(promise)); +} + +#if ENABLE(READABLE_STREAM_API) +void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchResponseSource& source) +{ + bool closeStream = false; + if (isArrayBuffer()) { + closeStream = source.enqueue(ArrayBuffer::tryCreate(arrayBufferBody().data(), arrayBufferBody().byteLength())); + m_data = nullptr; + } else if (isArrayBufferView()) { + closeStream = source.enqueue(ArrayBuffer::tryCreate(arrayBufferViewBody().baseAddress(), arrayBufferViewBody().byteLength())); + m_data = nullptr; + } else if (isText()) { + auto data = UTF8Encoding().encode(textBody(), EntitiesForUnencodables); + closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.length())); + m_data = nullptr; + } else if (isURLSearchParams()) { + auto data = UTF8Encoding().encode(urlSearchParamsBody().toString(), EntitiesForUnencodables); + closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.length())); + m_data = nullptr; + } else if (isBlob()) { + owner.loadBlob(blobBody(), nullptr); + m_data = nullptr; + } else if (isFormData()) + source.error(ASCIILiteral("not implemented")); + else if (m_consumer.hasData()) + closeStream = source.enqueue(m_consumer.takeAsArrayBuffer()); + else + closeStream = true; + + if (closeStream) + source.close(); +} +#endif + +void FetchBody::consumeArrayBuffer(Ref&& promise) +{ + m_consumer.resolveWithData(WTFMove(promise), static_cast(arrayBufferBody().data()), arrayBufferBody().byteLength()); + m_data = nullptr; +} + +void FetchBody::consumeArrayBufferView(Ref&& promise) +{ + m_consumer.resolveWithData(WTFMove(promise), static_cast(arrayBufferViewBody().baseAddress()), arrayBufferViewBody().byteLength()); + m_data = nullptr; +} + +void FetchBody::consumeText(Ref&& promise, const String& text) +{ + auto data = UTF8Encoding().encode(text, EntitiesForUnencodables); + m_consumer.resolveWithData(WTFMove(promise), reinterpret_cast(data.data()), data.length()); + m_data = nullptr; +} + +void FetchBody::consumeBlob(FetchBodyOwner& owner, Ref&& promise) +{ + m_consumePromise = WTFMove(promise); + owner.loadBlob(blobBody(), &m_consumer); + m_data = nullptr; +} + +void FetchBody::loadingFailed() +{ + if (m_consumePromise) { + m_consumePromise->reject(); + m_consumePromise = nullptr; + } +} + +void FetchBody::loadingSucceeded() +{ + if (m_consumePromise) + m_consumer.resolve(m_consumePromise.releaseNonNull()); +} + +RefPtr FetchBody::bodyForInternalRequest(ScriptExecutionContext& context) const +{ + if (isText()) + return FormData::create(UTF8Encoding().encode(textBody(), EntitiesForUnencodables)); + if (isURLSearchParams()) + return FormData::create(UTF8Encoding().encode(urlSearchParamsBody().toString(), EntitiesForUnencodables)); + if (isBlob()) { + RefPtr body = FormData::create(); + body->appendBlob(blobBody().url()); + return body; + } + if (isArrayBuffer()) + return FormData::create(arrayBufferBody().data(), arrayBufferBody().byteLength()); + if (isArrayBufferView()) + return FormData::create(arrayBufferViewBody().baseAddress(), arrayBufferViewBody().byteLength()); + if (isFormData()) { + ASSERT(!context.isWorkerGlobalScope()); + RefPtr body = const_cast(&formDataBody()); + body->generateFiles(static_cast(&context)); + return body; + } + ASSERT_NOT_REACHED(); + return nullptr; +} + +FetchBody FetchBody::clone() const +{ + ASSERT(!m_consumePromise); + FetchBody clone(m_consumer); + + if (isArrayBuffer()) + clone.m_data = arrayBufferBody(); + else if (isArrayBufferView()) + clone.m_data = arrayBufferViewBody(); + else if (isBlob()) + clone.m_data = blobBody(); + else if (isFormData()) + clone.m_data = const_cast(formDataBody()); + else if (isText()) + clone.m_data = textBody(); + else if (isURLSearchParams()) + clone.m_data = urlSearchParamsBody(); + return clone; +} + +} + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchBody.h b/Source/WebCore/Modules/fetch/FetchBody.h new file mode 100644 index 000000000..d57e6a935 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBody.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "Blob.h" +#include "FetchBodyConsumer.h" +#include "FetchLoader.h" +#include "FormData.h" +#include "JSDOMPromise.h" +#include "URLSearchParams.h" +#include +#include + +namespace JSC { +class ExecState; +class JSValue; +}; + +namespace WebCore { + +class FetchBodyOwner; +class FetchResponseSource; +class ScriptExecutionContext; + +class FetchBody { +public: + void arrayBuffer(FetchBodyOwner&, Ref&&); + void blob(FetchBodyOwner&, Ref&&, const String&); + void json(FetchBodyOwner&, Ref&&); + void text(FetchBodyOwner&, Ref&&); + void formData(FetchBodyOwner&, Ref&& promise) { promise.get().reject(0); } + +#if ENABLE(READABLE_STREAM_API) + void consumeAsStream(FetchBodyOwner&, FetchResponseSource&); +#endif + + bool isBlob() const { return WTF::holds_alternative>(m_data); } + bool isFormData() const { return WTF::holds_alternative>(m_data); } + bool isArrayBuffer() const { return WTF::holds_alternative>(m_data); } + bool isArrayBufferView() const { return WTF::holds_alternative>(m_data); } + bool isURLSearchParams() const { return WTF::holds_alternative>(m_data); } + bool isText() const { return WTF::holds_alternative(m_data); } + bool isReadableStream() const { return m_isReadableStream; } + + static std::optional extract(ScriptExecutionContext&, JSC::ExecState&, JSC::JSValue, String&); + static FetchBody loadingBody() { return { }; } + + void loadingFailed(); + void loadingSucceeded(); + + RefPtr bodyForInternalRequest(ScriptExecutionContext&) const; + + FetchBodyConsumer& consumer() { return m_consumer; } + + void consumeOnceLoadingFinished(FetchBodyConsumer::Type, Ref&&); + void cleanConsumePromise() { m_consumePromise = nullptr; } + + FetchBody clone() const; + +private: + explicit FetchBody(Ref&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(Ref&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(Ref&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(Ref&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(String&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(Ref&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(const FetchBodyConsumer& consumer) : m_consumer(consumer) { } + FetchBody() = default; + + void consume(FetchBodyOwner&, Ref&&); + + void consumeArrayBuffer(Ref&&); + void consumeArrayBufferView(Ref&&); + void consumeText(Ref&&, const String&); + void consumeBlob(FetchBodyOwner&, Ref&&); + + const Blob& blobBody() const { return WTF::get>(m_data).get(); } + FormData& formDataBody() { return WTF::get>(m_data).get(); } + const FormData& formDataBody() const { return WTF::get>(m_data).get(); } + const ArrayBuffer& arrayBufferBody() const { return WTF::get>(m_data).get(); } + const ArrayBufferView& arrayBufferViewBody() const { return WTF::get>(m_data).get(); } + String& textBody() { return WTF::get(m_data); } + const String& textBody() const { return WTF::get(m_data); } + const URLSearchParams& urlSearchParamsBody() const { return WTF::get>(m_data).get(); } + + Variant, Ref, Ref, Ref, Ref, String> m_data { nullptr }; + + FetchBodyConsumer m_consumer { FetchBodyConsumer::Type::None }; + RefPtr m_consumePromise; + + // FIXME: We probably want to keep the stream as a specific field in m_data when we will support stream data upload. + bool m_isReadableStream { false }; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchBody.idl b/Source/WebCore/Modules/fetch/FetchBody.idl new file mode 100644 index 000000000..5392c6f01 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBody.idl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=FETCH_API, + EnabledAtRuntime=FetchAPI, + Exposed=(Window,Worker), + InterfaceName=Body, + NoInterfaceObject +] +interface FetchBody { + [ImplementedAs=isDisturbed] readonly attribute boolean bodyUsed; + [NewObject] Promise arrayBuffer(); + [NewObject] Promise blob(); + // FIXME: Add support for form data consumption (https://bugs.webkit.org/show_bug.cgi?id=161190). + //[NewObject] Promise formData(); + [NewObject] Promise json(); + [NewObject] Promise text(); +}; diff --git a/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp b/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp new file mode 100644 index 000000000..e82a74a25 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchBodyConsumer.h" + +#if ENABLE(FETCH_API) + +#include "JSBlob.h" +#include "TextResourceDecoder.h" + +namespace WebCore { + +static inline Ref blobFromData(const unsigned char* data, unsigned length, const String& contentType) +{ + Vector value(length); + memcpy(value.data(), data, length); + return Blob::create(WTFMove(value), contentType); +} + +static inline bool shouldPrependBOM(const unsigned char* data, unsigned length) +{ + if (length < 3) + return true; + return data[0] != 0xef || data[1] != 0xbb || data[2] != 0xbf; +} + +static String textFromUTF8(const unsigned char* data, unsigned length) +{ + auto decoder = TextResourceDecoder::create("text/plain", "UTF-8"); + if (shouldPrependBOM(data, length)) + decoder->decode("\xef\xbb\xbf", 3); + return decoder->decodeAndFlush(reinterpret_cast(data), length); +} + +void FetchBodyConsumer::resolveWithData(Ref&& promise, const unsigned char* data, unsigned length) +{ + switch (m_type) { + case Type::ArrayBuffer: + fulfillPromiseWithArrayBuffer(WTFMove(promise), data, length); + return; + case Type::Blob: + promise->resolveWithNewlyCreated>(blobFromData(data, length, m_contentType).get()); + return; + case Type::JSON: + fulfillPromiseWithJSON(WTFMove(promise), textFromUTF8(data, length)); + return; + case Type::Text: + promise->resolve(textFromUTF8(data, length)); + return; + case Type::None: + ASSERT_NOT_REACHED(); + return; + } +} + +void FetchBodyConsumer::resolve(Ref&& promise) +{ + ASSERT(m_type != Type::None); + switch (m_type) { + case Type::ArrayBuffer: + fulfillPromiseWithArrayBuffer(WTFMove(promise), takeAsArrayBuffer().get()); + return; + case Type::Blob: + promise->resolveWithNewlyCreated>(takeAsBlob().get()); + return; + case Type::JSON: + fulfillPromiseWithJSON(WTFMove(promise), takeAsText()); + return; + case Type::Text: + promise->resolve(takeAsText()); + return; + case Type::None: + ASSERT_NOT_REACHED(); + return; + } +} + +void FetchBodyConsumer::append(const char* data, unsigned length) +{ + if (!m_buffer) { + m_buffer = SharedBuffer::create(data, length); + return; + } + m_buffer->append(data, length); +} + +void FetchBodyConsumer::append(const unsigned char* data, unsigned length) +{ + append(reinterpret_cast(data), length); +} + +RefPtr FetchBodyConsumer::takeData() +{ + return WTFMove(m_buffer); +} + +RefPtr FetchBodyConsumer::takeAsArrayBuffer() +{ + if (!m_buffer) + return ArrayBuffer::tryCreate(nullptr, 0); + + auto arrayBuffer = m_buffer->createArrayBuffer(); + m_buffer = nullptr; + return arrayBuffer; +} + +Ref FetchBodyConsumer::takeAsBlob() +{ + if (!m_buffer) + return Blob::create(Vector(), m_contentType); + + // FIXME: We should try to move m_buffer to Blob without doing extra copy. + return blobFromData(reinterpret_cast(m_buffer->data()), m_buffer->size(), m_contentType); +} + +String FetchBodyConsumer::takeAsText() +{ + // FIXME: We could probably text decode on the fly as soon as m_type is set to JSON or Text. + if (!m_buffer) + return String(); + + auto text = textFromUTF8(reinterpret_cast(m_buffer->data()), m_buffer->size()); + m_buffer = nullptr; + return text; +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchBodyConsumer.h b/Source/WebCore/Modules/fetch/FetchBodyConsumer.h new file mode 100644 index 000000000..4a0fb39f1 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBodyConsumer.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "JSDOMPromise.h" +#include "SharedBuffer.h" + +namespace WebCore { + +class Blob; + +class FetchBodyConsumer { +public: + // Type is used in FetchResponse.js and should be kept synchronized with it. + enum class Type { None, ArrayBuffer, Blob, JSON, Text }; + + FetchBodyConsumer(Type type) : m_type(type) { } + + void append(const char* data, unsigned); + void append(const unsigned char* data, unsigned); + + RefPtr takeData(); + RefPtr takeAsArrayBuffer(); + Ref takeAsBlob(); + String takeAsText(); + + void setContentType(const String& contentType) { m_contentType = contentType; } + void setType(Type type) { m_type = type; } + + void clean() { m_buffer = nullptr; } + + void resolve(Ref&&); + void resolveWithData(Ref&&, const unsigned char*, unsigned); + + bool hasData() const { return !!m_buffer; } + +private: + Type m_type; + String m_contentType; + RefPtr m_buffer; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp b/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp new file mode 100644 index 000000000..380c570e9 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchBodyOwner.h" + +#if ENABLE(FETCH_API) + +#include "FetchLoader.h" +#include "HTTPParsers.h" +#include "JSBlob.h" +#include "ResourceResponse.h" + +namespace WebCore { + +FetchBodyOwner::FetchBodyOwner(ScriptExecutionContext& context, std::optional&& body, Ref&& headers) + : ActiveDOMObject(&context) + , m_body(WTFMove(body)) + , m_headers(WTFMove(headers)) +{ + suspendIfNeeded(); +} + +void FetchBodyOwner::stop() +{ + if (m_body) + m_body->cleanConsumePromise(); + + if (m_blobLoader) { + bool isUniqueReference = hasOneRef(); + if (m_blobLoader->loader) + m_blobLoader->loader->stop(); + // After that point, 'this' may be destroyed, since unsetPendingActivity should have been called. + ASSERT_UNUSED(isUniqueReference, isUniqueReference || !m_blobLoader); + } +} + +bool FetchBodyOwner::isDisturbedOrLocked() const +{ + if (m_isDisturbed) + return true; + +#if ENABLE(READABLE_STREAM_API) + if (m_readableStreamSource && m_readableStreamSource->isReadableStreamLocked()) + return true; +#endif + + return false; +} + +void FetchBodyOwner::arrayBuffer(Ref&& promise) +{ + if (isBodyNull()) { + fulfillPromiseWithArrayBuffer(WTFMove(promise), nullptr, 0); + return; + } + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->arrayBuffer(*this, WTFMove(promise)); +} + +void FetchBodyOwner::blob(Ref&& promise) +{ + if (isBodyNull()) { + promise->resolve>(Blob::create({ }, Blob::normalizedContentType(extractMIMETypeFromMediaType(m_contentType))).get()); + return; + } + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->blob(*this, WTFMove(promise), m_contentType); +} + +void FetchBodyOwner::cloneBody(const FetchBodyOwner& owner) +{ + m_contentType = owner.m_contentType; + if (owner.isBodyNull()) + return; + m_body = owner.m_body->clone(); +} + +void FetchBodyOwner::extractBody(ScriptExecutionContext& context, JSC::ExecState& state, JSC::JSValue value) +{ + m_body = FetchBody::extract(context, state, value, m_contentType); +} + +void FetchBodyOwner::updateContentType() +{ + String contentType = m_headers->fastGet(HTTPHeaderName::ContentType); + if (!contentType.isNull()) { + m_contentType = WTFMove(contentType); + return; + } + if (!m_contentType.isNull()) + m_headers->fastSet(HTTPHeaderName::ContentType, m_contentType); +} + +void FetchBodyOwner::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Ref&& promise) +{ + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->consumeOnceLoadingFinished(type, WTFMove(promise)); +} + +void FetchBodyOwner::formData(Ref&& promise) +{ + if (isBodyNull()) { + promise->reject(); + return; + } + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->formData(*this, WTFMove(promise)); +} + +void FetchBodyOwner::json(Ref&& promise) +{ + if (isBodyNull()) { + promise->reject(SYNTAX_ERR); + return; + } + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->json(*this, WTFMove(promise)); +} + +void FetchBodyOwner::text(Ref&& promise) +{ + if (isBodyNull()) { + promise->resolve({ }); + return; + } + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->text(*this, WTFMove(promise)); +} + +void FetchBodyOwner::loadBlob(const Blob& blob, FetchBodyConsumer* consumer) +{ + // Can only be called once for a body instance. + ASSERT(isDisturbed()); + ASSERT(!m_blobLoader); + ASSERT(!isBodyNull()); + + if (!scriptExecutionContext()) { + m_body->loadingFailed(); + return; + } + + m_blobLoader.emplace(*this); + m_blobLoader->loader = std::make_unique(*m_blobLoader, consumer); + + m_blobLoader->loader->start(*scriptExecutionContext(), blob); + if (!m_blobLoader->loader->isStarted()) { + m_body->loadingFailed(); + m_blobLoader = std::nullopt; + return; + } + setPendingActivity(this); +} + +void FetchBodyOwner::finishBlobLoading() +{ + ASSERT(m_blobLoader); + + m_blobLoader = std::nullopt; + unsetPendingActivity(this); +} + +void FetchBodyOwner::blobLoadingSucceeded() +{ + ASSERT(!isBodyNull()); +#if ENABLE(READABLE_STREAM_API) + if (m_readableStreamSource) { + m_readableStreamSource->close(); + m_readableStreamSource = nullptr; + } +#endif + m_body->loadingSucceeded(); + finishBlobLoading(); +} + +void FetchBodyOwner::blobLoadingFailed() +{ + ASSERT(!isBodyNull()); +#if ENABLE(READABLE_STREAM_API) + if (m_readableStreamSource) { + if (!m_readableStreamSource->isCancelling()) + m_readableStreamSource->error(ASCIILiteral("Blob loading failed")); + m_readableStreamSource = nullptr; + } else +#endif + m_body->loadingFailed(); + + finishBlobLoading(); +} + +void FetchBodyOwner::blobChunk(const char* data, size_t size) +{ + ASSERT(data); +#if ENABLE(READABLE_STREAM_API) + ASSERT(m_readableStreamSource); + if (!m_readableStreamSource->enqueue(ArrayBuffer::tryCreate(data, size))) + stop(); +#else + UNUSED_PARAM(data); + UNUSED_PARAM(size); +#endif +} + +FetchBodyOwner::BlobLoader::BlobLoader(FetchBodyOwner& owner) + : owner(owner) +{ +} + +void FetchBodyOwner::BlobLoader::didReceiveResponse(const ResourceResponse& response) +{ + if (response.httpStatusCode() != 200) + didFail(); +} + +void FetchBodyOwner::BlobLoader::didFail() +{ + // didFail might be called within FetchLoader::start call. + if (loader->isStarted()) + owner.blobLoadingFailed(); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchBodyOwner.h b/Source/WebCore/Modules/fetch/FetchBodyOwner.h new file mode 100644 index 000000000..88e588f1c --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBodyOwner.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "ActiveDOMObject.h" +#include "FetchBody.h" +#include "FetchHeaders.h" +#include "FetchLoaderClient.h" +#include "FetchResponseSource.h" + +namespace WebCore { + +class FetchLoader; + +class FetchBodyOwner : public RefCounted, public ActiveDOMObject { +public: + FetchBodyOwner(ScriptExecutionContext&, std::optional&&, Ref&&); + + // Exposed Body API + bool isDisturbed() const { return m_isDisturbed; }; + + void arrayBuffer(Ref&&); + void blob(Ref&&); + void formData(Ref&&); + void json(Ref&&); + void text(Ref&&); + + bool isDisturbedOrLocked() const; + + void loadBlob(const Blob&, FetchBodyConsumer*); + + bool isActive() const { return !!m_blobLoader; } + +protected: + const FetchBody& body() const { return *m_body; } + FetchBody& body() { return *m_body; } + bool isBodyNull() const { return !m_body; } + void cloneBody(const FetchBodyOwner&); + + void extractBody(ScriptExecutionContext&, JSC::ExecState&, JSC::JSValue); + void updateContentType(); + void consumeOnceLoadingFinished(FetchBodyConsumer::Type, Ref&&); + + // ActiveDOMObject API + void stop() override; + + void setDisturbed() { m_isDisturbed = true; } + +private: + // Blob loading routines + void blobChunk(const char*, size_t); + void blobLoadingSucceeded(); + void blobLoadingFailed(); + void finishBlobLoading(); + + struct BlobLoader final : FetchLoaderClient { + BlobLoader(FetchBodyOwner&); + + // FetchLoaderClient API + void didReceiveResponse(const ResourceResponse&) final; + void didReceiveData(const char* data, size_t size) final { owner.blobChunk(data, size); } + void didFail() final; + void didSucceed() final { owner.blobLoadingSucceeded(); } + + FetchBodyOwner& owner; + std::unique_ptr loader; + }; + +protected: + std::optional m_body; + String m_contentType; + bool m_isDisturbed { false }; +#if ENABLE(READABLE_STREAM_API) + RefPtr m_readableStreamSource; +#endif + Ref m_headers; + +private: + std::optional m_blobLoader; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchHeaders.cpp b/Source/WebCore/Modules/fetch/FetchHeaders.cpp new file mode 100644 index 000000000..6e07674a9 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchHeaders.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchHeaders.h" + +#if ENABLE(FETCH_API) + +#include "ExceptionCode.h" +#include "HTTPParsers.h" + +namespace WebCore { + +static ExceptionOr canWriteHeader(const String& name, const String& value, FetchHeaders::Guard guard) +{ + if (!isValidHTTPToken(name) || !isValidHTTPHeaderValue(value)) + return Exception { TypeError }; + if (guard == FetchHeaders::Guard::Immutable) + return Exception { TypeError }; + if (guard == FetchHeaders::Guard::Request && isForbiddenHeaderName(name)) + return false; + if (guard == FetchHeaders::Guard::RequestNoCors && !isSimpleHeader(name, value)) + return false; + if (guard == FetchHeaders::Guard::Response && isForbiddenResponseHeaderName(name)) + return false; + return true; +} + +ExceptionOr FetchHeaders::append(const String& name, const String& value) +{ + String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value); + auto canWriteResult = canWriteHeader(name, normalizedValue, m_guard); + if (canWriteResult.hasException()) + return canWriteResult.releaseException(); + if (!canWriteResult.releaseReturnValue()) + return { }; + m_headers.add(name, normalizedValue); + return { }; +} + +ExceptionOr FetchHeaders::remove(const String& name) +{ + auto canWriteResult = canWriteHeader(name, { }, m_guard); + if (canWriteResult.hasException()) + return canWriteResult.releaseException(); + if (!canWriteResult.releaseReturnValue()) + return { }; + m_headers.remove(name); + return { }; +} + +ExceptionOr FetchHeaders::get(const String& name) const +{ + if (!isValidHTTPToken(name)) + return Exception { TypeError }; + return m_headers.get(name); +} + +ExceptionOr FetchHeaders::has(const String& name) const +{ + if (!isValidHTTPToken(name)) + return Exception { TypeError }; + return m_headers.contains(name); +} + +ExceptionOr FetchHeaders::set(const String& name, const String& value) +{ + String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value); + auto canWriteResult = canWriteHeader(name, normalizedValue, m_guard); + if (canWriteResult.hasException()) + return canWriteResult.releaseException(); + if (!canWriteResult.releaseReturnValue()) + return { }; + m_headers.set(name, normalizedValue); + return { }; +} + +void FetchHeaders::fill(const FetchHeaders* headers) +{ + ASSERT(m_guard != Guard::Immutable); + if (!headers) + return; + filterAndFill(headers->m_headers, m_guard); +} + +void FetchHeaders::filterAndFill(const HTTPHeaderMap& headers, Guard guard) +{ + for (auto& header : headers) { + auto canWriteResult = canWriteHeader(header.key, header.value, guard); + if (canWriteResult.hasException()) + continue; + if (!canWriteResult.releaseReturnValue()) + continue; + if (header.keyAsHTTPHeaderName) + m_headers.add(header.keyAsHTTPHeaderName.value(), header.value); + else + m_headers.add(header.key, header.value); + } +} + +std::optional> FetchHeaders::Iterator::next() +{ + while (m_currentIndex < m_keys.size()) { + auto key = m_keys[m_currentIndex++]; + auto value = m_headers->m_headers.get(key); + if (!value.isNull()) + return WTF::KeyValuePair { WTFMove(key), WTFMove(value) }; + } + return std::nullopt; +} + +FetchHeaders::Iterator::Iterator(FetchHeaders& headers) + : m_headers(headers) +{ + m_keys.reserveInitialCapacity(headers.m_headers.size()); + for (auto& header : headers.m_headers) + m_keys.uncheckedAppend(header.key.convertToASCIILowercase()); + std::sort(m_keys.begin(), m_keys.end(), WTF::codePointCompareLessThan); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchHeaders.h b/Source/WebCore/Modules/fetch/FetchHeaders.h new file mode 100644 index 000000000..7f64ed11a --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchHeaders.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "ExceptionOr.h" +#include "HTTPHeaderMap.h" +#include + +namespace WebCore { + +class FetchHeaders : public RefCounted { +public: + enum class Guard { + None, + Immutable, + Request, + RequestNoCors, + Response + }; + + static Ref create(Guard guard = Guard::None) { return adoptRef(*new FetchHeaders { guard }); } + static Ref create(const FetchHeaders& headers) { return adoptRef(*new FetchHeaders { headers }); } + + ExceptionOr append(const String& name, const String& value); + ExceptionOr remove(const String&); + ExceptionOr get(const String&) const; + ExceptionOr has(const String&) const; + ExceptionOr set(const String& name, const String& value); + + void fill(const FetchHeaders*); + void filterAndFill(const HTTPHeaderMap&, Guard); + + String fastGet(HTTPHeaderName name) const { return m_headers.get(name); } + void fastSet(HTTPHeaderName name, const String& value) { m_headers.set(name, value); } + + class Iterator { + public: + explicit Iterator(FetchHeaders&); + std::optional> next(); + + private: + Ref m_headers; + size_t m_currentIndex { 0 }; + Vector m_keys; + }; + Iterator createIterator() { return Iterator { *this }; } + + const HTTPHeaderMap& internalHeaders() const { return m_headers; } + + void setGuard(Guard); + +private: + FetchHeaders(Guard guard) : m_guard(guard) { } + FetchHeaders(const FetchHeaders&); + + Guard m_guard; + HTTPHeaderMap m_headers; +}; + +inline FetchHeaders::FetchHeaders(const FetchHeaders& other) + : RefCounted() + , m_guard(other.m_guard) + , m_headers(other.m_headers) +{ +} + +inline void FetchHeaders::setGuard(Guard guard) +{ + ASSERT(!m_headers.size()); + m_guard = guard; +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchHeaders.idl b/Source/WebCore/Modules/fetch/FetchHeaders.idl new file mode 100644 index 000000000..b2016a479 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchHeaders.idl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=FETCH_API, + EnabledAtRuntime=FetchAPI, + Exposed=(Window,Worker), + ImplementationLacksVTable, + InterfaceName=Headers, + JSBuiltinConstructor, + PrivateIdentifier, + PublicIdentifier, +] interface FetchHeaders { + [MayThrowException] void append(DOMString name, DOMString value); + [MayThrowException, ImplementedAs=remove] void delete(DOMString name); + [MayThrowException] DOMString? get(DOMString name); + [MayThrowException] boolean has(DOMString name); + [MayThrowException] void set(DOMString name, DOMString value); + + iterable; + + [ImplementedAs=append, MayThrowException, PrivateIdentifier] void appendFromJS(DOMString name, DOMString value); + [ImplementedAs=fill, PrivateIdentifier] void fillFromJS(FetchHeaders? headers); +}; diff --git a/Source/WebCore/Modules/fetch/FetchHeaders.js b/Source/WebCore/Modules/fetch/FetchHeaders.js new file mode 100644 index 000000000..e8c2d7598 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchHeaders.js @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CANON INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) + +function initializeFetchHeaders(headersInit) +{ + "use strict"; + + if (headersInit === @undefined) + return this; + + if (!@isObject(headersInit)) + @throwTypeError("headersInit must be an object"); + + @fillFetchHeaders(this, headersInit); + + return this; +} diff --git a/Source/WebCore/Modules/fetch/FetchInternals.js b/Source/WebCore/Modules/fetch/FetchInternals.js new file mode 100644 index 000000000..3b91cd3bd --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchInternals.js @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) +// @internal + +function fillFetchHeaders(headers, headersInit) +{ + "use strict"; + + if (headersInit === @undefined) + return; + + if (headersInit instanceof @Headers) { + @Headers.prototype.@fillFromJS.@call(headers, headersInit); + return; + } + + if (@isArray(headersInit)) { + for (let i = 0; i < headersInit.length; i++) { + let header = headersInit[i]; + if (header.length !== 2) + @throwTypeError("headersInit sequence items should contain two values"); + @Headers.prototype.@appendFromJS.@call(headers, header[0], header[1]); + } + return this; + } + + let propertyNames = @Object.@getOwnPropertyNames(headersInit); + for (let i = 0; i < propertyNames.length; ++i) { + let name = propertyNames[i]; + @Headers.prototype.@appendFromJS.@call(headers, name, headersInit[name]); + } +} + +function consumeStream(response, type) +{ + @assert(response instanceof @Response); + @assert(response.@body instanceof @ReadableStream); + + if (@isReadableStreamDisturbed(response.@body)) + return @Promise.@reject(new @TypeError("Cannot consume a disturbed Response body ReadableStream")); + + try { + let reader = new @ReadableStreamDefaultReader(response.@body); + + @Response.prototype.@startConsumingStream.@call(response, type); + let pull = (result) => { + if (result.done) + return @Response.prototype.@finishConsumingStream.@call(response); + @Response.prototype.@consumeChunk.@call(response, result.value); + return @Promise.prototype.@then.@call(@readableStreamDefaultReaderRead(reader), pull); + } + return @Promise.prototype.@then.@call(@readableStreamDefaultReaderRead(reader), pull); + } catch (e) { + return @Promise.@reject(e); + } +} diff --git a/Source/WebCore/Modules/fetch/FetchLoader.cpp b/Source/WebCore/Modules/fetch/FetchLoader.cpp new file mode 100644 index 000000000..6e2fd5c81 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchLoader.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchLoader.h" + +#if ENABLE(FETCH_API) + +#include "BlobURL.h" +#include "CachedResourceRequestInitiators.h" +#include "ContentSecurityPolicy.h" +#include "FetchBody.h" +#include "FetchLoaderClient.h" +#include "FetchRequest.h" +#include "ResourceRequest.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include "SharedBuffer.h" +#include "TextResourceDecoder.h" +#include "ThreadableBlobRegistry.h" + +namespace WebCore { + +void FetchLoader::start(ScriptExecutionContext& context, const Blob& blob) +{ + auto urlForReading = BlobURL::createPublicURL(context.securityOrigin()); + if (urlForReading.isEmpty()) { + m_client.didFail(); + return; + } + + ThreadableBlobRegistry::registerBlobURL(context.securityOrigin(), urlForReading, blob.url()); + + ResourceRequest request(urlForReading); + request.setInitiatorIdentifier(context.resourceRequestIdentifier()); + request.setHTTPMethod("GET"); + + ThreadableLoaderOptions options; + options.sendLoadCallbacks = SendCallbacks; + options.dataBufferingPolicy = DoNotBufferData; + options.preflightPolicy = ConsiderPreflight; + options.credentials = FetchOptions::Credentials::Include; + options.mode = FetchOptions::Mode::SameOrigin; + options.contentSecurityPolicyEnforcement = ContentSecurityPolicyEnforcement::DoNotEnforce; + + m_loader = ThreadableLoader::create(context, *this, WTFMove(request), options); + m_isStarted = m_loader; +} + +void FetchLoader::start(ScriptExecutionContext& context, const FetchRequest& request) +{ + ThreadableLoaderOptions options(request.fetchOptions(), ConsiderPreflight, + context.shouldBypassMainWorldContentSecurityPolicy() ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceConnectSrcDirective, + String(cachedResourceRequestInitiators().fetch), + ResponseFilteringPolicy::Enable); + options.sendLoadCallbacks = SendCallbacks; + options.dataBufferingPolicy = DoNotBufferData; + options.sameOriginDataURLFlag = SameOriginDataURLFlag::Set; + + ResourceRequest fetchRequest = request.internalRequest(); + + ASSERT(context.contentSecurityPolicy()); + auto& contentSecurityPolicy = *context.contentSecurityPolicy(); + + contentSecurityPolicy.upgradeInsecureRequestIfNeeded(fetchRequest, ContentSecurityPolicy::InsecureRequestType::Load); + + if (!context.shouldBypassMainWorldContentSecurityPolicy() && !contentSecurityPolicy.allowConnectToSource(fetchRequest.url())) { + m_client.didFail(); + return; + } + + String referrer = request.internalRequestReferrer(); + if (referrer == "no-referrer") { + options.referrerPolicy = FetchOptions::ReferrerPolicy::NoReferrer; + referrer = String(); + } else + referrer = (referrer == "client") ? context.url().strippedForUseAsReferrer() : URL(context.url(), referrer).strippedForUseAsReferrer(); + + m_loader = ThreadableLoader::create(context, *this, WTFMove(fetchRequest), options, WTFMove(referrer)); + m_isStarted = m_loader; +} + +FetchLoader::FetchLoader(FetchLoaderClient& client, FetchBodyConsumer* consumer) + : m_client(client) + , m_consumer(consumer) +{ +} + +void FetchLoader::stop() +{ + if (m_consumer) + m_consumer->clean(); + if (m_loader) + m_loader->cancel(); +} + +RefPtr FetchLoader::startStreaming() +{ + ASSERT(m_consumer); + auto firstChunk = m_consumer->takeData(); + m_consumer = nullptr; + return firstChunk; +} + +void FetchLoader::didReceiveResponse(unsigned long, const ResourceResponse& response) +{ + m_client.didReceiveResponse(response); +} + +void FetchLoader::didReceiveData(const char* value, int size) +{ + if (!m_consumer) { + m_client.didReceiveData(value, size); + return; + } + m_consumer->append(value, size); +} + +void FetchLoader::didFinishLoading(unsigned long, double) +{ + m_client.didSucceed(); +} + +void FetchLoader::didFail(const ResourceError&) +{ + m_client.didFail(); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchLoader.h b/Source/WebCore/Modules/fetch/FetchLoader.h new file mode 100644 index 000000000..44c1adbce --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchLoader.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "FetchBodyConsumer.h" +#include "ThreadableLoader.h" +#include "ThreadableLoaderClient.h" + +namespace WebCore { + +class Blob; +class FetchLoaderClient; +class FetchRequest; +class ScriptExecutionContext; + +class FetchLoader final : public ThreadableLoaderClient { +public: + FetchLoader(FetchLoaderClient&, FetchBodyConsumer*); + + RefPtr startStreaming(); + + void start(ScriptExecutionContext&, const FetchRequest&); + void start(ScriptExecutionContext&, const Blob&); + void stop(); + + bool isStarted() const { return m_isStarted; } + +private: + // ThreadableLoaderClient API. + void didReceiveResponse(unsigned long, const ResourceResponse&) final; + void didReceiveData(const char*, int) final; + void didFinishLoading(unsigned long, double) final; + void didFail(const ResourceError&) final; + +private: + FetchLoaderClient& m_client; + RefPtr m_loader; + FetchBodyConsumer* m_consumer; + bool m_isStarted { false }; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchLoaderClient.h b/Source/WebCore/Modules/fetch/FetchLoaderClient.h new file mode 100644 index 000000000..295b16b10 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchLoaderClient.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include + +namespace WebCore { + +class ResourceResponse; + +class FetchLoaderClient { +public: + virtual ~FetchLoaderClient() { } + + virtual void didReceiveResponse(const ResourceResponse&) { } + + virtual void didReceiveData(const char*, size_t) { } + + virtual void didSucceed() = 0; + virtual void didFail() = 0; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchRequest.cpp b/Source/WebCore/Modules/fetch/FetchRequest.cpp new file mode 100644 index 000000000..b879a7bcc --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchRequest.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchRequest.h" + +#if ENABLE(FETCH_API) + +#include "ExceptionCode.h" +#include "HTTPParsers.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" + +namespace WebCore { + +static std::optional setMethod(ResourceRequest& request, const String& initMethod) +{ + if (!isValidHTTPToken(initMethod)) + return Exception { TypeError, ASCIILiteral("Method is not a valid HTTP token.") }; + + String method = initMethod.convertToASCIIUppercase(); + if (method == "CONNECT" || method == "TRACE" || method == "TRACK") + return Exception { TypeError, ASCIILiteral("Method is forbidden.") }; + + request.setHTTPMethod((method == "DELETE" || method == "GET" || method == "HEAD" || method == "OPTIONS" || method == "POST" || method == "PUT") ? method : initMethod); + + return std::nullopt; +} + +static std::optional setReferrer(FetchRequest::InternalRequest& request, ScriptExecutionContext& context, const String& referrer) +{ + if (referrer.isEmpty()) { + request.referrer = ASCIILiteral("no-referrer"); + return std::nullopt; + } + // FIXME: Tighten the URL parsing algorithm according https://url.spec.whatwg.org/#concept-url-parser. + URL referrerURL = context.completeURL(referrer); + if (!referrerURL.isValid()) + return Exception { TypeError, ASCIILiteral("Referrer is not a valid URL.") }; + + if (referrerURL.protocolIs("about") && referrerURL.path() == "client") { + request.referrer = ASCIILiteral("client"); + return std::nullopt; + } + + if (!(context.securityOrigin() && context.securityOrigin()->canRequest(referrerURL))) + return Exception { TypeError, ASCIILiteral("Referrer is not same-origin.") }; + + request.referrer = referrerURL.string(); + return std::nullopt; +} + +static std::optional buildOptions(FetchRequest::InternalRequest& request, ScriptExecutionContext& context, const FetchRequest::Init& init) +{ + if (!init.window.isUndefinedOrNull()) + return Exception { TypeError, ASCIILiteral("Window can only be null.") }; + + if (!init.referrer.isNull()) { + if (auto exception = setReferrer(request, context, init.referrer)) + return exception; + } + + if (init.referrerPolicy) + request.options.referrerPolicy = init.referrerPolicy.value(); + + if (init.mode) + request.options.mode = init.mode.value(); + if (request.options.mode == FetchOptions::Mode::Navigate) + return Exception { TypeError, ASCIILiteral("Request constructor does not accept navigate fetch mode.") }; + + if (init.credentials) + request.options.credentials = init.credentials.value(); + + if (init.cache) + request.options.cache = init.cache.value(); + if (request.options.cache == FetchOptions::Cache::OnlyIfCached && request.options.mode != FetchOptions::Mode::SameOrigin) + return Exception { TypeError, ASCIILiteral("only-if-cached cache option requires fetch mode to be same-origin.") }; + + if (init.redirect) + request.options.redirect = init.redirect.value(); + + if (!init.integrity.isNull()) + request.integrity = init.integrity; + + if (!init.method.isNull()) { + if (auto exception = setMethod(request.request, init.method)) + return exception; + } + + return std::nullopt; +} + +static bool methodCanHaveBody(const FetchRequest::InternalRequest& internalRequest) +{ + return internalRequest.request.httpMethod() != "GET" && internalRequest.request.httpMethod() != "HEAD"; +} + +ExceptionOr FetchRequest::initializeOptions(const Init& init) +{ + ASSERT(scriptExecutionContext()); + + auto exception = buildOptions(m_internalRequest, *scriptExecutionContext(), init); + if (exception) + return WTFMove(exception.value()); + + if (m_internalRequest.options.mode == FetchOptions::Mode::NoCors) { + const String& method = m_internalRequest.request.httpMethod(); + if (method != "GET" && method != "POST" && method != "HEAD") + return Exception { TypeError, ASCIILiteral("Method must be GET, POST or HEAD in no-cors mode.") }; + if (!m_internalRequest.integrity.isEmpty()) + return Exception { TypeError, ASCIILiteral("There cannot be an integrity in no-cors mode.") }; + m_headers->setGuard(FetchHeaders::Guard::RequestNoCors); + } + return m_headers.get(); +} + +ExceptionOr FetchRequest::initializeWith(const String& url, const Init& init) +{ + ASSERT(scriptExecutionContext()); + // FIXME: Tighten the URL parsing algorithm according https://url.spec.whatwg.org/#concept-url-parser. + URL requestURL = scriptExecutionContext()->completeURL(url); + if (!requestURL.isValid() || !requestURL.user().isEmpty() || !requestURL.pass().isEmpty()) + return Exception { TypeError, ASCIILiteral("URL is not valid or contains user credentials.") }; + + m_internalRequest.options.mode = Mode::Cors; + m_internalRequest.options.credentials = Credentials::Omit; + m_internalRequest.referrer = ASCIILiteral("client"); + m_internalRequest.request.setURL(requestURL); + m_internalRequest.request.setRequester(ResourceRequest::Requester::Fetch); + m_internalRequest.request.setInitiatorIdentifier(scriptExecutionContext()->resourceRequestIdentifier()); + + return initializeOptions(init); +} + +ExceptionOr FetchRequest::initializeWith(FetchRequest& input, const Init& init) +{ + if (input.isDisturbedOrLocked()) + return Exception {TypeError, ASCIILiteral("Request input is disturbed or locked.") }; + + m_internalRequest = input.m_internalRequest; + + return initializeOptions(init); +} + +ExceptionOr FetchRequest::setBody(JSC::ExecState& execState, JSC::JSValue body, FetchRequest* request) +{ + if (!body.isNull()) { + if (!methodCanHaveBody(m_internalRequest)) + return Exception { TypeError }; + ASSERT(scriptExecutionContext()); + extractBody(*scriptExecutionContext(), execState, body); + if (isBodyNull()) + return Exception { TypeError }; + } else if (request && !request->isBodyNull()) { + if (!methodCanHaveBody(m_internalRequest)) + return Exception { TypeError }; + m_body = WTFMove(request->m_body); + request->setDisturbed(); + } + updateContentType(); + return { }; +} + +String FetchRequest::referrer() const +{ + if (m_internalRequest.referrer == "no-referrer") + return String(); + if (m_internalRequest.referrer == "client") + return ASCIILiteral("about:client"); + return m_internalRequest.referrer; +} + +const String& FetchRequest::url() const +{ + if (m_requestURL.isNull()) + m_requestURL = m_internalRequest.request.url().serialize(); + return m_requestURL; +} + +ResourceRequest FetchRequest::internalRequest() const +{ + ASSERT(scriptExecutionContext()); + + ResourceRequest request = m_internalRequest.request; + request.setHTTPHeaderFields(m_headers->internalHeaders()); + + if (!isBodyNull()) + request.setHTTPBody(body().bodyForInternalRequest(*scriptExecutionContext())); + + return request; +} + +ExceptionOr> FetchRequest::clone(ScriptExecutionContext& context) +{ + if (isDisturbedOrLocked()) + return Exception { TypeError }; + + auto clone = adoptRef(*new FetchRequest(context, std::nullopt, FetchHeaders::create(m_headers.get()), FetchRequest::InternalRequest(m_internalRequest))); + clone->cloneBody(*this); + return WTFMove(clone); +} + +const char* FetchRequest::activeDOMObjectName() const +{ + return "Request"; +} + +bool FetchRequest::canSuspendForDocumentSuspension() const +{ + // FIXME: We can probably do the same strategy as XHR. + return !isActive(); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchRequest.h b/Source/WebCore/Modules/fetch/FetchRequest.h new file mode 100644 index 000000000..47fbf78c3 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchRequest.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "ExceptionOr.h" +#include "FetchBodyOwner.h" +#include "FetchOptions.h" +#include "ResourceRequest.h" +#include + +namespace WebCore { + +class Blob; +class ScriptExecutionContext; +class URLSearchParams; + +class FetchRequest final : public FetchBodyOwner { +public: + static Ref create(ScriptExecutionContext& context) { return adoptRef(*new FetchRequest(context, std::nullopt, FetchHeaders::create(FetchHeaders::Guard::Request), { })); } + + using Cache = FetchOptions::Cache; + using Credentials = FetchOptions::Credentials; + using Destination = FetchOptions::Destination; + using Mode = FetchOptions::Mode; + using Redirect = FetchOptions::Redirect; + using ReferrerPolicy = FetchOptions::ReferrerPolicy; + using Type = FetchOptions::Type; + + struct Init { + String method; + String referrer; + std::optional referrerPolicy; + std::optional mode; + std::optional credentials; + std::optional cache; + std::optional redirect; + String integrity; + JSC::JSValue window; + }; + + ExceptionOr initializeWith(FetchRequest&, const Init&); + ExceptionOr initializeWith(const String&, const Init&); + ExceptionOr setBody(JSC::ExecState&, JSC::JSValue, FetchRequest*); + + const String& method() const { return m_internalRequest.request.httpMethod(); } + const String& url() const; + FetchHeaders& headers() { return m_headers.get(); } + + Type type() const; + Destination destination() const; + String referrer() const; + ReferrerPolicy referrerPolicy() const; + Mode mode() const; + Credentials credentials() const; + Cache cache() const; + Redirect redirect() const; + + const String& integrity() const { return m_internalRequest.integrity; } + + ExceptionOr> clone(ScriptExecutionContext&); + + struct InternalRequest { + ResourceRequest request; + FetchOptions options; + String referrer; + String integrity; + }; + + const FetchOptions& fetchOptions() const { return m_internalRequest.options; } + ResourceRequest internalRequest() const; + bool isBodyReadableStream() const { return !isBodyNull() && body().isReadableStream(); } + + const String& internalRequestReferrer() const { return m_internalRequest.referrer; } + +private: + FetchRequest(ScriptExecutionContext&, std::optional&&, Ref&&, InternalRequest&&); + + ExceptionOr initializeOptions(const Init&); + + const char* activeDOMObjectName() const final; + bool canSuspendForDocumentSuspension() const final; + + InternalRequest m_internalRequest; + mutable String m_requestURL; +}; + +inline FetchRequest::FetchRequest(ScriptExecutionContext& context, std::optional&& body, Ref&& headers, InternalRequest&& internalRequest) + : FetchBodyOwner(context, WTFMove(body), WTFMove(headers)) + , m_internalRequest(WTFMove(internalRequest)) +{ +} + +inline auto FetchRequest::cache() const -> Cache +{ + return m_internalRequest.options.cache; +} + +inline auto FetchRequest::credentials() const -> Credentials +{ + return m_internalRequest.options.credentials; +} + +inline auto FetchRequest::destination() const -> Destination +{ + return m_internalRequest.options.destination; +} + +inline auto FetchRequest::mode() const -> Mode +{ + return m_internalRequest.options.mode; +} + +inline auto FetchRequest::redirect() const -> Redirect +{ + return m_internalRequest.options.redirect; +} + +inline auto FetchRequest::referrerPolicy() const -> ReferrerPolicy +{ + return m_internalRequest.options.referrerPolicy; +} + +inline auto FetchRequest::type() const -> Type +{ + return m_internalRequest.options.type; +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchRequest.idl b/Source/WebCore/Modules/fetch/FetchRequest.idl new file mode 100644 index 000000000..d9d7960f9 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchRequest.idl @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum RequestType { "", "audio", "font", "image", "script", "style", "track", "video" }; +enum RequestDestination { "", "document", "sharedworker", "subresource", "unknown", "worker" }; +enum RequestMode { "navigate", "same-origin", "no-cors", "cors" }; +enum RequestCredentials { "omit", "same-origin", "include" }; +enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached" }; +enum RequestRedirect { "follow", "error", "manual" }; +enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "unsafe-url" }; + +dictionary RequestInit { + ByteString method; + // FIXME: Should add: HeadersInit headers; + // FIXME: Should add: BodyInit? body; + USVString referrer; + ReferrerPolicy referrerPolicy; + RequestMode mode; + RequestCredentials credentials; + RequestCache cache; + RequestRedirect redirect; + DOMString integrity; + // FIXME: Should add: boolean keepalive; + any window; // can only be set to null +}; + +[ + ActiveDOMObject, + Conditional=FETCH_API, + Constructor(any input, optional RequestInit init), + EnabledAtRuntime=FetchAPI, + Exposed=(Window,Worker), + InterfaceName=Request, + JSBuiltinConstructor, + PrivateIdentifier, + PublicIdentifier, +] interface FetchRequest { + readonly attribute ByteString method; + readonly attribute DOMString url; + readonly attribute FetchHeaders headers; + + readonly attribute RequestType type; + readonly attribute RequestDestination destination; + readonly attribute USVString referrer; + readonly attribute ReferrerPolicy referrerPolicy; + readonly attribute RequestMode mode; + readonly attribute RequestCredentials credentials; + readonly attribute RequestCache cache; + readonly attribute RequestRedirect redirect; + readonly attribute DOMString integrity; + + [CallWith=ScriptExecutionContext, MayThrowException, NewObject] FetchRequest clone(); + + [MayThrowException, NewObject, PrivateIdentifier] FetchHeaders initializeWith(FetchRequest input, RequestInit init); + [MayThrowException, NewObject, PrivateIdentifier] FetchHeaders initializeWith(DOMString input, RequestInit init); + [CallWith=ScriptState, MayThrowException, PrivateIdentifier] void setBody(any body, FetchRequest? request); +}; + +FetchRequest implements FetchBody; diff --git a/Source/WebCore/Modules/fetch/FetchRequest.js b/Source/WebCore/Modules/fetch/FetchRequest.js new file mode 100644 index 000000000..533a33b52 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchRequest.js @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) + +function initializeFetchRequest(input, init) +{ + "use strict"; + + if (init === @undefined) + init = { }; + else if (!@isObject(init)) + @throwTypeError("Request init must be an object"); + + let headers = this.@initializeWith(input, init); + @assert(headers instanceof @Headers); + + let inputIsRequest = input instanceof @Request; + if ("headers" in init) + @fillFetchHeaders(headers, init.headers) + else if (inputIsRequest) + @fillFetchHeaders(headers, input.headers) + + let hasInitBody = init.body !== @undefined && init.body !== null; + this.@setBody(hasInitBody ? init.body : null, inputIsRequest ? input : null); + + return this; +} diff --git a/Source/WebCore/Modules/fetch/FetchResponse.cpp b/Source/WebCore/Modules/fetch/FetchResponse.cpp new file mode 100644 index 000000000..40cec5b91 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponse.cpp @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchResponse.h" + +#if ENABLE(FETCH_API) + +#include "ExceptionCode.h" +#include "FetchRequest.h" +#include "HTTPParsers.h" +#include "JSBlob.h" +#include "JSFetchResponse.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +static inline bool isRedirectStatus(int status) +{ + return status == 301 || status == 302 || status == 303 || status == 307 || status == 308; +} + +Ref FetchResponse::error(ScriptExecutionContext& context) +{ + auto response = adoptRef(*new FetchResponse(context, { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { })); + response->m_response.setType(Type::Error); + return response; +} + +ExceptionOr> FetchResponse::redirect(ScriptExecutionContext& context, const String& url, int status) +{ + // FIXME: Tighten the URL parsing algorithm according https://url.spec.whatwg.org/#concept-url-parser. + URL requestURL = context.completeURL(url); + if (!requestURL.isValid() || !requestURL.user().isEmpty() || !requestURL.pass().isEmpty()) + return Exception { TypeError }; + if (!isRedirectStatus(status)) + return Exception { RangeError }; + auto redirectResponse = adoptRef(*new FetchResponse(context, { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { })); + redirectResponse->m_response.setHTTPStatusCode(status); + redirectResponse->m_headers->fastSet(HTTPHeaderName::Location, requestURL.string()); + return WTFMove(redirectResponse); +} + +ExceptionOr FetchResponse::setStatus(int status, const String& statusText) +{ + if (!isValidReasonPhrase(statusText)) + return Exception { TypeError }; + m_response.setHTTPStatusCode(status); + m_response.setHTTPStatusText(statusText); + return { }; +} + +void FetchResponse::initializeWith(JSC::ExecState& execState, JSC::JSValue body) +{ + ASSERT(scriptExecutionContext()); + extractBody(*scriptExecutionContext(), execState, body); + updateContentType(); +} + +FetchResponse::FetchResponse(ScriptExecutionContext& context, std::optional&& body, Ref&& headers, ResourceResponse&& response) + : FetchBodyOwner(context, WTFMove(body), WTFMove(headers)) + , m_response(WTFMove(response)) +{ +} + +Ref FetchResponse::cloneForJS() +{ + ASSERT(scriptExecutionContext()); + ASSERT(!isDisturbedOrLocked()); + + auto clone = adoptRef(*new FetchResponse(*scriptExecutionContext(), std::nullopt, FetchHeaders::create(headers()), ResourceResponse(m_response))); + clone->cloneBody(*this); + return clone; +} + +void FetchResponse::fetch(ScriptExecutionContext& context, FetchRequest& request, FetchPromise&& promise) +{ + if (request.isBodyReadableStream()) { + promise.reject(TypeError, "ReadableStream uploading is not supported"); + return; + } + auto response = adoptRef(*new FetchResponse(context, FetchBody::loadingBody(), FetchHeaders::create(FetchHeaders::Guard::Immutable), { })); + + response->m_bodyLoader.emplace(response.get(), WTFMove(promise)); + if (!response->m_bodyLoader->start(context, request)) + response->m_bodyLoader = std::nullopt; +} + +const String& FetchResponse::url() const +{ + if (m_responseURL.isNull()) + m_responseURL = m_response.url().serialize(true); + return m_responseURL; +} + +void FetchResponse::BodyLoader::didSucceed() +{ + ASSERT(m_response.hasPendingActivity()); + m_response.m_body->loadingSucceeded(); + +#if ENABLE(READABLE_STREAM_API) + if (m_response.m_readableStreamSource && !m_response.body().consumer().hasData()) + m_response.closeStream(); +#endif + + if (m_loader->isStarted()) { + Ref protector(m_response); + m_response.m_bodyLoader = std::nullopt; + } +} + +void FetchResponse::BodyLoader::didFail() +{ + ASSERT(m_response.hasPendingActivity()); + if (m_promise) + std::exchange(m_promise, std::nullopt)->reject(TypeError); + +#if ENABLE(READABLE_STREAM_API) + if (m_response.m_readableStreamSource) { + if (!m_response.m_readableStreamSource->isCancelling()) + m_response.m_readableStreamSource->error(ASCIILiteral("Loading failed")); + m_response.m_readableStreamSource = nullptr; + } +#endif + + // Check whether didFail is called as part of FetchLoader::start. + if (m_loader->isStarted()) { + Ref protector(m_response); + m_response.m_bodyLoader = std::nullopt; + } +} + +FetchResponse::BodyLoader::BodyLoader(FetchResponse& response, FetchPromise&& promise) + : m_response(response) + , m_promise(WTFMove(promise)) +{ + m_response.setPendingActivity(&m_response); +} + +FetchResponse::BodyLoader::~BodyLoader() +{ + m_response.unsetPendingActivity(&m_response); +} + +void FetchResponse::BodyLoader::didReceiveResponse(const ResourceResponse& resourceResponse) +{ + ASSERT(m_promise); + + m_response.m_response = resourceResponse; + m_response.m_headers->filterAndFill(resourceResponse.httpHeaderFields(), FetchHeaders::Guard::Response); + + std::exchange(m_promise, std::nullopt)->resolve(m_response); +} + +void FetchResponse::BodyLoader::didReceiveData(const char* data, size_t size) +{ +#if ENABLE(READABLE_STREAM_API) + ASSERT(m_response.m_readableStreamSource); + auto& source = *m_response.m_readableStreamSource; + + if (!source.isPulling()) { + m_response.body().consumer().append(data, size); + return; + } + + if (m_response.body().consumer().hasData() && !source.enqueue(m_response.body().consumer().takeAsArrayBuffer())) { + stop(); + return; + } + if (!source.enqueue(ArrayBuffer::tryCreate(data, size))) { + stop(); + return; + } + source.resolvePullPromise(); +#else + UNUSED_PARAM(data); + UNUSED_PARAM(size); +#endif +} + +bool FetchResponse::BodyLoader::start(ScriptExecutionContext& context, const FetchRequest& request) +{ + m_loader = std::make_unique(*this, &m_response.m_body->consumer()); + m_loader->start(context, request); + return m_loader->isStarted(); +} + +void FetchResponse::BodyLoader::stop() +{ + m_promise = std::nullopt; + if (m_loader) + m_loader->stop(); +} + +void FetchResponse::consume(unsigned type, Ref&& wrapper) +{ + ASSERT(type <= static_cast(FetchBodyConsumer::Type::Text)); + auto consumerType = static_cast(type); + + if (isLoading()) { + consumeOnceLoadingFinished(consumerType, WTFMove(wrapper)); + return; + } + + switch (consumerType) { + case FetchBodyConsumer::Type::ArrayBuffer: + arrayBuffer(WTFMove(wrapper)); + return; + case FetchBodyConsumer::Type::Blob: + blob(WTFMove(wrapper)); + return; + case FetchBodyConsumer::Type::JSON: + json(WTFMove(wrapper)); + return; + case FetchBodyConsumer::Type::Text: + text(WTFMove(wrapper)); + return; + case FetchBodyConsumer::Type::None: + ASSERT_NOT_REACHED(); + return; + } +} + +#if ENABLE(READABLE_STREAM_API) +void FetchResponse::startConsumingStream(unsigned type) +{ + m_isDisturbed = true; + m_consumer.setType(static_cast(type)); +} + +void FetchResponse::consumeChunk(Ref&& chunk) +{ + m_consumer.append(chunk->data(), chunk->byteLength()); +} + +void FetchResponse::finishConsumingStream(Ref&& promise) +{ + m_consumer.resolve(WTFMove(promise)); +} + +void FetchResponse::consumeBodyAsStream() +{ + ASSERT(m_readableStreamSource); + m_isDisturbed = true; + if (!isLoading()) { + body().consumeAsStream(*this, *m_readableStreamSource); + if (!m_readableStreamSource->isPulling()) + m_readableStreamSource = nullptr; + return; + } + + ASSERT(m_bodyLoader); + + RefPtr data = m_bodyLoader->startStreaming(); + if (data) { + if (!m_readableStreamSource->enqueue(data->createArrayBuffer())) { + stop(); + return; + } + m_readableStreamSource->resolvePullPromise(); + } +} + +void FetchResponse::closeStream() +{ + ASSERT(m_readableStreamSource); + m_readableStreamSource->close(); + m_readableStreamSource = nullptr; +} + +void FetchResponse::feedStream() +{ + ASSERT(m_readableStreamSource); + bool shouldCloseStream = !m_bodyLoader; + + if (body().consumer().hasData()) { + if (!m_readableStreamSource->enqueue(body().consumer().takeAsArrayBuffer())) { + stop(); + return; + } + if (!shouldCloseStream) { + m_readableStreamSource->resolvePullPromise(); + return; + } + } else if (!shouldCloseStream) + return; + + closeStream(); +} + +ReadableStreamSource* FetchResponse::createReadableStreamSource() +{ + ASSERT(!m_readableStreamSource); + ASSERT(!m_isDisturbed); + + if (isBodyNull()) + return nullptr; + + m_readableStreamSource = adoptRef(*new FetchResponseSource(*this)); + return m_readableStreamSource.get(); +} + +RefPtr FetchResponse::BodyLoader::startStreaming() +{ + ASSERT(m_loader); + return m_loader->startStreaming(); +} + +void FetchResponse::cancel() +{ + m_isDisturbed = true; + stop(); +} + +#endif + +void FetchResponse::stop() +{ + RefPtr protectedThis(this); + FetchBodyOwner::stop(); + if (m_bodyLoader) { + m_bodyLoader->stop(); + m_bodyLoader = std::nullopt; + } +} + +const char* FetchResponse::activeDOMObjectName() const +{ + return "Response"; +} + +bool FetchResponse::canSuspendForDocumentSuspension() const +{ + // FIXME: We can probably do the same strategy as XHR. + return !isActive(); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchResponse.h b/Source/WebCore/Modules/fetch/FetchResponse.h new file mode 100644 index 000000000..b4d84f9e7 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponse.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "FetchBodyOwner.h" +#include "ResourceResponse.h" +#include + +namespace JSC { +class ExecState; +class JSValue; +}; + +namespace WebCore { + +class FetchRequest; +class ReadableStreamSource; + +class FetchResponse final : public FetchBodyOwner { +public: + using Type = ResourceResponse::Type; + + static Ref create(ScriptExecutionContext& context) { return adoptRef(*new FetchResponse(context, std::nullopt, FetchHeaders::create(FetchHeaders::Guard::Response), ResourceResponse())); } + static Ref error(ScriptExecutionContext&); + static ExceptionOr> redirect(ScriptExecutionContext&, const String& url, int status); + + using FetchPromise = DOMPromise>; + static void fetch(ScriptExecutionContext&, FetchRequest&, FetchPromise&&); + + void consume(unsigned, Ref&&); +#if ENABLE(READABLE_STREAM_API) + void startConsumingStream(unsigned); + void consumeChunk(Ref&&); + void finishConsumingStream(Ref&&); +#endif + + ExceptionOr setStatus(int status, const String& statusText); + void initializeWith(JSC::ExecState&, JSC::JSValue); + + Type type() const { return m_response.type(); } + const String& url() const; + bool redirected() const { return m_response.isRedirected(); } + int status() const { return m_response.httpStatusCode(); } + bool ok() const { return m_response.isSuccessful(); } + const String& statusText() const { return m_response.httpStatusText(); } + + FetchHeaders& headers() { return m_headers; } + Ref cloneForJS(); + +#if ENABLE(READABLE_STREAM_API) + ReadableStreamSource* createReadableStreamSource(); + void consumeBodyAsStream(); + void feedStream(); + void cancel(); +#endif + + bool isLoading() const { return !!m_bodyLoader; } + +private: + FetchResponse(ScriptExecutionContext&, std::optional&&, Ref&&, ResourceResponse&&); + + static void startFetching(ScriptExecutionContext&, const FetchRequest&, FetchPromise&&); + + void stop() final; + const char* activeDOMObjectName() const final; + bool canSuspendForDocumentSuspension() const final; + +#if ENABLE(READABLE_STREAM_API) + void closeStream(); +#endif + + class BodyLoader final : public FetchLoaderClient { + public: + BodyLoader(FetchResponse&, FetchPromise&&); + ~BodyLoader(); + + bool start(ScriptExecutionContext&, const FetchRequest&); + void stop(); + +#if ENABLE(READABLE_STREAM_API) + RefPtr startStreaming(); +#endif + + private: + // FetchLoaderClient API + void didSucceed() final; + void didFail() final; + void didReceiveResponse(const ResourceResponse&) final; + void didReceiveData(const char*, size_t) final; + + FetchResponse& m_response; + std::optional m_promise; + std::unique_ptr m_loader; + }; + + ResourceResponse m_response; + std::optional m_bodyLoader; + mutable String m_responseURL; + + FetchBodyConsumer m_consumer { FetchBodyConsumer::Type::ArrayBuffer }; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchResponse.idl b/Source/WebCore/Modules/fetch/FetchResponse.idl new file mode 100644 index 000000000..03fe2010f --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponse.idl @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum ResponseType { "basic", "cors", "default", "error", "opaque", "opaqueredirect" }; + +[ + ActiveDOMObject, + Conditional=FETCH_API, + ConstructorCallWith=ScriptExecutionContext, + EnabledAtRuntime=FetchAPI, + Exposed=(Window,Worker), + InterfaceName=Response, + JSBuiltinConstructor, + PrivateIdentifier, + PublicIdentifier, +] interface FetchResponse { + [CallWith=ScriptExecutionContext, NewObject] static FetchResponse error(); + [CallWith=ScriptExecutionContext, MayThrowException, NewObject] static FetchResponse redirect(DOMString url, optional unsigned short status = 302); + + readonly attribute ResponseType type; + + readonly attribute DOMString url; + readonly attribute boolean redirected; + readonly attribute unsigned short status; + readonly attribute boolean ok; + readonly attribute DOMString statusText; + readonly attribute FetchHeaders headers; // FIXME: Add support for SameObject keyword; use it here. + [JSBuiltin] readonly attribute ReadableStream? body; + + // Copy of FetchBody IDL as we want to implement some of these as built-ins. + [JSBuiltin] readonly attribute boolean bodyUsed; + [JSBuiltin] Promise arrayBuffer(); + [JSBuiltin] Promise blob(); + [JSBuiltin] Promise formData(); + [JSBuiltin] Promise json(); + [JSBuiltin] Promise text(); + + [JSBuiltin] FetchResponse clone(); + + [NewObject, PrivateIdentifier] FetchResponse cloneForJS(); + + [Conditional=READABLE_STREAM_API, PrivateIdentifier] void startConsumingStream(unsigned short type); + [Conditional=READABLE_STREAM_API, PrivateIdentifier] void consumeChunk(Uint8Array chunk); + [Conditional=READABLE_STREAM_API, PrivateIdentifier] Promise finishConsumingStream(); + + [PrivateIdentifier] Promise consume(unsigned short type); + [PrivateIdentifier] boolean isLoading(); + [MayThrowException, PrivateIdentifier] void setStatus(unsigned short status, DOMString statusText); + [CallWith=ScriptState, PrivateIdentifier] void initializeWith(any body); + [NewObject, PrivateIdentifier] ReadableStreamSource createReadableStreamSource(); + [PrivateIdentifier] boolean isDisturbed(); +}; diff --git a/Source/WebCore/Modules/fetch/FetchResponse.js b/Source/WebCore/Modules/fetch/FetchResponse.js new file mode 100644 index 000000000..6f53924f1 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponse.js @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CANON INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) + +function initializeFetchResponse(body, init) +{ + "use strict"; + + if (init === @undefined) + init = { }; + else if (!@isObject(init)) + @throwTypeError("Response init must be an object"); + + let status = (init.status !== @undefined) ? @toNumber(init.status) : 200; + if (status < 200 || status > 599) + @throwRangeError("Status must be between 200 and 599"); + + let statusText = (init.statusText !== @undefined) ? init.statusText : "OK"; + + this.@setStatus(status, statusText); + + if (init.headers !== @undefined) + @fillFetchHeaders(this.headers, init.headers); + + if (body !== @undefined && body !== null) { + if (status === 101 || status === 204 || status === 205 || status === 304) + @throwTypeError("Response cannot have a body with the given status"); + + // FIXME: Use @isReadableStream once it is no longer guarded by READABLE_STREAM_API guard. + let isBodyReadableStream = (@isObject(body) && !!body.@readableStreamController); + if (isBodyReadableStream) + this.@body = body; + + this.@initializeWith(body); + } + + return this; +} + +function bodyUsed() +{ + if (!(this instanceof @Response)) + throw @makeGetterTypeError("Response", "bodyUsed"); + + if (this.@body) + return @isReadableStreamDisturbed(this.@body); + + return @Response.prototype.@isDisturbed.@call(this); +} + +function body() +{ + if (!(this instanceof @Response)) + throw @makeGetterTypeError("Response", "body"); + + if (!this.@body) { + if (@Response.prototype.@isDisturbed.@call(this)) { + this.@body = new @ReadableStream(); + // Get reader to lock it. + new @ReadableStreamDefaultReader(this.@body); + } else { + var source = @Response.prototype.@createReadableStreamSource.@call(this); + this.@body = source ? new @ReadableStream(source) : null; + } + } + return this.@body; +} + +function clone() +{ + if (!(this instanceof @Response)) + throw @makeThisTypeError("Response", "clone"); + + if (@Response.prototype.@isDisturbed.@call(this) || (this.@body && @isReadableStreamLocked(this.@body))) + @throwTypeError("Cannot clone a disturbed Response"); + + var cloned = @Response.prototype.@cloneForJS.@call(this); + + // Let's create @body if response body is loading to provide data to both clones. + if (@Response.prototype.@isLoading.@call(this) && !this.@body) { + var source = @Response.prototype.@createReadableStreamSource.@call(this); + @assert(!!source); + this.@body = new @ReadableStream(source); + } + + if (this.@body) { + var teedReadableStreams = @readableStreamTee(this.@body, true); + this.@body = teedReadableStreams[0]; + cloned.@body = teedReadableStreams[1]; + } + return cloned; +} + +// consume and consumeStream single parameter should be kept in sync with FetchBodyConsumer::Type. +function arrayBuffer() +{ + if (!(this instanceof @Response)) + return @Promise.@reject(@makeThisTypeError("Response", "arrayBuffer")); + + const arrayBufferConsumerType = 1; + if (!this.@body) + return @Response.prototype.@consume.@call(this, arrayBufferConsumerType); + + return @consumeStream(this, arrayBufferConsumerType); +} + +function blob() +{ + if (!(this instanceof @Response)) + return @Promise.@reject(@makeThisTypeError("Response", "blob")); + + const blobConsumerType = 2; + if (!this.@body) + return @Response.prototype.@consume.@call(this, blobConsumerType); + + return @consumeStream(this, blobConsumerType); +} + +function formData() +{ + if (!(this instanceof @Response)) + return @Promise.@reject(@makeThisTypeError("Response", "formData")); + + return @Promise.@reject("Not implemented"); +} + +function json() +{ + if (!(this instanceof @Response)) + return @Promise.@reject(@makeThisTypeError("Response", "json")); + + const jsonConsumerType = 3; + if (!this.@body) + return @Response.prototype.@consume.@call(this, jsonConsumerType); + + return @consumeStream(this, jsonConsumerType); +} + +function text() +{ + if (!(this instanceof @Response)) + return @Promise.@reject(@makeThisTypeError("Response", "text")); + + const textConsumerType = 4; + if (!this.@body) + return @Response.prototype.@consume.@call(this, textConsumerType); + + return @consumeStream(this, textConsumerType); +} diff --git a/Source/WebCore/Modules/fetch/FetchResponseSource.cpp b/Source/WebCore/Modules/fetch/FetchResponseSource.cpp new file mode 100644 index 000000000..5534ecd58 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponseSource.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchResponseSource.h" + +#if ENABLE(FETCH_API) && ENABLE(READABLE_STREAM_API) + +#include "FetchResponse.h" + +namespace WebCore { + +FetchResponseSource::FetchResponseSource(FetchResponse& response) + : m_response(response) +{ +} + +bool FetchResponseSource::isReadableStreamLocked() const +{ + return controller().isControlledReadableStreamLocked(); +} + +void FetchResponseSource::setActive() +{ + m_response.setPendingActivity(&m_response); +} + +void FetchResponseSource::setInactive() +{ + m_response.unsetPendingActivity(&m_response); +} + +void FetchResponseSource::doStart() +{ + m_response.consumeBodyAsStream(); +} + +void FetchResponseSource::doPull() +{ + m_response.feedStream(); +} + +void FetchResponseSource::doCancel() +{ + m_isCancelling = true; + m_response.cancel(); +} + +void FetchResponseSource::close() +{ + controller().close(); + clean(); +} +void FetchResponseSource::error(const String& value) +{ + controller().error(value); + clean(); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) && ENABLE(READABLE_STREAM_API) diff --git a/Source/WebCore/Modules/fetch/FetchResponseSource.h b/Source/WebCore/Modules/fetch/FetchResponseSource.h new file mode 100644 index 000000000..ba4424ec3 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponseSource.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) && ENABLE(READABLE_STREAM_API) + +#include "ReadableStreamSource.h" + +namespace JSC { +class ArrayBuffer; +}; + +namespace WebCore { + +class FetchResponse; + +class FetchResponseSource final : public ReadableStreamSource { +public: + FetchResponseSource(FetchResponse&); + + bool enqueue(RefPtr&& chunk) { return controller().enqueue(WTFMove(chunk)); } + void close(); + void error(const String&); + + bool isCancelling() const { return m_isCancelling; } + bool isReadableStreamLocked() const; + + void resolvePullPromise() { pullFinished(); } + +private: + void doStart() final; + void doPull() final; + void doCancel() final; + void setActive() final; + void setInactive() final; + + FetchResponse& m_response; + bool m_isCancelling { false }; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) && ENABLE(READABLE_STREAM_API) diff --git a/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.cpp b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.cpp new file mode 100644 index 000000000..1a30d5687 --- /dev/null +++ b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WorkerGlobalScopeFetch.h" + +#if ENABLE(FETCH_API) + +#include "FetchResponse.h" +#include "WorkerGlobalScope.h" + +namespace WebCore { + +void WorkerGlobalScopeFetch::fetch(WorkerGlobalScope& scope, FetchRequest& request, Ref&& promise) +{ + FetchResponse::fetch(scope, request, WTFMove(promise)); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.h b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.h new file mode 100644 index 000000000..5bbb28b88 --- /dev/null +++ b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "JSDOMPromise.h" + +namespace WebCore { + +class FetchRequest; +class WorkerGlobalScope; + +class WorkerGlobalScopeFetch { +public: + static void fetch(WorkerGlobalScope&, FetchRequest&, Ref&&); +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.idl b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.idl new file mode 100644 index 000000000..c53e72de9 --- /dev/null +++ b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=FETCH_API, + EnabledAtRuntime=FetchAPI, +] partial interface WorkerGlobalScope { + [JSBuiltin] Promise fetch(any input, optional RequestInit init); + [PrivateIdentifier, ImplementedAs=fetch] Promise fetchRequest(FetchRequest request); +}; diff --git a/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.js b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.js new file mode 100644 index 000000000..ef6d075ea --- /dev/null +++ b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.js @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) + +function fetch(input, init) +{ + "use strict"; + + try { + return @fetchRequest(new @Request(input, init)); + } catch (e) { + return @Promise.@reject(e); + } +} diff --git a/Source/WebCore/Modules/gamepad/Gamepad.cpp b/Source/WebCore/Modules/gamepad/Gamepad.cpp index 89105ceb8..d11c5508d 100644 --- a/Source/WebCore/Modules/gamepad/Gamepad.cpp +++ b/Source/WebCore/Modules/gamepad/Gamepad.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2011, Google Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * + * modification, are permitted provided that the following conditions + * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -28,32 +28,47 @@ #if ENABLE(GAMEPAD) +#include "GamepadButton.h" +#include "PlatformGamepad.h" #include namespace WebCore { -Gamepad::Gamepad() - : m_index(0) - , m_timestamp(0) +Gamepad::Gamepad(const PlatformGamepad& platformGamepad) + : m_id(platformGamepad.id()) + , m_index(platformGamepad.index()) + , m_connected(true) + , m_timestamp(platformGamepad.lastUpdateTime()) { + m_axes.resize(platformGamepad.axisValues().size()); + m_axes.fill(0.0); + unsigned buttonCount = platformGamepad.buttonValues().size(); + for (unsigned i = 0; i < buttonCount; ++i) + m_buttons.append(GamepadButton::create()); } -void Gamepad::axes(unsigned count, float* data) +Gamepad::~Gamepad() { - m_axes.resize(count); - if (count) - std::copy(data, data + count, m_axes.begin()); } -void Gamepad::buttons(unsigned count, float* data) +const Vector& Gamepad::axes() const { - m_buttons.resize(count); - if (count) - std::copy(data, data + count, m_buttons.begin()); + return m_axes; } -Gamepad::~Gamepad() +const Vector>& Gamepad::buttons() const { + return m_buttons; +} + +void Gamepad::updateFromPlatformGamepad(const PlatformGamepad& platformGamepad) +{ + for (unsigned i = 0; i < m_axes.size(); ++i) + m_axes[i] = platformGamepad.axisValues()[i]; + for (unsigned i = 0; i < m_buttons.size(); ++i) + m_buttons[i]->setValue(platformGamepad.buttonValues()[i]); + + m_timestamp = platformGamepad.lastUpdateTime(); } } // namespace WebCore diff --git a/Source/WebCore/Modules/gamepad/Gamepad.h b/Source/WebCore/Modules/gamepad/Gamepad.h index 5b9b1d2dc..f32992f28 100644 --- a/Source/WebCore/Modules/gamepad/Gamepad.h +++ b/Source/WebCore/Modules/gamepad/Gamepad.h @@ -1,30 +1,29 @@ /* - * Copyright (C) 2011, Google Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * + * modification, are permitted provided that the following conditions + * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Gamepad_h -#define Gamepad_h +#pragma once #if ENABLE(GAMEPAD) @@ -34,42 +33,40 @@ namespace WebCore { +class GamepadButton; +class PlatformGamepad; + class Gamepad: public RefCounted { public: - static PassRefPtr create() + static Ref create(const PlatformGamepad& platformGamepad) { - return adoptRef(new Gamepad); + return adoptRef(*new Gamepad(platformGamepad)); } ~Gamepad(); - typedef Vector FloatVector; - const String& id() const { return m_id; } - void id(const String& id) { m_id = id; } - unsigned index() const { return m_index; } - void index(unsigned val) { m_index = val; } + const String& mapping() const { return m_mapping; } - unsigned long long timestamp() const { return m_timestamp; } - void timestamp(unsigned long long val) { m_timestamp = val; } + bool connected() const { return m_connected; } + double timestamp() const { return m_timestamp; } + const Vector& axes() const; + const Vector>& buttons() const; - const FloatVector& axes() const { return m_axes; } - void axes(unsigned count, float* data); - - const FloatVector& buttons() const { return m_buttons; } - void buttons(unsigned count, float* data); + void updateFromPlatformGamepad(const PlatformGamepad&); private: - Gamepad(); + explicit Gamepad(const PlatformGamepad&); String m_id; unsigned m_index; - unsigned long long m_timestamp; - FloatVector m_axes; - FloatVector m_buttons; + bool m_connected; + double m_timestamp; + String m_mapping; + + Vector m_axes; + Vector> m_buttons; }; } // namespace WebCore #endif // ENABLE(GAMEPAD) - -#endif // Gamepad_h diff --git a/Source/WebCore/Modules/gamepad/Gamepad.idl b/Source/WebCore/Modules/gamepad/Gamepad.idl index 0c9c1e745..8f3b80343 100644 --- a/Source/WebCore/Modules/gamepad/Gamepad.idl +++ b/Source/WebCore/Modules/gamepad/Gamepad.idl @@ -1,37 +1,39 @@ /* - * Copyright (C) 2011, Google Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * + * modification, are permitted provided that the following conditions + * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ [ - NoInterfaceObject, + EnabledAtRuntime=Gamepads, Conditional=GAMEPAD, ImplementationLacksVTable ] interface Gamepad { readonly attribute DOMString id; readonly attribute unsigned long index; - readonly attribute unsigned long long timestamp; - readonly attribute float[] axes; - readonly attribute float[] buttons; + readonly attribute boolean connected; + readonly attribute double timestamp; + readonly attribute DOMString mapping; + readonly attribute sequence axes; + readonly attribute sequence buttons; }; diff --git a/Source/WebCore/Modules/gamepad/GamepadButton.cpp b/Source/WebCore/Modules/gamepad/GamepadButton.cpp new file mode 100644 index 000000000..99fe7c4c1 --- /dev/null +++ b/Source/WebCore/Modules/gamepad/GamepadButton.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "GamepadButton.h" + +#if ENABLE(GAMEPAD) + +namespace WebCore { + +GamepadButton::GamepadButton() + : m_value(0) +{ +} + +bool GamepadButton::pressed() const +{ + // FIXME: The spec states that for analog buttons there needs to be a + // threshold for determining the state of "pressed". + // What should that threshold be? + return m_value > 0.1; +} + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD) diff --git a/Source/WebCore/Modules/gamepad/GamepadButton.h b/Source/WebCore/Modules/gamepad/GamepadButton.h new file mode 100644 index 000000000..db4f9f587 --- /dev/null +++ b/Source/WebCore/Modules/gamepad/GamepadButton.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(GAMEPAD) + +#include +#include + +namespace WebCore { + +class GamepadButton : public RefCounted { +public: + static Ref create() + { + return adoptRef(*new GamepadButton); + } + + bool pressed() const; + double value() const { return m_value; } + void setValue(double value) { m_value = value; } + +private: + GamepadButton(); + + double m_value; +}; + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD) diff --git a/Source/WebCore/Modules/gamepad/GamepadButton.idl b/Source/WebCore/Modules/gamepad/GamepadButton.idl new file mode 100644 index 000000000..0a2e67b60 --- /dev/null +++ b/Source/WebCore/Modules/gamepad/GamepadButton.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + EnabledAtRuntime=Gamepads, + Conditional=GAMEPAD, + ImplementationLacksVTable +] interface GamepadButton { + readonly attribute boolean pressed; + readonly attribute double value; +}; + diff --git a/Source/WebCore/Modules/gamepad/GamepadEvent.cpp b/Source/WebCore/Modules/gamepad/GamepadEvent.cpp new file mode 100644 index 000000000..a6800af6d --- /dev/null +++ b/Source/WebCore/Modules/gamepad/GamepadEvent.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include "GamepadEvent.h" + +#if ENABLE(GAMEPAD) + +namespace WebCore { + +GamepadEvent::GamepadEvent(const AtomicString& eventType, Gamepad& gamepad) + : Event(eventType, false, false) + , m_gamepad(&gamepad) +{ +} + +GamepadEvent::GamepadEvent(const AtomicString& eventType, const Init& initializer, IsTrusted isTrusted) + : Event(eventType, initializer, isTrusted) + , m_gamepad(initializer.gamepad) +{ +} + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD) diff --git a/Source/WebCore/Modules/gamepad/GamepadEvent.h b/Source/WebCore/Modules/gamepad/GamepadEvent.h new file mode 100644 index 000000000..d43da58a9 --- /dev/null +++ b/Source/WebCore/Modules/gamepad/GamepadEvent.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(GAMEPAD) + +#include "Event.h" +#include "Gamepad.h" +#include + +namespace WebCore { + +class GamepadEvent : public Event { +public: + ~GamepadEvent() { } + + static Ref create(const AtomicString& eventType, Gamepad& gamepad) + { + return adoptRef(*new GamepadEvent(eventType, gamepad)); + } + + struct Init : EventInit { + RefPtr gamepad; + }; + + static Ref create(const AtomicString& eventType, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) + { + return adoptRef(*new GamepadEvent(eventType, initializer, isTrusted)); + } + + Gamepad* gamepad() const { return m_gamepad.get(); } + + EventInterface eventInterface() const override { return GamepadEventInterfaceType; } + +private: + explicit GamepadEvent(const AtomicString& eventType, Gamepad&); + GamepadEvent(const AtomicString& eventType, const Init&, IsTrusted); + + RefPtr m_gamepad; +}; + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD) diff --git a/Source/WebCore/Modules/gamepad/GamepadEvent.idl b/Source/WebCore/Modules/gamepad/GamepadEvent.idl new file mode 100644 index 000000000..d99b379f9 --- /dev/null +++ b/Source/WebCore/Modules/gamepad/GamepadEvent.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=GAMEPAD, + Constructor(DOMString type, GamepadEventInit eventInitDict), + EnabledAtRuntime=Gamepads, +] interface GamepadEvent : Event { + readonly attribute Gamepad? gamepad; +}; + +dictionary GamepadEventInit : EventInit { + // The specification says this member should be required and non-nullable. + // However, this does not match the behavior of Chrome or Firefox. + Gamepad? gamepad = null; +}; diff --git a/Source/WebCore/Modules/gamepad/GamepadList.cpp b/Source/WebCore/Modules/gamepad/GamepadList.cpp deleted file mode 100644 index 265022225..000000000 --- a/Source/WebCore/Modules/gamepad/GamepadList.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2011, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - */ - -#include "config.h" -#include "GamepadList.h" - -#include "Gamepad.h" - -#if ENABLE(GAMEPAD) - -namespace WebCore { - -GamepadList::~GamepadList() -{ -} - -void GamepadList::set(unsigned index, PassRefPtr gamepad) -{ - if (index >= kMaximumGamepads) - return; - m_items[index] = gamepad; -} - -unsigned GamepadList::length() const -{ - return kMaximumGamepads; -} - -Gamepad* GamepadList::item(unsigned index) -{ - return index < length() ? m_items[index].get() : 0; -} - -} // namespace WebCore - -#endif // ENABLE(GAMEPAD) diff --git a/Source/WebCore/Modules/gamepad/GamepadList.h b/Source/WebCore/Modules/gamepad/GamepadList.h deleted file mode 100644 index fa6af61da..000000000 --- a/Source/WebCore/Modules/gamepad/GamepadList.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2011, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - */ - -#ifndef GamepadList_h -#define GamepadList_h - -#if ENABLE(GAMEPAD) - -#include "Gamepad.h" -#include -#include -#include - -namespace WebCore { - -typedef Vector > GamepadVector; - -class GamepadList : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new GamepadList); } - ~GamepadList(); - - void set(unsigned index, PassRefPtr); - Gamepad* item(unsigned index); - unsigned length() const; - -private: - enum { kMaximumGamepads = 4 }; - GamepadList() { } - RefPtr m_items[kMaximumGamepads]; -}; - -} // namespace WebCore - -#endif // ENABLE(GAMEPAD) - -#endif // GamepadList_h diff --git a/Source/WebCore/Modules/gamepad/GamepadList.idl b/Source/WebCore/Modules/gamepad/GamepadList.idl deleted file mode 100644 index 4b256afef..000000000 --- a/Source/WebCore/Modules/gamepad/GamepadList.idl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2011, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - */ - -[ - NoInterfaceObject, - Conditional=GAMEPAD, - ImplementationLacksVTable, -] interface GamepadList { - readonly attribute unsigned long length; - getter Gamepad item([Default=Undefined] optional unsigned long index); -}; - diff --git a/Source/WebCore/Modules/gamepad/GamepadManager.cpp b/Source/WebCore/Modules/gamepad/GamepadManager.cpp new file mode 100644 index 000000000..b2ecba196 --- /dev/null +++ b/Source/WebCore/Modules/gamepad/GamepadManager.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include "GamepadManager.h" + +#if ENABLE(GAMEPAD) + +#include "DOMWindow.h" +#include "Document.h" +#include "EventNames.h" +#include "Gamepad.h" +#include "GamepadEvent.h" +#include "GamepadProvider.h" +#include "Logging.h" +#include "NavigatorGamepad.h" +#include "PlatformGamepad.h" + +namespace WebCore { + +static NavigatorGamepad* navigatorGamepadFromDOMWindow(DOMWindow* window) +{ + Navigator* navigator = window->navigator(); + if (!navigator) + return nullptr; + + return NavigatorGamepad::from(navigator); +} + +GamepadManager& GamepadManager::singleton() +{ + static NeverDestroyed sharedManager; + return sharedManager; +} + +GamepadManager::GamepadManager() + : m_isMonitoringGamepads(false) +{ +} + +void GamepadManager::platformGamepadConnected(PlatformGamepad& platformGamepad) +{ + // Notify blind Navigators and Windows about all gamepads except for this one. + for (auto* gamepad : GamepadProvider::singleton().platformGamepads()) { + if (!gamepad || gamepad == &platformGamepad) + continue; + + makeGamepadVisible(*gamepad, m_gamepadBlindNavigators, m_gamepadBlindDOMWindows); + } + + m_gamepadBlindNavigators.clear(); + m_gamepadBlindDOMWindows.clear(); + + // Notify everyone of this new gamepad. + makeGamepadVisible(platformGamepad, m_navigators, m_domWindows); +} + +void GamepadManager::platformGamepadDisconnected(PlatformGamepad& platformGamepad) +{ + Vector> weakWindows; + for (auto* domWindow : m_domWindows) + weakWindows.append(domWindow->createWeakPtr()); + + HashSet notifiedNavigators; + + // Handle the disconnect for all DOMWindows with event listeners and their Navigators. + for (auto& window : weakWindows) { + // Event dispatch might have made this window go away. + if (!window) + continue; + + // This DOMWindow's Navigator might not be accessible. e.g. The DOMWindow might be in the back/forward cache. + // If this happens the DOMWindow will not get this gamepaddisconnected event. + NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window.get()); + if (!navigator) + continue; + + // If this Navigator hasn't seen gamepads yet then its Window should not get the disconnect event. + if (m_gamepadBlindNavigators.contains(navigator)) + continue; + + Ref gamepad(navigator->gamepadFromPlatformGamepad(platformGamepad)); + + navigator->gamepadDisconnected(platformGamepad); + notifiedNavigators.add(navigator); + + window->dispatchEvent(GamepadEvent::create(eventNames().gamepaddisconnectedEvent, gamepad.get()), window->document()); + } + + // Notify all the Navigators that haven't already been notified. + for (auto* navigator : m_navigators) { + if (!notifiedNavigators.contains(navigator)) + navigator->gamepadDisconnected(platformGamepad); + } +} + +void GamepadManager::platformGamepadInputActivity(bool shouldMakeGamepadVisible) +{ + if (!shouldMakeGamepadVisible) + return; + + if (m_gamepadBlindNavigators.isEmpty() && m_gamepadBlindDOMWindows.isEmpty()) + return; + + for (auto* gamepad : GamepadProvider::singleton().platformGamepads()) { + if (gamepad) + makeGamepadVisible(*gamepad, m_gamepadBlindNavigators, m_gamepadBlindDOMWindows); + } + + m_gamepadBlindNavigators.clear(); + m_gamepadBlindDOMWindows.clear(); +} + +void GamepadManager::makeGamepadVisible(PlatformGamepad& platformGamepad, HashSet& navigatorSet, HashSet& domWindowSet) +{ + if (navigatorSet.isEmpty() && domWindowSet.isEmpty()) + return; + + for (auto* navigator : navigatorSet) + navigator->gamepadConnected(platformGamepad); + + Vector> weakWindows; + for (auto* domWindow : m_domWindows) + weakWindows.append(domWindow->createWeakPtr()); + + for (auto& window : weakWindows) { + // Event dispatch might have made this window go away. + if (!window) + continue; + + // This DOMWindow's Navigator might not be accessible. e.g. The DOMWindow might be in the back/forward cache. + // If this happens the DOMWindow will not get this gamepadconnected event. + // The new gamepad will still be visibile to it once it is restored from the back/forward cache. + NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window.get()); + if (!navigator) + continue; + + Ref gamepad(navigator->gamepadFromPlatformGamepad(platformGamepad)); + window->dispatchEvent(GamepadEvent::create(eventNames().gamepadconnectedEvent, gamepad.get()), window->document()); + } +} + +void GamepadManager::registerNavigator(NavigatorGamepad* navigator) +{ + LOG(Gamepad, "GamepadManager registering NavigatorGamepad %p", navigator); + + ASSERT(!m_navigators.contains(navigator)); + m_navigators.add(navigator); + m_gamepadBlindNavigators.add(navigator); + + maybeStartMonitoringGamepads(); +} + +void GamepadManager::unregisterNavigator(NavigatorGamepad* navigator) +{ + LOG(Gamepad, "GamepadManager unregistering NavigatorGamepad %p", navigator); + + ASSERT(m_navigators.contains(navigator)); + m_navigators.remove(navigator); + m_gamepadBlindNavigators.remove(navigator); + + maybeStopMonitoringGamepads(); +} + +void GamepadManager::registerDOMWindow(DOMWindow* window) +{ + LOG(Gamepad, "GamepadManager registering DOMWindow %p", window); + + ASSERT(!m_domWindows.contains(window)); + m_domWindows.add(window); + + // Anytime we register a DOMWindow, we should also double check that its NavigatorGamepad is registered. + NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window); + ASSERT(navigator); + + if (m_navigators.add(navigator).isNewEntry) + m_gamepadBlindNavigators.add(navigator); + + // If this DOMWindow's NavigatorGamepad was already registered but was still blind, + // then this DOMWindow should be blind. + if (m_gamepadBlindNavigators.contains(navigator)) + m_gamepadBlindDOMWindows.add(window); + + maybeStartMonitoringGamepads(); +} + +void GamepadManager::unregisterDOMWindow(DOMWindow* window) +{ + LOG(Gamepad, "GamepadManager unregistering DOMWindow %p", window); + + ASSERT(m_domWindows.contains(window)); + m_domWindows.remove(window); + m_gamepadBlindDOMWindows.remove(window); + + maybeStopMonitoringGamepads(); +} + +void GamepadManager::maybeStartMonitoringGamepads() +{ + if (m_isMonitoringGamepads) + return; + + if (!m_navigators.isEmpty() || !m_domWindows.isEmpty()) { + LOG(Gamepad, "GamepadManager has %i NavigatorGamepads and %i DOMWindows registered, is starting gamepad monitoring", m_navigators.size(), m_domWindows.size()); + m_isMonitoringGamepads = true; + GamepadProvider::singleton().startMonitoringGamepads(*this); + } +} + +void GamepadManager::maybeStopMonitoringGamepads() +{ + if (!m_isMonitoringGamepads) + return; + + if (m_navigators.isEmpty() && m_domWindows.isEmpty()) { + LOG(Gamepad, "GamepadManager has no NavigatorGamepads or DOMWindows registered, is stopping gamepad monitoring"); + m_isMonitoringGamepads = false; + GamepadProvider::singleton().stopMonitoringGamepads(*this); + } +} + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD) diff --git a/Source/WebCore/Modules/gamepad/GamepadManager.h b/Source/WebCore/Modules/gamepad/GamepadManager.h new file mode 100644 index 000000000..63b66fa6d --- /dev/null +++ b/Source/WebCore/Modules/gamepad/GamepadManager.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(GAMEPAD) + +#include "GamepadProviderClient.h" +#include +#include +#include +#include + +namespace WebCore { + +class DOMWindow; +class Gamepad; +class NavigatorGamepad; + +class GamepadManager : public GamepadProviderClient { + WTF_MAKE_NONCOPYABLE(GamepadManager); + friend class NeverDestroyed; +public: + static GamepadManager& singleton(); + + void platformGamepadConnected(PlatformGamepad&) final; + void platformGamepadDisconnected(PlatformGamepad&) final; + void platformGamepadInputActivity(bool shouldMakeGamepadVisible) final; + + void registerNavigator(NavigatorGamepad*); + void unregisterNavigator(NavigatorGamepad*); + void registerDOMWindow(DOMWindow*); + void unregisterDOMWindow(DOMWindow*); + +private: + GamepadManager(); + + void makeGamepadVisible(PlatformGamepad&, HashSet&, HashSet&); + void dispatchGamepadEvent(const WTF::AtomicString& eventName, PlatformGamepad&); + + void maybeStartMonitoringGamepads(); + void maybeStopMonitoringGamepads(); + + bool m_isMonitoringGamepads; + + HashSet m_navigators; + HashSet m_gamepadBlindNavigators; + HashSet m_domWindows; + HashSet m_gamepadBlindDOMWindows; +}; + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD) diff --git a/Source/WebCore/Modules/gamepad/NavigatorGamepad.cpp b/Source/WebCore/Modules/gamepad/NavigatorGamepad.cpp index 2079e7876..61020899c 100644 --- a/Source/WebCore/Modules/gamepad/NavigatorGamepad.cpp +++ b/Source/WebCore/Modules/gamepad/NavigatorGamepad.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2011, Google Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * + * modification, are permitted provided that the following conditions + * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -28,19 +28,22 @@ #if ENABLE(GAMEPAD) -#include "GamepadList.h" -#include "Gamepads.h" +#include "Gamepad.h" +#include "GamepadManager.h" +#include "GamepadProvider.h" #include "Navigator.h" -#include +#include "PlatformGamepad.h" namespace WebCore { NavigatorGamepad::NavigatorGamepad() { + GamepadManager::singleton().registerNavigator(this); } NavigatorGamepad::~NavigatorGamepad() { + GamepadManager::singleton().unregisterNavigator(this); } const char* NavigatorGamepad::supplementName() @@ -52,23 +55,90 @@ NavigatorGamepad* NavigatorGamepad::from(Navigator* navigator) { NavigatorGamepad* supplement = static_cast(Supplement::from(navigator, supplementName())); if (!supplement) { - supplement = new NavigatorGamepad(); - provideTo(navigator, supplementName(), adoptPtr(supplement)); + auto newSupplement = std::make_unique(); + supplement = newSupplement.get(); + provideTo(navigator, supplementName(), WTFMove(newSupplement)); } return supplement; } -GamepadList* NavigatorGamepad::webkitGetGamepads(Navigator* navigator) +Ref NavigatorGamepad::gamepadFromPlatformGamepad(PlatformGamepad& platformGamepad) +{ + unsigned index = platformGamepad.index(); + if (index >= m_gamepads.size() || !m_gamepads[index]) + return Gamepad::create(platformGamepad); + + return *m_gamepads[index]; +} + +const Vector>& NavigatorGamepad::getGamepads(Navigator& navigator) +{ + return NavigatorGamepad::from(&navigator)->gamepads(); +} + +const Vector>& NavigatorGamepad::gamepads() +{ + if (m_gamepads.isEmpty()) + return m_gamepads; + + const Vector& platformGamepads = GamepadProvider::singleton().platformGamepads(); + + for (unsigned i = 0; i < platformGamepads.size(); ++i) { + if (!platformGamepads[i]) { + ASSERT(!m_gamepads[i]); + continue; + } + + ASSERT(m_gamepads[i]); + m_gamepads[i]->updateFromPlatformGamepad(*platformGamepads[i]); + } + + return m_gamepads; +} + +void NavigatorGamepad::gamepadsBecameVisible() +{ + const Vector& platformGamepads = GamepadProvider::singleton().platformGamepads(); + m_gamepads.resize(platformGamepads.size()); + + for (unsigned i = 0; i < platformGamepads.size(); ++i) { + if (!platformGamepads[i]) + continue; + + m_gamepads[i] = Gamepad::create(*platformGamepads[i]); + } +} + +void NavigatorGamepad::gamepadConnected(PlatformGamepad& platformGamepad) { - return NavigatorGamepad::from(navigator)->gamepads(); + // If this is the first gamepad this Navigator object has seen, then all gamepads just became visible. + if (m_gamepads.isEmpty()) { + gamepadsBecameVisible(); + return; + } + + unsigned index = platformGamepad.index(); + ASSERT(GamepadProvider::singleton().platformGamepads()[index] == &platformGamepad); + + // The new index should already fit in the existing array, or should be exactly one past-the-end of the existing array. + ASSERT(index <= m_gamepads.size()); + + if (index < m_gamepads.size()) + m_gamepads[index] = Gamepad::create(platformGamepad); + else if (index == m_gamepads.size()) + m_gamepads.append(Gamepad::create(platformGamepad)); } -GamepadList* NavigatorGamepad::gamepads() +void NavigatorGamepad::gamepadDisconnected(PlatformGamepad& platformGamepad) { - if (!m_gamepads) - m_gamepads = GamepadList::create(); - sampleGamepads(m_gamepads.get()); - return m_gamepads.get(); + // If this Navigator hasn't seen any gamepads yet its Vector will still be empty. + if (!m_gamepads.size()) + return; + + ASSERT(platformGamepad.index() < m_gamepads.size()); + ASSERT(m_gamepads[platformGamepad.index()]); + + m_gamepads[platformGamepad.index()] = nullptr; } } // namespace WebCore diff --git a/Source/WebCore/Modules/gamepad/NavigatorGamepad.h b/Source/WebCore/Modules/gamepad/NavigatorGamepad.h index 3ee8ba91c..dee01e69c 100644 --- a/Source/WebCore/Modules/gamepad/NavigatorGamepad.h +++ b/Source/WebCore/Modules/gamepad/NavigatorGamepad.h @@ -1,58 +1,67 @@ /* - * Copyright (C) 2011, Google Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * + * modification, are permitted provided that the following conditions + * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef NavigatorGamepad_h -#define NavigatorGamepad_h +#pragma once #if ENABLE(GAMEPAD) #include "Supplementable.h" +#include namespace WebCore { -class GamepadList; +class Gamepad; class Navigator; +class PlatformGamepad; class NavigatorGamepad : public Supplement { public: + NavigatorGamepad(); virtual ~NavigatorGamepad(); + static NavigatorGamepad* from(Navigator*); - static GamepadList* webkitGetGamepads(Navigator*); + // The array of Gamepads might be sparse. + // Null checking each entry is necessary. + static const Vector>& getGamepads(Navigator&); + + void gamepadConnected(PlatformGamepad&); + void gamepadDisconnected(PlatformGamepad&); - GamepadList* gamepads(); + Ref gamepadFromPlatformGamepad(PlatformGamepad&); private: - NavigatorGamepad(); static const char* supplementName(); - RefPtr m_gamepads; + void gamepadsBecameVisible(); + + const Vector>& gamepads(); + + Vector> m_gamepads; }; } // namespace WebCore #endif // ENABLE(GAMEPAD) - -#endif // NavigatorGamepad_h diff --git a/Source/WebCore/Modules/gamepad/NavigatorGamepad.idl b/Source/WebCore/Modules/gamepad/NavigatorGamepad.idl index a26c5eae5..c1960938f 100644 --- a/Source/WebCore/Modules/gamepad/NavigatorGamepad.idl +++ b/Source/WebCore/Modules/gamepad/NavigatorGamepad.idl @@ -1,25 +1,31 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ [ Conditional=GAMEPAD, ] partial interface Navigator { - GamepadList webkitGetGamepads(); + [EnabledAtRuntime=Gamepads] sequence getGamepads(); }; diff --git a/Source/WebCore/Modules/gamepad/deprecated/Gamepad.cpp b/Source/WebCore/Modules/gamepad/deprecated/Gamepad.cpp new file mode 100644 index 000000000..52e274906 --- /dev/null +++ b/Source/WebCore/Modules/gamepad/deprecated/Gamepad.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#include "config.h" +#include "Gamepad.h" + +#if ENABLE(GAMEPAD_DEPRECATED) + +#include + +namespace WebCore { + +Gamepad::Gamepad() + : m_index(0) + , m_timestamp(0) +{ +} + +void Gamepad::axes(unsigned count, float* data) +{ + m_axes.resize(count); + if (count) + std::copy(data, data + count, m_axes.begin()); +} + +void Gamepad::buttons(unsigned count, float* data) +{ + m_buttons.resize(count); + if (count) + std::copy(data, data + count, m_buttons.begin()); +} + +Gamepad::~Gamepad() +{ +} + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD_DEPRECATED) diff --git a/Source/WebCore/Modules/gamepad/deprecated/Gamepad.h b/Source/WebCore/Modules/gamepad/deprecated/Gamepad.h new file mode 100644 index 000000000..54c949b8c --- /dev/null +++ b/Source/WebCore/Modules/gamepad/deprecated/Gamepad.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#pragma once + +#if ENABLE(GAMEPAD_DEPRECATED) + +#include +#include +#include + +namespace WebCore { + +class Gamepad: public RefCounted { +public: + static Ref create() + { + return adoptRef(*new Gamepad); + } + ~Gamepad(); + + typedef Vector FloatVector; + + const String& id() const { return m_id; } + void id(const String& id) { m_id = id; } + + unsigned index() const { return m_index; } + void index(unsigned val) { m_index = val; } + + unsigned long long timestamp() const { return m_timestamp; } + void timestamp(unsigned long long val) { m_timestamp = val; } + + const FloatVector& axes() const { return m_axes; } + void axes(unsigned count, float* data); + + const FloatVector& buttons() const { return m_buttons; } + void buttons(unsigned count, float* data); + +private: + Gamepad(); + String m_id; + unsigned m_index; + unsigned long long m_timestamp; + FloatVector m_axes; + FloatVector m_buttons; +}; + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD_DEPRECATED) diff --git a/Source/WebCore/Modules/gamepad/deprecated/Gamepad.idl b/Source/WebCore/Modules/gamepad/deprecated/Gamepad.idl new file mode 100644 index 000000000..824e97e96 --- /dev/null +++ b/Source/WebCore/Modules/gamepad/deprecated/Gamepad.idl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2011, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +[ + NoInterfaceObject, + Conditional=GAMEPAD_DEPRECATED, + ImplementationLacksVTable +] interface Gamepad { + readonly attribute DOMString id; + readonly attribute unsigned long index; + readonly attribute unsigned long long timestamp; + readonly attribute sequence axes; + readonly attribute sequence buttons; +}; + diff --git a/Source/WebCore/Modules/gamepad/deprecated/GamepadList.cpp b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.cpp new file mode 100644 index 000000000..22e028867 --- /dev/null +++ b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#include "config.h" +#include "GamepadList.h" + +#include "Gamepad.h" + +#if ENABLE(GAMEPAD_DEPRECATED) + +namespace WebCore { + +GamepadList::~GamepadList() +{ +} + +void GamepadList::set(unsigned index, RefPtr&& gamepad) +{ + if (index >= kMaximumGamepads) + return; + m_items[index] = WTFMove(gamepad); +} + +unsigned GamepadList::length() const +{ + return kMaximumGamepads; +} + +Gamepad* GamepadList::item(unsigned index) +{ + return index < length() ? m_items[index].get() : 0; +} + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD_DEPRECATED) diff --git a/Source/WebCore/Modules/gamepad/deprecated/GamepadList.h b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.h new file mode 100644 index 000000000..ab3626b4a --- /dev/null +++ b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#pragma once + +#if ENABLE(GAMEPAD_DEPRECATED) + +#include "Gamepad.h" +#include +#include + +namespace WebCore { + +typedef Vector > GamepadVector; + +class GamepadList : public RefCounted { +public: + static Ref create() { return adoptRef(*new GamepadList); } + ~GamepadList(); + + void set(unsigned index, RefPtr&&); + Gamepad* item(unsigned index); + unsigned length() const; + +private: + enum { kMaximumGamepads = 4 }; + GamepadList() { } + RefPtr m_items[kMaximumGamepads]; +}; + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD_DEPRECATED) diff --git a/Source/WebCore/Modules/gamepad/deprecated/GamepadList.idl b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.idl new file mode 100644 index 000000000..9ab4c57fe --- /dev/null +++ b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +[ + NoInterfaceObject, + Conditional=GAMEPAD_DEPRECATED, + ImplementationLacksVTable, +] interface GamepadList { + readonly attribute unsigned long length; + getter Gamepad item(unsigned long index); +}; + diff --git a/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.cpp b/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.cpp new file mode 100644 index 000000000..2321d5ada --- /dev/null +++ b/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2011, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#include "config.h" +#include "NavigatorGamepad.h" + +#if ENABLE(GAMEPAD_DEPRECATED) + +#include "GamepadList.h" +#include "Gamepads.h" +#include "Navigator.h" + +namespace WebCore { + +NavigatorGamepad::NavigatorGamepad() +{ +} + +NavigatorGamepad::~NavigatorGamepad() +{ +} + +const char* NavigatorGamepad::supplementName() +{ + return "NavigatorGamepad"; +} + +NavigatorGamepad* NavigatorGamepad::from(Navigator* navigator) +{ + NavigatorGamepad* supplement = static_cast(Supplement::from(navigator, supplementName())); + if (!supplement) { + auto newSupplement = std::make_unique(); + supplement = newSupplement.get(); + provideTo(navigator, supplementName(), WTFMove(newSupplement)); + } + return supplement; +} + +GamepadList* NavigatorGamepad::webkitGetGamepads(Navigator& navigator) +{ + return NavigatorGamepad::from(&navigator)->gamepads(); +} + +GamepadList* NavigatorGamepad::gamepads() +{ + if (!m_gamepads) + m_gamepads = GamepadList::create(); + sampleGamepads(m_gamepads.get()); + return m_gamepads.get(); +} + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD_DEPRECATED) diff --git a/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.h b/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.h new file mode 100644 index 000000000..6f57882d8 --- /dev/null +++ b/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2011, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#pragma once + +#if ENABLE(GAMEPAD_DEPRECATED) + +#include "Supplementable.h" + +namespace WebCore { + +class GamepadList; +class Navigator; + +class NavigatorGamepad : public Supplement { +public: + NavigatorGamepad(); + virtual ~NavigatorGamepad(); + + static NavigatorGamepad* from(Navigator*); + + static GamepadList* webkitGetGamepads(Navigator&); + + GamepadList* gamepads(); + +private: + static const char* supplementName(); + + RefPtr m_gamepads; +}; + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD_DEPRECATED) diff --git a/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.idl b/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.idl new file mode 100644 index 000000000..d19d843ef --- /dev/null +++ b/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.idl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +[ + Conditional=GAMEPAD_DEPRECATED, +] partial interface Navigator { + GamepadList webkitGetGamepads(); +}; + diff --git a/Source/WebCore/Modules/geolocation/Coordinates.cpp b/Source/WebCore/Modules/geolocation/Coordinates.cpp index d7b759d37..8981fb55e 100644 --- a/Source/WebCore/Modules/geolocation/Coordinates.cpp +++ b/Source/WebCore/Modules/geolocation/Coordinates.cpp @@ -28,40 +28,32 @@ namespace WebCore { -double Coordinates::altitude(bool& isNull) const +std::optional Coordinates::altitude() const { - if (m_canProvideAltitude) - return m_altitude; - - isNull = true; - return 0; + if (!m_canProvideAltitude) + return std::nullopt; + return m_altitude; } -double Coordinates::altitudeAccuracy(bool& isNull) const +std::optional Coordinates::altitudeAccuracy() const { - if (m_canProvideAltitudeAccuracy) - return m_altitudeAccuracy; - - isNull = true; - return 0; + if (!m_canProvideAltitudeAccuracy) + return std::nullopt; + return m_altitudeAccuracy; } -double Coordinates::heading(bool& isNull) const +std::optional Coordinates::heading() const { - if (m_canProvideHeading) - return m_heading; - - isNull = true; - return 0; + if (!m_canProvideHeading) + return std::nullopt; + return m_heading; } -double Coordinates::speed(bool& isNull) const +std::optional Coordinates::speed() const { - if (m_canProvideSpeed) - return m_speed; - - isNull = true; - return 0; + if (!m_canProvideSpeed) + return std::nullopt; + return m_speed; } } // namespace WebCore diff --git a/Source/WebCore/Modules/geolocation/Coordinates.h b/Source/WebCore/Modules/geolocation/Coordinates.h index fb134a502..08c313b18 100644 --- a/Source/WebCore/Modules/geolocation/Coordinates.h +++ b/Source/WebCore/Modules/geolocation/Coordinates.h @@ -23,30 +23,30 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Coordinates_h -#define Coordinates_h +#pragma once #include "Event.h" +#include #include namespace WebCore { class Coordinates : public RefCounted { public: - static PassRefPtr create(double latitude, double longitude, bool providesAltitude, double altitude, double accuracy, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed) { return adoptRef(new Coordinates(latitude, longitude, providesAltitude, altitude, accuracy, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed)); } + static Ref create(double latitude, double longitude, bool providesAltitude, double altitude, double accuracy, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed) { return adoptRef(*new Coordinates(latitude, longitude, providesAltitude, altitude, accuracy, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed)); } - PassRefPtr isolatedCopy() const + Ref isolatedCopy() const { return Coordinates::create(m_latitude, m_longitude, m_canProvideAltitude, m_altitude, m_accuracy, m_canProvideAltitudeAccuracy, m_altitudeAccuracy, m_canProvideHeading, m_heading, m_canProvideSpeed, m_speed); } double latitude() const { return m_latitude; } double longitude() const { return m_longitude; } - double altitude(bool& isNull) const; + std::optional altitude() const; double accuracy() const { return m_accuracy; } - double altitudeAccuracy(bool& isNull) const; - double heading(bool& isNull) const; - double speed(bool& isNull) const; + std::optional altitudeAccuracy() const; + std::optional heading() const; + std::optional speed() const; private: Coordinates(double latitude, double longitude, bool providesAltitude, double altitude, double accuracy, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed) @@ -79,5 +79,3 @@ private: }; } // namespace WebCore - -#endif // Coordinates_h diff --git a/Source/WebCore/Modules/geolocation/Coordinates.idl b/Source/WebCore/Modules/geolocation/Coordinates.idl index d4fc882cb..5812b6f29 100644 --- a/Source/WebCore/Modules/geolocation/Coordinates.idl +++ b/Source/WebCore/Modules/geolocation/Coordinates.idl @@ -28,11 +28,11 @@ Conditional=GEOLOCATION, ImplementationLacksVTable ] interface Coordinates { - readonly attribute double latitude; - readonly attribute double longitude; - readonly attribute double? altitude; - readonly attribute double accuracy; - readonly attribute double? altitudeAccuracy; - readonly attribute double? heading; - readonly attribute double? speed; + readonly attribute unrestricted double latitude; + readonly attribute unrestricted double longitude; + readonly attribute unrestricted double? altitude; + readonly attribute unrestricted double accuracy; + readonly attribute unrestricted double? altitudeAccuracy; + readonly attribute unrestricted double? heading; + readonly attribute unrestricted double? speed; }; diff --git a/Source/WebCore/Modules/geolocation/GeoNotifier.cpp b/Source/WebCore/Modules/geolocation/GeoNotifier.cpp new file mode 100644 index 000000000..09c4ec960 --- /dev/null +++ b/Source/WebCore/Modules/geolocation/GeoNotifier.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2008-2011, 2015 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "GeoNotifier.h" + +#if ENABLE(GEOLOCATION) + +#include "Geolocation.h" + +namespace WebCore { + +GeoNotifier::GeoNotifier(Geolocation& geolocation, Ref&& successCallback, RefPtr&& errorCallback, PositionOptions&& options) + : m_geolocation(geolocation) + , m_successCallback(WTFMove(successCallback)) + , m_errorCallback(WTFMove(errorCallback)) + , m_options(WTFMove(options)) + , m_timer(*this, &GeoNotifier::timerFired) + , m_useCachedPosition(false) +{ +} + +void GeoNotifier::setFatalError(RefPtr&& error) +{ + // If a fatal error has already been set, stick with it. This makes sure that + // when permission is denied, this is the error reported, as required by the + // spec. + if (m_fatalError) + return; + + m_fatalError = WTFMove(error); + // An existing timer may not have a zero timeout. + m_timer.stop(); + m_timer.startOneShot(0); +} + +void GeoNotifier::setUseCachedPosition() +{ + m_useCachedPosition = true; + m_timer.startOneShot(0); +} + +bool GeoNotifier::hasZeroTimeout() const +{ + return !m_options.timeout; +} + +void GeoNotifier::runSuccessCallback(Geoposition* position) +{ + // If we are here and the Geolocation permission is not approved, something has + // gone horribly wrong. + if (!m_geolocation->isAllowed()) + CRASH(); + + m_successCallback->handleEvent(position); +} + +void GeoNotifier::runErrorCallback(PositionError* error) +{ + if (m_errorCallback) + m_errorCallback->handleEvent(error); +} + +void GeoNotifier::startTimerIfNeeded() +{ + m_timer.startOneShot(m_options.timeout / 1000.0); +} + +void GeoNotifier::stopTimer() +{ + m_timer.stop(); +} + +void GeoNotifier::timerFired() +{ + m_timer.stop(); + + // Protect this GeoNotifier object, since it + // could be deleted by a call to clearWatch in a callback. + Ref protectedThis(*this); + + // Test for fatal error first. This is required for the case where the Frame is + // disconnected and requests are cancelled. + if (m_fatalError) { + runErrorCallback(m_fatalError.get()); + // This will cause this notifier to be deleted. + m_geolocation->fatalErrorOccurred(this); + return; + } + + if (m_useCachedPosition) { + // Clear the cached position flag in case this is a watch request, which + // will continue to run. + m_useCachedPosition = false; + m_geolocation->requestUsesCachedPosition(this); + return; + } + + if (m_errorCallback) { + RefPtr error = PositionError::create(PositionError::TIMEOUT, ASCIILiteral("Timeout expired")); + m_errorCallback->handleEvent(error.get()); + } + m_geolocation->requestTimedOut(this); +} + +} // namespace WebCore + +#endif // ENABLE(GEOLOCATION) diff --git a/Source/WebCore/Modules/geolocation/GeoNotifier.h b/Source/WebCore/Modules/geolocation/GeoNotifier.h new file mode 100644 index 000000000..b2d30f3da --- /dev/null +++ b/Source/WebCore/Modules/geolocation/GeoNotifier.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008-2011, 2015 Apple Inc. All Rights Reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(GEOLOCATION) + +#include "PositionOptions.h" +#include "Timer.h" +#include +#include +#include + +namespace WebCore { + +class Geoposition; +class Geolocation; +class PositionCallback; +class PositionError; +class PositionErrorCallback; + +class GeoNotifier : public RefCounted { +public: + static Ref create(Geolocation& geolocation, Ref&& positionCallback, RefPtr&& positionErrorCallback, PositionOptions&& options) + { + return adoptRef(*new GeoNotifier(geolocation, WTFMove(positionCallback), WTFMove(positionErrorCallback), WTFMove(options))); + } + + const PositionOptions& options() const { return m_options; } + void setFatalError(RefPtr&&); + + bool useCachedPosition() const { return m_useCachedPosition; } + void setUseCachedPosition(); + + void runSuccessCallback(Geoposition*); + void runErrorCallback(PositionError*); + + void startTimerIfNeeded(); + void stopTimer(); + void timerFired(); + bool hasZeroTimeout() const; + +private: + GeoNotifier(Geolocation&, Ref&&, RefPtr&&, PositionOptions&&); + + Ref m_geolocation; + Ref m_successCallback; + RefPtr m_errorCallback; + PositionOptions m_options; + Timer m_timer; + RefPtr m_fatalError; + bool m_useCachedPosition; +}; + +} // namespace WebCore + +#endif // ENABLE(GEOLOCATION) diff --git a/Source/WebCore/Modules/geolocation/Geolocation.cpp b/Source/WebCore/Modules/geolocation/Geolocation.cpp index 15cd1b80e..36109f35f 100644 --- a/Source/WebCore/Modules/geolocation/Geolocation.cpp +++ b/Source/WebCore/Modules/geolocation/Geolocation.cpp @@ -30,37 +30,41 @@ #if ENABLE(GEOLOCATION) +#include "Coordinates.h" #include "Document.h" #include "Frame.h" -#include "Geoposition.h" -#include "Page.h" -#include -#include - -#include "Coordinates.h" +#include "GeoNotifier.h" #include "GeolocationController.h" #include "GeolocationError.h" #include "GeolocationPosition.h" +#include "Geoposition.h" +#include "Page.h" #include "PositionError.h" +#include "SecurityOrigin.h" +#include +#include +#include namespace WebCore { static const char permissionDeniedErrorMessage[] = "User denied Geolocation"; static const char failedToStartServiceErrorMessage[] = "Failed to start Geolocation service"; static const char framelessDocumentErrorMessage[] = "Geolocation cannot be used in frameless documents"; +static const char originCannotRequestGeolocationErrorMessage[] = "Origin does not have permission to use Geolocation service"; -static PassRefPtr createGeoposition(GeolocationPosition* position) +static RefPtr createGeoposition(GeolocationPosition* position) { if (!position) - return 0; + return nullptr; - RefPtr coordinates = Coordinates::create(position->latitude(), position->longitude(), position->canProvideAltitude(), position->altitude(), - position->accuracy(), position->canProvideAltitudeAccuracy(), position->altitudeAccuracy(), - position->canProvideHeading(), position->heading(), position->canProvideSpeed(), position->speed()); - return Geoposition::create(coordinates.release(), convertSecondsToDOMTimeStamp(position->timestamp())); + auto coordinates = Coordinates::create(position->latitude(), position->longitude(), position->canProvideAltitude(), position->altitude(), + position->accuracy(), position->canProvideAltitudeAccuracy(), position->altitudeAccuracy(), + position->canProvideHeading(), position->heading(), position->canProvideSpeed(), position->speed()); + + return Geoposition::create(WTFMove(coordinates), convertSecondsToDOMTimeStamp(position->timestamp())); } -static PassRefPtr createPositionError(GeolocationError* error) +static Ref createPositionError(GeolocationError* error) { PositionError::ErrorCode code = PositionError::POSITION_UNAVAILABLE; switch (error->code()) { @@ -75,117 +79,17 @@ static PassRefPtr createPositionError(GeolocationError* error) return PositionError::create(code, error->message()); } -Geolocation::GeoNotifier::GeoNotifier(Geolocation* geolocation, PassRefPtr successCallback, PassRefPtr errorCallback, PassRefPtr options) - : m_geolocation(geolocation) - , m_successCallback(successCallback) - , m_errorCallback(errorCallback) - , m_options(options) - , m_timer(this, &Geolocation::GeoNotifier::timerFired) - , m_useCachedPosition(false) -{ - ASSERT(m_geolocation); - ASSERT(m_successCallback); - // If no options were supplied from JS, we should have created a default set - // of options in JSGeolocationCustom.cpp. - ASSERT(m_options); -} - -void Geolocation::GeoNotifier::setFatalError(PassRefPtr error) -{ - // If a fatal error has already been set, stick with it. This makes sure that - // when permission is denied, this is the error reported, as required by the - // spec. - if (m_fatalError) - return; - - m_fatalError = error; - // An existing timer may not have a zero timeout. - m_timer.stop(); - m_timer.startOneShot(0); -} - -void Geolocation::GeoNotifier::setUseCachedPosition() -{ - m_useCachedPosition = true; - m_timer.startOneShot(0); -} - -bool Geolocation::GeoNotifier::hasZeroTimeout() const -{ - return m_options->hasTimeout() && m_options->timeout() == 0; -} - -void Geolocation::GeoNotifier::runSuccessCallback(Geoposition* position) -{ - // If we are here and the Geolocation permission is not approved, something has - // gone horribly wrong. - if (!m_geolocation->isAllowed()) - CRASH(); - - m_successCallback->handleEvent(position); -} - -void Geolocation::GeoNotifier::runErrorCallback(PositionError* error) -{ - if (m_errorCallback) - m_errorCallback->handleEvent(error); -} - -void Geolocation::GeoNotifier::startTimerIfNeeded() -{ - if (m_options->hasTimeout()) - m_timer.startOneShot(m_options->timeout() / 1000.0); -} - -void Geolocation::GeoNotifier::stopTimer() -{ - m_timer.stop(); -} - -void Geolocation::GeoNotifier::timerFired(Timer&) -{ - m_timer.stop(); - - // Protect this GeoNotifier object, since it - // could be deleted by a call to clearWatch in a callback. - Ref protect(*this); - - // Test for fatal error first. This is required for the case where the Frame is - // disconnected and requests are cancelled. - if (m_fatalError) { - runErrorCallback(m_fatalError.get()); - // This will cause this notifier to be deleted. - m_geolocation->fatalErrorOccurred(this); - return; - } - - if (m_useCachedPosition) { - // Clear the cached position flag in case this is a watch request, which - // will continue to run. - m_useCachedPosition = false; - m_geolocation->requestUsesCachedPosition(this); - return; - } - - if (m_errorCallback) { - RefPtr error = PositionError::create(PositionError::TIMEOUT, ASCIILiteral("Timeout expired")); - m_errorCallback->handleEvent(error.get()); - } - m_geolocation->requestTimedOut(this); -} - -bool Geolocation::Watchers::add(int id, PassRefPtr prpNotifier) +bool Geolocation::Watchers::add(int id, RefPtr&& notifier) { ASSERT(id > 0); - RefPtr notifier = prpNotifier; if (!m_idToNotifierMap.add(id, notifier.get()).isNewEntry) return false; - m_notifierToIdMap.set(notifier.release(), id); + m_notifierToIdMap.set(WTFMove(notifier), id); return true; } -Geolocation::GeoNotifier* Geolocation::Watchers::find(int id) +GeoNotifier* Geolocation::Watchers::find(int id) { ASSERT(id > 0); return m_idToNotifierMap.get(id); @@ -225,21 +129,19 @@ void Geolocation::Watchers::getNotifiersVector(GeoNotifierVector& copy) const copyValuesToVector(m_idToNotifierMap, copy); } -PassRefPtr Geolocation::create(ScriptExecutionContext* context) +Ref Geolocation::create(ScriptExecutionContext* context) { - RefPtr geolocation = adoptRef(new Geolocation(context)); - geolocation->suspendIfNeeded(); - return geolocation.release(); + auto geolocation = adoptRef(*new Geolocation(context)); + geolocation.get().suspendIfNeeded(); + return geolocation; } Geolocation::Geolocation(ScriptExecutionContext* context) : ActiveDOMObject(context) , m_allowGeolocation(Unknown) -#if PLATFORM(IOS) , m_isSuspended(false) , m_hasChangedPosition(false) - , m_resumeTimer(this, &Geolocation::resumeTimerFired) -#endif + , m_resumeTimer(*this, &Geolocation::resumeTimerFired) { } @@ -248,33 +150,24 @@ Geolocation::~Geolocation() ASSERT(m_allowGeolocation != InProgress); } -Document* Geolocation::document() const +SecurityOrigin* Geolocation::securityOrigin() const { - return toDocument(scriptExecutionContext()); -} - -Frame* Geolocation::frame() const -{ - return document() ? document()->frame() : 0; + return scriptExecutionContext()->securityOrigin(); } Page* Geolocation::page() const { - return document() ? document()->page() : 0; + return document() ? document()->page() : nullptr; } -#if PLATFORM(IOS) -bool Geolocation::canSuspend() const +bool Geolocation::canSuspendForDocumentSuspension() const { - return !hasListeners(); + return true; } - + void Geolocation::suspend(ReasonForSuspension reason) { - // Allow pages that no longer have listeners to enter the page cache. - // Have them stop updating and reset geolocation permissions when the page is resumed. - if (reason == ActiveDOMObject::DocumentWillBecomeInactive) { - ASSERT(!hasListeners()); + if (reason == ActiveDOMObject::PageCache) { stop(); m_resetOnResume = true; } @@ -290,14 +183,16 @@ void Geolocation::suspend(ReasonForSuspension reason) void Geolocation::resume() { +#if USE(WEB_THREAD) ASSERT(WebThreadIsLockedOrDisabled()); +#endif ActiveDOMObject::resume(); if (!m_resumeTimer.isActive()) m_resumeTimer.startOneShot(0); } -void Geolocation::resumeTimerFired(Timer&) +void Geolocation::resumeTimerFired() { m_isSuspended = false; @@ -308,13 +203,12 @@ void Geolocation::resumeTimerFired(Timer&) // Resume GeoNotifier timeout timers. if (hasListeners()) { - GeoNotifierSet::const_iterator end = m_oneShots.end(); - for (GeoNotifierSet::const_iterator it = m_oneShots.begin(); it != end; ++it) - (*it)->startTimerIfNeeded(); + for (auto& notifier : m_oneShots) + notifier->startTimerIfNeeded(); GeoNotifierVector watcherCopy; m_watchers.getNotifiersVector(watcherCopy); - for (size_t i = 0; i < watcherCopy.size(); ++i) - watcherCopy[i]->startTimerIfNeeded(); + for (auto& watcher : watcherCopy) + watcher->startTimerIfNeeded(); } if ((isAllowed() || isDenied()) && !m_pendingForPermissionNotifiers.isEmpty()) { @@ -369,16 +263,14 @@ void Geolocation::resetAllGeolocationPermission() stopTimers(); // Go over the one shot and re-request permission. - GeoNotifierSet::iterator end = m_oneShots.end(); - for (GeoNotifierSet::iterator it = m_oneShots.begin(); it != end; ++it) - startRequest((*it).get()); + for (auto& notifier : m_oneShots) + startRequest(notifier.get()); // Go over the watchers and re-request permission. GeoNotifierVector watcherCopy; m_watchers.getNotifiersVector(watcherCopy); - for (size_t i = 0; i < watcherCopy.size(); ++i) - startRequest(watcherCopy[i].get()); + for (auto& watcher : watcherCopy) + startRequest(watcher.get()); } -#endif // PLATFORM(IOS) void Geolocation::stop() { @@ -389,13 +281,16 @@ void Geolocation::stop() m_allowGeolocation = Unknown; cancelAllRequests(); stopUpdating(); -#if PLATFORM(IOS) m_hasChangedPosition = false; m_errorWaitingForResume = nullptr; -#endif // PLATFORM(IOS) m_pendingForPermissionNotifiers.clear(); } +const char* Geolocation::activeDOMObjectName() const +{ + return "Geolocation"; +} + Geoposition* Geolocation::lastPosition() { Page* page = this->page(); @@ -407,35 +302,72 @@ Geoposition* Geolocation::lastPosition() return m_lastPosition.get(); } -void Geolocation::getCurrentPosition(PassRefPtr successCallback, PassRefPtr errorCallback, PassRefPtr options) +void Geolocation::getCurrentPosition(Ref&& successCallback, RefPtr&& errorCallback, PositionOptions&& options) { if (!frame()) return; - RefPtr notifier = GeoNotifier::create(this, successCallback, errorCallback, options); + RefPtr notifier = GeoNotifier::create(*this, WTFMove(successCallback), WTFMove(errorCallback), WTFMove(options)); startRequest(notifier.get()); m_oneShots.add(notifier); } -int Geolocation::watchPosition(PassRefPtr successCallback, PassRefPtr errorCallback, PassRefPtr options) +int Geolocation::watchPosition(Ref&& successCallback, RefPtr&& errorCallback, PositionOptions&& options) { if (!frame()) return 0; - RefPtr notifier = GeoNotifier::create(this, successCallback, errorCallback, options); + RefPtr notifier = GeoNotifier::create(*this, WTFMove(successCallback), WTFMove(errorCallback), WTFMove(options)); startRequest(notifier.get()); int watchID; // Keep asking for the next id until we're given one that we don't already have. do { watchID = m_scriptExecutionContext->circularSequentialID(); - } while (!m_watchers.add(watchID, notifier)); + } while (!m_watchers.add(watchID, WTFMove(notifier))); return watchID; } -void Geolocation::startRequest(GeoNotifier *notifier) +static void logError(const String& target, const bool isSecure, const bool isMixedContent, Document* document) { + StringBuilder message; + message.append("[blocked] Access to geolocation was blocked over"); + + if (!isSecure) + message.append(" insecure connection to "); + else if (isMixedContent) + message.append(" secure connection with mixed content to "); + else + return; + + message.append(target); + message.append(".\n"); + document->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message.toString()); +} + +bool Geolocation::shouldBlockGeolocationRequests() +{ + bool isSecure = SecurityOrigin::isSecure(document()->url()); + bool hasMixedContent = document()->foundMixedContent(); + bool isLocalFile = document()->url().isLocalFile(); + if (securityOrigin()->canRequestGeolocation()) { + if (isLocalFile || (isSecure && !hasMixedContent)) + return false; + } + + logError(securityOrigin()->toString(), isSecure, hasMixedContent, document()); + return true; +} + +void Geolocation::startRequest(GeoNotifier* notifier) +{ + if (shouldBlockGeolocationRequests()) { + notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(originCannotRequestGeolocationErrorMessage))); + return; + } + document()->setGeolocationAccessed(); + // Check whether permissions have already been denied. Note that if this is the case, // the permission state can not change again in the lifetime of this page. if (isDenied()) @@ -454,7 +386,7 @@ void Geolocation::startRequest(GeoNotifier *notifier) notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(failedToStartServiceErrorMessage))); } -void Geolocation::fatalErrorOccurred(Geolocation::GeoNotifier* notifier) +void Geolocation::fatalErrorOccurred(GeoNotifier* notifier) { // This request has failed fatally. Remove it from our lists. m_oneShots.remove(notifier); @@ -490,15 +422,13 @@ void Geolocation::makeCachedPositionCallbacks() // All modifications to m_requestsAwaitingCachedPosition are done // asynchronously, so we don't need to worry about it being modified from // the callbacks. - GeoNotifierSet::const_iterator end = m_requestsAwaitingCachedPosition.end(); - for (GeoNotifierSet::const_iterator iter = m_requestsAwaitingCachedPosition.begin(); iter != end; ++iter) { - GeoNotifier* notifier = iter->get(); + for (auto& notifier : m_requestsAwaitingCachedPosition) { notifier->runSuccessCallback(lastPosition()); // If this is a one-shot request, stop it. Otherwise, if the watch still // exists, start the service to get updates. - if (!m_oneShots.remove(notifier) && m_watchers.contains(notifier)) { - if (notifier->hasZeroTimeout() || startUpdating(notifier)) + if (!m_oneShots.remove(notifier.get()) && m_watchers.contains(notifier.get())) { + if (notifier->hasZeroTimeout() || startUpdating(notifier.get())) notifier->startTimerIfNeeded(); else notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(failedToStartServiceErrorMessage))); @@ -520,17 +450,15 @@ void Geolocation::requestTimedOut(GeoNotifier* notifier) stopUpdating(); } -bool Geolocation::haveSuitableCachedPosition(PositionOptions* options) +bool Geolocation::haveSuitableCachedPosition(const PositionOptions& options) { Geoposition* cachedPosition = lastPosition(); if (!cachedPosition) return false; - if (!options->hasMaximumAge()) - return true; - if (!options->maximumAge()) + if (!options.maximumAge) return false; DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(currentTime()); - return cachedPosition->timestamp() > currentTimeMillis - options->maximumAge(); + return cachedPosition->timestamp() > currentTimeMillis - options.maximumAge; } void Geolocation::clearWatch(int watchID) @@ -549,16 +477,14 @@ void Geolocation::clearWatch(int watchID) void Geolocation::setIsAllowed(bool allowed) { // Protect the Geolocation object from garbage collection during a callback. - Ref protect(*this); + Ref protectedThis(*this); // This may be due to either a new position from the service, or a cached // position. m_allowGeolocation = allowed ? Yes : No; -#if PLATFORM(IOS) if (m_isSuspended) return; -#endif // Permission request was made during the startRequest process if (!m_pendingForPermissionNotifiers.isEmpty()) { @@ -572,10 +498,8 @@ void Geolocation::setIsAllowed(bool allowed) error->setIsFatal(true); handleError(error.get()); m_requestsAwaitingCachedPosition.clear(); -#if PLATFORM(IOS) m_hasChangedPosition = false; m_errorWaitingForResume = nullptr; -#endif return; } @@ -591,26 +515,20 @@ void Geolocation::setIsAllowed(bool allowed) void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError* error) { - GeoNotifierVector::const_iterator end = notifiers.end(); - for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) { - RefPtr notifier = *it; - - notifier->runErrorCallback(error); - } + for (auto& notifier : notifiers) + notifier->runErrorCallback(error); } void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition* position) { - GeoNotifierVector::const_iterator end = notifiers.end(); - for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) - (*it)->runSuccessCallback(position); + for (auto& notifier : notifiers) + notifier->runSuccessCallback(position); } void Geolocation::stopTimer(GeoNotifierVector& notifiers) { - GeoNotifierVector::const_iterator end = notifiers.end(); - for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) - (*it)->stopTimer(); + for (auto& notifier : notifiers) + notifier->stopTimer(); } void Geolocation::stopTimersForOneShots() @@ -637,9 +555,8 @@ void Geolocation::stopTimers() void Geolocation::cancelRequests(GeoNotifierVector& notifiers) { - GeoNotifierVector::const_iterator end = notifiers.end(); - for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) - (*it)->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(framelessDocumentErrorMessage))); + for (auto& notifier : notifiers) + notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(framelessDocumentErrorMessage))); } void Geolocation::cancelAllRequests() @@ -654,25 +571,20 @@ void Geolocation::cancelAllRequests() void Geolocation::extractNotifiersWithCachedPosition(GeoNotifierVector& notifiers, GeoNotifierVector* cached) { GeoNotifierVector nonCached; - GeoNotifierVector::iterator end = notifiers.end(); - for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) { - GeoNotifier* notifier = it->get(); + for (auto& notifier : notifiers) { if (notifier->useCachedPosition()) { if (cached) - cached->append(notifier); + cached->append(notifier.get()); } else - nonCached.append(notifier); + nonCached.append(notifier.get()); } notifiers.swap(nonCached); } void Geolocation::copyToSet(const GeoNotifierVector& src, GeoNotifierSet& dest) { - GeoNotifierVector::const_iterator end = src.end(); - for (GeoNotifierVector::const_iterator it = src.begin(); it != end; ++it) { - GeoNotifier* notifier = it->get(); - dest.add(notifier); - } + for (auto& notifier : src) + dest.add(notifier.get()); } void Geolocation::handleError(PositionError* error) @@ -756,24 +668,20 @@ void Geolocation::positionChanged() // Stop all currently running timers. stopTimers(); -#if PLATFORM(IOS) if (m_isSuspended) { m_hasChangedPosition = true; return; } -#endif makeSuccessCallbacks(); } void Geolocation::setError(GeolocationError* error) { -#if PLATFORM(IOS) if (m_isSuspended) { m_errorWaitingForResume = createPositionError(error); return; } -#endif RefPtr positionError = createPositionError(error); handleError(positionError.get()); } @@ -784,7 +692,7 @@ bool Geolocation::startUpdating(GeoNotifier* notifier) if (!page) return false; - GeolocationController::from(page)->addObserver(this, notifier->options()->enableHighAccuracy()); + GeolocationController::from(page)->addObserver(this, notifier->options().enableHighAccuracy); return true; } @@ -801,14 +709,11 @@ void Geolocation::handlePendingPermissionNotifiers() { // While we iterate through the list, we need not worry about list being modified as the permission // is already set to Yes/No and no new listeners will be added to the pending list - GeoNotifierSet::const_iterator end = m_pendingForPermissionNotifiers.end(); - for (GeoNotifierSet::const_iterator iter = m_pendingForPermissionNotifiers.begin(); iter != end; ++iter) { - GeoNotifier* notifier = iter->get(); - + for (auto& notifier : m_pendingForPermissionNotifiers) { if (isAllowed()) { // start all pending notification requests as permission granted. // The notifier is always ref'ed by m_oneShots or m_watchers. - if (startUpdating(notifier)) + if (startUpdating(notifier.get())) notifier->startTimerIfNeeded(); else notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(failedToStartServiceErrorMessage))); diff --git a/Source/WebCore/Modules/geolocation/Geolocation.h b/Source/WebCore/Modules/geolocation/Geolocation.h index ab486443e..3396cdc85 100644 --- a/Source/WebCore/Modules/geolocation/Geolocation.h +++ b/Source/WebCore/Modules/geolocation/Geolocation.h @@ -24,12 +24,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Geolocation_h -#define Geolocation_h +#pragma once #if ENABLE(GEOLOCATION) #include "ActiveDOMObject.h" +#include "Document.h" #include "Geoposition.h" #include "PositionCallback.h" #include "PositionError.h" @@ -37,41 +37,40 @@ #include "PositionOptions.h" #include "ScriptWrappable.h" #include "Timer.h" +#include +#include namespace WebCore { -class Document; class Frame; -class GeolocationController; +class GeoNotifier; class GeolocationError; -class GeolocationPosition; class Page; class ScriptExecutionContext; +class SecurityOrigin; +struct PositionOptions; -class Geolocation : public ScriptWrappable, public RefCounted, public ActiveDOMObject -{ +class Geolocation : public ScriptWrappable, public RefCounted, public ActiveDOMObject { + friend class GeoNotifier; public: - static PassRefPtr create(ScriptExecutionContext*); - ~Geolocation(); - -#if PLATFORM(IOS) - virtual bool canSuspend() const override; - virtual void suspend(ReasonForSuspension) override; - virtual void resume() override; - void resetAllGeolocationPermission(); -#endif // PLATFORM(IOS) - Document* document() const; - Frame* frame() const; - - void getCurrentPosition(PassRefPtr, PassRefPtr, PassRefPtr); - int watchPosition(PassRefPtr, PassRefPtr, PassRefPtr); + static Ref create(ScriptExecutionContext*); + WEBCORE_EXPORT ~Geolocation(); + + WEBCORE_EXPORT void resetAllGeolocationPermission(); + Document* document() const { return downcast(scriptExecutionContext()); } + Frame* frame() const { return document() ? document()->frame() : nullptr; } + + void getCurrentPosition(Ref&&, RefPtr&&, PositionOptions&&); + int watchPosition(Ref&&, RefPtr&&, PositionOptions&&); void clearWatch(int watchID); - void setIsAllowed(bool); + WEBCORE_EXPORT void setIsAllowed(bool); + void resetIsAllowed() { m_allowGeolocation = Unknown; } bool isAllowed() const { return m_allowGeolocation == Yes; } void positionChanged(); void setError(GeolocationError*); + bool shouldBlockGeolocationRequests(); private: explicit Geolocation(ScriptExecutionContext*); @@ -79,48 +78,23 @@ private: Geoposition* lastPosition(); // ActiveDOMObject - virtual void stop() override; + void stop() override; + bool canSuspendForDocumentSuspension() const override; + void suspend(ReasonForSuspension) override; + void resume() override; + const char* activeDOMObjectName() const override; bool isDenied() const { return m_allowGeolocation == No; } Page* page() const; - - class GeoNotifier : public RefCounted { - public: - static PassRefPtr create(Geolocation* geolocation, PassRefPtr positionCallback, PassRefPtr positionErrorCallback, PassRefPtr options) { return adoptRef(new GeoNotifier(geolocation, positionCallback, positionErrorCallback, options)); } - - PositionOptions* options() const { return m_options.get(); }; - void setFatalError(PassRefPtr); - - bool useCachedPosition() const { return m_useCachedPosition; } - void setUseCachedPosition(); - - void runSuccessCallback(Geoposition*); - void runErrorCallback(PositionError*); - - void startTimerIfNeeded(); - void stopTimer(); - void timerFired(Timer&); - bool hasZeroTimeout() const; - - private: - GeoNotifier(Geolocation*, PassRefPtr, PassRefPtr, PassRefPtr); - - RefPtr m_geolocation; - RefPtr m_successCallback; - RefPtr m_errorCallback; - RefPtr m_options; - Timer m_timer; - RefPtr m_fatalError; - bool m_useCachedPosition; - }; + SecurityOrigin* securityOrigin() const; typedef Vector> GeoNotifierVector; typedef HashSet> GeoNotifierSet; class Watchers { public: - bool add(int id, PassRefPtr); + bool add(int id, RefPtr&&); GeoNotifier* find(int id); void remove(int id); void remove(GeoNotifier*); @@ -166,7 +140,7 @@ private: void fatalErrorOccurred(GeoNotifier*); void requestTimedOut(GeoNotifier*); void requestUsesCachedPosition(GeoNotifier*); - bool haveSuitableCachedPosition(PositionOptions*); + bool haveSuitableCachedPosition(const PositionOptions&); void makeCachedPositionCallbacks(); GeoNotifierSet m_oneShots; @@ -180,15 +154,13 @@ private: Yes, No } m_allowGeolocation; -#if PLATFORM(IOS) bool m_isSuspended; bool m_resetOnResume; bool m_hasChangedPosition; RefPtr m_errorWaitingForResume; - void resumeTimerFired(Timer&); - Timer m_resumeTimer; -#endif // PLATFORM(IOS) + void resumeTimerFired(); + Timer m_resumeTimer; GeoNotifierSet m_requestsAwaitingCachedPosition; }; @@ -196,6 +168,3 @@ private: } // namespace WebCore #endif // ENABLE(GEOLOCATION) - -#endif // Geolocation_h - diff --git a/Source/WebCore/Modules/geolocation/Geolocation.idl b/Source/WebCore/Modules/geolocation/Geolocation.idl index e3ac9ea68..489af42ea 100644 --- a/Source/WebCore/Modules/geolocation/Geolocation.idl +++ b/Source/WebCore/Modules/geolocation/Geolocation.idl @@ -29,14 +29,16 @@ Conditional=GEOLOCATION, GenerateIsReachable=ImplFrame, ] interface Geolocation { - [Custom] void getCurrentPosition(PositionCallback successCallback, - optional PositionErrorCallback errorCallback, - optional PositionOptions options); + // FIXME: PositionErrorCallback should not be nullable + void getCurrentPosition(PositionCallback successCallback, + optional PositionErrorCallback? errorCallback, + optional PositionOptions options); - [Custom] long watchPosition(PositionCallback successCallback, - optional PositionErrorCallback errorCallback, - optional PositionOptions options); + // FIXME: PositionErrorCallback should not be nullable + long watchPosition(PositionCallback successCallback, + optional PositionErrorCallback? errorCallback, + optional PositionOptions options); - void clearWatch(long watchID); + void clearWatch(long watchId); }; diff --git a/Source/WebCore/Modules/geolocation/GeolocationClient.h b/Source/WebCore/Modules/geolocation/GeolocationClient.h index d89387105..b35d5ef1d 100644 --- a/Source/WebCore/Modules/geolocation/GeolocationClient.h +++ b/Source/WebCore/Modules/geolocation/GeolocationClient.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GeolocationClient_h -#define GeolocationClient_h +#pragma once namespace WebCore { @@ -54,8 +53,6 @@ protected: virtual ~GeolocationClient() { } }; -void provideGeolocationTo(Page*, GeolocationClient*); +WEBCORE_EXPORT void provideGeolocationTo(Page*, GeolocationClient*); } // namespace WebCore - -#endif // GeolocationClient_h diff --git a/Source/WebCore/Modules/geolocation/GeolocationController.cpp b/Source/WebCore/Modules/geolocation/GeolocationController.cpp index 95717296c..9e7a085fa 100644 --- a/Source/WebCore/Modules/geolocation/GeolocationController.cpp +++ b/Source/WebCore/Modules/geolocation/GeolocationController.cpp @@ -34,22 +34,22 @@ namespace WebCore { -GeolocationController::GeolocationController(GeolocationClient* client) - : m_client(client) +GeolocationController::GeolocationController(Page& page, GeolocationClient& client) + : m_page(page) + , m_client(client) { + m_page.addActivityStateChangeObserver(*this); } GeolocationController::~GeolocationController() { ASSERT(m_observers.isEmpty()); - if (m_client) - m_client->geolocationDestroyed(); -} + // NOTE: We don't have to remove ourselves from page's ActivityStateChangeObserver set, since + // we are supplement of the Page, and our destructor getting called means the page is being + // torn down. -PassOwnPtr GeolocationController::create(GeolocationClient* client) -{ - return adoptPtr(new GeolocationController(client)); + m_client.geolocationDestroyed(); } void GeolocationController::addObserver(Geolocation* observer, bool enableHighAccuracy) @@ -61,12 +61,10 @@ void GeolocationController::addObserver(Geolocation* observer, bool enableHighAc if (enableHighAccuracy) m_highAccuracyObservers.add(observer); - if (m_client) { - if (enableHighAccuracy) - m_client->setEnableHighAccuracy(true); - if (wasEmpty) - m_client->startUpdating(); - } + if (enableHighAccuracy) + m_client.setEnableHighAccuracy(true); + if (wasEmpty && m_page.isVisible()) + m_client.startUpdating(); } void GeolocationController::removeObserver(Geolocation* observer) @@ -77,24 +75,28 @@ void GeolocationController::removeObserver(Geolocation* observer) m_observers.remove(observer); m_highAccuracyObservers.remove(observer); - if (m_client) { - if (m_observers.isEmpty()) - m_client->stopUpdating(); - else if (m_highAccuracyObservers.isEmpty()) - m_client->setEnableHighAccuracy(false); - } + if (m_observers.isEmpty()) + m_client.stopUpdating(); + else if (m_highAccuracyObservers.isEmpty()) + m_client.setEnableHighAccuracy(false); } void GeolocationController::requestPermission(Geolocation* geolocation) { - if (m_client) - m_client->requestPermission(geolocation); + if (!m_page.isVisible()) { + m_pendedPermissionRequest.add(geolocation); + return; + } + + m_client.requestPermission(geolocation); } void GeolocationController::cancelPermissionRequest(Geolocation* geolocation) { - if (m_client) - m_client->cancelPermissionRequest(geolocation); + if (m_pendedPermissionRequest.remove(geolocation)) + return; + + m_client.cancelPermissionRequest(geolocation); } void GeolocationController::positionChanged(GeolocationPosition* position) @@ -102,16 +104,16 @@ void GeolocationController::positionChanged(GeolocationPosition* position) m_lastPosition = position; Vector> observersVector; copyToVector(m_observers, observersVector); - for (size_t i = 0; i < observersVector.size(); ++i) - observersVector[i]->positionChanged(); + for (auto& observer : observersVector) + observer->positionChanged(); } void GeolocationController::errorOccurred(GeolocationError* error) { Vector> observersVector; copyToVector(m_observers, observersVector); - for (size_t i = 0; i < observersVector.size(); ++i) - observersVector[i]->setError(error); + for (auto& observer : observersVector) + observer->setError(error); } GeolocationPosition* GeolocationController::lastPosition() @@ -119,10 +121,26 @@ GeolocationPosition* GeolocationController::lastPosition() if (m_lastPosition.get()) return m_lastPosition.get(); - if (!m_client) - return 0; + return m_client.lastPosition(); +} + +void GeolocationController::activityStateDidChange(ActivityState::Flags oldActivityState, ActivityState::Flags newActivityState) +{ + // Toggle GPS based on page visibility to save battery. + ActivityState::Flags changed = oldActivityState ^ newActivityState; + if (changed & ActivityState::IsVisible && !m_observers.isEmpty()) { + if (newActivityState & ActivityState::IsVisible) + m_client.startUpdating(); + else + m_client.stopUpdating(); + } + + if (!m_page.isVisible()) + return; - return m_client->lastPosition(); + HashSet> pendedPermissionRequests = WTFMove(m_pendedPermissionRequest); + for (auto& permissionRequest : pendedPermissionRequests) + m_client.requestPermission(permissionRequest.get()); } const char* GeolocationController::supplementName() @@ -132,7 +150,9 @@ const char* GeolocationController::supplementName() void provideGeolocationTo(Page* page, GeolocationClient* client) { - Supplement::provideTo(page, GeolocationController::supplementName(), GeolocationController::create(client)); + ASSERT(page); + ASSERT(client); + Supplement::provideTo(page, GeolocationController::supplementName(), std::make_unique(*page, *client)); } } // namespace WebCore diff --git a/Source/WebCore/Modules/geolocation/GeolocationController.h b/Source/WebCore/Modules/geolocation/GeolocationController.h index 9192c60dc..474708777 100644 --- a/Source/WebCore/Modules/geolocation/GeolocationController.h +++ b/Source/WebCore/Modules/geolocation/GeolocationController.h @@ -23,11 +23,11 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GeolocationController_h -#define GeolocationController_h +#pragma once #if ENABLE(GEOLOCATION) +#include "ActivityStateChangeObserver.h" #include "Geolocation.h" #include "Page.h" #include @@ -39,45 +39,47 @@ namespace WebCore { class GeolocationClient; class GeolocationError; class GeolocationPosition; -class Page; -class GeolocationController : public Supplement { +class GeolocationController : public Supplement, private ActivityStateChangeObserver { + WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(GeolocationController); public: + GeolocationController(Page&, GeolocationClient&); ~GeolocationController(); - static PassOwnPtr create(GeolocationClient*); - void addObserver(Geolocation*, bool enableHighAccuracy); void removeObserver(Geolocation*); void requestPermission(Geolocation*); void cancelPermissionRequest(Geolocation*); - void positionChanged(GeolocationPosition*); - void errorOccurred(GeolocationError*); + WEBCORE_EXPORT void positionChanged(GeolocationPosition*); + WEBCORE_EXPORT void errorOccurred(GeolocationError*); GeolocationPosition* lastPosition(); - GeolocationClient* client() { return m_client; } + GeolocationClient& client() { return m_client; } - static const char* supplementName(); + WEBCORE_EXPORT static const char* supplementName(); static GeolocationController* from(Page* page) { return static_cast(Supplement::from(page, supplementName())); } private: - GeolocationController(GeolocationClient*); + Page& m_page; + GeolocationClient& m_client; - GeolocationClient* m_client; + void activityStateDidChange(ActivityState::Flags oldActivityState, ActivityState::Flags newActivityState) override; RefPtr m_lastPosition; + typedef HashSet> ObserversSet; // All observers; both those requesting high accuracy and those not. ObserversSet m_observers; ObserversSet m_highAccuracyObservers; + + // While the page is not visible, we pend permission requests. + HashSet> m_pendedPermissionRequest; }; } // namespace WebCore #endif // ENABLE(GEOLOCATION) - -#endif // GeolocationController_h diff --git a/Source/WebCore/Modules/geolocation/GeolocationError.h b/Source/WebCore/Modules/geolocation/GeolocationError.h index bfb61aabe..b7250a2bb 100644 --- a/Source/WebCore/Modules/geolocation/GeolocationError.h +++ b/Source/WebCore/Modules/geolocation/GeolocationError.h @@ -23,12 +23,9 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GeolocationError_h -#define GeolocationError_h +#pragma once -#include #include -#include #include namespace WebCore { @@ -40,7 +37,7 @@ public: PositionUnavailable }; - static PassRefPtr create(ErrorCode code, const String& message) { return adoptRef(new GeolocationError(code, message)); } + static Ref create(ErrorCode code, const String& message) { return adoptRef(*new GeolocationError(code, message)); } ErrorCode code() const { return m_code; } const String& message() const { return m_message; } @@ -57,5 +54,3 @@ private: }; } // namespace WebCore - -#endif // GeolocationError_h diff --git a/Source/WebCore/Modules/geolocation/GeolocationPosition.h b/Source/WebCore/Modules/geolocation/GeolocationPosition.h index 7b15648f8..d3548b658 100644 --- a/Source/WebCore/Modules/geolocation/GeolocationPosition.h +++ b/Source/WebCore/Modules/geolocation/GeolocationPosition.h @@ -23,20 +23,24 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GeolocationPosition_h -#define GeolocationPosition_h +#pragma once -#include +#include #include -#include namespace WebCore { class GeolocationPosition : public RefCounted { public: - static PassRefPtr create(double timestamp, double latitude, double longitude, double accuracy) { return adoptRef(new GeolocationPosition(timestamp, latitude, longitude, accuracy)); } + static Ref create(double timestamp, double latitude, double longitude, double accuracy) + { + return adoptRef(*new GeolocationPosition(timestamp, latitude, longitude, accuracy)); + } - static PassRefPtr create(double timestamp, double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed) { return adoptRef(new GeolocationPosition(timestamp, latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed)); } + static Ref create(double timestamp, double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed) + { + return adoptRef(*new GeolocationPosition(timestamp, latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed)); + } double timestamp() const { return m_timestamp; } @@ -103,5 +107,3 @@ private: }; } // namespace WebCore - -#endif // GeolocationPosition_h diff --git a/Source/WebCore/Modules/geolocation/Geoposition.h b/Source/WebCore/Modules/geolocation/Geoposition.h index 27064dddf..cfcf319dc 100644 --- a/Source/WebCore/Modules/geolocation/Geoposition.h +++ b/Source/WebCore/Modules/geolocation/Geoposition.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Geoposition_h -#define Geoposition_h +#pragma once #include "Coordinates.h" #include "Event.h" @@ -35,31 +34,28 @@ namespace WebCore { class Geoposition : public RefCounted { public: - static PassRefPtr create(PassRefPtr coordinates, DOMTimeStamp timestamp) + static Ref create(Ref&& coordinates, DOMTimeStamp timestamp) { - return adoptRef(new Geoposition(coordinates, timestamp)); + return adoptRef(*new Geoposition(WTFMove(coordinates), timestamp)); } - PassRefPtr isolatedCopy() const + Ref isolatedCopy() const { - return Geoposition::create(m_coordinates->isolatedCopy(), m_timestamp); + return create(m_coordinates->isolatedCopy(), m_timestamp); } DOMTimeStamp timestamp() const { return m_timestamp; } - Coordinates* coords() const { return m_coordinates.get(); } + const Coordinates& coords() const { return m_coordinates.get(); } private: - Geoposition(PassRefPtr coordinates, DOMTimeStamp timestamp) - : m_coordinates(coordinates) + Geoposition(Ref&& coordinates, DOMTimeStamp timestamp) + : m_coordinates(WTFMove(coordinates)) , m_timestamp(timestamp) { - ASSERT(m_coordinates); } - RefPtr m_coordinates; + Ref m_coordinates; DOMTimeStamp m_timestamp; }; } // namespace WebCore - -#endif // Geoposition_h diff --git a/Source/WebCore/Modules/geolocation/Geoposition.idl b/Source/WebCore/Modules/geolocation/Geoposition.idl index bf8cb09b2..1a8b70caa 100644 --- a/Source/WebCore/Modules/geolocation/Geoposition.idl +++ b/Source/WebCore/Modules/geolocation/Geoposition.idl @@ -23,6 +23,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// FIXME: This should be an implementation provided typedef +// https://heycam.github.io/webidl/#DOMTimeStamp +typedef unsigned long long DOMTimeStamp; + [ NoInterfaceObject, Conditional=GEOLOCATION, diff --git a/Source/WebCore/Modules/geolocation/NavigatorGeolocation.cpp b/Source/WebCore/Modules/geolocation/NavigatorGeolocation.cpp index cb9e97a28..ad56fcacf 100644 --- a/Source/WebCore/Modules/geolocation/NavigatorGeolocation.cpp +++ b/Source/WebCore/Modules/geolocation/NavigatorGeolocation.cpp @@ -2,7 +2,7 @@ * Copyright (C) 2000 Harri Porten (porten@kde.org) * Copyright (c) 2000 Daniel Molkentin (molkentin@kde.org) * Copyright (c) 2000 Stefan Schimanski (schimmi@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2003, 2004, 2005, 2006 Apple Inc. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or @@ -51,8 +51,9 @@ NavigatorGeolocation* NavigatorGeolocation::from(Navigator* navigator) { NavigatorGeolocation* supplement = static_cast(Supplement::from(navigator, supplementName())); if (!supplement) { - supplement = new NavigatorGeolocation(navigator->frame()); - provideTo(navigator, supplementName(), adoptPtr(supplement)); + auto newSupplement = std::make_unique(navigator->frame()); + supplement = newSupplement.get(); + provideTo(navigator, supplementName(), WTFMove(newSupplement)); } return supplement; } @@ -65,9 +66,9 @@ void NavigatorGeolocation::resetAllGeolocationPermission() } #endif // PLATFORM(IOS) -Geolocation* NavigatorGeolocation::geolocation(Navigator* navigator) +Geolocation* NavigatorGeolocation::geolocation(Navigator& navigator) { - return NavigatorGeolocation::from(navigator)->geolocation(); + return NavigatorGeolocation::from(&navigator)->geolocation(); } Geolocation* NavigatorGeolocation::geolocation() const diff --git a/Source/WebCore/Modules/geolocation/NavigatorGeolocation.h b/Source/WebCore/Modules/geolocation/NavigatorGeolocation.h index c5f654002..83f8e25c0 100644 --- a/Source/WebCore/Modules/geolocation/NavigatorGeolocation.h +++ b/Source/WebCore/Modules/geolocation/NavigatorGeolocation.h @@ -17,8 +17,7 @@ Boston, MA 02110-1301, USA. */ -#ifndef NavigatorGeolocation_h -#define NavigatorGeolocation_h +#pragma once #if ENABLE(GEOLOCATION) @@ -32,11 +31,13 @@ class Geolocation; class Navigator; class NavigatorGeolocation : public Supplement, public DOMWindowProperty { + WTF_MAKE_FAST_ALLOCATED; public: + explicit NavigatorGeolocation(Frame*); virtual ~NavigatorGeolocation(); static NavigatorGeolocation* from(Navigator*); - static Geolocation* geolocation(Navigator*); + static Geolocation* geolocation(Navigator&); Geolocation* geolocation() const; #if PLATFORM(IOS) @@ -44,7 +45,6 @@ public: #endif // PLATFORM(IOS) private: - NavigatorGeolocation(Frame*); static const char* supplementName(); mutable RefPtr m_geolocation; @@ -53,5 +53,3 @@ private: } // namespace WebCore #endif // ENABLE(GEOLOCATION) - -#endif // NavigatorGeolocation_h diff --git a/Source/WebCore/Modules/geolocation/PositionCallback.h b/Source/WebCore/Modules/geolocation/PositionCallback.h index 09d7eb692..32ca1bcc6 100644 --- a/Source/WebCore/Modules/geolocation/PositionCallback.h +++ b/Source/WebCore/Modules/geolocation/PositionCallback.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PositionCallback_h -#define PositionCallback_h +#pragma once #include @@ -39,5 +38,3 @@ namespace WebCore { }; } // namespace WebCore - -#endif // PositionCallback_h diff --git a/Source/WebCore/Modules/geolocation/PositionCallback.idl b/Source/WebCore/Modules/geolocation/PositionCallback.idl index 51cbde5ac..e2ce962c1 100644 --- a/Source/WebCore/Modules/geolocation/PositionCallback.idl +++ b/Source/WebCore/Modules/geolocation/PositionCallback.idl @@ -24,6 +24,4 @@ [ Conditional=GEOLOCATION, -] callback interface PositionCallback { - boolean handleEvent(Geoposition position); -}; +] callback PositionCallback = void (Geoposition position); diff --git a/Source/WebCore/Modules/geolocation/PositionError.h b/Source/WebCore/Modules/geolocation/PositionError.h index aa51c6a33..26422ef9b 100644 --- a/Source/WebCore/Modules/geolocation/PositionError.h +++ b/Source/WebCore/Modules/geolocation/PositionError.h @@ -23,10 +23,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PositionError_h -#define PositionError_h +#pragma once -#include #include #include @@ -40,7 +38,7 @@ public: TIMEOUT = 3 }; - static PassRefPtr create(ErrorCode code, const String& message) { return adoptRef(new PositionError(code, message)); } + static Ref create(ErrorCode code, const String& message) { return adoptRef(*new PositionError(code, message)); } ErrorCode code() const { return m_code; } const String& message() const { return m_message; } @@ -63,5 +61,3 @@ private: }; } // namespace WebCore - -#endif // PositionError_h diff --git a/Source/WebCore/Modules/geolocation/PositionErrorCallback.h b/Source/WebCore/Modules/geolocation/PositionErrorCallback.h index d152785c1..79650b5dc 100644 --- a/Source/WebCore/Modules/geolocation/PositionErrorCallback.h +++ b/Source/WebCore/Modules/geolocation/PositionErrorCallback.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PositionErrorCallback_h -#define PositionErrorCallback_h +#pragma once #include @@ -39,5 +38,3 @@ namespace WebCore { }; } // namespace WebCore - -#endif // PositionErrorCallback_h diff --git a/Source/WebCore/Modules/geolocation/PositionErrorCallback.idl b/Source/WebCore/Modules/geolocation/PositionErrorCallback.idl index 78121b8cf..57b493ccb 100644 --- a/Source/WebCore/Modules/geolocation/PositionErrorCallback.idl +++ b/Source/WebCore/Modules/geolocation/PositionErrorCallback.idl @@ -24,6 +24,4 @@ [ Conditional=GEOLOCATION, -] callback interface PositionErrorCallback { - boolean handleEvent(PositionError error); -}; +] callback PositionErrorCallback = void (PositionError error); diff --git a/Source/WebCore/Modules/geolocation/PositionOptions.h b/Source/WebCore/Modules/geolocation/PositionOptions.h index 5cb66f735..20a62e31c 100644 --- a/Source/WebCore/Modules/geolocation/PositionOptions.h +++ b/Source/WebCore/Modules/geolocation/PositionOptions.h @@ -23,61 +23,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PositionOptions_h -#define PositionOptions_h - -#include -#include +#pragma once namespace WebCore { -class PositionOptions : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new PositionOptions()); } - - bool enableHighAccuracy() const { return m_highAccuracy; } - void setEnableHighAccuracy(bool enable) { m_highAccuracy = enable; } - bool hasTimeout() const { return m_hasTimeout; } - int timeout() const - { - ASSERT(hasTimeout()); - return m_timeout; - } - void setTimeout(int timeout) - { - ASSERT(timeout >= 0); - m_hasTimeout = true; - m_timeout = timeout; - } - bool hasMaximumAge() const { return m_hasMaximumAge; } - int maximumAge() const - { - ASSERT(hasMaximumAge()); - return m_maximumAge; - } - void clearMaximumAge() { m_hasMaximumAge = false; } - void setMaximumAge(int age) - { - ASSERT(age >= 0); - m_hasMaximumAge = true; - m_maximumAge = age; - } - -private: - PositionOptions() - : m_highAccuracy(false) - , m_hasTimeout(false) - { - setMaximumAge(0); - } - - bool m_highAccuracy; - bool m_hasTimeout; - int m_timeout; - bool m_hasMaximumAge; - int m_maximumAge; +struct PositionOptions { + bool enableHighAccuracy { false }; + unsigned timeout { 0xFFFFFFFF }; + unsigned maximumAge { 0 }; }; } // namespace WebCore - -#endif // PositionOptions_h diff --git a/Source/WebCore/Modules/geolocation/PositionOptions.idl b/Source/WebCore/Modules/geolocation/PositionOptions.idl new file mode 100644 index 000000000..1f6a73621 --- /dev/null +++ b/Source/WebCore/Modules/geolocation/PositionOptions.idl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +dictionary PositionOptions { + boolean enableHighAccuracy = false; + [Clamp] unsigned long timeout = 0xFFFFFFFF; + [Clamp] unsigned long maximumAge = 0; +}; diff --git a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.cpp b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.cpp index 75e7a66a6..803844156 100644 --- a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.cpp +++ b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.cpp @@ -29,11 +29,10 @@ #if ENABLE(INDEXED_DATABASE) #include "DOMWindow.h" +#include "DatabaseProvider.h" #include "Document.h" #include "IDBFactory.h" #include "Page.h" -#include "PageGroupIndexedDatabase.h" -#include "SecurityOrigin.h" namespace WebCore { @@ -56,22 +55,23 @@ DOMWindowIndexedDatabase* DOMWindowIndexedDatabase::from(DOMWindow* window) { DOMWindowIndexedDatabase* supplement = static_cast(Supplement::from(window, supplementName())); if (!supplement) { - supplement = new DOMWindowIndexedDatabase(window); - provideTo(window, supplementName(), adoptPtr(supplement)); + auto newSupplement = std::make_unique(window); + supplement = newSupplement.get(); + provideTo(window, supplementName(), WTFMove(newSupplement)); } return supplement; } -void DOMWindowIndexedDatabase::disconnectFrameForPageCache() +void DOMWindowIndexedDatabase::disconnectFrameForDocumentSuspension() { - m_suspendedIDBFactory = m_idbFactory.release(); - DOMWindowProperty::disconnectFrameForPageCache(); + m_suspendedIDBFactory = WTFMove(m_idbFactory); + DOMWindowProperty::disconnectFrameForDocumentSuspension(); } -void DOMWindowIndexedDatabase::reconnectFrameFromPageCache(Frame* frame) +void DOMWindowIndexedDatabase::reconnectFrameFromDocumentSuspension(Frame* frame) { - DOMWindowProperty::reconnectFrameFromPageCache(frame); - m_idbFactory = m_suspendedIDBFactory.release(); + DOMWindowProperty::reconnectFrameFromDocumentSuspension(frame); + m_idbFactory = WTFMove(m_suspendedIDBFactory); } void DOMWindowIndexedDatabase::willDestroyGlobalObjectInCachedFrame() @@ -92,26 +92,32 @@ void DOMWindowIndexedDatabase::willDetachGlobalObjectFromFrame() DOMWindowProperty::willDetachGlobalObjectFromFrame(); } -IDBFactory* DOMWindowIndexedDatabase::indexedDB(DOMWindow* window) +IDBFactory* DOMWindowIndexedDatabase::indexedDB(DOMWindow& window) { - return from(window)->indexedDB(); + return from(&window)->indexedDB(); } IDBFactory* DOMWindowIndexedDatabase::indexedDB() { Document* document = m_window->document(); if (!document) - return 0; + return nullptr; Page* page = document->page(); if (!page) - return 0; + return nullptr; if (!m_window->isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; + + if (!m_idbFactory) { + auto* connectionProxy = document->idbConnectionProxy(); + if (!connectionProxy) + return nullptr; + + m_idbFactory = IDBFactory::create(*connectionProxy); + } - if (!m_idbFactory) - m_idbFactory = IDBFactory::create(PageGroupIndexedDatabase::from(page->group())->factoryBackend()); return m_idbFactory.get(); } diff --git a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.h b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.h index 1d14b07d9..3de7de2e7 100644 --- a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.h +++ b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.h @@ -23,8 +23,7 @@ * DAMAGE. */ -#ifndef DOMWindowIndexedDatabase_h -#define DOMWindowIndexedDatabase_h +#pragma once #if ENABLE(INDEXED_DATABASE) @@ -37,21 +36,22 @@ class IDBFactory; class DOMWindow; class DOMWindowIndexedDatabase : public DOMWindowProperty, public Supplement { + WTF_MAKE_FAST_ALLOCATED; public: + explicit DOMWindowIndexedDatabase(DOMWindow*); virtual ~DOMWindowIndexedDatabase(); + static DOMWindowIndexedDatabase* from(DOMWindow*); - static IDBFactory* indexedDB(DOMWindow*); + WEBCORE_EXPORT static IDBFactory* indexedDB(DOMWindow&); - virtual void disconnectFrameForPageCache() override; - virtual void reconnectFrameFromPageCache(Frame*) override; - virtual void willDestroyGlobalObjectInCachedFrame() override; - virtual void willDestroyGlobalObjectInFrame() override; - virtual void willDetachGlobalObjectFromFrame() override; + void disconnectFrameForDocumentSuspension() override; + void reconnectFrameFromDocumentSuspension(Frame*) override; + void willDestroyGlobalObjectInCachedFrame() override; + void willDestroyGlobalObjectInFrame() override; + void willDetachGlobalObjectFromFrame() override; private: - explicit DOMWindowIndexedDatabase(DOMWindow*); - IDBFactory* indexedDB(); static const char* supplementName(); @@ -63,5 +63,3 @@ private: } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) - -#endif // DOMWindowIndexedDatabase_h diff --git a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.idl b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.idl index 19cfdb6b6..59f93d322 100644 --- a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.idl +++ b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.idl @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,8 +27,7 @@ [ Conditional=INDEXED_DATABASE, ] partial interface DOMWindow { - [ImplementedAs=indexedDB] readonly attribute IDBFactory webkitIndexedDB; - readonly attribute IDBFactory indexedDB; + [ImplementedAs=indexedDB] readonly attribute IDBFactory webkitIndexedDB; }; diff --git a/Source/WebCore/Modules/indexeddb/IDBActiveDOMObject.h b/Source/WebCore/Modules/indexeddb/IDBActiveDOMObject.h new file mode 100644 index 000000000..141b9383a --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBActiveDOMObject.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ActiveDOMObject.h" +#include "ScriptExecutionContext.h" +#include +#include +#include + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBActiveDOMObject : public ActiveDOMObject { +public: + ThreadIdentifier originThreadID() const { return m_originThreadID; } + + void contextDestroyed() final { + ASSERT(currentThread() == m_originThreadID); + + Locker lock(m_scriptExecutionContextLock); + ActiveDOMObject::contextDestroyed(); + } + + template + void performCallbackOnOriginThread(T& object, void (T::*method)(Parameters...), Arguments&&... arguments) + { + ASSERT(originThreadID() == object.originThreadID()); + + if (object.originThreadID() == currentThread()) { + (object.*method)(arguments...); + return; + } + + Locker lock(m_scriptExecutionContextLock); + + ScriptExecutionContext* context = scriptExecutionContext(); + if (!context) + return; + + context->postCrossThreadTask(object, method, arguments...); + } + + void callFunctionOnOriginThread(WTF::Function&& function) + { + if (originThreadID() == currentThread()) { + function(); + return; + } + + Locker lock(m_scriptExecutionContextLock); + + ScriptExecutionContext* context = scriptExecutionContext(); + if (!context) + return; + + context->postTask(WTFMove(function)); + } + +protected: + IDBActiveDOMObject(ScriptExecutionContext* context) + : ActiveDOMObject(context) + { + ASSERT(context); + } + +private: + ThreadIdentifier m_originThreadID { currentThread() }; + Lock m_scriptExecutionContextLock; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBAny.cpp b/Source/WebCore/Modules/indexeddb/IDBAny.cpp deleted file mode 100644 index 827a4cd2f..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBAny.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBAny.h" - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBCursorWithValue.h" -#include "IDBDatabase.h" -#include "IDBFactory.h" -#include "IDBIndex.h" -#include "IDBKeyPath.h" -#include "IDBObjectStore.h" -#include "DOMStringList.h" - -namespace WebCore { - -PassRefPtr IDBAny::createInvalid() -{ - return adoptRef(new IDBAny(UndefinedType)); -} - -PassRefPtr IDBAny::createNull() -{ - return adoptRef(new IDBAny(NullType)); -} - -PassRefPtr IDBAny::createString(const String& value) -{ - return adoptRef(new IDBAny(value)); -} - -IDBAny::IDBAny(Type type) - : m_type(type) - , m_integer(0) -{ - ASSERT(type == UndefinedType || type == NullType); -} - -IDBAny::~IDBAny() -{ -} - -PassRefPtr IDBAny::domStringList() -{ - ASSERT(m_type == DOMStringListType); - return m_domStringList; -} - -PassRefPtr IDBAny::idbCursor() -{ - ASSERT(m_type == IDBCursorType); - return m_idbCursor; -} - -PassRefPtr IDBAny::idbCursorWithValue() -{ - ASSERT(m_type == IDBCursorWithValueType); - return m_idbCursorWithValue; -} - -PassRefPtr IDBAny::idbDatabase() -{ - ASSERT(m_type == IDBDatabaseType); - return m_idbDatabase; -} - -PassRefPtr IDBAny::idbFactory() -{ - ASSERT(m_type == IDBFactoryType); - return m_idbFactory; -} - -PassRefPtr IDBAny::idbIndex() -{ - ASSERT(m_type == IDBIndexType); - return m_idbIndex; -} - -PassRefPtr IDBAny::idbObjectStore() -{ - ASSERT(m_type == IDBObjectStoreType); - return m_idbObjectStore; -} - -PassRefPtr IDBAny::idbTransaction() -{ - ASSERT(m_type == IDBTransactionType); - return m_idbTransaction; -} - -const Deprecated::ScriptValue& IDBAny::scriptValue() -{ - ASSERT(m_type == ScriptValueType); - return m_scriptValue; -} - -const String& IDBAny::string() -{ - ASSERT(m_type == StringType); - return m_string; -} - -int64_t IDBAny::integer() -{ - ASSERT(m_type == IntegerType); - return m_integer; -} - -IDBAny::IDBAny(PassRefPtr value) - : m_type(DOMStringListType) - , m_domStringList(value) - , m_integer(0) -{ -} - -IDBAny::IDBAny(PassRefPtr value) - : m_type(IDBCursorWithValueType) - , m_idbCursorWithValue(value) - , m_integer(0) -{ -} - -IDBAny::IDBAny(PassRefPtr value) - : m_type(IDBCursorType) - , m_idbCursor(value) - , m_integer(0) -{ -} - -IDBAny::IDBAny(PassRefPtr value) - : m_type(IDBDatabaseType) - , m_idbDatabase(value) - , m_integer(0) -{ -} - -IDBAny::IDBAny(PassRefPtr value) - : m_type(IDBFactoryType) - , m_idbFactory(value) - , m_integer(0) -{ -} - -IDBAny::IDBAny(PassRefPtr value) - : m_type(IDBIndexType) - , m_idbIndex(value) - , m_integer(0) -{ -} - -IDBAny::IDBAny(PassRefPtr value) - : m_type(IDBTransactionType) - , m_idbTransaction(value) - , m_integer(0) -{ -} - -IDBAny::IDBAny(PassRefPtr value) - : m_type(IDBObjectStoreType) - , m_idbObjectStore(value) - , m_integer(0) -{ -} - -IDBAny::IDBAny(const Deprecated::ScriptValue& value) - : m_type(ScriptValueType) - , m_scriptValue(value) - , m_integer(0) -{ -} - -IDBAny::IDBAny(const IDBKeyPath& value) - : m_type(KeyPathType) - , m_idbKeyPath(value) - , m_integer(0) -{ -} - -IDBAny::IDBAny(const String& value) - : m_type(StringType) - , m_string(value) - , m_integer(0) -{ -} - -IDBAny::IDBAny(int64_t value) - : m_type(IntegerType) - , m_integer(value) -{ -} - -} // namespace WebCore - -#endif diff --git a/Source/WebCore/Modules/indexeddb/IDBAny.h b/Source/WebCore/Modules/indexeddb/IDBAny.h deleted file mode 100644 index d728456d6..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBAny.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBAny_h -#define IDBAny_h - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBKeyPath.h" -#include "ScriptWrappable.h" -#include -#include -#include -#include -#include - -namespace WebCore { - -class DOMStringList; -class IDBCursor; -class IDBCursorWithValue; -class IDBDatabase; -class IDBFactory; -class IDBIndex; -class IDBKeyPath; -class IDBObjectStore; -class IDBTransaction; - -class IDBAny : public ScriptWrappable, public RefCounted { -public: - static PassRefPtr createInvalid(); - static PassRefPtr createNull(); - static PassRefPtr createString(const String&); - template - static PassRefPtr create(T* idbObject) - { - return adoptRef(new IDBAny(idbObject)); - } - template - static PassRefPtr create(const T& idbObject) - { - return adoptRef(new IDBAny(idbObject)); - } - template - static PassRefPtr create(PassRefPtr idbObject) - { - return adoptRef(new IDBAny(idbObject)); - } - static PassRefPtr create(int64_t value) - { - return adoptRef(new IDBAny(value)); - } - ~IDBAny(); - - enum Type { - UndefinedType = 0, - NullType, - DOMStringListType, - IDBCursorType, - IDBCursorWithValueType, - IDBDatabaseType, - IDBFactoryType, - IDBIndexType, - IDBObjectStoreType, - IDBTransactionType, - ScriptValueType, - IntegerType, - StringType, - KeyPathType, - }; - - Type type() const { return m_type; } - // Use type() to figure out which one of these you're allowed to call. - PassRefPtr domStringList(); - PassRefPtr idbCursor(); - PassRefPtr idbCursorWithValue(); - PassRefPtr idbDatabase(); - PassRefPtr idbFactory(); - PassRefPtr idbIndex(); - PassRefPtr idbObjectStore(); - PassRefPtr idbTransaction(); - const Deprecated::ScriptValue& scriptValue(); - int64_t integer(); - const String& string(); - const IDBKeyPath& keyPath() { return m_idbKeyPath; }; - -private: - explicit IDBAny(Type); - explicit IDBAny(PassRefPtr); - explicit IDBAny(PassRefPtr); - explicit IDBAny(PassRefPtr); - explicit IDBAny(PassRefPtr); - explicit IDBAny(PassRefPtr); - explicit IDBAny(PassRefPtr); - explicit IDBAny(PassRefPtr); - explicit IDBAny(PassRefPtr); - explicit IDBAny(const IDBKeyPath&); - explicit IDBAny(const String&); - explicit IDBAny(const Deprecated::ScriptValue&); - explicit IDBAny(int64_t); - - const Type m_type; - - // Only one of the following should ever be in use at any given time. - const RefPtr m_domStringList; - const RefPtr m_idbCursor; - const RefPtr m_idbCursorWithValue; - const RefPtr m_idbDatabase; - const RefPtr m_idbFactory; - const RefPtr m_idbIndex; - const RefPtr m_idbObjectStore; - const RefPtr m_idbTransaction; - const IDBKeyPath m_idbKeyPath; - const Deprecated::ScriptValue m_scriptValue; - const String m_string; - const int64_t m_integer; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBAny_h diff --git a/Source/WebCore/Modules/indexeddb/IDBAny.idl b/Source/WebCore/Modules/indexeddb/IDBAny.idl deleted file mode 100644 index fb480c92e..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBAny.idl +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -[ - NoInterfaceObject, - Conditional=INDEXED_DATABASE, - CustomToJSObject, - JSNoStaticTables -] interface IDBAny { - // This space is intentionally left blank. -}; diff --git a/Source/WebCore/Modules/indexeddb/IDBCallbacks.h b/Source/WebCore/Modules/indexeddb/IDBCallbacks.h deleted file mode 100644 index 15382bcb2..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBCallbacks.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBCallbacks_h -#define IDBCallbacks_h - -#include "IDBDatabaseError.h" -#include "IDBKey.h" -#include "IDBKeyPath.h" -#include "SharedBuffer.h" -#include - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { -class DOMStringList; -class IDBCursorBackend; -class IDBDatabaseBackend; - -struct IDBDatabaseMetadata; - -class IDBCallbacks : public RefCounted { -public: - virtual ~IDBCallbacks() { } - - virtual void onError(PassRefPtr) = 0; - // From IDBFactory.webkitGetDatabaseNames() - virtual void onSuccess(PassRefPtr) = 0; - // From IDBObjectStore/IDBIndex.openCursor(), IDBIndex.openKeyCursor() - virtual void onSuccess(PassRefPtr, PassRefPtr, PassRefPtr primaryKey, PassRefPtr) = 0; - // From IDBObjectStore.add()/put(), IDBIndex.getKey() - virtual void onSuccess(PassRefPtr) = 0; - // From IDBObjectStore/IDBIndex.get()/count(), and various methods that yield null/undefined. - virtual void onSuccess(PassRefPtr) = 0; - // From IDBObjectStore/IDBIndex.get() (with key injection) - virtual void onSuccess(PassRefPtr, PassRefPtr, const IDBKeyPath&) = 0; - // From IDBObjectStore/IDBIndex.count() - virtual void onSuccess(int64_t value) = 0; - - // From IDBFactor.deleteDatabase(), IDBObjectStore/IDBIndex.get(), IDBObjectStore.delete(), IDBObjectStore.clear() - virtual void onSuccess() = 0; - - // From IDBCursor.advance()/continue() - virtual void onSuccess(PassRefPtr, PassRefPtr primaryKey, PassRefPtr) = 0; - // From IDBCursor.advance()/continue() - virtual void onSuccessWithPrefetch(const Vector>& keys, const Vector>& primaryKeys, const Vector>& values) = 0; - // From IDBFactory.open()/deleteDatabase() - virtual void onBlocked(uint64_t /* existingVersion */) { ASSERT_NOT_REACHED(); } - // From IDBFactory.open() - virtual void onUpgradeNeeded(uint64_t /* oldVersion */, PassRefPtr, const IDBDatabaseMetadata&) { ASSERT_NOT_REACHED(); } - virtual void onSuccess(PassRefPtr, const IDBDatabaseMetadata&) { ASSERT_NOT_REACHED(); } -}; - -} // namespace WebCore - -#endif - -#endif // IDBCallbacks_h diff --git a/Source/WebCore/Modules/indexeddb/IDBCursor.cpp b/Source/WebCore/Modules/indexeddb/IDBCursor.cpp index f6ea30ed5..7f490e986 100644 --- a/Source/WebCore/Modules/indexeddb/IDBCursor.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBCursor.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -28,309 +28,357 @@ #if ENABLE(INDEXED_DATABASE) -#include "IDBAny.h" +#include "ExceptionCode.h" #include "IDBBindingUtilities.h" -#include "IDBCallbacks.h" -#include "IDBCursorBackend.h" -#include "IDBKey.h" +#include "IDBDatabase.h" +#include "IDBDatabaseException.h" +#include "IDBGetResult.h" +#include "IDBIndex.h" +#include "IDBIterateCursorData.h" #include "IDBObjectStore.h" #include "IDBRequest.h" #include "IDBTransaction.h" #include "Logging.h" -#include "ScriptCallStack.h" #include "ScriptExecutionContext.h" -#include +#include +#include +#include + +using namespace JSC; namespace WebCore { -PassRefPtr IDBCursor::create(PassRefPtr backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) +Ref IDBCursor::create(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info) { - return adoptRef(new IDBCursor(backend, direction, request, source, transaction)); + return adoptRef(*new IDBCursor(transaction, objectStore, info)); } -const AtomicString& IDBCursor::directionNext() +Ref IDBCursor::create(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info) { - DEFINE_STATIC_LOCAL(AtomicString, next, ("next", AtomicString::ConstructFromLiteral)); - return next; + return adoptRef(*new IDBCursor(transaction, index, info)); } -const AtomicString& IDBCursor::directionNextUnique() +IDBCursor::IDBCursor(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info) + : ActiveDOMObject(transaction.scriptExecutionContext()) + , m_info(info) + , m_source(&objectStore) { - DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique", AtomicString::ConstructFromLiteral)); - return nextunique; -} + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); -const AtomicString& IDBCursor::directionPrev() -{ - DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev", AtomicString::ConstructFromLiteral)); - return prev; + suspendIfNeeded(); } -const AtomicString& IDBCursor::directionPrevUnique() +IDBCursor::IDBCursor(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info) + : ActiveDOMObject(transaction.scriptExecutionContext()) + , m_info(info) + , m_source(&index) { - DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique", AtomicString::ConstructFromLiteral)); - return prevunique; -} - + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); -IDBCursor::IDBCursor(PassRefPtr backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) - : m_backend(backend) - , m_request(request) - , m_direction(direction) - , m_source(source) - , m_transaction(transaction) - , m_transactionNotifier(transaction, this) - , m_gotValue(false) -{ - ASSERT(m_backend); - ASSERT(m_request); - ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType); - ASSERT(m_transaction); + suspendIfNeeded(); } IDBCursor::~IDBCursor() { + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); } -const String& IDBCursor::direction() const +bool IDBCursor::sourcesDeleted() const { - LOG(StorageAPI, "IDBCursor::direction"); - return directionToString(m_direction); -} + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); -const Deprecated::ScriptValue& IDBCursor::key() const -{ - LOG(StorageAPI, "IDBCursor::key"); - return m_currentKeyValue; + return WTF::switchOn(m_source, + [] (const RefPtr& objectStore) { return objectStore->isDeleted(); }, + [] (const RefPtr& index) { return index->isDeleted() || index->objectStore().isDeleted(); } + ); } -const Deprecated::ScriptValue& IDBCursor::primaryKey() const +IDBObjectStore& IDBCursor::effectiveObjectStore() const { - LOG(StorageAPI, "IDBCursor::primaryKey"); - return m_currentPrimaryKeyValue; + return WTF::switchOn(m_source, + [] (const RefPtr& objectStore) -> IDBObjectStore& { return *objectStore; }, + [] (const RefPtr& index) -> IDBObjectStore& { return index->objectStore(); } + ); } -const Deprecated::ScriptValue& IDBCursor::value() const +IDBTransaction& IDBCursor::transaction() const { - LOG(StorageAPI, "IDBCursor::value"); - return m_currentValue; + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); + return effectiveObjectStore().transaction(); } -IDBAny* IDBCursor::source() const +ExceptionOr> IDBCursor::update(ExecState& state, JSValue value) { - return m_source.get(); -} + LOG(IndexedDB, "IDBCursor::update"); + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); -PassRefPtr IDBCursor::update(JSC::ExecState* state, Deprecated::ScriptValue& value, ExceptionCode& ec) -{ - LOG(StorageAPI, "IDBCursor::update"); + if (sourcesDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor's source or effective object store has been deleted.") }; - if (!m_gotValue || isKeyCursor()) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - if (m_transaction->isReadOnly()) { - ec = IDBDatabaseException::ReadOnlyError; - return 0; - } + if (!transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The transaction is inactive or finished.") }; + + if (transaction().isReadOnly()) + return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The record may not be updated inside a read-only transaction.") }; - RefPtr objectStore = effectiveObjectStore(); - const IDBKeyPath& keyPath = objectStore->metadata().keyPath; - const bool usesInLineKeys = !keyPath.isNull(); + if (!m_gotValue) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") }; + + if (!isKeyCursorWithValue()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is a key cursor.") }; + + auto& objectStore = effectiveObjectStore(); + auto& optionalKeyPath = objectStore.info().keyPath(); + const bool usesInLineKeys = !!optionalKeyPath; if (usesInLineKeys) { - RefPtr keyPathKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, keyPath); - if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) { - ec = IDBDatabaseException::DataError; - return 0; - } + RefPtr keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, optionalKeyPath.value()); + IDBKeyData keyPathKeyData(keyPathKey.get()); + if (!keyPathKey || keyPathKeyData != m_currentPrimaryKeyData) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The effective object store of this cursor uses in-line keys and evaluating the key path of the value parameter results in a different value than the cursor's effective key.") }; } - return objectStore->put(IDBDatabaseBackend::CursorUpdate, IDBAny::create(this), state, value, m_currentPrimaryKey, ec); + auto putResult = effectiveObjectStore().putForCursorUpdate(state, value, m_currentPrimaryKey.get()); + if (putResult.hasException()) + return putResult.releaseException(); + + auto request = putResult.releaseReturnValue(); + request->setSource(*this); + ++m_outstandingRequestCount; + + return WTFMove(request); } -void IDBCursor::advance(unsigned long count, ExceptionCode& ec) +ExceptionOr IDBCursor::advance(unsigned count) { - ec = 0; - LOG(StorageAPI, "IDBCursor::advance"); - if (!m_gotValue) { - ec = IDBDatabaseException::InvalidStateError; - return; - } + LOG(IndexedDB, "IDBCursor::advance"); + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return; - } + if (!m_request) + return Exception { IDBDatabaseException::InvalidStateError }; - if (!count) { - ec = TypeError; - return; + if (!count) + return Exception { TypeError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': A count argument with value 0 (zero) was supplied, must be greater than 0.") }; + + if (!transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The transaction is inactive or finished.") }; + + if (sourcesDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor's source or effective object store has been deleted.") }; + + if (!m_gotValue) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") }; + + m_gotValue = false; + + uncheckedIterateCursor(IDBKeyData(), count); + + return { }; +} + +ExceptionOr IDBCursor::continuePrimaryKey(ExecState& state, JSValue keyValue, JSValue primaryKeyValue) +{ + if (!transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The transaction is inactive or finished.") }; + + if (sourcesDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's source or effective object store has been deleted.") }; + + if (!WTF::holds_alternative>(m_source)) + return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's source is not an index.") }; + + auto direction = m_info.cursorDirection(); + if (direction != IndexedDB::CursorDirection::Next && direction != IndexedDB::CursorDirection::Prev) + return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's direction must be either \"next\" or \"prev\".") }; + + if (!m_gotValue) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") }; + + RefPtr key = scriptValueToIDBKey(state, keyValue); + if (!key->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is not a valid key.") }; + + RefPtr primaryKey = scriptValueToIDBKey(state, primaryKeyValue); + if (!primaryKey->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The second parameter is not a valid key.") }; + + IDBKeyData keyData = { key.get() }; + IDBKeyData primaryKeyData = { primaryKey.get() }; + + if (keyData < m_currentKeyData && direction == IndexedDB::CursorDirection::Next) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is less than this cursor's position and this cursor's direction is \"next\".") }; + + if (keyData > m_currentKeyData && direction == IndexedDB::CursorDirection::Prev) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is greater than this cursor's position and this cursor's direction is \"prev\".") }; + + if (keyData == m_currentKeyData) { + if (primaryKeyData <= m_currentPrimaryKeyData && direction == IndexedDB::CursorDirection::Next) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The key parameters represent a position less-than-or-equal-to this cursor's position and this cursor's direction is \"next\".") }; + if (primaryKeyData >= m_currentPrimaryKeyData && direction == IndexedDB::CursorDirection::Prev) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The key parameters represent a position greater-than-or-equal-to this cursor's position and this cursor's direction is \"prev\".") }; } - m_request->setPendingCursor(this); m_gotValue = false; - m_backend->advance(count, m_request, ec); - ASSERT(!ec); + + uncheckedIterateCursor(keyData, primaryKeyData); + + return { }; } -void IDBCursor::continueFunction(ScriptExecutionContext* context, const Deprecated::ScriptValue& keyValue, ExceptionCode& ec) +ExceptionOr IDBCursor::continueFunction(ExecState& execState, JSValue keyValue) { - DOMRequestState requestState(context); - RefPtr key = scriptValueToIDBKey(&requestState, keyValue); - continueFunction(key.release(), ec); + RefPtr key; + if (!keyValue.isUndefined()) + key = scriptValueToIDBKey(execState, keyValue); + + return continueFunction(key.get()); } -void IDBCursor::continueFunction(PassRefPtr key, ExceptionCode& ec) +ExceptionOr IDBCursor::continueFunction(const IDBKeyData& key) { - ec = 0; - LOG(StorageAPI, "IDBCursor::continue"); - if (key && !key->isValid()) { - ec = IDBDatabaseException::DataError; - return; - } + LOG(IndexedDB, "IDBCursor::continueFunction (to key %s)", key.loggingString().utf8().data()); + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return; - } + if (!m_request) + return Exception { IDBDatabaseException::InvalidStateError }; - if (!m_gotValue) { - ec = IDBDatabaseException::InvalidStateError; - return; - } + if (!transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The transaction is inactive or finished.") }; + + if (sourcesDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor's source or effective object store has been deleted.") }; + + if (!m_gotValue) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") }; + + if (!key.isNull() && !key.isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is not a valid key.") }; - if (key) { - ASSERT(m_currentKey); - if (m_direction == IndexedDB::CursorDirection::Next || m_direction == IndexedDB::CursorDirection::NextNoDuplicate) { - if (!m_currentKey->isLessThan(key.get())) { - ec = IDBDatabaseException::DataError; - return; - } - } else { - if (!key->isLessThan(m_currentKey.get())) { - ec = IDBDatabaseException::DataError; - return; - } - } + if (m_info.isDirectionForward()) { + if (!key.isNull() && key.compare(m_currentKeyData) <= 0) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is less than or equal to this cursor's position.") }; + } else { + if (!key.isNull() && key.compare(m_currentKeyData) >= 0) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is greater than or equal to this cursor's position.") }; } - // FIXME: We're not using the context from when continue was called, which means the callback - // will be on the original context openCursor was called on. Is this right? - m_request->setPendingCursor(this); m_gotValue = false; - m_backend->continueFunction(key, m_request, ec); - ASSERT(!ec); + + uncheckedIterateCursor(key, 0); + + return { }; } -PassRefPtr IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec) +void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, unsigned count) { - ec = 0; - LOG(StorageAPI, "IDBCursor::delete"); - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - if (m_transaction->isReadOnly()) { - ec = IDBDatabaseException::ReadOnlyError; - return 0; - } + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); - if (!m_gotValue || isKeyCursor()) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - RefPtr request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - m_backend->deleteFunction(request, ec); - ASSERT(!ec); - return request.release(); + ++m_outstandingRequestCount; + + m_request->willIterateCursor(*this); + transaction().iterateCursor(*this, { key, { }, count }); } -void IDBCursor::postSuccessHandlerCallback() +void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, const IDBKeyData& primaryKey) { - m_backend->postSuccessHandlerCallback(); + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); + + ++m_outstandingRequestCount; + + m_request->willIterateCursor(*this); + transaction().iterateCursor(*this, { key, primaryKey, 0 }); } -void IDBCursor::close() +ExceptionOr> IDBCursor::deleteFunction(ExecState& state) { - m_transactionNotifier.cursorFinished(); - if (m_request) { - m_request->finishCursor(); - m_request.clear(); - } + LOG(IndexedDB, "IDBCursor::deleteFunction"); + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); + + if (sourcesDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor's source or effective object store has been deleted.") }; + + if (!transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The transaction is inactive or finished.") }; + + if (transaction().isReadOnly()) + return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The record may not be deleted inside a read-only transaction.") }; + + if (!m_gotValue) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") }; + + if (!isKeyCursorWithValue()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is a key cursor.") }; + + auto result = effectiveObjectStore().deleteFunction(state, m_currentPrimaryKey.get()); + if (result.hasException()) + return result.releaseException(); + + auto request = result.releaseReturnValue(); + request->setSource(*this); + ++m_outstandingRequestCount; + + return WTFMove(request); } -void IDBCursor::setValueReady(DOMRequestState* state, PassRefPtr key, PassRefPtr primaryKey, Deprecated::ScriptValue& value) +void IDBCursor::setGetResult(IDBRequest& request, const IDBGetResult& getResult) { - m_currentKey = key; - m_currentKeyValue = idbKeyToScriptValue(state, m_currentKey); - - m_currentPrimaryKey = primaryKey; - m_currentPrimaryKeyValue = idbKeyToScriptValue(state, m_currentPrimaryKey); - - if (!isKeyCursor()) { - RefPtr objectStore = effectiveObjectStore(); - const IDBObjectStoreMetadata metadata = objectStore->metadata(); - if (metadata.autoIncrement && !metadata.keyPath.isNull()) { -#ifndef NDEBUG - RefPtr expectedKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, metadata.keyPath); - ASSERT(!expectedKey || expectedKey->isEqual(m_currentPrimaryKey.get())); -#endif - bool injected = injectIDBKeyIntoScriptValue(m_request->requestState(), m_currentPrimaryKey, value, metadata.keyPath); - // FIXME: There is no way to report errors here. Move this into onSuccessWithContinuation so that we can abort the transaction there. See: https://bugs.webkit.org/show_bug.cgi?id=92278 - ASSERT_UNUSED(injected, injected); - } + LOG(IndexedDB, "IDBCursor::setGetResult - current key %s", getResult.keyData().loggingString().substring(0, 100).utf8().data()); + ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID()); + + auto* context = request.scriptExecutionContext(); + if (!context) + return; + + auto* exec = context->execState(); + if (!exec) + return; + + if (!getResult.isDefined()) { + m_currentKey = { }; + m_currentKeyData = { }; + m_currentPrimaryKey = { }; + m_currentPrimaryKeyData = { }; + m_currentValue = { }; + + m_gotValue = false; + return; } - m_currentValue = value; + + auto& vm = context->vm(); + + // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object + // of the IDBCursor wrapper can be used, rather than the lexicalGlobalObject. + m_currentKey = { vm, toJS(*exec, *exec->lexicalGlobalObject(), getResult.keyData().maybeCreateIDBKey().get()) }; + m_currentKeyData = getResult.keyData(); + m_currentPrimaryKey = { vm, toJS(*exec, *exec->lexicalGlobalObject(), getResult.primaryKeyData().maybeCreateIDBKey().get()) }; + m_currentPrimaryKeyData = getResult.primaryKeyData(); + + if (isKeyCursorWithValue()) + m_currentValue = { vm, deserializeIDBValueToJSValue(*exec, getResult.value()) }; + else + m_currentValue = { }; m_gotValue = true; } -PassRefPtr IDBCursor::effectiveObjectStore() +const char* IDBCursor::activeDOMObjectName() const { - if (m_source->type() == IDBAny::IDBObjectStoreType) - return m_source->idbObjectStore(); - RefPtr index = m_source->idbIndex(); - return index->objectStore(); + return "IDBCursor"; } -IndexedDB::CursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionCode& ec) +bool IDBCursor::canSuspendForDocumentSuspension() const { - if (directionString == IDBCursor::directionNext()) - return IndexedDB::CursorDirection::Next; - if (directionString == IDBCursor::directionNextUnique()) - return IndexedDB::CursorDirection::NextNoDuplicate; - if (directionString == IDBCursor::directionPrev()) - return IndexedDB::CursorDirection::Prev; - if (directionString == IDBCursor::directionPrevUnique()) - return IndexedDB::CursorDirection::PrevNoDuplicate; - - ec = TypeError; - return IndexedDB::CursorDirection::Next; + return false; } -const AtomicString& IDBCursor::directionToString(IndexedDB::CursorDirection direction) +bool IDBCursor::hasPendingActivity() const { - switch (direction) { - case IndexedDB::CursorDirection::Next: - return IDBCursor::directionNext(); - - case IndexedDB::CursorDirection::NextNoDuplicate: - return IDBCursor::directionNextUnique(); - - case IndexedDB::CursorDirection::Prev: - return IDBCursor::directionPrev(); - - case IndexedDB::CursorDirection::PrevNoDuplicate: - return IDBCursor::directionPrevUnique(); + return m_outstandingRequestCount; +} - default: - ASSERT_NOT_REACHED(); - return IDBCursor::directionNext(); - } +void IDBCursor::decrementOutstandingRequestCount() +{ + ASSERT(m_outstandingRequestCount); + --m_outstandingRequestCount; } } // namespace WebCore diff --git a/Source/WebCore/Modules/indexeddb/IDBCursor.h b/Source/WebCore/Modules/indexeddb/IDBCursor.h index b58d5aee9..5c48e70ad 100644 --- a/Source/WebCore/Modules/indexeddb/IDBCursor.h +++ b/Source/WebCore/Modules/indexeddb/IDBCursor.h @@ -1,111 +1,142 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBCursor_h -#define IDBCursor_h +#pragma once #if ENABLE(INDEXED_DATABASE) -#include "IDBKey.h" -#include "IDBTransaction.h" -#include "IndexedDB.h" -#include "ScriptWrappable.h" -#include -#include -#include -#include +#include "ActiveDOMObject.h" +#include "DOMWrapperWorld.h" +#include "ExceptionOr.h" +#include "IDBCursorDirection.h" +#include "IDBCursorInfo.h" +#include +#include namespace WebCore { -class DOMRequestState; -class IDBAny; -class IDBCallbacks; -class IDBCursorBackend; -class IDBRequest; -class ScriptExecutionContext; - -typedef int ExceptionCode; +class IDBGetResult; +class IDBIndex; +class IDBObjectStore; +class IDBTransaction; -class IDBCursor : public ScriptWrappable, public RefCounted { +class IDBCursor : public ScriptWrappable, public RefCounted, public ActiveDOMObject { public: - static const AtomicString& directionNext(); - static const AtomicString& directionNextUnique(); - static const AtomicString& directionPrev(); - static const AtomicString& directionPrevUnique(); + static Ref create(IDBTransaction&, IDBObjectStore&, const IDBCursorInfo&); + static Ref create(IDBTransaction&, IDBIndex&, const IDBCursorInfo&); + + virtual ~IDBCursor(); - static IndexedDB::CursorDirection stringToDirection(const String& modeString, ExceptionCode&); - static const AtomicString& directionToString(IndexedDB::CursorDirection mode); + using Source = Variant, RefPtr>; - static PassRefPtr create(PassRefPtr, IndexedDB::CursorDirection, IDBRequest*, IDBAny* source, IDBTransaction*); - virtual ~IDBCursor(); + const Source& source() const; + IDBCursorDirection direction() const; + JSC::JSValue key() const; + JSC::JSValue primaryKey() const; + JSC::JSValue value() const; + + ExceptionOr> update(JSC::ExecState&, JSC::JSValue); + ExceptionOr advance(unsigned); + ExceptionOr continueFunction(JSC::ExecState&, JSC::JSValue key); + ExceptionOr continuePrimaryKey(JSC::ExecState&, JSC::JSValue key, JSC::JSValue primaryKey); + ExceptionOr> deleteFunction(JSC::ExecState&); + + ExceptionOr continueFunction(const IDBKeyData&); + + const IDBCursorInfo& info() const { return m_info; } - // Implement the IDL - const String& direction() const; - const Deprecated::ScriptValue& key() const; - const Deprecated::ScriptValue& primaryKey() const; - const Deprecated::ScriptValue& value() const; - IDBAny* source() const; - - PassRefPtr update(JSC::ExecState*, Deprecated::ScriptValue&, ExceptionCode&); - void advance(unsigned long, ExceptionCode&); - // FIXME: Try to modify the code generator so this overload is unneeded. - void continueFunction(ScriptExecutionContext*, ExceptionCode& ec) { continueFunction(static_cast(0), ec); } - void continueFunction(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&); - PassRefPtr deleteFunction(ScriptExecutionContext*, ExceptionCode&); - - void continueFunction(PassRefPtr, ExceptionCode&); - void postSuccessHandlerCallback(); - void close(); - void setValueReady(DOMRequestState*, PassRefPtr, PassRefPtr primaryKey, Deprecated::ScriptValue&); - PassRefPtr idbPrimaryKey() { return m_currentPrimaryKey; } + void setRequest(IDBRequest& request) { m_request = &request; } + void clearRequest() { m_request = nullptr; } + IDBRequest* request() { return m_request; } + + void setGetResult(IDBRequest&, const IDBGetResult&); + + virtual bool isKeyCursorWithValue() const { return false; } + + void decrementOutstandingRequestCount(); + + bool hasPendingActivity() const final; protected: - IDBCursor(PassRefPtr, IndexedDB::CursorDirection, IDBRequest*, IDBAny* source, IDBTransaction*); - virtual bool isKeyCursor() const { return true; } + IDBCursor(IDBTransaction&, IDBObjectStore&, const IDBCursorInfo&); + IDBCursor(IDBTransaction&, IDBIndex&, const IDBCursorInfo&); private: - PassRefPtr effectiveObjectStore(); - - RefPtr m_backend; - RefPtr m_request; - const IndexedDB::CursorDirection m_direction; - RefPtr m_source; - RefPtr m_transaction; - IDBTransaction::OpenCursorNotifier m_transactionNotifier; - bool m_gotValue; - // These values are held because m_backend may advance while they - // are still valid for the current success handlers. - Deprecated::ScriptValue m_currentKeyValue; - Deprecated::ScriptValue m_currentPrimaryKeyValue; - RefPtr m_currentKey; - RefPtr m_currentPrimaryKey; - Deprecated::ScriptValue m_currentValue; + const char* activeDOMObjectName() const final; + bool canSuspendForDocumentSuspension() const final; + + bool sourcesDeleted() const; + IDBObjectStore& effectiveObjectStore() const; + IDBTransaction& transaction() const; + + void uncheckedIterateCursor(const IDBKeyData&, unsigned count); + void uncheckedIterateCursor(const IDBKeyData&, const IDBKeyData&); + + // Cursors are created with an outstanding iteration request. + unsigned m_outstandingRequestCount { 1 }; + + IDBCursorInfo m_info; + Source m_source; + IDBRequest* m_request { nullptr }; + + bool m_gotValue { false }; + + IDBKeyData m_currentKeyData; + IDBKeyData m_currentPrimaryKeyData; + + JSC::Strong m_currentKey; + JSC::Strong m_currentPrimaryKey; + JSC::Strong m_currentValue; }; -} // namespace WebCore -#endif +inline const IDBCursor::Source& IDBCursor::source() const +{ + return m_source; +} + +inline IDBCursorDirection IDBCursor::direction() const +{ + return m_info.cursorDirection(); +} + +inline JSC::JSValue IDBCursor::key() const +{ + return m_currentKey.get(); +} + +inline JSC::JSValue IDBCursor::primaryKey() const +{ + return m_currentPrimaryKey.get(); +} + +inline JSC::JSValue IDBCursor::value() const +{ + return m_currentValue.get(); +} + +} // namespace WebCore -#endif // IDBCursor_h +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBCursor.idl b/Source/WebCore/Modules/indexeddb/IDBCursor.idl index e34ec827b..867fa46c4 100644 --- a/Source/WebCore/Modules/indexeddb/IDBCursor.idl +++ b/Source/WebCore/Modules/indexeddb/IDBCursor.idl @@ -24,16 +24,20 @@ */ [ + ActiveDOMObject, Conditional=INDEXED_DATABASE, - JSNoStaticTables, + CustomToJSObject, + JSCustomMarkFunction, + SkipVTableValidation, ] interface IDBCursor { - readonly attribute IDBAny source; - readonly attribute DOMString direction; + readonly attribute (IDBObjectStore or IDBIndex) source; + readonly attribute IDBCursorDirection direction; readonly attribute any key; readonly attribute any primaryKey; - [CallWith=ScriptState, RaisesException] IDBRequest update(any value); - [RaisesException] void advance([EnforceRange] unsigned long count); - [CallWith=ScriptExecutionContext, ImplementedAs=continueFunction, RaisesException] void continue(optional any key); - [CallWith=ScriptExecutionContext, ImplementedAs=deleteFunction, RaisesException] IDBRequest delete(); + [CallWith=ScriptState, MayThrowException] IDBRequest update(any value); + [MayThrowException] void advance([EnforceRange] unsigned long count); + [CallWith=ScriptState, ImplementedAs=continueFunction, MayThrowException] void continue(optional any key); + [CallWith=ScriptState, MayThrowException] void continuePrimaryKey(any key, any primaryKey); + [CallWith=ScriptState, ImplementedAs=deleteFunction, MayThrowException] IDBRequest delete(); }; diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorBackend.cpp b/Source/WebCore/Modules/indexeddb/IDBCursorBackend.cpp deleted file mode 100644 index 7bb600b2b..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBCursorBackend.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBCursorBackend.h" - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBCallbacks.h" -#include "IDBCursorBackendOperations.h" -#include "IDBDatabaseBackend.h" -#include "IDBDatabaseCallbacks.h" -#include "IDBDatabaseError.h" -#include "IDBDatabaseException.h" -#include "IDBKeyRange.h" -#include "IDBOperation.h" -#include "IDBServerConnection.h" -#include "Logging.h" -#include "SharedBuffer.h" - -namespace WebCore { - -IDBCursorBackend::IDBCursorBackend(int64_t cursorID, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, IDBTransactionBackend& transaction, int64_t objectStoreID) - : m_taskType(taskType) - , m_cursorType(cursorType) - , m_transaction(&transaction) - , m_objectStoreID(objectStoreID) - , m_cursorID(cursorID) - , m_savedCursorID(0) - , m_closed(false) -{ - m_transaction->registerOpenCursor(this); -} - -IDBCursorBackend::~IDBCursorBackend() -{ - m_transaction->unregisterOpenCursor(this); -} - -void IDBCursorBackend::continueFunction(PassRefPtr key, PassRefPtr prpCallbacks, ExceptionCode&) -{ - LOG(StorageAPI, "IDBCursorBackend::continue"); - RefPtr callbacks = prpCallbacks; - m_transaction->scheduleTask(m_taskType, CursorIterationOperation::create(this, key, callbacks)); -} - -void IDBCursorBackend::advance(unsigned long count, PassRefPtr prpCallbacks, ExceptionCode&) -{ - LOG(StorageAPI, "IDBCursorBackend::advance"); - RefPtr callbacks = prpCallbacks; - m_transaction->scheduleTask(CursorAdvanceOperation::create(this, count, callbacks)); -} - -void IDBCursorBackend::deleteFunction(PassRefPtr prpCallbacks, ExceptionCode&) -{ - LOG(StorageAPI, "IDBCursorBackend::delete"); - ASSERT(m_transaction->mode() != IndexedDB::TransactionMode::ReadOnly); - RefPtr keyRange = IDBKeyRange::create(primaryKey()); - m_transaction->database().deleteRange(m_transaction->id(), m_objectStoreID, keyRange.release(), prpCallbacks); -} - -void IDBCursorBackend::close() -{ - LOG(StorageAPI, "IDBCursorBackend::close"); - clear(); - m_closed = true; - m_savedCursorID = 0; -} - -void IDBCursorBackend::updateCursorData(IDBKey* key, IDBKey* primaryKey, SharedBuffer* value) -{ - m_currentKey = key; - m_currentPrimaryKey = primaryKey; - m_currentValue = value; -} - -void IDBCursorBackend::clear() -{ - m_cursorID = 0; - m_currentKey = 0; - m_currentPrimaryKey = 0; - m_currentValue = 0; -} - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorBackend.h b/Source/WebCore/Modules/indexeddb/IDBCursorBackend.h deleted file mode 100644 index 3ee5b94a4..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBCursorBackend.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#ifndef IDBCursorBackend_h -#define IDBCursorBackend_h - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBDatabaseBackend.h" -#include "IDBTransactionBackend.h" -#include "SharedBuffer.h" -#include -#include -#include - -namespace WebCore { - -class IDBKeyRange; - -class IDBCursorBackend : public RefCounted { -public: - static PassRefPtr create(int64_t cursorID, IndexedDB::CursorType cursorType, IDBTransactionBackend& transaction, int64_t objectStoreID) - { - return adoptRef(new IDBCursorBackend(cursorID, cursorType, IDBDatabaseBackend::NormalTask, transaction, objectStoreID)); - } - static PassRefPtr create(int64_t cursorID, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, IDBTransactionBackend& transaction, int64_t objectStoreID) - { - return adoptRef(new IDBCursorBackend(cursorID, cursorType, taskType, transaction, objectStoreID)); - } - ~IDBCursorBackend(); - - void advance(unsigned long, PassRefPtr, ExceptionCode&); - void continueFunction(PassRefPtr, PassRefPtr, ExceptionCode&); - void deleteFunction(PassRefPtr, ExceptionCode&); - void postSuccessHandlerCallback() { } - - IDBKey* key() const { return m_currentKey.get(); } - IDBKey* primaryKey() const { return m_currentPrimaryKey.get(); } - SharedBuffer* value() const { return (m_cursorType == IndexedDB::CursorType::KeyOnly) ? 0 : m_currentValue.get(); } - void updateCursorData(IDBKey*, IDBKey* primaryKey, SharedBuffer* value); - - void close(); - - IndexedDB::CursorType cursorType() const { return m_cursorType; } - - int64_t id() const { return m_cursorID; } - - IDBTransactionBackend& transaction() { return *m_transaction; } - - void clear(); - void setSavedCursorID(int64_t cursorID) { m_savedCursorID = cursorID; } - -private: - IDBCursorBackend(int64_t cursorID, IndexedDB::CursorType, IDBDatabaseBackend::TaskType, IDBTransactionBackend&, int64_t objectStoreID); - - IDBDatabaseBackend::TaskType m_taskType; - IndexedDB::CursorType m_cursorType; - RefPtr m_transaction; - const int64_t m_objectStoreID; - - int64_t m_cursorID; - int64_t m_savedCursorID; - - RefPtr m_currentKey; - RefPtr m_currentPrimaryKey; - RefPtr m_currentValue; - - bool m_closed; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBCursorBackend_h diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp b/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp deleted file mode 100644 index 9860745dc..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBCursorBackendOperations.h" - -#include "IDBServerConnection.h" - -#if ENABLE(INDEXED_DATABASE) - -#include "Logging.h" - -namespace WebCore { - -void CursorAdvanceOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "CursorAdvanceOperation"); - - RefPtr operation(this); - auto callback = [this, operation, completionCallback](PassRefPtr key, PassRefPtr primaryKey, PassRefPtr value, PassRefPtr error) { - if (error) { - m_cursor->clear(); - // FIXME: The LevelDB backend calls onSuccess even on failure. - // This will probably have to change soon (for sanity) and will probably break LevelDB - m_callbacks->onSuccess(static_cast(0)); - } else { - m_cursor->updateCursorData(key.get(), primaryKey.get(), value.get()); - m_callbacks->onSuccess(key, primaryKey, value); - } - - // FIXME: Cursor operations should be able to pass along an error instead of success - completionCallback(); - }; - - m_cursor->transaction().database().serverConnection().cursorAdvance(*m_cursor, *this, callback); -} - -void CursorIterationOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "CursorIterationOperation"); - - RefPtr operation(this); - auto callback = [this, operation, completionCallback](PassRefPtr key, PassRefPtr primaryKey, PassRefPtr value, PassRefPtr error) { - if (error) { - m_cursor->clear(); - // FIXME: The LevelDB backend calls onSuccess even on failure. - // This will probably have to change soon (for sanity) and will probably break LevelDB - m_callbacks->onSuccess(static_cast(0)); - } else { - m_cursor->updateCursorData(key.get(), primaryKey.get(), value.get()); - m_callbacks->onSuccess(key, primaryKey, value); - } - - // FIXME: Cursor operations should be able to pass along an error instead of success - completionCallback(); - }; - - m_cursor->transaction().database().serverConnection().cursorIterate(*m_cursor, *this, callback); -} - -} // namespace WebCore - -#endif // ENABLED(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.h b/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.h deleted file mode 100644 index 467f1deb4..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBCursorBackendOperations_h -#define IDBCursorBackendOperations_h - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBCursorBackend.h" -#include "IDBOperation.h" - -namespace WebCore { - -class CursorIterationOperation : public IDBOperation { -public: - static PassRefPtr create(PassRefPtr cursor, PassRefPtr key, PassRefPtr callbacks) - { - return adoptRef(new CursorIterationOperation(cursor, key, callbacks)); - } - virtual void perform(std::function completionCallback) override final; - - int64_t cursorID() const { return m_cursor->id(); } - IDBKey* key() const { return m_key.get(); } - -private: - CursorIterationOperation(PassRefPtr cursor, PassRefPtr key, PassRefPtr callbacks) - : m_cursor(cursor) - , m_key(key) - , m_callbacks(callbacks) - { - } - - RefPtr m_cursor; - RefPtr m_key; - RefPtr m_callbacks; -}; - -class CursorAdvanceOperation : public IDBOperation { -public: - static PassRefPtr create(PassRefPtr cursor, unsigned long count, PassRefPtr callbacks) - { - return adoptRef(new CursorAdvanceOperation(cursor, count, callbacks)); - } - virtual void perform(std::function completionCallback) override final; - - int64_t cursorID() const { return m_cursor->id(); } - unsigned long count() const { return m_count; } - -private: - CursorAdvanceOperation(PassRefPtr cursor, unsigned long count, PassRefPtr callbacks) - : m_cursor(cursor) - , m_count(count) - , m_callbacks(callbacks) - { - } - - RefPtr m_cursor; - unsigned long m_count; - RefPtr m_callbacks; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBCursorBackendOperations_h diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorDirection.h b/Source/WebCore/Modules/indexeddb/IDBCursorDirection.h new file mode 100644 index 000000000..e40fb8630 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBCursorDirection.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "IndexedDB.h" + +namespace WebCore { + +using IDBCursorDirection = IndexedDB::CursorDirection; + +} diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorDirection.idl b/Source/WebCore/Modules/indexeddb/IDBCursorDirection.idl new file mode 100644 index 000000000..b85fdc0f4 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBCursorDirection.idl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=INDEXED_DATABASE, +] enum IDBCursorDirection { + "next", + "nextunique", + "prev", + "prevunique" +}; diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.cpp b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.cpp index 90ec57af5..603f1b2fc 100644 --- a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -28,24 +28,27 @@ #if ENABLE(INDEXED_DATABASE) -#include "IDBCursorBackend.h" -#include "IDBKey.h" +#include namespace WebCore { -PassRefPtr IDBCursorWithValue::create(PassRefPtr backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) +Ref IDBCursorWithValue::create(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info) +{ + return adoptRef(*new IDBCursorWithValue(transaction, objectStore, info)); +} + +Ref IDBCursorWithValue::create(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info) { - return adoptRef(new IDBCursorWithValue(backend, direction, request, source, transaction)); + return adoptRef(*new IDBCursorWithValue(transaction, index, info)); } -PassRefPtr IDBCursorWithValue::fromCursor(PassRefPtr prpCursor) +IDBCursorWithValue::IDBCursorWithValue(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info) + : IDBCursor(transaction, objectStore, info) { - RefPtr cursorWithValue(static_cast(prpCursor.get())); - return cursorWithValue.release(); } -IDBCursorWithValue::IDBCursorWithValue(PassRefPtr backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction) - : IDBCursor(backend, direction, request, source, transaction) +IDBCursorWithValue::IDBCursorWithValue(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info) + : IDBCursor(transaction, index, info) { } diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.h b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.h index 96f96b0e1..f78891b7d 100644 --- a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.h +++ b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.h @@ -1,55 +1,55 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBCursorWithValue_h -#define IDBCursorWithValue_h +#pragma once #if ENABLE(INDEXED_DATABASE) #include "IDBCursor.h" +#include namespace WebCore { -class IDBCursorWithValue : public IDBCursor { +class IDBCursorWithValue final : public IDBCursor { public: - static PassRefPtr create(PassRefPtr, IndexedDB::CursorDirection, IDBRequest*, IDBAny* source, IDBTransaction*); - static PassRefPtr fromCursor(PassRefPtr); - virtual ~IDBCursorWithValue(); + static Ref create(IDBTransaction&, IDBObjectStore&, const IDBCursorInfo&); + static Ref create(IDBTransaction&, IDBIndex&, const IDBCursorInfo&); - // The value attribute defined in the IDL is simply implemented in IDBCursor (but not exposed via - // its IDL). This is to make the implementation more simple while matching what the spec says. + virtual ~IDBCursorWithValue(); -protected: - virtual bool isKeyCursor() const override { return false; } + bool isKeyCursorWithValue() const override { return true; } private: - IDBCursorWithValue(PassRefPtr, IndexedDB::CursorDirection, IDBRequest*, IDBAny* source, IDBTransaction*); + IDBCursorWithValue(IDBTransaction&, IDBObjectStore&, const IDBCursorInfo&); + IDBCursorWithValue(IDBTransaction&, IDBIndex&, const IDBCursorInfo&); }; } // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::IDBCursorWithValue) + static bool isType(const WebCore::IDBCursor& cursor) { return cursor.isKeyCursorWithValue(); } +SPECIALIZE_TYPE_TRAITS_END() -#endif // IDBCursorWithValue_h +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.idl b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.idl index 7ee92da02..422a1bcd4 100644 --- a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.idl +++ b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.idl @@ -25,7 +25,9 @@ [ Conditional=INDEXED_DATABASE, - JSNoStaticTables + ActiveDOMObject, + SkipVTableValidation, + JSCustomMarkFunction, ] interface IDBCursorWithValue : IDBCursor { readonly attribute any value; }; diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabase.cpp b/Source/WebCore/Modules/indexeddb/IDBDatabase.cpp index e8ca31c29..fad585ca1 100644 --- a/Source/WebCore/Modules/indexeddb/IDBDatabase.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBDatabase.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -29,358 +29,482 @@ #if ENABLE(INDEXED_DATABASE) #include "DOMStringList.h" +#include "EventNames.h" #include "EventQueue.h" #include "ExceptionCode.h" -#include "HistogramSupport.h" -#include "IDBAny.h" -#include "IDBDatabaseCallbacks.h" -#include "IDBDatabaseError.h" +#include "IDBConnectionProxy.h" +#include "IDBConnectionToServer.h" #include "IDBDatabaseException.h" -#include "IDBEventDispatcher.h" -#include "IDBHistograms.h" #include "IDBIndex.h" -#include "IDBKeyPath.h" #include "IDBObjectStore.h" +#include "IDBOpenDBRequest.h" +#include "IDBResultData.h" #include "IDBTransaction.h" #include "IDBVersionChangeEvent.h" #include "Logging.h" -#include "ScriptCallStack.h" #include "ScriptExecutionContext.h" -#include -#include -#include +#include namespace WebCore { -PassRefPtr IDBDatabase::create(ScriptExecutionContext* context, PassRefPtr database, PassRefPtr callbacks) +Ref IDBDatabase::create(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBResultData& resultData) { - RefPtr idbDatabase(adoptRef(new IDBDatabase(context, database, callbacks))); - idbDatabase->suspendIfNeeded(); - return idbDatabase.release(); + return adoptRef(*new IDBDatabase(context, connectionProxy, resultData)); } -IDBDatabase::IDBDatabase(ScriptExecutionContext* context, PassRefPtr backend, PassRefPtr callbacks) - : ActiveDOMObject(context) - , m_backend(backend) - , m_closePending(false) - , m_contextStopped(false) - , m_databaseCallbacks(callbacks) +IDBDatabase::IDBDatabase(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBResultData& resultData) + : IDBActiveDOMObject(&context) + , m_connectionProxy(connectionProxy) + , m_info(resultData.databaseInfo()) + , m_databaseConnectionIdentifier(resultData.databaseConnectionIdentifier()) + , m_eventNames(eventNames()) { - // We pass a reference of this object before it can be adopted. - relaxAdoptionRequirement(); + LOG(IndexedDB, "IDBDatabase::IDBDatabase - Creating database %s with version %" PRIu64 " connection %" PRIu64 " (%p)", m_info.name().utf8().data(), m_info.version(), m_databaseConnectionIdentifier, this); + suspendIfNeeded(); + m_connectionProxy->registerDatabaseConnection(*this); } IDBDatabase::~IDBDatabase() { - // This does what IDBDatabase::close does, but without any ref/deref of the - // database since it is already in the process of being deleted. The logic here - // is also simpler since we know there are no transactions (since they ref the - // database when they are alive). + ASSERT(currentThread() == originThreadID()); - ASSERT(m_transactions.isEmpty()); + if (!m_closedInServer) + m_connectionProxy->databaseConnectionClosed(*this); - if (!m_closePending) { - m_closePending = true; - m_backend->close(m_databaseCallbacks); - } + m_connectionProxy->unregisterDatabaseConnection(*this); +} - if (auto* context = scriptExecutionContext()) { - // Remove any pending versionchange events scheduled to fire on this - // connection. They would have been scheduled by the backend when another - // connection called setVersion, but the frontend connection is being - // closed before they could fire. - for (auto& event : m_enqueuedEvents) - context->eventQueue().cancelEvent(*event); - } +bool IDBDatabase::hasPendingActivity() const +{ + ASSERT(currentThread() == originThreadID() || mayBeGCThread()); + + if (m_closedInServer) + return false; + + if (!m_activeTransactions.isEmpty() || !m_committingTransactions.isEmpty() || !m_abortingTransactions.isEmpty()) + return true; + + return hasEventListeners(m_eventNames.abortEvent) || hasEventListeners(m_eventNames.errorEvent) || hasEventListeners(m_eventNames.versionchangeEvent); } -int64_t IDBDatabase::nextTransactionId() +const String IDBDatabase::name() const { - // Only keep a 32-bit counter to allow ports to use the other 32 - // bits of the id. - static std::atomic currentTransactionId; + ASSERT(currentThread() == originThreadID()); + return m_info.name(); +} - return ++currentTransactionId; +uint64_t IDBDatabase::version() const +{ + ASSERT(currentThread() == originThreadID()); + return m_info.version(); } -void IDBDatabase::transactionCreated(IDBTransaction* transaction) +RefPtr IDBDatabase::objectStoreNames() const { - ASSERT(transaction); - ASSERT(!m_transactions.contains(transaction->id())); - m_transactions.add(transaction->id(), transaction); + ASSERT(currentThread() == originThreadID()); - if (transaction->isVersionChange()) { - ASSERT(!m_versionChangeTransaction); - m_versionChangeTransaction = transaction; - } + RefPtr objectStoreNames = DOMStringList::create(); + for (auto& name : m_info.objectStoreNames()) + objectStoreNames->append(name); + objectStoreNames->sort(); + return objectStoreNames; } -void IDBDatabase::transactionFinished(IDBTransaction* transaction) +void IDBDatabase::renameObjectStore(IDBObjectStore& objectStore, const String& newName) { - ASSERT(transaction); - ASSERT(m_transactions.contains(transaction->id())); - ASSERT(m_transactions.get(transaction->id()) == transaction); - m_transactions.remove(transaction->id()); - - if (transaction->isVersionChange()) { - ASSERT(m_versionChangeTransaction == transaction); - m_versionChangeTransaction = 0; - } + ASSERT(currentThread() == originThreadID()); + ASSERT(m_versionChangeTransaction); + ASSERT(m_info.hasObjectStore(objectStore.info().name())); + + m_info.renameObjectStore(objectStore.info().identifier(), newName); - if (m_closePending && m_transactions.isEmpty()) - closeConnection(); + m_versionChangeTransaction->renameObjectStore(objectStore, newName); } -void IDBDatabase::onAbort(int64_t transactionId, PassRefPtr error) +void IDBDatabase::renameIndex(IDBIndex& index, const String& newName) { - ASSERT(m_transactions.contains(transactionId)); - m_transactions.get(transactionId)->onAbort(error); + ASSERT(currentThread() == originThreadID()); + ASSERT(m_versionChangeTransaction); + ASSERT(m_info.hasObjectStore(index.objectStore().info().name())); + ASSERT(m_info.infoForExistingObjectStore(index.objectStore().info().name())->hasIndex(index.info().name())); + + m_info.infoForExistingObjectStore(index.objectStore().info().name())->infoForExistingIndex(index.info().identifier())->rename(newName); + + m_versionChangeTransaction->renameIndex(index, newName); } -void IDBDatabase::onComplete(int64_t transactionId) +ExceptionOr> IDBDatabase::createObjectStore(const String& name, ObjectStoreParameters&& parameters) { - ASSERT(m_transactions.contains(transactionId)); - m_transactions.get(transactionId)->onComplete(); + LOG(IndexedDB, "IDBDatabase::createObjectStore - (%s %s)", m_info.name().utf8().data(), name.utf8().data()); + + ASSERT(currentThread() == originThreadID()); + ASSERT(!m_versionChangeTransaction || m_versionChangeTransaction->isVersionChange()); + + if (!m_versionChangeTransaction) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'createObjectStore' on 'IDBDatabase': The database is not running a version change transaction.") }; + + if (!m_versionChangeTransaction->isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError }; + + if (m_info.hasObjectStore(name)) + return Exception { IDBDatabaseException::ConstraintError, ASCIILiteral("Failed to execute 'createObjectStore' on 'IDBDatabase': An object store with the specified name already exists.") }; + + auto& keyPath = parameters.keyPath; + if (keyPath && !isIDBKeyPathValid(keyPath.value())) + return Exception { IDBDatabaseException::SyntaxError, ASCIILiteral("Failed to execute 'createObjectStore' on 'IDBDatabase': The keyPath option is not a valid key path.") }; + + if (keyPath && parameters.autoIncrement && ((WTF::holds_alternative(keyPath.value()) && WTF::get(keyPath.value()).isEmpty()) || WTF::holds_alternative>(keyPath.value()))) + return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'createObjectStore' on 'IDBDatabase': The autoIncrement option was set but the keyPath option was empty or an array.") }; + + // Install the new ObjectStore into the connection's metadata. + auto info = m_info.createNewObjectStore(name, WTFMove(keyPath), parameters.autoIncrement); + + // Create the actual IDBObjectStore from the transaction, which also schedules the operation server side. + return m_versionChangeTransaction->createObjectStore(info); } -PassRefPtr IDBDatabase::objectStoreNames() const +ExceptionOr> IDBDatabase::transaction(StringOrVectorOfStrings&& storeNames, IDBTransactionMode mode) { - RefPtr objectStoreNames = DOMStringList::create(); - for (IDBDatabaseMetadata::ObjectStoreMap::const_iterator it = m_metadata.objectStores.begin(); it != m_metadata.objectStores.end(); ++it) - objectStoreNames->append(it->value.name); - objectStoreNames->sort(); - return objectStoreNames.release(); + LOG(IndexedDB, "IDBDatabase::transaction"); + + ASSERT(currentThread() == originThreadID()); + + if (m_closePending) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'transaction' on 'IDBDatabase': The database connection is closing.") }; + + Vector objectStores; + if (WTF::holds_alternative>(storeNames)) + objectStores = WTFMove(WTF::get>(storeNames)); + else + objectStores.append(WTFMove(WTF::get(storeNames))); + + if (objectStores.isEmpty()) + return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'transaction' on 'IDBDatabase': The storeNames parameter was empty.") }; + + if (mode != IDBTransactionMode::Readonly && mode != IDBTransactionMode::Readwrite) + return Exception { TypeError }; + + if (m_versionChangeTransaction && !m_versionChangeTransaction->isFinishedOrFinishing()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'transaction' on 'IDBDatabase': A version change transaction is running.") }; + + // It is valid for javascript to pass in a list of object store names with the same name listed twice, + // so we need to put them all in a set to get a unique list. + HashSet objectStoreSet; + for (auto& objectStore : objectStores) + objectStoreSet.add(objectStore); + + objectStores.clear(); + copyToVector(objectStoreSet, objectStores); + + for (auto& objectStoreName : objectStores) { + if (m_info.hasObjectStore(objectStoreName)) + continue; + return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'transaction' on 'IDBDatabase': One of the specified object stores was not found.") }; + } + + auto info = IDBTransactionInfo::clientTransaction(m_connectionProxy.get(), objectStores, mode); + + LOG(IndexedDBOperations, "IDB creating transaction: %s", info.loggingString().utf8().data()); + auto transaction = IDBTransaction::create(*this, info); + + LOG(IndexedDB, "IDBDatabase::transaction - Added active transaction %s", info.identifier().loggingString().utf8().data()); + + m_activeTransactions.set(info.identifier(), transaction.ptr()); + + return WTFMove(transaction); } -uint64_t IDBDatabase::version() const +ExceptionOr IDBDatabase::deleteObjectStore(const String& objectStoreName) { - return m_metadata.version; + LOG(IndexedDB, "IDBDatabase::deleteObjectStore"); + + ASSERT(currentThread() == originThreadID()); + + if (!m_versionChangeTransaction) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'deleteObjectStore' on 'IDBDatabase': The database is not running a version change transaction.") }; + + if (!m_versionChangeTransaction->isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError }; + + if (!m_info.hasObjectStore(objectStoreName)) + return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'deleteObjectStore' on 'IDBDatabase': The specified object store was not found.") }; + + m_info.deleteObjectStore(objectStoreName); + m_versionChangeTransaction->deleteObjectStore(objectStoreName); + + return { }; } -PassRefPtr IDBDatabase::createObjectStore(const String& name, const Dictionary& options, ExceptionCode& ec) +void IDBDatabase::close() { - IDBKeyPath keyPath; - bool autoIncrement = false; - if (!options.isUndefinedOrNull()) { - String keyPathString; - Vector keyPathArray; - if (options.get("keyPath", keyPathArray)) - keyPath = IDBKeyPath(keyPathArray); - else if (options.getWithUndefinedOrNullCheck("keyPath", keyPathString)) - keyPath = IDBKeyPath(keyPathString); - - options.get("autoIncrement", autoIncrement); + LOG(IndexedDB, "IDBDatabase::close - %" PRIu64, m_databaseConnectionIdentifier); + + ASSERT(currentThread() == originThreadID()); + + if (!m_closePending) { + m_closePending = true; + m_connectionProxy->databaseConnectionPendingClose(*this); } - return createObjectStore(name, keyPath, autoIncrement, ec); + maybeCloseInServer(); } -PassRefPtr IDBDatabase::createObjectStore(const String& name, const IDBKeyPath& keyPath, bool autoIncrement, ExceptionCode& ec) +void IDBDatabase::didCloseFromServer(const IDBError& error) { - LOG(StorageAPI, "IDBDatabase::createObjectStore"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.FrontEndAPICalls", IDBCreateObjectStoreCall, IDBMethodsMax); - if (!m_versionChangeTransaction) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_versionChangeTransaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } + LOG(IndexedDB, "IDBDatabase::didCloseFromServer - %" PRIu64, m_databaseConnectionIdentifier); - if (containsObjectStore(name)) { - ec = IDBDatabaseException::ConstraintError; - return 0; - } + connectionToServerLost(error); - if (!keyPath.isNull() && !keyPath.isValid()) { - ec = IDBDatabaseException::SyntaxError; - return 0; - } + m_connectionProxy->confirmDidCloseFromServer(*this); +} - if (autoIncrement && ((keyPath.type() == IDBKeyPath::StringType && keyPath.string().isEmpty()) || keyPath.type() == IDBKeyPath::ArrayType)) { - ec = IDBDatabaseException::InvalidAccessError; - return 0; - } +void IDBDatabase::connectionToServerLost(const IDBError& error) +{ + LOG(IndexedDB, "IDBDatabase::connectionToServerLost - %" PRIu64, m_databaseConnectionIdentifier); + + ASSERT(currentThread() == originThreadID()); + + m_closePending = true; + m_closedInServer = true; + + for (auto& transaction : m_activeTransactions.values()) + transaction->connectionClosedFromServer(error); + + auto errorEvent = Event::create(m_eventNames.errorEvent, true, false); + errorEvent->setTarget(this); - int64_t objectStoreId = m_metadata.maxObjectStoreId + 1; - m_backend->createObjectStore(m_versionChangeTransaction->id(), objectStoreId, name, keyPath, autoIncrement); + if (auto* context = scriptExecutionContext()) + context->eventQueue().enqueueEvent(WTFMove(errorEvent)); - IDBObjectStoreMetadata metadata(name, objectStoreId, keyPath, autoIncrement, IDBDatabaseBackend::MinimumIndexId); - RefPtr objectStore = IDBObjectStore::create(metadata, m_versionChangeTransaction.get()); - m_metadata.objectStores.set(metadata.id, metadata); - ++m_metadata.maxObjectStoreId; + auto closeEvent = Event::create(m_eventNames.closeEvent, true, false); + closeEvent->setTarget(this); - m_versionChangeTransaction->objectStoreCreated(name, objectStore); - return objectStore.release(); + if (auto* context = scriptExecutionContext()) + context->eventQueue().enqueueEvent(WTFMove(closeEvent)); } -void IDBDatabase::deleteObjectStore(const String& name, ExceptionCode& ec) +void IDBDatabase::maybeCloseInServer() { - LOG(StorageAPI, "IDBDatabase::deleteObjectStore"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.FrontEndAPICalls", IDBDeleteObjectStoreCall, IDBMethodsMax); - if (!m_versionChangeTransaction) { - ec = IDBDatabaseException::InvalidStateError; - return; - } - if (!m_versionChangeTransaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; + LOG(IndexedDB, "IDBDatabase::maybeCloseInServer - %" PRIu64, m_databaseConnectionIdentifier); + + ASSERT(currentThread() == originThreadID()); + + if (m_closedInServer) return; - } - int64_t objectStoreId = findObjectStoreId(name); - if (objectStoreId == IDBObjectStoreMetadata::InvalidId) { - ec = IDBDatabaseException::NotFoundError; + // 3.3.9 Database closing steps + // Wait for all transactions created using this connection to complete. + // Once they are complete, this connection is closed. + if (!m_activeTransactions.isEmpty() || !m_committingTransactions.isEmpty()) return; - } - m_backend->deleteObjectStore(m_versionChangeTransaction->id(), objectStoreId); - m_versionChangeTransaction->objectStoreDeleted(name); - m_metadata.objectStores.remove(objectStoreId); + m_closedInServer = true; + m_connectionProxy->databaseConnectionClosed(*this); } -PassRefPtr IDBDatabase::transaction(ScriptExecutionContext* context, const Vector& scope, const String& modeString, ExceptionCode& ec) +const char* IDBDatabase::activeDOMObjectName() const { - LOG(StorageAPI, "IDBDatabase::transaction"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.FrontEndAPICalls", IDBTransactionCall, IDBMethodsMax); - if (!scope.size()) { - ec = IDBDatabaseException::InvalidAccessError; - return 0; - } + ASSERT(currentThread() == originThreadID()); + return "IDBDatabase"; +} - IndexedDB::TransactionMode mode = IDBTransaction::stringToMode(modeString, ec); - if (ec) - return 0; +bool IDBDatabase::canSuspendForDocumentSuspension() const +{ + ASSERT(currentThread() == originThreadID()); - if (m_versionChangeTransaction || m_closePending) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } + // FIXME: This value will sometimes be false when database operations are actually in progress. + // Such database operations do not yet exist. + return true; +} + +void IDBDatabase::stop() +{ + LOG(IndexedDB, "IDBDatabase::stop - %" PRIu64, m_databaseConnectionIdentifier); - Vector objectStoreIds; - for (size_t i = 0; i < scope.size(); ++i) { - int64_t objectStoreId = findObjectStoreId(scope[i]); - if (objectStoreId == IDBObjectStoreMetadata::InvalidId) { - ec = IDBDatabaseException::NotFoundError; - return 0; - } - objectStoreIds.append(objectStoreId); + ASSERT(currentThread() == originThreadID()); + + removeAllEventListeners(); + + Vector transactionIdentifiers; + transactionIdentifiers.reserveInitialCapacity(m_activeTransactions.size()); + + for (auto& id : m_activeTransactions.keys()) + transactionIdentifiers.uncheckedAppend(id); + + for (auto& id : transactionIdentifiers) { + IDBTransaction* transaction = m_activeTransactions.get(id); + if (transaction) + transaction->stop(); } - int64_t transactionId = nextTransactionId(); - m_backend->createTransaction(transactionId, m_databaseCallbacks, objectStoreIds, mode); + close(); +} + +Ref IDBDatabase::startVersionChangeTransaction(const IDBTransactionInfo& info, IDBOpenDBRequest& request) +{ + LOG(IndexedDB, "IDBDatabase::startVersionChangeTransaction %s", info.identifier().loggingString().utf8().data()); - RefPtr transaction = IDBTransaction::create(context, transactionId, scope, mode, this); - return transaction.release(); + ASSERT(currentThread() == originThreadID()); + ASSERT(!m_versionChangeTransaction); + ASSERT(info.mode() == IDBTransactionMode::Versionchange); + ASSERT(!m_closePending); + ASSERT(scriptExecutionContext()); + + Ref transaction = IDBTransaction::create(*this, info, request); + m_versionChangeTransaction = &transaction.get(); + + m_activeTransactions.set(transaction->info().identifier(), &transaction.get()); + + return transaction; } -PassRefPtr IDBDatabase::transaction(ScriptExecutionContext* context, const String& storeName, const String& mode, ExceptionCode& ec) +void IDBDatabase::didStartTransaction(IDBTransaction& transaction) { - RefPtr storeNames = DOMStringList::create(); - storeNames->append(storeName); - return transaction(context, storeNames, mode, ec); + LOG(IndexedDB, "IDBDatabase::didStartTransaction %s", transaction.info().identifier().loggingString().utf8().data()); + ASSERT(!m_versionChangeTransaction); + ASSERT(currentThread() == originThreadID()); + + // It is possible for the client to have aborted a transaction before the server replies back that it has started. + if (m_abortingTransactions.contains(transaction.info().identifier())) + return; + + m_activeTransactions.set(transaction.info().identifier(), &transaction); } -void IDBDatabase::forceClose() +void IDBDatabase::willCommitTransaction(IDBTransaction& transaction) { - for (TransactionMap::const_iterator::Values it = m_transactions.begin().values(), end = m_transactions.end().values(); it != end; ++it) - (*it)->abort(IGNORE_EXCEPTION); - this->close(); + LOG(IndexedDB, "IDBDatabase::willCommitTransaction %s", transaction.info().identifier().loggingString().utf8().data()); + + ASSERT(currentThread() == originThreadID()); + + auto refTransaction = m_activeTransactions.take(transaction.info().identifier()); + ASSERT(refTransaction); + m_committingTransactions.set(transaction.info().identifier(), WTFMove(refTransaction)); } -void IDBDatabase::close() +void IDBDatabase::didCommitTransaction(IDBTransaction& transaction) { - LOG(StorageAPI, "IDBDatabase::close"); - if (m_closePending) - return; + LOG(IndexedDB, "IDBDatabase::didCommitTransaction %s", transaction.info().identifier().loggingString().utf8().data()); - m_closePending = true; + ASSERT(currentThread() == originThreadID()); + + if (m_versionChangeTransaction == &transaction) + m_info.setVersion(transaction.info().newVersion()); - if (m_transactions.isEmpty()) - closeConnection(); + didCommitOrAbortTransaction(transaction); } -void IDBDatabase::closeConnection() +void IDBDatabase::willAbortTransaction(IDBTransaction& transaction) { - ASSERT(m_closePending); - ASSERT(m_transactions.isEmpty()); + LOG(IndexedDB, "IDBDatabase::willAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data()); - // Closing may result in deallocating the last transaction, which could result in deleting - // this IDBDatabase. We need the deallocation to happen after we are through. - Ref protect(*this); + ASSERT(currentThread() == originThreadID()); - m_backend->close(m_databaseCallbacks); + auto refTransaction = m_activeTransactions.take(transaction.info().identifier()); + if (!refTransaction) + refTransaction = m_committingTransactions.take(transaction.info().identifier()); - if (m_contextStopped || !scriptExecutionContext()) - return; + ASSERT(refTransaction); + m_abortingTransactions.set(transaction.info().identifier(), WTFMove(refTransaction)); - EventQueue& eventQueue = scriptExecutionContext()->eventQueue(); - // Remove any pending versionchange events scheduled to fire on this - // connection. They would have been scheduled by the backend when another - // connection called setVersion, but the frontend connection is being - // closed before they could fire. - for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { - bool removed = eventQueue.cancelEvent(*m_enqueuedEvents[i]); - ASSERT_UNUSED(removed, removed); + if (transaction.isVersionChange()) { + ASSERT(transaction.originalDatabaseInfo()); + m_info = *transaction.originalDatabaseInfo(); + m_closePending = true; } } -void IDBDatabase::onVersionChange(uint64_t oldVersion, uint64_t newVersion, IndexedDB::VersionNullness newVersionNullness) +void IDBDatabase::didAbortTransaction(IDBTransaction& transaction) { - LOG(StorageAPI, "IDBDatabase::onVersionChange"); - if (m_contextStopped || !scriptExecutionContext()) - return; + LOG(IndexedDB, "IDBDatabase::didAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data()); - if (m_closePending) - return; + ASSERT(currentThread() == originThreadID()); + + if (transaction.isVersionChange()) { + ASSERT(transaction.originalDatabaseInfo()); + ASSERT(m_info.version() == transaction.originalDatabaseInfo()->version()); + m_closePending = true; + maybeCloseInServer(); + } - enqueueEvent(IDBVersionChangeEvent::create(oldVersion, newVersion, newVersionNullness)); + didCommitOrAbortTransaction(transaction); } -void IDBDatabase::enqueueEvent(PassRefPtr event) +void IDBDatabase::didCommitOrAbortTransaction(IDBTransaction& transaction) { - ASSERT(!m_contextStopped); - ASSERT(scriptExecutionContext()); - event->setTarget(this); - scriptExecutionContext()->eventQueue().enqueueEvent(event.get()); - m_enqueuedEvents.append(event); + LOG(IndexedDB, "IDBDatabase::didCommitOrAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data()); + + ASSERT(currentThread() == originThreadID()); + + if (m_versionChangeTransaction == &transaction) + m_versionChangeTransaction = nullptr; + +#ifndef NDBEBUG + unsigned count = 0; + if (m_activeTransactions.contains(transaction.info().identifier())) + ++count; + if (m_committingTransactions.contains(transaction.info().identifier())) + ++count; + if (m_abortingTransactions.contains(transaction.info().identifier())) + ++count; + + ASSERT(count == 1); +#endif + + m_activeTransactions.remove(transaction.info().identifier()); + m_committingTransactions.remove(transaction.info().identifier()); + m_abortingTransactions.remove(transaction.info().identifier()); + + if (m_closePending) + maybeCloseInServer(); } -bool IDBDatabase::dispatchEvent(PassRefPtr event) +void IDBDatabase::fireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) { - LOG(StorageAPI, "IDBDatabase::dispatchEvent"); - ASSERT(event->type() == eventNames().versionchangeEvent); - for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { - if (m_enqueuedEvents[i].get() == event.get()) - m_enqueuedEvents.remove(i); + uint64_t currentVersion = m_info.version(); + LOG(IndexedDB, "IDBDatabase::fireVersionChangeEvent - current version %" PRIu64 ", requested version %" PRIu64 ", connection %" PRIu64 " (%p)", currentVersion, requestedVersion, m_databaseConnectionIdentifier, this); + + ASSERT(currentThread() == originThreadID()); + + if (!scriptExecutionContext() || m_closePending) { + connectionProxy().didFireVersionChangeEvent(m_databaseConnectionIdentifier, requestIdentifier); + return; } - return EventTarget::dispatchEvent(event.get()); + + Ref event = IDBVersionChangeEvent::create(requestIdentifier, currentVersion, requestedVersion, m_eventNames.versionchangeEvent); + event->setTarget(this); + scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event)); } -int64_t IDBDatabase::findObjectStoreId(const String& name) const +bool IDBDatabase::dispatchEvent(Event& event) { - for (IDBDatabaseMetadata::ObjectStoreMap::const_iterator it = m_metadata.objectStores.begin(); it != m_metadata.objectStores.end(); ++it) { - if (it->value.name == name) { - ASSERT(it->key != IDBObjectStoreMetadata::InvalidId); - return it->key; - } - } - return IDBObjectStoreMetadata::InvalidId; + LOG(IndexedDB, "IDBDatabase::dispatchEvent (%" PRIu64 ") (%p)", m_databaseConnectionIdentifier, this); + ASSERT(currentThread() == originThreadID()); + + bool result = EventTargetWithInlineData::dispatchEvent(event); + + if (event.isVersionChangeEvent() && event.type() == m_eventNames.versionchangeEvent) + connectionProxy().didFireVersionChangeEvent(m_databaseConnectionIdentifier, downcast(event).requestIdentifier()); + + return result; } -bool IDBDatabase::hasPendingActivity() const +void IDBDatabase::didCreateIndexInfo(const IDBIndexInfo& info) { - // The script wrapper must not be collected before the object is closed or - // we can't fire a "versionchange" event to let script manually close the connection. - return !m_closePending && hasEventListeners() && !m_contextStopped; + ASSERT(currentThread() == originThreadID()); + + auto* objectStore = m_info.infoForExistingObjectStore(info.objectStoreIdentifier()); + ASSERT(objectStore); + objectStore->addExistingIndex(info); } -void IDBDatabase::stop() +void IDBDatabase::didDeleteIndexInfo(const IDBIndexInfo& info) { - // Stop fires at a deterministic time, so we need to call close in it. - close(); + ASSERT(currentThread() == originThreadID()); - m_contextStopped = true; + auto* objectStore = m_info.infoForExistingObjectStore(info.objectStoreIdentifier()); + ASSERT(objectStore); + objectStore->deleteIndex(info.name()); } } // namespace WebCore diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabase.h b/Source/WebCore/Modules/indexeddb/IDBDatabase.h index 5cf6032c5..56b94c6c0 100644 --- a/Source/WebCore/Modules/indexeddb/IDBDatabase.h +++ b/Source/WebCore/Modules/indexeddb/IDBDatabase.h @@ -1,143 +1,138 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBDatabase_h -#define IDBDatabase_h - -#include "ActiveDOMObject.h" -#include "DOMStringList.h" -#include "Dictionary.h" -#include "Event.h" -#include "EventTarget.h" -#include "IDBDatabaseCallbacks.h" -#include "IDBDatabaseMetadata.h" -#include "IDBObjectStore.h" -#include "IDBTransaction.h" -#include "IndexedDB.h" -#include "ScriptWrappable.h" -#include -#include -#include +#pragma once #if ENABLE(INDEXED_DATABASE) +#include "EventTarget.h" +#include "IDBActiveDOMObject.h" +#include "IDBConnectionProxy.h" +#include "IDBConnectionToServer.h" +#include "IDBDatabaseInfo.h" +#include "IDBKeyPath.h" +#include "IDBTransactionMode.h" + namespace WebCore { -class ScriptExecutionContext; +class DOMStringList; +class IDBObjectStore; +class IDBOpenDBRequest; +class IDBResultData; +class IDBTransaction; +class IDBTransactionInfo; -typedef int ExceptionCode; +struct EventNames; -class IDBDatabase final : public RefCounted, public ScriptWrappable, public EventTargetWithInlineData, public ActiveDOMObject { +class IDBDatabase : public ThreadSafeRefCounted, public EventTargetWithInlineData, public IDBActiveDOMObject { public: - static PassRefPtr create(ScriptExecutionContext*, PassRefPtr, PassRefPtr); - ~IDBDatabase(); + static Ref create(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBResultData&); - void setMetadata(const IDBDatabaseMetadata& metadata) { m_metadata = metadata; } - void transactionCreated(IDBTransaction*); - void transactionFinished(IDBTransaction*); + virtual ~IDBDatabase(); - // Implement the IDL - const String name() const { return m_metadata.name; } + // IDBDatabase IDL + const String name() const; uint64_t version() const; - PassRefPtr objectStoreNames() const; - - PassRefPtr createObjectStore(const String& name, const Dictionary&, ExceptionCode&); - PassRefPtr createObjectStore(const String& name, const IDBKeyPath&, bool autoIncrement, ExceptionCode&); - PassRefPtr transaction(ScriptExecutionContext* context, PassRefPtr scope, const String& mode, ExceptionCode& ec) { return transaction(context, *scope, mode, ec); } - PassRefPtr transaction(ScriptExecutionContext*, const Vector&, const String& mode, ExceptionCode&); - PassRefPtr transaction(ScriptExecutionContext*, const String&, const String& mode, ExceptionCode&); - void deleteObjectStore(const String& name, ExceptionCode&); - void close(); + RefPtr objectStoreNames() const; + + struct ObjectStoreParameters { + std::optional keyPath; + bool autoIncrement; + }; - DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); - DEFINE_ATTRIBUTE_EVENT_LISTENER(error); - DEFINE_ATTRIBUTE_EVENT_LISTENER(versionchange); + ExceptionOr> createObjectStore(const String& name, ObjectStoreParameters&&); - // IDBDatabaseCallbacks - virtual void onVersionChange(uint64_t oldVersion, uint64_t newVersion, IndexedDB::VersionNullness newVersionNullness); - virtual void onAbort(int64_t, PassRefPtr); - virtual void onComplete(int64_t); + using StringOrVectorOfStrings = WTF::Variant>; + ExceptionOr> transaction(StringOrVectorOfStrings&& storeNames, IDBTransactionMode); + ExceptionOr deleteObjectStore(const String& name); + void close(); - // ActiveDOMObject - virtual bool hasPendingActivity() const override; + void renameObjectStore(IDBObjectStore&, const String& newName); + void renameIndex(IDBIndex&, const String& newName); // EventTarget - virtual EventTargetInterface eventTargetInterface() const override final { return IDBDatabaseEventTargetInterfaceType; } - virtual ScriptExecutionContext* scriptExecutionContext() const override final { return ActiveDOMObject::scriptExecutionContext(); } + EventTargetInterface eventTargetInterface() const final { return IDBDatabaseEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); } + void refEventTarget() final { ThreadSafeRefCounted::ref(); } + void derefEventTarget() final { ThreadSafeRefCounted::deref(); } - bool isClosePending() const { return m_closePending; } - void forceClose(); - const IDBDatabaseMetadata metadata() const { return m_metadata; } - void enqueueEvent(PassRefPtr); + using ThreadSafeRefCounted::ref; + using ThreadSafeRefCounted::deref; - using EventTarget::dispatchEvent; - virtual bool dispatchEvent(PassRefPtr) override; + const char* activeDOMObjectName() const final; + bool canSuspendForDocumentSuspension() const final; + void stop() final; - int64_t findObjectStoreId(const String& name) const; - bool containsObjectStore(const String& name) const - { - return findObjectStoreId(name) != IDBObjectStoreMetadata::InvalidId; - } + const IDBDatabaseInfo& info() const { return m_info; } + uint64_t databaseConnectionIdentifier() const { return m_databaseConnectionIdentifier; } - IDBDatabaseBackend* backend() const { return m_backend.get(); } + Ref startVersionChangeTransaction(const IDBTransactionInfo&, IDBOpenDBRequest&); + void didStartTransaction(IDBTransaction&); - static int64_t nextTransactionId(); + void willCommitTransaction(IDBTransaction&); + void didCommitTransaction(IDBTransaction&); + void willAbortTransaction(IDBTransaction&); + void didAbortTransaction(IDBTransaction&); - using RefCounted::ref; - using RefCounted::deref; + void fireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion); + void didCloseFromServer(const IDBError&); + void connectionToServerLost(const IDBError&); -private: - IDBDatabase(ScriptExecutionContext*, PassRefPtr, PassRefPtr); + IDBClient::IDBConnectionProxy& connectionProxy() { return m_connectionProxy.get(); } - // ActiveDOMObject - virtual void stop() override; + void didCreateIndexInfo(const IDBIndexInfo&); + void didDeleteIndexInfo(const IDBIndexInfo&); - // EventTarget - virtual void refEventTarget() override final { ref(); } - virtual void derefEventTarget() override final { deref(); } + bool isClosingOrClosed() const { return m_closePending || m_closedInServer; } - void closeConnection(); + bool dispatchEvent(Event&) final; - IDBDatabaseMetadata m_metadata; - RefPtr m_backend; - RefPtr m_versionChangeTransaction; - typedef HashMap TransactionMap; - TransactionMap m_transactions; + bool hasPendingActivity() const final; + +private: + IDBDatabase(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBResultData&); - bool m_closePending; - bool m_contextStopped; + void didCommitOrAbortTransaction(IDBTransaction&); - // Keep track of the versionchange events waiting to be fired on this - // database so that we can cancel them if the database closes. - Vector> m_enqueuedEvents; + void maybeCloseInServer(); - RefPtr m_databaseCallbacks; + Ref m_connectionProxy; + IDBDatabaseInfo m_info; + uint64_t m_databaseConnectionIdentifier { 0 }; + + bool m_closePending { false }; + bool m_closedInServer { false }; + + RefPtr m_versionChangeTransaction; + HashMap> m_activeTransactions; + HashMap> m_committingTransactions; + HashMap> m_abortingTransactions; + + const EventNames& m_eventNames; // Need to cache this so we can use it from GC threads. }; } // namespace WebCore -#endif - -#endif // IDBDatabase_h +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabase.idl b/Source/WebCore/Modules/indexeddb/IDBDatabase.idl index 0b5e57e4e..3241c636b 100644 --- a/Source/WebCore/Modules/indexeddb/IDBDatabase.idl +++ b/Source/WebCore/Modules/indexeddb/IDBDatabase.idl @@ -27,24 +27,29 @@ [ Conditional=INDEXED_DATABASE, ActiveDOMObject, - EventTarget, - JSNoStaticTables, - JSGenerateToJSObject, - JSGenerateToNativeObject, + SkipVTableValidation, ] interface IDBDatabase : EventTarget { readonly attribute DOMString name; readonly attribute unsigned long long version; readonly attribute DOMStringList objectStoreNames; - [Custom, RaisesException] IDBObjectStore createObjectStore(DOMString name, optional Dictionary options); - [RaisesException] void deleteObjectStore(DOMString name); - [CallWith=ScriptExecutionContext, RaisesException] IDBTransaction transaction(DOMString storeName, [Default=NullString] optional DOMString mode); - [CallWith=ScriptExecutionContext, RaisesException] IDBTransaction transaction(sequence storeNames, [Default=NullString] optional DOMString mode); - void close(); + [MayThrowException] IDBObjectStore createObjectStore(DOMString name, optional IDBObjectStoreParameters parameters); + [MayThrowException] void deleteObjectStore(DOMString name); + + [MayThrowException] IDBTransaction transaction((DOMString or sequence) storeNames, optional IDBTransactionMode mode = "readonly"); - attribute EventListener onabort; - attribute EventListener onerror; - attribute EventListener onversionchange; + // FIXME: This is not part of the spec, but is needed for compatibility. + // See https://github.com/w3c/IndexedDB/issues/85 + [MayThrowException] IDBTransaction transaction(DOMStringList storeNames, optional IDBTransactionMode mode = "readonly"); + void close(); + attribute EventHandler onabort; + attribute EventHandler onclose; + attribute EventHandler onerror; + attribute EventHandler onversionchange; }; +dictionary IDBObjectStoreParameters { + (DOMString or sequence)? keyPath = null; + boolean autoIncrement = false; +}; diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.cpp b/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.cpp deleted file mode 100644 index 3cef3f3cd..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.cpp +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBDatabaseBackend.h" - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBCursorBackend.h" -#include "IDBDatabaseCallbacks.h" -#include "IDBDatabaseException.h" -#include "IDBFactoryBackendInterface.h" -#include "IDBKeyRange.h" -#include "IDBRecordIdentifier.h" -#include "IDBServerConnection.h" -#include "IDBTransactionBackend.h" -#include "IDBTransactionCoordinator.h" -#include "Logging.h" -#include "SharedBuffer.h" -#include - -namespace WebCore { - -PassRefPtr IDBDatabaseBackend::create(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface* factory, IDBServerConnection& serverConnection) -{ - RefPtr backend = adoptRef(new IDBDatabaseBackend(name, uniqueIdentifier, factory, serverConnection)); - backend->openInternalAsync(); - - return backend.release(); -} - -IDBDatabaseBackend::IDBDatabaseBackend(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface* factory, IDBServerConnection& serverConnection) - : m_metadata(name, InvalidId, 0, InvalidId) - , m_identifier(uniqueIdentifier) - , m_factory(factory) - , m_serverConnection(serverConnection) - , m_transactionCoordinator(IDBTransactionCoordinator::create()) - , m_closingConnection(false) -{ - ASSERT(!m_metadata.name.isNull()); -} - -void IDBDatabaseBackend::addObjectStore(const IDBObjectStoreMetadata& objectStore, int64_t newMaxObjectStoreId) -{ - ASSERT(!m_metadata.objectStores.contains(objectStore.id)); - if (newMaxObjectStoreId != IDBObjectStoreMetadata::InvalidId) { - ASSERT(m_metadata.maxObjectStoreId < newMaxObjectStoreId); - m_metadata.maxObjectStoreId = newMaxObjectStoreId; - } - m_metadata.objectStores.set(objectStore.id, objectStore); -} - -void IDBDatabaseBackend::removeObjectStore(int64_t objectStoreId) -{ - ASSERT(m_metadata.objectStores.contains(objectStoreId)); - m_metadata.objectStores.remove(objectStoreId); -} - -void IDBDatabaseBackend::addIndex(int64_t objectStoreId, const IDBIndexMetadata& index, int64_t newMaxIndexId) -{ - ASSERT(m_metadata.objectStores.contains(objectStoreId)); - IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId); - - ASSERT(!objectStore.indexes.contains(index.id)); - objectStore.indexes.set(index.id, index); - if (newMaxIndexId != IDBIndexMetadata::InvalidId) { - ASSERT(objectStore.maxIndexId < newMaxIndexId); - objectStore.maxIndexId = newMaxIndexId; - } - m_metadata.objectStores.set(objectStoreId, objectStore); -} - -void IDBDatabaseBackend::removeIndex(int64_t objectStoreId, int64_t indexId) -{ - ASSERT(m_metadata.objectStores.contains(objectStoreId)); - IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId); - - ASSERT(objectStore.indexes.contains(indexId)); - objectStore.indexes.remove(indexId); - m_metadata.objectStores.set(objectStoreId, objectStore); -} - -void IDBDatabaseBackend::openInternalAsync() -{ - RefPtr self = this; - m_serverConnection->getOrEstablishIDBDatabaseMetadata([self](const IDBDatabaseMetadata& metadata, bool success) { - self->didOpenInternalAsync(metadata, success); - }); -} - -void IDBDatabaseBackend::didOpenInternalAsync(const IDBDatabaseMetadata& metadata, bool success) -{ - if (!success) { - processPendingOpenCalls(false); - return; - } - - m_metadata = metadata; - - processPendingCalls(); -} - -IDBDatabaseBackend::~IDBDatabaseBackend() -{ - m_factory->removeIDBDatabaseBackend(m_identifier); -} - -void IDBDatabaseBackend::createObjectStore(int64_t transactionId, int64_t objectStoreId, const String& name, const IDBKeyPath& keyPath, bool autoIncrement) -{ - LOG(StorageAPI, "IDBDatabaseBackend::createObjectStore"); - IDBTransactionBackend* transaction = m_transactions.get(transactionId); - if (!transaction) - return; - ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange); - - ASSERT(!m_metadata.objectStores.contains(objectStoreId)); - IDBObjectStoreMetadata objectStoreMetadata(name, objectStoreId, keyPath, autoIncrement, IDBDatabaseBackend::MinimumIndexId); - - transaction->scheduleCreateObjectStoreOperation(objectStoreMetadata); - addObjectStore(objectStoreMetadata, objectStoreId); -} - -void IDBDatabaseBackend::deleteObjectStore(int64_t transactionId, int64_t objectStoreId) -{ - LOG(StorageAPI, "IDBDatabaseBackend::deleteObjectStore"); - IDBTransactionBackend* transaction = m_transactions.get(transactionId); - if (!transaction) - return; - ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange); - - ASSERT(m_metadata.objectStores.contains(objectStoreId)); - const IDBObjectStoreMetadata& objectStoreMetadata = m_metadata.objectStores.get(objectStoreId); - - transaction->scheduleDeleteObjectStoreOperation(objectStoreMetadata); - removeObjectStore(objectStoreId); -} - -void IDBDatabaseBackend::createIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry) -{ - LOG(StorageAPI, "IDBDatabaseBackend::createIndex"); - IDBTransactionBackend* transaction = m_transactions.get(transactionId); - if (!transaction) - return; - ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange); - - ASSERT(m_metadata.objectStores.contains(objectStoreId)); - const IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId); - - ASSERT(!objectStore.indexes.contains(indexId)); - const IDBIndexMetadata indexMetadata(name, indexId, keyPath, unique, multiEntry); - - transaction->scheduleCreateIndexOperation(objectStoreId, indexMetadata); - - addIndex(objectStoreId, indexMetadata, indexId); -} - -void IDBDatabaseBackend::deleteIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId) -{ - LOG(StorageAPI, "IDBDatabaseBackend::deleteIndex"); - IDBTransactionBackend* transaction = m_transactions.get(transactionId); - if (!transaction) - return; - ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange); - - ASSERT(m_metadata.objectStores.contains(objectStoreId)); - const IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId); - - ASSERT(objectStore.indexes.contains(indexId)); - const IDBIndexMetadata& indexMetadata = objectStore.indexes.get(indexId); - - transaction->scheduleDeleteIndexOperation(objectStoreId, indexMetadata); - - removeIndex(objectStoreId, indexId); -} - -void IDBDatabaseBackend::commit(int64_t transactionId) -{ - // The frontend suggests that we commit, but we may have previously initiated an abort, and so have disposed of the transaction. onAbort has already been dispatched to the frontend, so it will find out about that asynchronously. - if (m_transactions.contains(transactionId)) - m_transactions.get(transactionId)->commit(); -} - -void IDBDatabaseBackend::abort(int64_t transactionId) -{ - // If the transaction is unknown, then it has already been aborted by the backend before this call so it is safe to ignore it. - if (m_transactions.contains(transactionId)) - m_transactions.get(transactionId)->abort(); -} - -void IDBDatabaseBackend::abort(int64_t transactionId, PassRefPtr error) -{ - // If the transaction is unknown, then it has already been aborted by the backend before this call so it is safe to ignore it. - if (m_transactions.contains(transactionId)) - m_transactions.get(transactionId)->abort(error); -} - -void IDBDatabaseBackend::get(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, bool keyOnly, PassRefPtr callbacks) -{ - LOG(StorageAPI, "IDBDatabaseBackend::get"); - IDBTransactionBackend* transaction = m_transactions.get(transactionId); - if (!transaction) - return; - - transaction->scheduleGetOperation(m_metadata, objectStoreId, indexId, keyRange, keyOnly ? IndexedDB::CursorType::KeyOnly : IndexedDB::CursorType::KeyAndValue, callbacks); -} - -void IDBDatabaseBackend::put(int64_t transactionId, int64_t objectStoreId, PassRefPtr value, PassRefPtr key, PutMode putMode, PassRefPtr callbacks, const Vector& indexIds, const Vector& indexKeys) -{ - LOG(StorageAPI, "IDBDatabaseBackend::put"); - IDBTransactionBackend* transaction = m_transactions.get(transactionId); - if (!transaction) - return; - ASSERT(transaction->mode() != IndexedDB::TransactionMode::ReadOnly); - - const IDBObjectStoreMetadata objectStoreMetadata = m_metadata.objectStores.get(objectStoreId); - - ASSERT(objectStoreMetadata.autoIncrement || key.get()); - - transaction->schedulePutOperation(objectStoreMetadata, value, key, putMode, callbacks, indexIds, indexKeys); -} - -void IDBDatabaseBackend::setIndexKeys(int64_t transactionID, int64_t objectStoreID, PassRefPtr prpPrimaryKey, const Vector& indexIDs, const Vector& indexKeys) -{ - LOG(StorageAPI, "IDBDatabaseBackend::setIndexKeys"); - ASSERT(prpPrimaryKey); - ASSERT(m_metadata.objectStores.contains(objectStoreID)); - - RefPtr transaction = m_transactions.get(transactionID); - if (!transaction) - return; - ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange); - - RefPtr primaryKey = prpPrimaryKey; - m_serverConnection->setIndexKeys(transactionID, m_metadata.id, objectStoreID, m_metadata.objectStores.get(objectStoreID), *primaryKey, indexIDs, indexKeys, [transaction](PassRefPtr error) { - if (error) - transaction->abort(error); - }); -} - -void IDBDatabaseBackend::setIndexesReady(int64_t transactionId, int64_t, const Vector& indexIds) -{ - LOG(StorageAPI, "IDBDatabaseBackend::setIndexesReady"); - - IDBTransactionBackend* transaction = m_transactions.get(transactionId); - if (!transaction) - return; - - transaction->scheduleSetIndexesReadyOperation(indexIds.size()); -} - -void IDBDatabaseBackend::openCursor(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, IndexedDB::CursorDirection direction, bool keyOnly, TaskType taskType, PassRefPtr callbacks) -{ - LOG(StorageAPI, "IDBDatabaseBackend::openCursor"); - IDBTransactionBackend* transaction = m_transactions.get(transactionId); - if (!transaction) - return; - - transaction->scheduleOpenCursorOperation(objectStoreId, indexId, keyRange, direction, keyOnly ? IndexedDB::CursorType::KeyOnly : IndexedDB::CursorType::KeyAndValue, taskType, callbacks); -} - -void IDBDatabaseBackend::count(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, PassRefPtr callbacks) -{ - LOG(StorageAPI, "IDBDatabaseBackend::count"); - IDBTransactionBackend* transaction = m_transactions.get(transactionId); - if (!transaction) - return; - - ASSERT(m_metadata.objectStores.contains(objectStoreId)); - transaction->scheduleCountOperation(objectStoreId, indexId, keyRange, callbacks); -} - - -void IDBDatabaseBackend::deleteRange(int64_t transactionId, int64_t objectStoreId, PassRefPtr keyRange, PassRefPtr callbacks) -{ - LOG(StorageAPI, "IDBDatabaseBackend::deleteRange"); - IDBTransactionBackend* transaction = m_transactions.get(transactionId); - if (!transaction) - return; - ASSERT(transaction->mode() != IndexedDB::TransactionMode::ReadOnly); - - transaction->scheduleDeleteRangeOperation(objectStoreId, keyRange, callbacks); -} - -void IDBDatabaseBackend::clearObjectStore(int64_t transactionId, int64_t objectStoreId, PassRefPtr callbacks) -{ - LOG(StorageAPI, "IDBDatabaseBackend::clear"); - IDBTransactionBackend* transaction = m_transactions.get(transactionId); - if (!transaction) - return; - ASSERT(transaction->mode() != IndexedDB::TransactionMode::ReadOnly); - - transaction->scheduleClearObjectStoreOperation(objectStoreId, callbacks); -} - -void IDBDatabaseBackend::transactionStarted(IDBTransactionBackend* transaction) -{ - if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) { - ASSERT(!m_runningVersionChangeTransaction); - m_runningVersionChangeTransaction = transaction; - } -} - -void IDBDatabaseBackend::transactionFinished(IDBTransactionBackend* rawTransaction) -{ - RefPtr transaction = rawTransaction; - ASSERT(m_transactions.contains(transaction->id())); - ASSERT(m_transactions.get(transaction->id()) == transaction.get()); - m_transactions.remove(transaction->id()); - if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) { - ASSERT(transaction.get() == m_runningVersionChangeTransaction.get()); - m_runningVersionChangeTransaction.clear(); - } -} - -void IDBDatabaseBackend::transactionFinishedAndAbortFired(IDBTransactionBackend* rawTransaction) -{ - RefPtr transaction = rawTransaction; - if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) { - // If this was an open-with-version call, there will be a "second - // half" open call waiting for us in processPendingCalls. - // FIXME: When we no longer support setVersion, assert such a thing. - if (m_pendingSecondHalfOpen) { - m_pendingSecondHalfOpen->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "Version change transaction was aborted in upgradeneeded event handler.")); - m_pendingSecondHalfOpen.release(); - } - processPendingCalls(); - } -} - -void IDBDatabaseBackend::transactionFinishedAndCompleteFired(IDBTransactionBackend* rawTransaction) -{ - RefPtr transaction = rawTransaction; - if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) - processPendingCalls(); -} - -size_t IDBDatabaseBackend::connectionCount() -{ - // This does not include pending open calls, as those should not block version changes and deletes. - return m_databaseCallbacksSet.size(); -} - -void IDBDatabaseBackend::processPendingCalls() -{ - if (m_pendingSecondHalfOpen) { - ASSERT(m_pendingSecondHalfOpen->version() == m_metadata.version); - ASSERT(m_metadata.id != InvalidId); - m_pendingSecondHalfOpen->callbacks()->onSuccess(this, this->metadata()); - m_pendingSecondHalfOpen.release(); - // Fall through when complete, as pending deletes may be (partially) unblocked. - } - - // Note that this check is only an optimization to reduce queue-churn and - // not necessary for correctness; deleteDatabase and openConnection will - // requeue their calls if this condition is true. - if (m_runningVersionChangeTransaction) - return; - - if (!m_pendingDeleteCalls.isEmpty() && isDeleteDatabaseBlocked()) - return; - while (!m_pendingDeleteCalls.isEmpty()) { - OwnPtr pendingDeleteCall = m_pendingDeleteCalls.takeFirst(); - m_deleteCallbacksWaitingCompletion.add(pendingDeleteCall->callbacks()); - deleteDatabaseAsync(pendingDeleteCall->callbacks()); - } - - // deleteDatabaseAsync should never re-queue calls. - ASSERT(m_pendingDeleteCalls.isEmpty()); - - // If there are any database deletions waiting for completion, we're done for now. - // Further callbacks will be handled in a future call to processPendingCalls(). - if (!m_deleteCallbacksWaitingCompletion.isEmpty()) - return; - - if (m_runningVersionChangeTransaction) - return; - - processPendingOpenCalls(true); -} - -void IDBDatabaseBackend::processPendingOpenCalls(bool success) -{ - // Open calls can be requeued if an open call started a version change transaction or deletes the database. - Deque> pendingOpenCalls; - m_pendingOpenCalls.swap(pendingOpenCalls); - - while (!pendingOpenCalls.isEmpty()) { - OwnPtr pendingOpenCall = pendingOpenCalls.takeFirst(); - if (success) { - if (m_metadata.id == InvalidId) { - // This database was deleted then quickly re-opened. - // openInternalAsync() will recreate it in the backing store and then resume processing pending callbacks. - pendingOpenCalls.prepend(pendingOpenCall.release()); - pendingOpenCalls.swap(m_pendingOpenCalls); - - openInternalAsync(); - return; - } - openConnectionInternal(pendingOpenCall->callbacks(), pendingOpenCall->databaseCallbacks(), pendingOpenCall->transactionId(), pendingOpenCall->version()); - } else { - String message; - if (pendingOpenCall->version() == IDBDatabaseMetadata::NoIntVersion) - message = "Internal error opening database with no version specified."; - else - message = String::format("Internal error opening database with version %llu", static_cast(pendingOpenCall->version())); - pendingOpenCall->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, message)); - } - } -} - -void IDBDatabaseBackend::createTransaction(int64_t transactionID, PassRefPtr callbacks, const Vector& objectStoreIDs, IndexedDB::TransactionMode mode) -{ - RefPtr transaction = IDBTransactionBackend::create(this, transactionID, callbacks, objectStoreIDs, mode); - - ASSERT(!m_transactions.contains(transactionID)); - m_transactions.add(transactionID, transaction.get()); -} - -void IDBDatabaseBackend::openConnection(PassRefPtr prpCallbacks, PassRefPtr prpDatabaseCallbacks, int64_t transactionId, uint64_t version) -{ - RefPtr callbacks = prpCallbacks; - RefPtr databaseCallbacks = prpDatabaseCallbacks; - - m_pendingOpenCalls.append(IDBPendingOpenCall::create(*callbacks, *databaseCallbacks, transactionId, version)); - - processPendingCalls(); -} - -void IDBDatabaseBackend::openConnectionInternal(PassRefPtr prpCallbacks, PassRefPtr prpDatabaseCallbacks, int64_t transactionId, uint64_t version) -{ - ASSERT(m_pendingDeleteCalls.isEmpty()); - ASSERT(!m_runningVersionChangeTransaction); - - RefPtr callbacks = prpCallbacks; - RefPtr databaseCallbacks = prpDatabaseCallbacks; - - // We infer that the database didn't exist from its lack of either type of version. - bool isNewDatabase = m_metadata.version == IDBDatabaseMetadata::NoIntVersion; - - if (version == IDBDatabaseMetadata::DefaultIntVersion) { - // FIXME: this comments was related to Chromium code. It may be incorrect - // For unit tests only - skip upgrade steps. Calling from script with DefaultIntVersion throws exception. - ASSERT(isNewDatabase); - m_databaseCallbacksSet.add(databaseCallbacks); - callbacks->onSuccess(this, this->metadata()); - return; - } - - if (version == IDBDatabaseMetadata::NoIntVersion) { - if (!isNewDatabase) { - m_databaseCallbacksSet.add(RefPtr(databaseCallbacks)); - callbacks->onSuccess(this, this->metadata()); - return; - } - // Spec says: If no version is specified and no database exists, set database version to 1. - version = 1; - } - - if (version > m_metadata.version) { - runIntVersionChangeTransaction(callbacks, databaseCallbacks, transactionId, version); - return; - } - - if (version < m_metadata.version) { - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::VersionError, String::format("The requested version (%llu) is less than the existing version (%llu).", static_cast(version), static_cast(m_metadata.version)))); - return; - } - - ASSERT(version == m_metadata.version); - m_databaseCallbacksSet.add(databaseCallbacks); - callbacks->onSuccess(this, this->metadata()); -} - -void IDBDatabaseBackend::runIntVersionChangeTransaction(PassRefPtr prpCallbacks, PassRefPtr prpDatabaseCallbacks, int64_t transactionId, int64_t requestedVersion) -{ - RefPtr callbacks = prpCallbacks; - RefPtr databaseCallbacks = prpDatabaseCallbacks; - ASSERT(callbacks); - for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) { - // Front end ensures the event is not fired at connections that have closePending set. - if (*it != databaseCallbacks) - (*it)->onVersionChange(m_metadata.version, requestedVersion, IndexedDB::VersionNullness::Null); - } - // The spec dictates we wait until all the version change events are - // delivered and then check m_databaseCallbacks.empty() before proceeding - // or firing a blocked event, but instead we should be consistent with how - // the old setVersion (incorrectly) did it. - // FIXME: Remove the call to onBlocked and instead wait until the frontend - // tells us that all the blocked events have been delivered. See - // https://bugs.webkit.org/show_bug.cgi?id=71130 - if (connectionCount()) - callbacks->onBlocked(m_metadata.version); - // FIXME: Add test for m_runningVersionChangeTransaction. - if (m_runningVersionChangeTransaction || connectionCount()) { - m_pendingOpenCalls.append(IDBPendingOpenCall::create(*callbacks, *databaseCallbacks, transactionId, requestedVersion)); - return; - } - - Vector objectStoreIds; - createTransaction(transactionId, databaseCallbacks, objectStoreIds, IndexedDB::TransactionMode::VersionChange); - RefPtr transaction = m_transactions.get(transactionId); - - transaction->scheduleVersionChangeOperation(requestedVersion, callbacks, databaseCallbacks, m_metadata); - - ASSERT(!m_pendingSecondHalfOpen); - m_databaseCallbacksSet.add(databaseCallbacks); -} - -void IDBDatabaseBackend::deleteDatabase(PassRefPtr prpCallbacks) -{ - RefPtr callbacks = prpCallbacks; - if (isDeleteDatabaseBlocked()) { - for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) { - // Front end ensures the event is not fired at connections that have closePending set. - (*it)->onVersionChange(m_metadata.version, 0, IndexedDB::VersionNullness::Null); - } - // FIXME: Only fire onBlocked if there are open connections after the - // VersionChangeEvents are received, not just set up to fire. - // https://bugs.webkit.org/show_bug.cgi?id=71130 - callbacks->onBlocked(m_metadata.version); - m_pendingDeleteCalls.append(IDBPendingDeleteCall::create(callbacks.release())); - return; - } - deleteDatabaseAsync(callbacks.release()); -} - -bool IDBDatabaseBackend::isDeleteDatabaseBlocked() -{ - return connectionCount(); -} - -void IDBDatabaseBackend::deleteDatabaseAsync(PassRefPtr callbacks) -{ - ASSERT(!isDeleteDatabaseBlocked()); - - RefPtr self(this); - m_serverConnection->deleteDatabase(m_metadata.name, [self, callbacks](bool success) { - ASSERT(self->m_deleteCallbacksWaitingCompletion.contains(callbacks)); - self->m_deleteCallbacksWaitingCompletion.remove(callbacks); - - // If this IDBDatabaseBackend was closed while waiting for deleteDatabase to complete, no point in performing any callbacks. - if (!self->m_serverConnection->isClosed()) - return; - - if (success) { - self->m_metadata.id = InvalidId; - self->m_metadata.version = IDBDatabaseMetadata::NoIntVersion; - self->m_metadata.objectStores.clear(); - callbacks->onSuccess(); - } else - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error deleting database.")); - - self->processPendingCalls(); - }); -} - -void IDBDatabaseBackend::close(PassRefPtr prpCallbacks) -{ - RefPtr callbacks = prpCallbacks; - ASSERT(m_databaseCallbacksSet.contains(callbacks)); - - m_databaseCallbacksSet.remove(callbacks); - if (m_pendingSecondHalfOpen && m_pendingSecondHalfOpen->databaseCallbacks() == callbacks) { - m_pendingSecondHalfOpen->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "The connection was closed.")); - m_pendingSecondHalfOpen.release(); - } - - if (connectionCount() > 1) - return; - - // processPendingCalls allows the inspector to process a pending open call - // and call close, reentering IDBDatabaseBackend::close. Then the - // backend would be removed both by the inspector closing its connection, and - // by the connection that first called close. - // To avoid that situation, don't proceed in case of reentrancy. - if (m_closingConnection) - return; - TemporaryChange closingConnection(m_closingConnection, true); - processPendingCalls(); - - // FIXME: Add a test for the m_pendingOpenCalls cases below. - if (!connectionCount() && !m_pendingOpenCalls.size() && !m_pendingDeleteCalls.size()) { - TransactionMap transactions(m_transactions); - RefPtr error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Connection is closing."); - for (TransactionMap::const_iterator::Values it = transactions.values().begin(), end = transactions.values().end(); it != end; ++it) - (*it)->abort(error); - - ASSERT(m_transactions.isEmpty()); - - m_serverConnection->close(); - - // This check should only be false in unit tests. - ASSERT(m_factory); - if (m_factory) - m_factory->removeIDBDatabaseBackend(m_identifier); - } -} - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.h deleted file mode 100644 index 6815d6cc3..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBDatabaseBackend_h -#define IDBDatabaseBackend_h - -#include "IDBDatabaseCallbacks.h" -#include "IDBDatabaseMetadata.h" -#include "IDBKeyRange.h" -#include "IDBPendingDeleteCall.h" -#include "IDBPendingOpenCall.h" - -#include -#include -#include -#include - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -class IDBDatabase; -class IDBFactoryBackendInterface; -class IDBKey; -class IDBKeyPath; -class IDBServerConnection; -class IDBTransactionBackend; -class IDBTransactionCoordinator; -class SharedBuffer; - -struct IDBDatabaseMetadata; -struct IDBIndexMetadata; -struct IDBObjectStoreMetadata; - -typedef Vector> IndexKeys; -typedef int ExceptionCode; - -class IDBDatabaseBackend : public RefCounted { -public: - static PassRefPtr create(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface*, IDBServerConnection&); - ~IDBDatabaseBackend(); - - IDBServerConnection& serverConnection() { return m_serverConnection.get(); } - - static const int64_t InvalidId = 0; - int64_t id() const { return m_metadata.id; } - void addObjectStore(const IDBObjectStoreMetadata&, int64_t newMaxObjectStoreId); - void removeObjectStore(int64_t objectStoreId); - void addIndex(int64_t objectStoreId, const IDBIndexMetadata&, int64_t newMaxIndexId); - void removeIndex(int64_t objectStoreId, int64_t indexId); - - void openConnection(PassRefPtr, PassRefPtr, int64_t transactionId, uint64_t version); - void deleteDatabase(PassRefPtr); - - // IDBDatabaseBackend - void createObjectStore(int64_t transactionId, int64_t objectStoreId, const String& name, const IDBKeyPath&, bool autoIncrement); - void deleteObjectStore(int64_t transactionId, int64_t objectStoreId); - void createTransaction(int64_t transactionId, PassRefPtr, const Vector& objectStoreIds, IndexedDB::TransactionMode); - void close(PassRefPtr); - - void commit(int64_t transactionId); - void abort(int64_t transactionId); - void abort(int64_t transactionId, PassRefPtr); - - void createIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath&, bool unique, bool multiEntry); - void deleteIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId); - - IDBTransactionCoordinator* transactionCoordinator() const { return m_transactionCoordinator.get(); } - void transactionStarted(IDBTransactionBackend*); - void transactionFinished(IDBTransactionBackend*); - void transactionFinishedAndCompleteFired(IDBTransactionBackend*); - void transactionFinishedAndAbortFired(IDBTransactionBackend*); - - enum TaskType { - NormalTask = 0, - PreemptiveTask - }; - - enum PutMode { - AddOrUpdate, - AddOnly, - CursorUpdate - }; - - static const int64_t MinimumIndexId = 30; - - void get(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr, bool keyOnly, PassRefPtr); - void put(int64_t transactionId, int64_t objectStoreId, PassRefPtr value, PassRefPtr, PutMode, PassRefPtr, const Vector& indexIds, const Vector&); - void setIndexKeys(int64_t transactionId, int64_t objectStoreId, PassRefPtr prpPrimaryKey, const Vector& indexIds, const Vector&); - void setIndexesReady(int64_t transactionId, int64_t objectStoreId, const Vector& indexIds); - void openCursor(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr, IndexedDB::CursorDirection, bool keyOnly, TaskType, PassRefPtr); - void count(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr, PassRefPtr); - void deleteRange(int64_t transactionId, int64_t objectStoreId, PassRefPtr, PassRefPtr); - void clearObjectStore(int64_t transactionId, int64_t objectStoreId, PassRefPtr); - - const IDBDatabaseMetadata& metadata() const { return m_metadata; } - void setCurrentVersion(uint64_t version) { m_metadata.version = version; } - - bool hasPendingSecondHalfOpen() { return m_pendingSecondHalfOpen; } - void setPendingSecondHalfOpen(PassOwnPtr pendingOpenCall) { m_pendingSecondHalfOpen = pendingOpenCall; } - - IDBFactoryBackendInterface& factoryBackend() { return *m_factory; } - - class VersionChangeOperation; - class VersionChangeAbortOperation; - -private: - IDBDatabaseBackend(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface*, IDBServerConnection&); - - void openConnectionInternal(PassRefPtr, PassRefPtr, int64_t transactionId, uint64_t version); - - void openInternalAsync(); - void didOpenInternalAsync(const IDBDatabaseMetadata&, bool success); - - void runIntVersionChangeTransaction(PassRefPtr, PassRefPtr, int64_t transactionId, int64_t requestedVersion); - size_t connectionCount(); - void processPendingCalls(); - void processPendingOpenCalls(bool success); - - bool isDeleteDatabaseBlocked(); - void deleteDatabaseAsync(PassRefPtr); - - IDBDatabaseMetadata m_metadata; - - String m_identifier; - - RefPtr m_factory; - Ref m_serverConnection; - - OwnPtr m_transactionCoordinator; - RefPtr m_runningVersionChangeTransaction; - - typedef HashMap TransactionMap; - TransactionMap m_transactions; - - Deque> m_pendingOpenCalls; - OwnPtr m_pendingSecondHalfOpen; - - Deque> m_pendingDeleteCalls; - HashSet> m_deleteCallbacksWaitingCompletion; - - typedef ListHashSet> DatabaseCallbacksSet; - DatabaseCallbacksSet m_databaseCallbacksSet; - - bool m_closingConnection; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBDatabaseBackend_h diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacks.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacks.h deleted file mode 100644 index 7770abf6b..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacks.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBDatabaseCallbacks_h -#define IDBDatabaseCallbacks_h - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBDatabaseError.h" -#include "IndexedDB.h" -#include -#include - -namespace WebCore { - -class IDBDatabase; - -class IDBDatabaseCallbacks : public RefCounted { -public: - virtual ~IDBDatabaseCallbacks() { } - - virtual void onForcedClose() = 0; - virtual void onVersionChange(uint64_t oldVersion, uint64_t newVersion, IndexedDB::VersionNullness newVersionNullness) = 0; - - virtual void onAbort(int64_t transactionId, PassRefPtr) = 0; - virtual void onComplete(int64_t transactionId) = 0; - - virtual void connect(IDBDatabase*) = 0; -}; - -} // namespace WebCore - -#endif - -#endif // IDBDatabaseCallbacks_h diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.cpp b/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.cpp deleted file mode 100644 index 587c4a756..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBDatabaseCallbacksImpl.h" - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBDatabase.h" - -namespace WebCore { - -PassRefPtr IDBDatabaseCallbacksImpl::create() -{ - return adoptRef(new IDBDatabaseCallbacksImpl()); -} - -IDBDatabaseCallbacksImpl::IDBDatabaseCallbacksImpl() - : m_database(0) -{ -} - -IDBDatabaseCallbacksImpl::~IDBDatabaseCallbacksImpl() -{ -} - -void IDBDatabaseCallbacksImpl::onForcedClose() -{ - if (m_database) - m_database->forceClose(); -} - -void IDBDatabaseCallbacksImpl::onVersionChange(uint64_t oldVersion, uint64_t newVersion, IndexedDB::VersionNullness newVersionNullness) -{ - if (m_database) - m_database->onVersionChange(oldVersion, newVersion, newVersionNullness); -} - -void IDBDatabaseCallbacksImpl::connect(IDBDatabase* database) -{ - ASSERT(!m_database); - ASSERT(database); - m_database = database; -} - -void IDBDatabaseCallbacksImpl::onAbort(int64_t transactionId, PassRefPtr error) -{ - if (m_database) - m_database->onAbort(transactionId, error); -} - -void IDBDatabaseCallbacksImpl::onComplete(int64_t transactionId) -{ - if (m_database) - m_database->onComplete(transactionId); -} - -} // namespace WebCore - -#endif diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.h deleted file mode 100644 index 248332a35..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBDatabaseCallbacksImpl_h -#define IDBDatabaseCallbacksImpl_h - -#include "IDBDatabaseCallbacks.h" -#include -#include - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -class IDBDatabase; - -class IDBDatabaseCallbacksImpl final : public IDBDatabaseCallbacks { -public: - static PassRefPtr create(); - virtual ~IDBDatabaseCallbacksImpl() override; - - // IDBDatabaseCallbacks - virtual void onForcedClose() override; - virtual void onVersionChange(uint64_t oldVersion, uint64_t newVersion, IndexedDB::VersionNullness newVersionNullness) override; - - virtual void onAbort(int64_t transactionId, PassRefPtr) override; - virtual void onComplete(int64_t transactionId) override; - - virtual void connect(IDBDatabase*) override; - -private: - IDBDatabaseCallbacksImpl(); - - // The initial IDBOpenDBRequest or final IDBDatabase maintains a RefPtr to this - IDBDatabase* m_database; -}; - -} // namespace WebCore - -#endif - -#endif // IDBDatabaseCallbacksImpl_h diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseError.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseError.h index 7a939b5b6..a27cda681 100644 --- a/Source/WebCore/Modules/indexeddb/IDBDatabaseError.h +++ b/Source/WebCore/Modules/indexeddb/IDBDatabaseError.h @@ -23,11 +23,9 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBDatabaseError_h -#define IDBDatabaseError_h +#pragma once #include "IDBDatabaseException.h" -#include #include #include @@ -37,18 +35,18 @@ namespace WebCore { class IDBDatabaseError : public RefCounted { public: - static PassRefPtr create(unsigned short code) + static Ref create(unsigned short code) { ASSERT(code >= IDBDatabaseException::IDBDatabaseExceptionOffset); ASSERT(code < IDBDatabaseException::IDBDatabaseExceptionMax); - return adoptRef(new IDBDatabaseError(code)); + return adoptRef(*new IDBDatabaseError(code)); } - static PassRefPtr create(unsigned short code, const String& message) + static Ref create(unsigned short code, const String& message) { ASSERT_WITH_MESSAGE(code >= IDBDatabaseException::IDBDatabaseExceptionOffset, "%d >= %d", code, IDBDatabaseException::IDBDatabaseExceptionOffset); ASSERT(code < IDBDatabaseException::IDBDatabaseExceptionMax); - return adoptRef(new IDBDatabaseError(code, message)); + return adoptRef(*new IDBDatabaseError(code, message)); } ~IDBDatabaseError() { } @@ -70,6 +68,4 @@ private: } // namespace WebCore -#endif - -#endif // IDBDatabaseError_h +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseException.cpp b/Source/WebCore/Modules/indexeddb/IDBDatabaseException.cpp index 5f11cca09..84ef23efc 100644 --- a/Source/WebCore/Modules/indexeddb/IDBDatabaseException.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBDatabaseException.cpp @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -27,10 +27,12 @@ */ #include "config.h" +#include "IDBDatabaseException.h" #if ENABLE(INDEXED_DATABASE) -#include "IDBDatabaseException.h" +#include "ExceptionCode.h" +#include "ExceptionCodeDescription.h" namespace WebCore { @@ -77,7 +79,7 @@ bool IDBDatabaseException::initializeDescription(ExceptionCode ec, ExceptionCode description->typeName = "DOM IDBDatabase"; description->code = entry->code; - description->type = DOMCoreExceptionType; + description->type = IDBDatabaseExceptionType; description->name = entry ? entry->name : 0; description->description = entry ? entry->description : 0; @@ -90,7 +92,7 @@ String IDBDatabaseException::getErrorName(ExceptionCode ec) const IDBDatabaseExceptionNameDescription* entry = getErrorEntry(ec); ASSERT(entry); if (!entry) - return "UnknownError"; + return ASCIILiteral("UnknownError"); return entry->name; } @@ -100,7 +102,7 @@ String IDBDatabaseException::getErrorDescription(ExceptionCode ec) const IDBDatabaseExceptionNameDescription* entry = getErrorEntry(ec); ASSERT(entry); if (!entry) - return "Unknown error."; + return ASCIILiteral("Unknown error."); return entry->description; } diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseException.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseException.h index 15e58af28..9e0ce98b0 100644 --- a/Source/WebCore/Modules/indexeddb/IDBDatabaseException.h +++ b/Source/WebCore/Modules/indexeddb/IDBDatabaseException.h @@ -23,8 +23,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBDatabaseException_h -#define IDBDatabaseException_h +#pragma once #if ENABLE(INDEXED_DATABASE) @@ -34,9 +33,9 @@ namespace WebCore { class IDBDatabaseException : public ExceptionBase { public: - static PassRefPtr create(const ExceptionCodeDescription& description) + static Ref create(const ExceptionCodeDescription& description) { - return adoptRef(new IDBDatabaseException(description)); + return adoptRef(*new IDBDatabaseException(description)); } static const int IDBDatabaseExceptionOffset = 1200; @@ -85,6 +84,4 @@ private: } // namespace WebCore -#endif - -#endif // IDBDatabaseException_h +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.cpp b/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.cpp new file mode 100644 index 000000000..81975a261 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBDatabaseIdentifier.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "FileSystem.h" +#include "SecurityOrigin.h" +#include +#include + +namespace WebCore { + +IDBDatabaseIdentifier::IDBDatabaseIdentifier(const String& databaseName, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin) + : m_databaseName(databaseName) + , m_openingOrigin(SecurityOriginData::fromSecurityOrigin(openingOrigin)) + , m_mainFrameOrigin(SecurityOriginData::fromSecurityOrigin(mainFrameOrigin)) + +{ + // The empty string is a valid database name, but a null string is not. + ASSERT(!databaseName.isNull()); +} + +IDBDatabaseIdentifier IDBDatabaseIdentifier::isolatedCopy() const +{ + IDBDatabaseIdentifier identifier; + + identifier.m_databaseName = m_databaseName.isolatedCopy(); + identifier.m_openingOrigin = m_openingOrigin.isolatedCopy(); + identifier.m_mainFrameOrigin = m_mainFrameOrigin.isolatedCopy(); + + return identifier; +} + +String IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(const String& rootDirectory) const +{ + return databaseDirectoryRelativeToRoot(m_mainFrameOrigin, m_openingOrigin, rootDirectory); +} + +String IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(const SecurityOriginData& topLevelOrigin, const SecurityOriginData& openingOrigin, const String& rootDirectory) +{ + String mainFrameDirectory = pathByAppendingComponent(rootDirectory, topLevelOrigin.databaseIdentifier()); + + // If the opening origin and main frame origins are the same, there is no partitioning. + if (openingOrigin == topLevelOrigin) + return mainFrameDirectory; + + return pathByAppendingComponent(mainFrameDirectory, openingOrigin.databaseIdentifier()); +} + +#if !LOG_DISABLED +String IDBDatabaseIdentifier::debugString() const +{ + return makeString(m_databaseName, "@", m_openingOrigin.debugString(), ":", m_mainFrameOrigin.debugString()); +} +#endif + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.h new file mode 100644 index 000000000..dd9f14ffa --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "SecurityOriginData.h" +#include +#include +#include + +namespace WebCore { + +class SecurityOrigin; + +class IDBDatabaseIdentifier { +public: + IDBDatabaseIdentifier() { } + IDBDatabaseIdentifier(WTF::HashTableDeletedValueType) + : m_databaseName(WTF::HashTableDeletedValue) + { + } + + IDBDatabaseIdentifier isolatedCopy() const; + + bool isHashTableDeletedValue() const + { + return m_databaseName.isHashTableDeletedValue(); + } + + unsigned hash() const + { + unsigned nameHash = StringHash::hash(m_databaseName); + unsigned openingProtocolHash = StringHash::hash(m_openingOrigin.protocol); + unsigned openingHostHash = StringHash::hash(m_openingOrigin.host); + unsigned mainFrameProtocolHash = StringHash::hash(m_mainFrameOrigin.protocol); + unsigned mainFrameHostHash = StringHash::hash(m_mainFrameOrigin.host); + + unsigned hashCodes[7] = { nameHash, openingProtocolHash, openingHostHash, m_openingOrigin.port.value_or(0), mainFrameProtocolHash, mainFrameHostHash, m_mainFrameOrigin.port.value_or(0) }; + return StringHasher::hashMemory(hashCodes); + } + + IDBDatabaseIdentifier(const String& databaseName, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin); + + bool isValid() const + { + return !m_databaseName.isNull() + && !m_databaseName.isHashTableDeletedValue(); + } + + bool isEmpty() const + { + return m_databaseName.isNull(); + } + + bool operator==(const IDBDatabaseIdentifier& other) const + { + return other.m_databaseName == m_databaseName + && other.m_openingOrigin == m_openingOrigin + && other.m_mainFrameOrigin == m_mainFrameOrigin; + } + + const String& databaseName() const { return m_databaseName; } + + String databaseDirectoryRelativeToRoot(const String& rootDirectory) const; + static String databaseDirectoryRelativeToRoot(const SecurityOriginData& topLevelOrigin, const SecurityOriginData& openingOrigin, const String& rootDirectory); + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBDatabaseIdentifier&); + +#if !LOG_DISABLED + String debugString() const; +#endif + + bool isRelatedToOrigin(const SecurityOriginData& other) const + { + return m_openingOrigin == other || m_mainFrameOrigin == other; + } + +private: + String m_databaseName; + SecurityOriginData m_openingOrigin; + SecurityOriginData m_mainFrameOrigin; +}; + +struct IDBDatabaseIdentifierHash { + static unsigned hash(const IDBDatabaseIdentifier& a) { return a.hash(); } + static bool equal(const IDBDatabaseIdentifier& a, const IDBDatabaseIdentifier& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +struct IDBDatabaseIdentifierHashTraits : WTF::SimpleClassHashTraits { + static const bool hasIsEmptyValueFunction = true; + static const bool emptyValueIsZero = false; + static bool isEmptyValue(const IDBDatabaseIdentifier& info) { return info.isEmpty(); } +}; + +template +void IDBDatabaseIdentifier::encode(Encoder& encoder) const +{ + encoder << m_databaseName << m_openingOrigin << m_mainFrameOrigin; +} + +template +bool IDBDatabaseIdentifier::decode(Decoder& decoder, IDBDatabaseIdentifier& identifier) +{ + if (!decoder.decode(identifier.m_databaseName)) + return false; + + if (!decoder.decode(identifier.m_openingOrigin)) + return false; + + if (!decoder.decode(identifier.m_mainFrameOrigin)) + return false; + + return true; +} + +} // namespace WebCore + +namespace WTF { + +template<> struct HashTraits : WebCore::IDBDatabaseIdentifierHashTraits { }; +template<> struct DefaultHash { + typedef WebCore::IDBDatabaseIdentifierHash Hash; +}; + +} // namespace WTF + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.cpp b/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.cpp deleted file mode 100644 index 0d14288ce..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBDatabaseMetadata.h" - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -IDBDatabaseMetadata IDBDatabaseMetadata::isolatedCopy() const -{ - IDBDatabaseMetadata result; - result.id = id; - result.version = version; - result.maxObjectStoreId = maxObjectStoreId; - - result.name = name.isolatedCopy(); - - for (auto i = objectStores.begin(), end = objectStores.end(); i != end; ++i) - result.objectStores.set(i->key, i->value.isolatedCopy()); - - return result; -} - -IDBObjectStoreMetadata IDBObjectStoreMetadata::isolatedCopy() const -{ - IDBObjectStoreMetadata result; - result.id = id; - result.autoIncrement = autoIncrement; - result.maxIndexId = maxIndexId; - - result.name = name.isolatedCopy(); - result.keyPath = keyPath.isolatedCopy(); - - for (auto i = indexes.begin(), end = indexes.end(); i != end; ++i) - result.indexes.set(i->key, i->value.isolatedCopy()); - - return result; -} - -IDBIndexMetadata IDBIndexMetadata::isolatedCopy() const -{ - IDBIndexMetadata result; - result.id = id; - result.unique = unique; - result.multiEntry = multiEntry; - - result.name = name.isolatedCopy(); - result.keyPath = keyPath.isolatedCopy(); - - return result; -} - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.h deleted file mode 100644 index 75717440a..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBDatabaseMetadata_h -#define IDBDatabaseMetadata_h - -#include "IDBObjectStoreMetadata.h" - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -struct IDBDatabaseMetadata { - - // FIXME: These are only here to support the LevelDB backend which incorrectly handles versioning. - // Once LevelDB supports a single, uint64_t version and throws out the old string version, these - // should be gotten rid of. - // Also, "NoIntVersion" used to be a magic number of -1, which doesn't work with the new unsigned type. - // Changing the value to INT64_MAX here seems like a reasonable temporary fix as the current LevelDB - // already cannot represent valid version numbers between INT64_MAX and UINT64_MAX. - -#ifndef INT64_MAX -#define INT64_MAX 9223372036854775807LL -#endif - - enum { - NoIntVersion = INT64_MAX, - DefaultIntVersion = 0 - }; - - IDBDatabaseMetadata() - : id(0) - , version(0) - , maxObjectStoreId(0) - { - } - - IDBDatabaseMetadata(const String& name, int64_t id, uint64_t version, int64_t maxObjectStoreId) - : name(name) - , id(id) - , version(version) - , maxObjectStoreId(maxObjectStoreId) - { - } - - String name; - int64_t id; - uint64_t version; - int64_t maxObjectStoreId; - - typedef HashMap ObjectStoreMap; - ObjectStoreMap objectStores; - - IDBDatabaseMetadata isolatedCopy() const; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBDatabaseMetadata_h diff --git a/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.cpp b/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.cpp index 116f7d526..e513143b4 100644 --- a/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.cpp @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -36,30 +36,30 @@ namespace WebCore { -bool IDBEventDispatcher::dispatch(Event* event, Vector>& eventTargets) +bool IDBEventDispatcher::dispatch(Event& event, Vector>& eventTargets) { size_t size = eventTargets.size(); ASSERT(size); - event->setEventPhase(Event::CAPTURING_PHASE); + event.setEventPhase(Event::CAPTURING_PHASE); for (size_t i = size - 1; i; --i) { // Don't do the first element. - event->setCurrentTarget(eventTargets[i].get()); + event.setCurrentTarget(eventTargets[i].get()); eventTargets[i]->fireEventListeners(event); - if (event->propagationStopped()) + if (event.propagationStopped()) goto doneDispatching; } - event->setEventPhase(Event::AT_TARGET); - event->setCurrentTarget(eventTargets[0].get()); + event.setEventPhase(Event::AT_TARGET); + event.setCurrentTarget(eventTargets[0].get()); eventTargets[0]->fireEventListeners(event); - if (event->propagationStopped() || !event->bubbles() || event->cancelBubble()) + if (event.propagationStopped() || !event.bubbles()) goto doneDispatching; - event->setEventPhase(Event::BUBBLING_PHASE); + event.setEventPhase(Event::BUBBLING_PHASE); for (size_t i = 1; i < size; ++i) { // Don't do the first element. - event->setCurrentTarget(eventTargets[i].get()); + event.setCurrentTarget(eventTargets[i].get()); eventTargets[i]->fireEventListeners(event); - if (event->propagationStopped() || event->cancelBubble()) + if (event.propagationStopped()) goto doneDispatching; } @@ -82,9 +82,9 @@ bool IDBEventDispatcher::dispatch(Event* event, Vector>& eve // event on the window until that has been implemented)." -- Jonas Sicking doneDispatching: - event->setCurrentTarget(0); - event->setEventPhase(0); - return !event->defaultPrevented(); + event.setCurrentTarget(nullptr); + event.setEventPhase(0); + return !event.defaultPrevented(); } } // namespace WebCore diff --git a/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.h b/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.h index 1d263b560..47e5e9230 100644 --- a/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.h +++ b/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.h @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -26,12 +26,10 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBEventDispatcher_h -#define IDBEventDispatcher_h +#pragma once #if ENABLE(INDEXED_DATABASE) -#include #include namespace WebCore { @@ -41,7 +39,7 @@ class EventTarget; class IDBEventDispatcher { public: - static bool dispatch(Event*, Vector>&); // The target first and then its ancestors in order of how the event bubbles. + static bool dispatch(Event&, Vector>&); // The target first and then its ancestors in order of how the event bubbles. private: IDBEventDispatcher(); @@ -50,5 +48,3 @@ private: } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBEventDispatcher_h diff --git a/Source/WebCore/Modules/indexeddb/IDBFactory.cpp b/Source/WebCore/Modules/indexeddb/IDBFactory.cpp index 7c73d8449..db9aa0e66 100644 --- a/Source/WebCore/Modules/indexeddb/IDBFactory.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBFactory.cpp @@ -1,29 +1,26 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -33,153 +30,113 @@ #include "Document.h" #include "ExceptionCode.h" -#include "Frame.h" -#include "GroupSettings.h" -#include "HistogramSupport.h" #include "IDBBindingUtilities.h" -#include "IDBDatabase.h" -#include "IDBDatabaseCallbacksImpl.h" -#include "IDBDatabaseException.h" -#include "IDBFactoryBackendInterface.h" -#include "IDBHistograms.h" +#include "IDBConnectionProxy.h" +#include "IDBDatabaseIdentifier.h" #include "IDBKey.h" -#include "IDBKeyRange.h" #include "IDBOpenDBRequest.h" #include "Logging.h" #include "Page.h" -#include "PageGroup.h" +#include "ScriptExecutionContext.h" #include "SecurityOrigin.h" -#include "WorkerGlobalScope.h" -#include "WorkerLoaderProxy.h" -#include "WorkerThread.h" + +using namespace JSC; namespace WebCore { -IDBFactory::IDBFactory(IDBFactoryBackendInterface* factory) - : m_backend(factory) +static bool shouldThrowSecurityException(ScriptExecutionContext& context) { - // We pass a reference to this object before it can be adopted. - relaxAdoptionRequirement(); + ASSERT(is(context) || context.isWorkerGlobalScope()); + if (is(context)) { + Document& document = downcast(context); + if (!document.frame()) + return true; + if (!document.page()) + return true; + } + + if (!context.securityOrigin()->canAccessDatabase(context.topOrigin())) + return true; + + return false; } -IDBFactory::~IDBFactory() +Ref IDBFactory::create(IDBClient::IDBConnectionProxy& connectionProxy) { + return adoptRef(*new IDBFactory(connectionProxy)); } -namespace { -static bool isContextValid(ScriptExecutionContext* context) +IDBFactory::IDBFactory(IDBClient::IDBConnectionProxy& connectionProxy) + : m_connectionProxy(connectionProxy) { - ASSERT(context->isDocument() || context->isWorkerGlobalScope()); - if (context->isDocument()) { - Document* document = toDocument(context); - return document->frame() && document->page(); - } - return true; } -static String getIndexedDBDatabasePath(ScriptExecutionContext* context) +IDBFactory::~IDBFactory() { - ASSERT(isContextValid(context)); - if (context->isDocument()) { - Document* document = toDocument(context); - return document->page()->group().groupSettings().indexedDBDatabasePath(); - } - WorkerGlobalScope* workerGlobalScope = static_cast(context); - const GroupSettings* groupSettings = workerGlobalScope->groupSettings(); - if (groupSettings) - return groupSettings->indexedDBDatabasePath(); - return String(); -} } -PassRefPtr IDBFactory::getDatabaseNames(ScriptExecutionContext* context, ExceptionCode& ec) +ExceptionOr> IDBFactory::open(ScriptExecutionContext& context, const String& name, std::optional version) { - LOG(StorageAPI, "IDBFactory::getDatabaseNames"); - if (!isContextValid(context)) - return 0; - if (!context->securityOrigin()->canAccessDatabase(context->topOrigin())) { - ec = SECURITY_ERR; - return 0; - } + LOG(IndexedDB, "IDBFactory::open"); + + if (version && !version.value()) + return Exception { TypeError, ASCIILiteral("IDBFactory.open() called with a version of 0") }; - RefPtr request = IDBRequest::create(context, IDBAny::create(this), 0); - m_backend->getDatabaseNames(request, context->securityOrigin(), context, getIndexedDBDatabasePath(context)); - return request; + return openInternal(context, name, version.value_or(0)); } -PassRefPtr IDBFactory::open(ScriptExecutionContext* context, const String& name, ExceptionCode& ec) +ExceptionOr> IDBFactory::openInternal(ScriptExecutionContext& context, const String& name, uint64_t version) { - LOG(StorageAPI, "IDBFactory::open"); - return openInternal(context, name, 0, IndexedDB::VersionNullness::Null, ec); -} + if (name.isNull()) + return Exception { TypeError, ASCIILiteral("IDBFactory.open() called without a database name") }; -PassRefPtr IDBFactory::open(ScriptExecutionContext* context, const String& name, unsigned long long version, ExceptionCode& ec) -{ - LOG(StorageAPI, "IDBFactory::open"); - if (!version) { - ec = TypeError; - return 0; - } - return openInternal(context, name, version, IndexedDB::VersionNullness::NonNull, ec); -} + if (shouldThrowSecurityException(context)) + return Exception { SECURITY_ERR, ASCIILiteral("IDBFactory.open() called in an invalid security context") }; -PassRefPtr IDBFactory::openInternal(ScriptExecutionContext* context, const String& name, uint64_t version, IndexedDB::VersionNullness versionNullness, ExceptionCode& ec) -{ - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.FrontEndAPICalls", IDBOpenCall, IDBMethodsMax); - ASSERT(version >= 1 || versionNullness == IndexedDB::VersionNullness::Null); - if (name.isNull()) { - ec = TypeError; - return 0; - } - if (!isContextValid(context)) - return 0; - if (!context->securityOrigin()->canAccessDatabase(context->topOrigin())) { - ec = SECURITY_ERR; - return 0; - } + ASSERT(context.securityOrigin()); + IDBDatabaseIdentifier databaseIdentifier(name, *context.securityOrigin(), context.topOrigin()); + if (!databaseIdentifier.isValid()) + return Exception { TypeError, ASCIILiteral("IDBFactory.open() called with an invalid security origin") }; - RefPtr databaseCallbacks = IDBDatabaseCallbacksImpl::create(); - int64_t transactionId = IDBDatabase::nextTransactionId(); - RefPtr request = IDBOpenDBRequest::create(context, databaseCallbacks, transactionId, version, versionNullness); - m_backend->open(name, version, transactionId, request, databaseCallbacks, *(context->securityOrigin()), *(context->topOrigin())); - return request; + LOG(IndexedDBOperations, "IDB opening database: %s %" PRIu64, name.utf8().data(), version); + + return m_connectionProxy->openDatabase(context, databaseIdentifier, version); } -PassRefPtr IDBFactory::deleteDatabase(ScriptExecutionContext* context, const String& name, ExceptionCode& ec) +ExceptionOr> IDBFactory::deleteDatabase(ScriptExecutionContext& context, const String& name) { - LOG(StorageAPI, "IDBFactory::deleteDatabase"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.FrontEndAPICalls", IDBDeleteDatabaseCall, IDBMethodsMax); - if (name.isNull()) { - ec = TypeError; - return 0; - } - if (!isContextValid(context)) - return 0; - if (!context->securityOrigin()->canAccessDatabase(context->topOrigin())) { - ec = SECURITY_ERR; - return 0; - } + LOG(IndexedDB, "IDBFactory::deleteDatabase - %s", name.utf8().data()); - RefPtr request = IDBOpenDBRequest::create(context, 0, 0, 0, IndexedDB::VersionNullness::Null); - m_backend->deleteDatabase(name, request, context->securityOrigin(), context, getIndexedDBDatabasePath(context)); - return request; + if (name.isNull()) + return Exception { TypeError, ASCIILiteral("IDBFactory.deleteDatabase() called without a database name") }; + + if (shouldThrowSecurityException(context)) + return Exception { SECURITY_ERR, ASCIILiteral("IDBFactory.deleteDatabase() called in an invalid security context") }; + + ASSERT(context.securityOrigin()); + IDBDatabaseIdentifier databaseIdentifier(name, *context.securityOrigin(), context.topOrigin()); + if (!databaseIdentifier.isValid()) + return Exception { TypeError, ASCIILiteral("IDBFactory.deleteDatabase() called with an invalid security origin") }; + + LOG(IndexedDBOperations, "IDB deleting database: %s", name.utf8().data()); + + return m_connectionProxy->deleteDatabase(context, databaseIdentifier); } -short IDBFactory::cmp(ScriptExecutionContext* context, const Deprecated::ScriptValue& firstValue, const Deprecated::ScriptValue& secondValue, ExceptionCode& ec) +ExceptionOr IDBFactory::cmp(ExecState& execState, JSValue firstValue, JSValue secondValue) { - DOMRequestState requestState(context); - RefPtr first = scriptValueToIDBKey(&requestState, firstValue); - RefPtr second = scriptValueToIDBKey(&requestState, secondValue); + auto first = scriptValueToIDBKey(execState, firstValue); + auto second = scriptValueToIDBKey(execState, secondValue); - ASSERT(first); - ASSERT(second); + if (!first->isValid() || !second->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'cmp' on 'IDBFactory': The parameter is not a valid key.") }; - if (!first->isValid() || !second->isValid()) { - ec = IDBDatabaseException::DataError; - return 0; - } + return first->compare(second.get()); +} - return static_cast(first->compare(second.get())); +void IDBFactory::getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function&)> callback) +{ + m_connectionProxy->getAllDatabaseNames(mainFrameOrigin, openingOrigin, callback); } } // namespace WebCore diff --git a/Source/WebCore/Modules/indexeddb/IDBFactory.h b/Source/WebCore/Modules/indexeddb/IDBFactory.h index 778fd54e4..8dbc11ce6 100644 --- a/Source/WebCore/Modules/indexeddb/IDBFactory.h +++ b/Source/WebCore/Modules/indexeddb/IDBFactory.h @@ -1,83 +1,74 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBFactory_h -#define IDBFactory_h -#include "IDBOpenDBRequest.h" -#include "ScriptWrappable.h" -#include -#include -#include -#include +#pragma once #if ENABLE(INDEXED_DATABASE) +#include "ExceptionOr.h" +#include +#include +#include +#include +#include + +namespace JSC { +class ExecState; +class JSValue; +} + namespace WebCore { -class IDBKey; -class IDBKeyRange; -class IDBFactoryBackendInterface; +class IDBOpenDBRequest; class ScriptExecutionContext; +class SecurityOrigin; -typedef int ExceptionCode; +namespace IDBClient { +class IDBConnectionProxy; +} -class IDBFactory : public ScriptWrappable, public RefCounted { +class IDBFactory : public ThreadSafeRefCounted { public: - static PassRefPtr create(IDBFactoryBackendInterface* factory) - { - // FIXME: While the feature is under development we'll handle a null factory backend here, - // returning null, so javascript can't try to use the feature. - // Once the feature is fully functional we should remove the null factory backend. - return factory ? adoptRef(new IDBFactory(factory)) : nullptr; - } + static Ref create(IDBClient::IDBConnectionProxy&); ~IDBFactory(); - // FIXME: getDatabaseNames is no longer a web-facing API, and should be removed from IDBFactory. - // The Web Inspector currently uses this to enumerate the list of databases, but is more complicated as a result. - // We should provide a simpler API to the Web Inspector then remove getDatabaseNames. - PassRefPtr getDatabaseNames(ScriptExecutionContext*, ExceptionCode&); + ExceptionOr> open(ScriptExecutionContext&, const String& name, std::optional version); + ExceptionOr> deleteDatabase(ScriptExecutionContext&, const String& name); - PassRefPtr open(ScriptExecutionContext*, const String& name, ExceptionCode&); - PassRefPtr open(ScriptExecutionContext*, const String& name, unsigned long long version, ExceptionCode&); - PassRefPtr deleteDatabase(ScriptExecutionContext*, const String& name, ExceptionCode&); + ExceptionOr cmp(JSC::ExecState&, JSC::JSValue first, JSC::JSValue second); - short cmp(ScriptExecutionContext*, const Deprecated::ScriptValue& first, const Deprecated::ScriptValue& second, ExceptionCode&); + WEBCORE_EXPORT void getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function&)>); private: - IDBFactory(IDBFactoryBackendInterface*); + explicit IDBFactory(IDBClient::IDBConnectionProxy&); - PassRefPtr openInternal(ScriptExecutionContext*, const String& name, uint64_t version, IndexedDB::VersionNullness, ExceptionCode&); + ExceptionOr> openInternal(ScriptExecutionContext&, const String& name, uint64_t version); - RefPtr m_backend; + Ref m_connectionProxy; }; } // namespace WebCore -#endif - -#endif // IDBFactory_h +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBFactory.idl b/Source/WebCore/Modules/indexeddb/IDBFactory.idl index beb7abd72..d0f63282a 100644 --- a/Source/WebCore/Modules/indexeddb/IDBFactory.idl +++ b/Source/WebCore/Modules/indexeddb/IDBFactory.idl @@ -25,11 +25,9 @@ [ Conditional=INDEXED_DATABASE, - JSNoStaticTables, - ImplementationLacksVTable, + SkipVTableValidation, ] interface IDBFactory { - [CallWith=ScriptExecutionContext, RaisesException] IDBOpenDBRequest open(DOMString name, [EnforceRange] optional unsigned long long version); - [CallWith=ScriptExecutionContext, RaisesException] IDBOpenDBRequest deleteDatabase(DOMString name); - [CallWith=ScriptExecutionContext, RaisesException] short cmp(any first, any second); + [CallWith=ScriptExecutionContext, MayThrowException] IDBOpenDBRequest open(DOMString name, [EnforceRange] optional unsigned long long version); + [CallWith=ScriptExecutionContext, MayThrowException] IDBOpenDBRequest deleteDatabase(DOMString name); + [CallWith=ScriptState, MayThrowException] short cmp(any first, any second); }; - diff --git a/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.cpp b/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.cpp deleted file mode 100644 index d1c9fbde5..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "config.h" -#include "IDBFactoryBackendInterface.h" - -#if ENABLE(INDEXED_DATABASE) - -#include "DatabaseStrategy.h" -#include "PlatformStrategies.h" - -namespace WebCore { - -PassRefPtr IDBFactoryBackendInterface::create(const String& databaseDirectoryIdentifier) -{ - return platformStrategies()->databaseStrategy()->createIDBFactoryBackend(databaseDirectoryIdentifier); -} - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.h b/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.h deleted file mode 100644 index 3c7c59306..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef IDBFactoryBackendInterface_h -#define IDBFactoryBackendInterface_h - -#include "IDBDatabaseBackend.h" -#include "IndexedDB.h" - -#include -#include -#include - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -class IDBCallbacks; -class IDBCursorBackend; -class IDBDatabase; -class IDBDatabaseBackend; -class IDBDatabaseCallbacks; -class IDBTransactionBackend; -class SecurityOrigin; -class ScriptExecutionContext; - -typedef int ExceptionCode; - -// This class is shared by IDBFactory (async) and IDBFactorySync (sync). -// This is implemented by IDBFactoryBackendImpl and optionally others (in order to proxy -// calls across process barriers). All calls to these classes should be non-blocking and -// trigger work on a background thread if necessary. -class IDBFactoryBackendInterface : public RefCounted { -public: - static PassRefPtr create(const String& databaseDirectoryIdentifier); - virtual ~IDBFactoryBackendInterface() { } - - virtual void getDatabaseNames(PassRefPtr, PassRefPtr, ScriptExecutionContext*, const String& dataDir) = 0; - virtual void open(const String& name, uint64_t version, int64_t transactionId, PassRefPtr, PassRefPtr, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin) = 0; - virtual void deleteDatabase(const String& name, PassRefPtr, PassRefPtr, ScriptExecutionContext*, const String& dataDir) = 0; - - virtual void removeIDBDatabaseBackend(const String& uniqueIdentifier) = 0; - -}; - -} // namespace WebCore - -#endif // IDBFactoryBackendInterface_h - -#endif // IDBFactoryBackendInterface_h diff --git a/Source/WebCore/Modules/indexeddb/IDBGetAllResult.cpp b/Source/WebCore/Modules/indexeddb/IDBGetAllResult.cpp new file mode 100644 index 000000000..4a45a37a2 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBGetAllResult.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBGetAllResult.h" + +#if ENABLE(INDEXED_DATABASE) + +#include + +namespace WebCore { + +template void isolatedCopyOfVariant(const WTF::Variant, Vector, std::nullptr_t>& source, WTF::Variant, Vector, std::nullptr_t>& target) +{ + target = Vector(); + auto& sourceVector = WTF::get>(source); + auto& targetVector = WTF::get>(target); + targetVector.reserveInitialCapacity(sourceVector.size()); + for (auto& element : sourceVector) + targetVector.uncheckedAppend(element.isolatedCopy()); +} + +IDBGetAllResult::IDBGetAllResult(const IDBGetAllResult& that, IsolatedCopyTag) +{ + isolatedCopy(that, *this); +} + +IDBGetAllResult IDBGetAllResult::isolatedCopy() const +{ + return { *this, IsolatedCopy }; +} + +void IDBGetAllResult::isolatedCopy(const IDBGetAllResult& source, IDBGetAllResult& destination) +{ + destination.m_type = source.m_type; + + if (WTF::holds_alternative(source.m_results)) + return; + + switch (source.m_type) { + case IndexedDB::GetAllType::Keys: + isolatedCopyOfVariant(source.m_results, destination.m_results); + break; + case IndexedDB::GetAllType::Values: + isolatedCopyOfVariant(source.m_results, destination.m_results); + break; + } +} + +void IDBGetAllResult::addKey(IDBKeyData&& key) +{ + ASSERT(m_type == IndexedDB::GetAllType::Keys); + ASSERT(WTF::holds_alternative>(m_results)); + WTF::get>(m_results).append(WTFMove(key)); +} + +void IDBGetAllResult::addValue(IDBValue&& value) +{ + ASSERT(m_type == IndexedDB::GetAllType::Values); + ASSERT(WTF::holds_alternative>(m_results)); + WTF::get>(m_results).append(WTFMove(value)); +} + +const Vector& IDBGetAllResult::keys() const +{ + ASSERT(m_type == IndexedDB::GetAllType::Keys); + ASSERT(WTF::holds_alternative>(m_results)); + return WTF::get>(m_results); +} + +const Vector& IDBGetAllResult::values() const +{ + ASSERT(m_type == IndexedDB::GetAllType::Values); + ASSERT(WTF::holds_alternative>(m_results)); + return WTF::get>(m_results); +} + +Vector IDBGetAllResult::allBlobFilePaths() const +{ + ASSERT(m_type == IndexedDB::GetAllType::Values); + + HashSet pathSet; + for (auto& value : WTF::get>(m_results)) { + for (auto& path : value.blobFilePaths()) + pathSet.add(path); + } + + Vector paths; + copyToVector(pathSet, paths); + + return paths; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBGetAllResult.h b/Source/WebCore/Modules/indexeddb/IDBGetAllResult.h new file mode 100644 index 000000000..ef7c2578e --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBGetAllResult.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyData.h" +#include "IDBValue.h" +#include "IndexedDB.h" + +#include + +namespace WebCore { + +class IDBGetAllResult { +public: + IDBGetAllResult() + { + } + + IDBGetAllResult(IndexedDB::GetAllType type) + : m_type(type) + { + switch (m_type) { + case IndexedDB::GetAllType::Keys: + m_results = Vector(); + break; + case IndexedDB::GetAllType::Values: + m_results = Vector(); + break; + } + } + + enum IsolatedCopyTag { IsolatedCopy }; + IDBGetAllResult(const IDBGetAllResult&, IsolatedCopyTag); + IDBGetAllResult isolatedCopy() const; + + IndexedDB::GetAllType type() const { return m_type; } + const Vector& keys() const; + const Vector& values() const; + + void addKey(IDBKeyData&&); + void addValue(IDBValue&&); + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBGetAllResult&); + + WEBCORE_EXPORT Vector allBlobFilePaths() const; + +private: + static void isolatedCopy(const IDBGetAllResult& source, IDBGetAllResult& destination); + + IndexedDB::GetAllType m_type { IndexedDB::GetAllType::Keys }; + WTF::Variant, Vector, std::nullptr_t> m_results { nullptr }; +}; + +template +void IDBGetAllResult::encode(Encoder& encoder) const +{ + encoder << m_type << static_cast(m_results.index()); + + switch (m_results.index()) { + case 0: + encoder << WTF::get>(m_results); + break; + case 1: + encoder << WTF::get>(m_results); + break; + case 2: + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } +} + +template +bool IDBGetAllResult::decode(Decoder& decoder, IDBGetAllResult& result) +{ + if (!decoder.decode(result.m_type)) + return false; + + uint64_t index; + if (!decoder.decode(index)) + return false; + + switch (index) { + case 0: + result.m_results = Vector(); + if (!decoder.decode(WTF::get>(result.m_results))) + return false; + break; + case 1: + result.m_results = Vector(); + if (!decoder.decode(WTF::get>(result.m_results))) + return false; + break; + case 2: + result.m_results = nullptr; + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBGetResult.cpp b/Source/WebCore/Modules/indexeddb/IDBGetResult.cpp new file mode 100644 index 000000000..aa36451f2 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBGetResult.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBGetResult.h" + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +void IDBGetResult::dataFromBuffer(SharedBuffer& buffer) +{ + Vector data(buffer.size()); + memcpy(data.data(), buffer.data(), buffer.size()); + + m_value = ThreadSafeDataBuffer::adoptVector(data); +} + +IDBGetResult::IDBGetResult(const IDBGetResult& that, IsolatedCopyTag) +{ + isolatedCopy(that, *this); +} + +IDBGetResult IDBGetResult::isolatedCopy() const +{ + return { *this, IsolatedCopy }; +} + +void IDBGetResult::isolatedCopy(const IDBGetResult& source, IDBGetResult& destination) +{ + destination.m_value = source.m_value.isolatedCopy(); + destination.m_keyData = source.m_keyData.isolatedCopy(); + destination.m_primaryKeyData = source.m_primaryKeyData.isolatedCopy(); + destination.m_keyPath = WebCore::isolatedCopy(source.m_keyPath); + destination.m_isDefined = source.m_isDefined; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBGetResult.h b/Source/WebCore/Modules/indexeddb/IDBGetResult.h index 04ae0f4ae..6224464ef 100644 --- a/Source/WebCore/Modules/indexeddb/IDBGetResult.h +++ b/Source/WebCore/Modules/indexeddb/IDBGetResult.h @@ -23,57 +23,132 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBGetResult_h -#define IDBGetResult_h +#pragma once #if ENABLE(INDEXED_DATABASE) #include "IDBKey.h" #include "IDBKeyData.h" #include "IDBKeyPath.h" +#include "IDBValue.h" #include "SharedBuffer.h" namespace WebCore { -struct IDBGetResult { +class IDBGetResult { +public: IDBGetResult() + : m_isDefined(false) { } - IDBGetResult(PassRefPtr buffer) - : valueBuffer(buffer) + IDBGetResult(const IDBValue& value, const IDBKeyData& currentPrimaryKey) + : m_value(value) + , m_primaryKeyData(currentPrimaryKey) { } - IDBGetResult(PassRefPtr key) - : keyData(key.get()) + IDBGetResult(const ThreadSafeDataBuffer& buffer) + : m_value(buffer) { } - IDBGetResult(PassRefPtr buffer, PassRefPtr key, const IDBKeyPath& path) - : valueBuffer(buffer) - , keyData(key.get()) - , keyPath(path) + IDBGetResult(IDBValue&& buffer) + : m_value(WTFMove(buffer)) { } - IDBGetResult isolatedCopy() const + IDBGetResult(IDBKey& key) + : m_keyData(&key) { - IDBGetResult result; - if (valueBuffer) - result.valueBuffer = valueBuffer->copy(); + } + + IDBGetResult(const IDBKeyData& keyData) + : m_keyData(keyData) + { + } + + IDBGetResult(SharedBuffer* buffer, IDBKey& key, const IDBKeyPath& path) + : m_keyData(&key) + , m_keyPath(path) + { + if (buffer) + dataFromBuffer(*buffer); + } - result.keyData = keyData.isolatedCopy(); - result.keyPath = keyPath.isolatedCopy(); - return result; + IDBGetResult(const IDBKeyData& keyData, const IDBKeyData& primaryKeyData) + : m_keyData(keyData) + , m_primaryKeyData(primaryKeyData) + { + } + + IDBGetResult(const IDBKeyData& keyData, const IDBKeyData& primaryKeyData, IDBValue&& value) + : m_value(WTFMove(value)) + , m_keyData(keyData) + , m_primaryKeyData(primaryKeyData) + { + } + + IDBGetResult(const IDBKeyData& keyData, const IDBKeyData& primaryKeyData, const IDBValue& value) + : m_value(value) + , m_keyData(keyData) + , m_primaryKeyData(primaryKeyData) + { } - RefPtr valueBuffer; - IDBKeyData keyData; - IDBKeyPath keyPath; + enum IsolatedCopyTag { IsolatedCopy }; + IDBGetResult(const IDBGetResult&, IsolatedCopyTag); + + IDBGetResult isolatedCopy() const; + + const IDBValue& value() const { return m_value; } + const IDBKeyData& keyData() const { return m_keyData; } + const IDBKeyData& primaryKeyData() const { return m_primaryKeyData; } + const IDBKeyPath& keyPath() const { return m_keyPath; } + bool isDefined() const { return m_isDefined; } + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBGetResult&); + +private: + void dataFromBuffer(SharedBuffer&); + + static void isolatedCopy(const IDBGetResult& source, IDBGetResult& destination); + + IDBValue m_value; + IDBKeyData m_keyData; + IDBKeyData m_primaryKeyData; + IDBKeyPath m_keyPath; + bool m_isDefined { true }; }; +template +void IDBGetResult::encode(Encoder& encoder) const +{ + encoder << m_keyData << m_primaryKeyData << m_keyPath << m_isDefined << m_value; +} + +template +bool IDBGetResult::decode(Decoder& decoder, IDBGetResult& result) +{ + if (!decoder.decode(result.m_keyData)) + return false; + + if (!decoder.decode(result.m_primaryKeyData)) + return false; + + if (!decoder.decode(result.m_keyPath)) + return false; + + if (!decoder.decode(result.m_isDefined)) + return false; + + if (!decoder.decode(result.m_value)) + return false; + + return true; +} + } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) -#endif // IDBGetResult_h diff --git a/Source/WebCore/Modules/indexeddb/IDBHistograms.h b/Source/WebCore/Modules/indexeddb/IDBHistograms.h deleted file mode 100644 index d58f088bd..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBHistograms.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBHistograms_h -#define IDBHistograms_h - -namespace WebCore { - -enum IndexedDatabaseMethods { - IDBCreateObjectStoreCall, - IDBDeleteObjectStoreCall, - IDBTransactionCall, - IDBDeleteDatabaseCall, - IDBOpenCall, - IDBMethodsMax, -}; - -} - -#endif diff --git a/Source/WebCore/Modules/indexeddb/IDBIndex.cpp b/Source/WebCore/Modules/indexeddb/IDBIndex.cpp index e6fdf5cb8..3ec51603c 100644 --- a/Source/WebCore/Modules/indexeddb/IDBIndex.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBIndex.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -28,181 +28,354 @@ #if ENABLE(INDEXED_DATABASE) +#include "ExceptionCode.h" +#include "IDBBindingUtilities.h" +#include "IDBCursor.h" +#include "IDBDatabase.h" #include "IDBDatabaseException.h" -#include "IDBKey.h" -#include "IDBKeyRange.h" +#include "IDBKeyRangeData.h" #include "IDBObjectStore.h" #include "IDBRequest.h" #include "IDBTransaction.h" #include "Logging.h" -#include "ScriptExecutionContext.h" +#include + +using namespace JSC; namespace WebCore { -IDBIndex::IDBIndex(const IDBIndexMetadata& metadata, IDBObjectStore* objectStore, IDBTransaction* transaction) - : m_metadata(metadata) +IDBIndex::IDBIndex(ScriptExecutionContext& context, const IDBIndexInfo& info, IDBObjectStore& objectStore) + : ActiveDOMObject(&context) + , m_info(info) + , m_originalInfo(info) , m_objectStore(objectStore) - , m_transaction(transaction) - , m_deleted(false) { - ASSERT(m_objectStore); - ASSERT(m_transaction); - ASSERT(m_metadata.id != IDBIndexMetadata::InvalidId); + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + suspendIfNeeded(); } IDBIndex::~IDBIndex() { + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); } -PassRefPtr IDBIndex::openCursor(ScriptExecutionContext* context, PassRefPtr keyRange, const String& directionString, ExceptionCode& ec) +const char* IDBIndex::activeDOMObjectName() const { - LOG(StorageAPI, "IDBIndex::openCursor"); - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec); - if (ec) - return 0; + return "IDBIndex"; +} - RefPtr request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - request->setCursorDetails(IndexedDB::CursorType::KeyAndValue, direction); - backendDB()->openCursor(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, direction, false, IDBDatabaseBackend::NormalTask, request); - return request; +bool IDBIndex::canSuspendForDocumentSuspension() const +{ + return false; } -PassRefPtr IDBIndex::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode& ec) +bool IDBIndex::hasPendingActivity() const { - LOG(StorageAPI, "IDBIndex::openCursor"); - RefPtr keyRange = IDBKeyRange::only(context, key, ec); - if (ec) - return 0; - return openCursor(context, keyRange.release(), direction, ec); + return !m_objectStore.transaction().isFinished(); } -PassRefPtr IDBIndex::count(ScriptExecutionContext* context, PassRefPtr keyRange, ExceptionCode& ec) +const String& IDBIndex::name() const { - LOG(StorageAPI, "IDBIndex::count"); - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - RefPtr request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - backendDB()->count(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, request); - return request; + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + return m_info.name(); } -PassRefPtr IDBIndex::count(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) +ExceptionOr IDBIndex::setName(const String& name) { - LOG(StorageAPI, "IDBIndex::count"); - RefPtr keyRange = IDBKeyRange::only(context, key, ec); - if (ec) - return 0; - return count(context, keyRange.release(), ec); + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + if (m_deleted) + return Exception { INVALID_STATE_ERR, ASCIILiteral("Failed set property 'name' on 'IDBIndex': The index has been deleted.") }; + + if (m_objectStore.isDeleted()) + return Exception { INVALID_STATE_ERR, ASCIILiteral("Failed set property 'name' on 'IDBIndex': The index's object store has been deleted.") }; + + if (!m_objectStore.transaction().isVersionChange()) + return Exception { INVALID_STATE_ERR, ASCIILiteral("Failed set property 'name' on 'IDBIndex': The index's transaction is not a version change transaction.") }; + + if (!m_objectStore.transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed set property 'name' on 'IDBIndex': The index's transaction is not active.") }; + + if (m_info.name() == name) + return { }; + + if (m_objectStore.info().hasIndex(name)) + return Exception { IDBDatabaseException::ConstraintError, makeString("Failed set property 'name' on 'IDBIndex': The owning object store already has an index named '", name, "'.") }; + + m_objectStore.transaction().database().renameIndex(*this, name); + m_info.rename(name); + + return { }; } -PassRefPtr IDBIndex::openKeyCursor(ScriptExecutionContext* context, PassRefPtr keyRange, const String& directionString, ExceptionCode& ec) +IDBObjectStore& IDBIndex::objectStore() { - LOG(StorageAPI, "IDBIndex::openKeyCursor"); - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec); - if (ec) - return 0; + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + return m_objectStore; +} - RefPtr request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - request->setCursorDetails(IndexedDB::CursorType::KeyOnly, direction); - backendDB()->openCursor(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, direction, true, IDBDatabaseBackend::NormalTask, request); - return request; +const IDBKeyPath& IDBIndex::keyPath() const +{ + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + return m_info.keyPath(); } -PassRefPtr IDBIndex::openKeyCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode& ec) +bool IDBIndex::unique() const { - LOG(StorageAPI, "IDBIndex::openKeyCursor"); - RefPtr keyRange = IDBKeyRange::only(context, key, ec); - if (ec) - return 0; - return openKeyCursor(context, keyRange.release(), direction, ec); + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + return m_info.unique(); } -PassRefPtr IDBIndex::get(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) +bool IDBIndex::multiEntry() const { - LOG(StorageAPI, "IDBIndex::get"); - RefPtr keyRange = IDBKeyRange::only(context, key, ec); - if (ec) - return 0; - return get(context, keyRange.release(), ec); + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + return m_info.multiEntry(); } -PassRefPtr IDBIndex::get(ScriptExecutionContext* context, PassRefPtr keyRange, ExceptionCode& ec) +void IDBIndex::rollbackInfoForVersionChangeAbort() { - LOG(StorageAPI, "IDBIndex::get"); - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - if (!keyRange) { - ec = IDBDatabaseException::DataError; - return 0; + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + // Only rollback to the original info if this index still exists in the rolled-back database info. + auto* objectStoreInfo = m_objectStore.transaction().database().info().infoForExistingObjectStore(m_objectStore.info().identifier()); + if (!objectStoreInfo) + return; + + if (!objectStoreInfo->hasIndex(m_info.identifier())) { + m_deleted = true; + return; } - RefPtr request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - backendDB()->get(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, false, request); - return request; + m_info = m_originalInfo; + m_deleted = false; } -PassRefPtr IDBIndex::getKey(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) +ExceptionOr> IDBIndex::openCursor(ExecState& execState, IDBKeyRange* range, IDBCursorDirection direction) { - LOG(StorageAPI, "IDBIndex::getKey"); - RefPtr keyRange = IDBKeyRange::only(context, key, ec); - if (ec) - return 0; + LOG(IndexedDB, "IDBIndex::openCursor"); + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + if (m_deleted || m_objectStore.isDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBIndex': The index or its object store has been deleted.") }; + + if (!m_objectStore.transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBIndex': The transaction is inactive or finished.") }; - return getKey(context, keyRange.release(), ec); + IDBKeyRangeData rangeData = range; + if (rangeData.lowerKey.isNull()) + rangeData.lowerKey = IDBKeyData::minimum(); + if (rangeData.upperKey.isNull()) + rangeData.upperKey = IDBKeyData::maximum(); + + auto info = IDBCursorInfo::indexCursor(m_objectStore.transaction(), m_objectStore.info().identifier(), m_info.identifier(), rangeData, direction, IndexedDB::CursorType::KeyAndValue); + return m_objectStore.transaction().requestOpenCursor(execState, *this, info); } -PassRefPtr IDBIndex::getKey(ScriptExecutionContext* context, PassRefPtr keyRange, ExceptionCode& ec) +ExceptionOr> IDBIndex::openCursor(ExecState& execState, JSValue key, IDBCursorDirection direction) { - LOG(StorageAPI, "IDBIndex::getKey"); - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - if (!keyRange) { - ec = IDBDatabaseException::DataError; - return 0; - } + LOG(IndexedDB, "IDBIndex::openCursor"); + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + auto keyRange = IDBKeyRange::only(execState, key); + if (keyRange.hasException()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBIndex': The parameter is not a valid key.") }; + + return openCursor(execState, keyRange.releaseReturnValue().ptr(), direction); +} + +ExceptionOr> IDBIndex::openKeyCursor(ExecState& execState, IDBKeyRange* range, IDBCursorDirection direction) +{ + LOG(IndexedDB, "IDBIndex::openKeyCursor"); + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + if (m_deleted || m_objectStore.isDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBIndex': The index or its object store has been deleted.") }; + + if (!m_objectStore.transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBIndex': The transaction is inactive or finished.") }; - RefPtr request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - backendDB()->get(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, true, request); - return request; + auto info = IDBCursorInfo::indexCursor(m_objectStore.transaction(), m_objectStore.info().identifier(), m_info.identifier(), range, direction, IndexedDB::CursorType::KeyOnly); + return m_objectStore.transaction().requestOpenCursor(execState, *this, info); +} + +ExceptionOr> IDBIndex::openKeyCursor(ExecState& execState, JSValue key, IDBCursorDirection direction) +{ + LOG(IndexedDB, "IDBIndex::openKeyCursor"); + + auto keyRange = IDBKeyRange::only(execState, key); + if (keyRange.hasException()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBIndex': The parameter is not a valid key.") }; + return openKeyCursor(execState, keyRange.releaseReturnValue().ptr(), direction); +} + +ExceptionOr> IDBIndex::count(ExecState& execState, IDBKeyRange* range) +{ + LOG(IndexedDB, "IDBIndex::count"); + + return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys()); +} + +ExceptionOr> IDBIndex::count(ExecState& execState, JSValue key) +{ + LOG(IndexedDB, "IDBIndex::count"); + + auto idbKey = scriptValueToIDBKey(execState, key); + if (!idbKey->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'count' on 'IDBIndex': The parameter is not a valid key.") }; + + return doCount(execState, IDBKeyRangeData(idbKey.ptr())); +} + +ExceptionOr> IDBIndex::doCount(ExecState& execState, const IDBKeyRangeData& range) +{ + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + if (m_deleted || m_objectStore.isDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'count' on 'IDBIndex': The index or its object store has been deleted.") }; + + if (!range.isValid()) + return Exception { IDBDatabaseException::DataError }; + + auto& transaction = m_objectStore.transaction(); + if (!transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'count' on 'IDBIndex': The transaction is inactive or finished.") }; + + return transaction.requestCount(execState, *this, range); +} + +ExceptionOr> IDBIndex::get(ExecState& execState, IDBKeyRange* range) +{ + LOG(IndexedDB, "IDBIndex::get"); + + return doGet(execState, IDBKeyRangeData(range)); +} + +ExceptionOr> IDBIndex::get(ExecState& execState, JSValue key) +{ + LOG(IndexedDB, "IDBIndex::get"); + + auto idbKey = scriptValueToIDBKey(execState, key); + if (!idbKey->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'get' on 'IDBIndex': The parameter is not a valid key.") }; + + return doGet(execState, IDBKeyRangeData(idbKey.ptr())); +} + +ExceptionOr> IDBIndex::doGet(ExecState& execState, const IDBKeyRangeData& range) +{ + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + if (m_deleted || m_objectStore.isDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'get' on 'IDBIndex': The index or its object store has been deleted.") }; + + if (range.isNull) + return Exception { IDBDatabaseException::DataError }; + + auto& transaction = m_objectStore.transaction(); + if (!transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'get' on 'IDBIndex': The transaction is inactive or finished.") }; + + return transaction.requestGetValue(execState, *this, range); +} + +ExceptionOr> IDBIndex::getKey(ExecState& execState, IDBKeyRange* range) +{ + LOG(IndexedDB, "IDBIndex::getKey"); + + return doGetKey(execState, IDBKeyRangeData(range)); +} + +ExceptionOr> IDBIndex::getKey(ExecState& execState, JSValue key) +{ + LOG(IndexedDB, "IDBIndex::getKey"); + + auto idbKey = scriptValueToIDBKey(execState, key); + if (!idbKey->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getKey' on 'IDBIndex': The parameter is not a valid key.") }; + + return doGetKey(execState, IDBKeyRangeData(idbKey.ptr())); +} + +ExceptionOr> IDBIndex::doGetKey(ExecState& execState, const IDBKeyRangeData& range) +{ + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + if (m_deleted || m_objectStore.isDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getKey' on 'IDBIndex': The index or its object store has been deleted.") }; + + if (range.isNull) + return Exception { IDBDatabaseException::DataError }; + + auto& transaction = m_objectStore.transaction(); + if (!transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getKey' on 'IDBIndex': The transaction is inactive or finished.") }; + + return transaction.requestGetKey(execState, *this, range); +} + +ExceptionOr> IDBIndex::getAll(ExecState& execState, RefPtr range, std::optional count) +{ + LOG(IndexedDB, "IDBIndex::getAll"); + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + if (m_deleted || m_objectStore.isDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getAll' on 'IDBIndex': The index or its object store has been deleted.") }; + + if (!m_objectStore.transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getAll' on 'IDBIndex': The transaction is inactive or finished.") }; + + return m_objectStore.transaction().requestGetAllIndexRecords(execState, *this, range.get(), IndexedDB::GetAllType::Values, count); +} + +ExceptionOr> IDBIndex::getAll(ExecState& execState, JSValue key, std::optional count) +{ + auto onlyResult = IDBKeyRange::only(execState, key); + if (onlyResult.hasException()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getAll' on 'IDBIndex': The parameter is not a valid key.") }; + + return getAll(execState, onlyResult.releaseReturnValue(), count); +} + +ExceptionOr> IDBIndex::getAllKeys(ExecState& execState, RefPtr range, std::optional count) +{ + LOG(IndexedDB, "IDBIndex::getAllKeys"); + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + if (m_deleted || m_objectStore.isDeleted()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBIndex': The index or its object store has been deleted.") }; + + if (!m_objectStore.transaction().isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBIndex': The transaction is inactive or finished.") }; + + return m_objectStore.transaction().requestGetAllIndexRecords(execState, *this, range.get(), IndexedDB::GetAllType::Keys, count); +} + +ExceptionOr> IDBIndex::getAllKeys(ExecState& execState, JSValue key, std::optional count) +{ + auto onlyResult = IDBKeyRange::only(execState, key); + if (onlyResult.hasException()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBIndex': The parameter is not a valid key.") }; + + return getAllKeys(execState, onlyResult.releaseReturnValue(), count); +} + +void IDBIndex::markAsDeleted() +{ + ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID()); + + ASSERT(!m_deleted); + m_deleted = true; +} + +void IDBIndex::ref() +{ + m_objectStore.ref(); } -IDBDatabaseBackend* IDBIndex::backendDB() const +void IDBIndex::deref() { - return m_transaction->backendDB(); + m_objectStore.deref(); } } // namespace WebCore diff --git a/Source/WebCore/Modules/indexeddb/IDBIndex.h b/Source/WebCore/Modules/indexeddb/IDBIndex.h index f954ad6ac..b3134fde2 100644 --- a/Source/WebCore/Modules/indexeddb/IDBIndex.h +++ b/Source/WebCore/Modules/indexeddb/IDBIndex.h @@ -1,101 +1,108 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBIndex_h -#define IDBIndex_h +#pragma once + +#if ENABLE(INDEXED_DATABASE) #include "IDBCursor.h" -#include "IDBDatabase.h" -#include "IDBDatabaseMetadata.h" -#include "IDBKeyPath.h" -#include "IDBKeyRange.h" -#include "IDBObjectStore.h" +#include "IDBIndexInfo.h" #include "IDBRequest.h" -#include "ScriptWrappable.h" -#include -#include -#if ENABLE(INDEXED_DATABASE) +namespace JSC { +class ExecState; +} namespace WebCore { -class IDBObjectStore; +class IDBKeyRange; + +struct IDBKeyRangeData; -class IDBIndex : public ScriptWrappable, public RefCounted { +class IDBIndex final : private ActiveDOMObject { public: - static PassRefPtr create(const IDBIndexMetadata& metadata, IDBObjectStore* objectStore, IDBTransaction* transaction) - { - return adoptRef(new IDBIndex(metadata, objectStore, transaction)); - } - ~IDBIndex(); - - // Implement the IDL - const String name() const { return m_metadata.name; } - PassRefPtr objectStore() const { return m_objectStore; } - PassRefPtr keyPathAny() const { return IDBAny::create(m_metadata.keyPath); } - const IDBKeyPath keyPath() const { return m_metadata.keyPath; } - bool unique() const { return m_metadata.unique; } - bool multiEntry() const { return m_metadata.multiEntry; } - int64_t id() const { return m_metadata.id; } - - // FIXME: Try to modify the code generator so this is unneeded. - PassRefPtr openCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openCursor(context, static_cast(0), ec); } - PassRefPtr openCursor(ScriptExecutionContext* context, PassRefPtr keyRange, ExceptionCode& ec) { return openCursor(context, keyRange, IDBCursor::directionNext(), ec); } - PassRefPtr openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) { return openCursor(context, key, IDBCursor::directionNext(), ec); } - PassRefPtr openCursor(ScriptExecutionContext*, PassRefPtr, const String& direction, ExceptionCode&); - PassRefPtr openCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode&); - PassRefPtr count(ScriptExecutionContext* context, ExceptionCode& ec) { return count(context, static_cast(0), ec); } - PassRefPtr count(ScriptExecutionContext*, PassRefPtr, ExceptionCode&); - PassRefPtr count(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&); - - PassRefPtr openKeyCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openKeyCursor(context, static_cast(0), ec); } - PassRefPtr openKeyCursor(ScriptExecutionContext* context, PassRefPtr keyRange, ExceptionCode& ec) { return openKeyCursor(context, keyRange, IDBCursor::directionNext(), ec); } - PassRefPtr openKeyCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) { return openKeyCursor(context, key, IDBCursor::directionNext(), ec); } - PassRefPtr openKeyCursor(ScriptExecutionContext*, PassRefPtr, const String& direction, ExceptionCode&); - PassRefPtr openKeyCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode&); - - PassRefPtr get(ScriptExecutionContext*, PassRefPtr, ExceptionCode&); - PassRefPtr get(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&); - PassRefPtr getKey(ScriptExecutionContext*, PassRefPtr, ExceptionCode&); - PassRefPtr getKey(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&); - - void markDeleted() { m_deleted = true; } - - IDBDatabaseBackend* backendDB() const; + IDBIndex(ScriptExecutionContext&, const IDBIndexInfo&, IDBObjectStore&); + + virtual ~IDBIndex(); + + const String& name() const; + ExceptionOr setName(const String&); + IDBObjectStore& objectStore(); + const IDBKeyPath& keyPath() const; + bool unique() const; + bool multiEntry() const; + + void rollbackInfoForVersionChangeAbort(); + + ExceptionOr> openCursor(JSC::ExecState&, IDBKeyRange*, IDBCursorDirection); + ExceptionOr> openCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection); + ExceptionOr> openKeyCursor(JSC::ExecState&, IDBKeyRange*, IDBCursorDirection); + ExceptionOr> openKeyCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection); + + ExceptionOr> count(JSC::ExecState&, IDBKeyRange*); + ExceptionOr> count(JSC::ExecState&, JSC::JSValue key); + + ExceptionOr> get(JSC::ExecState&, IDBKeyRange*); + ExceptionOr> get(JSC::ExecState&, JSC::JSValue key); + ExceptionOr> getKey(JSC::ExecState&, IDBKeyRange*); + ExceptionOr> getKey(JSC::ExecState&, JSC::JSValue key); + + ExceptionOr> getAll(JSC::ExecState&, RefPtr, std::optional count); + ExceptionOr> getAll(JSC::ExecState&, JSC::JSValue key, std::optional count); + ExceptionOr> getAllKeys(JSC::ExecState&, RefPtr, std::optional count); + ExceptionOr> getAllKeys(JSC::ExecState&, JSC::JSValue key, std::optional count); + + const IDBIndexInfo& info() const { return m_info; } + + void markAsDeleted(); + bool isDeleted() const { return m_deleted; } + + void ref(); + void deref(); + + void* objectStoreAsOpaqueRoot() { return &m_objectStore; } private: - IDBIndex(const IDBIndexMetadata&, IDBObjectStore*, IDBTransaction*); + ExceptionOr> doCount(JSC::ExecState&, const IDBKeyRangeData&); + ExceptionOr> doGet(JSC::ExecState&, const IDBKeyRangeData&); + ExceptionOr> doGetKey(JSC::ExecState&, const IDBKeyRangeData&); + + const char* activeDOMObjectName() const final; + bool canSuspendForDocumentSuspension() const final; + bool hasPendingActivity() const final; + + IDBIndexInfo m_info; + IDBIndexInfo m_originalInfo; - IDBIndexMetadata m_metadata; - RefPtr m_objectStore; - RefPtr m_transaction; - bool m_deleted; + bool m_deleted { false }; + + // IDBIndex objects are always owned by their referencing IDBObjectStore. + // Indexes will never outlive ObjectStores so its okay to keep a raw C++ reference here. + IDBObjectStore& m_objectStore; }; } // namespace WebCore -#endif - -#endif // IDBIndex_h +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBIndex.idl b/Source/WebCore/Modules/indexeddb/IDBIndex.idl index 344f516aa..8d7e99dd4 100644 --- a/Source/WebCore/Modules/indexeddb/IDBIndex.idl +++ b/Source/WebCore/Modules/indexeddb/IDBIndex.idl @@ -23,28 +23,36 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// NOTE: This type is specified as 'any' in the IndexedDB specification, but is always +// constrained to this union. +typedef (DOMString or sequence) IDBKeyPath; + [ Conditional=INDEXED_DATABASE, - JSNoStaticTables, - ImplementationLacksVTable, + GenerateIsReachable=Impl, + JSCustomMarkFunction, + SkipVTableValidation, ] interface IDBIndex { - readonly attribute DOMString name; + [SetterMayThrowException] attribute DOMString name; readonly attribute IDBObjectStore objectStore; - [ImplementedAs=keyPathAny] readonly attribute IDBAny keyPath; + readonly attribute IDBKeyPath? keyPath; readonly attribute boolean multiEntry; readonly attribute boolean unique; - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openCursor(optional IDBKeyRange? range, optional DOMString direction); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openCursor(any key, optional DOMString direction); + [CallWith=ScriptState, MayThrowException] IDBRequest openCursor(optional IDBKeyRange? range = null, optional IDBCursorDirection direction = "next"); + [CallWith=ScriptState, MayThrowException] IDBRequest openCursor(any key, optional IDBCursorDirection direction = "next"); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openKeyCursor(optional IDBKeyRange? range, optional DOMString direction); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openKeyCursor(any key, optional DOMString direction); + [CallWith=ScriptState, MayThrowException] IDBRequest openKeyCursor(optional IDBKeyRange? range = null, optional IDBCursorDirection direction = "next"); + [CallWith=ScriptState, MayThrowException] IDBRequest openKeyCursor(any key, optional IDBCursorDirection direction = "next"); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest get(IDBKeyRange? key); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest get(any key); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest getKey(IDBKeyRange? key); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest getKey(any key); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest count(optional IDBKeyRange? range); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest count(any key); + [CallWith=ScriptState, MayThrowException] IDBRequest get(IDBKeyRange? key); + [CallWith=ScriptState, MayThrowException] IDBRequest get(any key); + [CallWith=ScriptState, MayThrowException] IDBRequest getKey(IDBKeyRange? key); + [CallWith=ScriptState, MayThrowException] IDBRequest getKey(any key); + [CallWith=ScriptState, MayThrowException] IDBRequest getAll(optional IDBKeyRange? range = null, [EnforceRange] optional unsigned long count); + [CallWith=ScriptState, MayThrowException] IDBRequest getAll(any key, [EnforceRange] optional unsigned long count); + [CallWith=ScriptState, MayThrowException] IDBRequest getAllKeys(optional IDBKeyRange? range = null, [EnforceRange] optional unsigned long count); + [CallWith=ScriptState, MayThrowException] IDBRequest getAllKeys(any key, [EnforceRange] optional unsigned long count); + [CallWith=ScriptState, MayThrowException] IDBRequest count(optional IDBKeyRange? range = null); + [CallWith=ScriptState, MayThrowException] IDBRequest count(any key); }; - diff --git a/Source/WebCore/Modules/indexeddb/IDBIndexMetadata.h b/Source/WebCore/Modules/indexeddb/IDBIndexMetadata.h deleted file mode 100644 index 724bf093d..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBIndexMetadata.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBIndexMetadata_h -#define IDBIndexMetadata_h - -#include "IDBKeyPath.h" -#include -#include - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -struct IDBIndexMetadata { - IDBIndexMetadata() - { - } - - IDBIndexMetadata(const String& name, int64_t id, const IDBKeyPath& keyPath, bool unique, bool multiEntry) - : name(name) - , id(id) - , keyPath(keyPath) - , unique(unique) - , multiEntry(multiEntry) - { - } - - String name; - int64_t id; - IDBKeyPath keyPath; - bool unique; - bool multiEntry; - - IDBIndexMetadata isolatedCopy() const; - - static const int64_t InvalidId = -1; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBIndexMetadata_h diff --git a/Source/WebCore/Modules/indexeddb/IDBKey.cpp b/Source/WebCore/Modules/indexeddb/IDBKey.cpp index 5196decbd..3e638685c 100644 --- a/Source/WebCore/Modules/indexeddb/IDBKey.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBKey.cpp @@ -29,21 +29,72 @@ #if ENABLE(INDEXED_DATABASE) #include "IDBKeyData.h" +#include +#include +#include +#include namespace WebCore { +using IDBKeyVector = Vector>; + +Ref IDBKey::createBinary(const ThreadSafeDataBuffer& buffer) +{ + return adoptRef(*new IDBKey(buffer)); +} + +Ref IDBKey::createBinary(JSC::JSArrayBuffer& arrayBuffer) +{ + auto* buffer = arrayBuffer.impl(); + return adoptRef(*new IDBKey(ThreadSafeDataBuffer::copyData(buffer->data(), buffer->byteLength()))); +} + +Ref IDBKey::createBinary(JSC::JSArrayBufferView& arrayBufferView) +{ + auto bufferView = arrayBufferView.possiblySharedImpl(); + return adoptRef(*new IDBKey(ThreadSafeDataBuffer::copyData(bufferView->data(), bufferView->byteLength()))); +} + +IDBKey::IDBKey(KeyType type, double number) + : m_type(type) + , m_value(number) + , m_sizeEstimate(OverheadSize + sizeof(double)) +{ +} + +IDBKey::IDBKey(const String& value) + : m_type(KeyType::String) + , m_value(value) + , m_sizeEstimate(OverheadSize + value.length() * sizeof(UChar)) +{ +} + +IDBKey::IDBKey(const IDBKeyVector& keyArray, size_t arraySize) + : m_type(KeyType::Array) + , m_value(keyArray) + , m_sizeEstimate(OverheadSize + arraySize) +{ +} + +IDBKey::IDBKey(const ThreadSafeDataBuffer& buffer) + : m_type(KeyType::Binary) + , m_value(buffer) + , m_sizeEstimate(OverheadSize + buffer.size()) +{ +} + IDBKey::~IDBKey() { } bool IDBKey::isValid() const { - if (m_type == InvalidType) + if (m_type == KeyType::Invalid) return false; - if (m_type == ArrayType) { - for (size_t i = 0; i < m_array.size(); i++) { - if (!m_array[i]->isValid()) + if (m_type == KeyType::Array) { + for (auto& key : WTF::get(m_value)) { + if (!key->isValid()) return false; } } @@ -51,31 +102,38 @@ bool IDBKey::isValid() const return true; } -int IDBKey::compare(const IDBKey* other) const +int IDBKey::compare(const IDBKey& other) const { - ASSERT(other); - if (m_type != other->m_type) - return m_type > other->m_type ? -1 : 1; + if (m_type != other.m_type) + return m_type > other.m_type ? -1 : 1; switch (m_type) { - case ArrayType: - for (size_t i = 0; i < m_array.size() && i < other->m_array.size(); ++i) { - if (int result = m_array[i]->compare(other->m_array[i].get())) + case KeyType::Array: { + auto& array = WTF::get(m_value); + auto& otherArray = WTF::get(other.m_value); + for (size_t i = 0; i < array.size() && i < otherArray.size(); ++i) { + if (int result = array[i]->compare(*otherArray[i])) return result; } - if (m_array.size() < other->m_array.size()) + if (array.size() < otherArray.size()) return -1; - if (m_array.size() > other->m_array.size()) + if (array.size() > otherArray.size()) return 1; return 0; - case StringType: - return -codePointCompare(other->m_string, m_string); - case DateType: - case NumberType: - return (m_number < other->m_number) ? -1 : - (m_number > other-> m_number) ? 1 : 0; - case InvalidType: - case MinType: + } + case KeyType::Binary: + return compareBinaryKeyData(WTF::get(m_value), WTF::get(other.m_value)); + case KeyType::String: + return -codePointCompare(WTF::get(other.m_value), WTF::get(m_value)); + case KeyType::Date: + case KeyType::Number: { + auto number = WTF::get(m_value); + auto otherNumber = WTF::get(other.m_value); + return (number < otherNumber) ? -1 : ((number > otherNumber) ? 1 : 0); + } + case KeyType::Invalid: + case KeyType::Min: + case KeyType::Max: ASSERT_NOT_REACHED(); return 0; } @@ -84,20 +142,23 @@ int IDBKey::compare(const IDBKey* other) const return 0; } -bool IDBKey::isLessThan(const IDBKey* other) const +bool IDBKey::isLessThan(const IDBKey& other) const { - ASSERT(other); return compare(other) == -1; } -bool IDBKey::isEqual(const IDBKey* other) const +bool IDBKey::isEqual(const IDBKey& other) const { - if (!other) - return false; - return !compare(other); } +#if !LOG_DISABLED +String IDBKey::loggingString() const +{ + return IDBKeyData(this).loggingString(); +} +#endif + } // namespace WebCore #endif diff --git a/Source/WebCore/Modules/indexeddb/IDBKey.h b/Source/WebCore/Modules/indexeddb/IDBKey.h index 90b62d599..bc5ed5032 100644 --- a/Source/WebCore/Modules/indexeddb/IDBKey.h +++ b/Source/WebCore/Modules/indexeddb/IDBKey.h @@ -23,123 +23,130 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBKey_h -#define IDBKey_h +#pragma once #if ENABLE(INDEXED_DATABASE) +#include "IndexedDB.h" +#include "ThreadSafeDataBuffer.h" #include #include +#include #include #include +using WebCore::IndexedDB::KeyType; + +namespace JSC { +class JSArrayBuffer; +class JSArrayBufferView; +} + namespace WebCore { class IDBKey : public RefCounted { public: - typedef Vector> KeyArray; - - static PassRefPtr createInvalid() + static Ref createInvalid() { - return adoptRef(new IDBKey()); + return adoptRef(*new IDBKey()); } - static PassRefPtr createNumber(double number) + static Ref createNumber(double number) { - return adoptRef(new IDBKey(NumberType, number)); + return adoptRef(*new IDBKey(KeyType::Number, number)); } - static PassRefPtr createString(const String& string) + static Ref createString(const String& string) { - return adoptRef(new IDBKey(string)); + return adoptRef(*new IDBKey(string)); } - static PassRefPtr createDate(double date) + static Ref createDate(double date) { - return adoptRef(new IDBKey(DateType, date)); + return adoptRef(*new IDBKey(KeyType::Date, date)); } - static PassRefPtr createMultiEntryArray(const KeyArray& array) + static Ref createMultiEntryArray(const Vector>& array) { - KeyArray result; + Vector> result; size_t sizeEstimate = 0; - for (size_t i = 0; i < array.size(); i++) { - if (!array[i]->isValid()) + for (auto& key : array) { + if (!key->isValid()) continue; bool skip = false; - for (size_t j = 0; j < result.size(); j++) { - if (array[i]->isEqual(result[j].get())) { + for (auto& resultKey : result) { + if (key->isEqual(*resultKey)) { skip = true; break; } } if (!skip) { - result.append(array[i]); - sizeEstimate += array[i]->m_sizeEstimate; + result.append(key); + sizeEstimate += key->m_sizeEstimate; } } - RefPtr idbKey = adoptRef(new IDBKey(result, sizeEstimate)); + Ref idbKey = adoptRef(*new IDBKey(result, sizeEstimate)); ASSERT(idbKey->isValid()); - return idbKey.release(); + return idbKey; } - static PassRefPtr createArray(const KeyArray& array) + static Ref createArray(const Vector>& array) { size_t sizeEstimate = 0; - for (size_t i = 0; i < array.size(); ++i) - sizeEstimate += array[i]->m_sizeEstimate; + for (auto& key : array) + sizeEstimate += key->m_sizeEstimate; - return adoptRef(new IDBKey(array, sizeEstimate)); + return adoptRef(*new IDBKey(array, sizeEstimate)); } - ~IDBKey(); + static Ref createBinary(const ThreadSafeDataBuffer&); + static Ref createBinary(JSC::JSArrayBuffer&); + static Ref createBinary(JSC::JSArrayBufferView&); - // In order of the least to the highest precedent in terms of sort order. - enum Type { - InvalidType = 0, - ArrayType, - StringType, - DateType, - NumberType, - MinType - }; + WEBCORE_EXPORT ~IDBKey(); - Type type() const { return m_type; } - bool isValid() const; + KeyType type() const { return m_type; } + WEBCORE_EXPORT bool isValid() const; - const KeyArray& array() const + const Vector>& array() const { - ASSERT(m_type == ArrayType); - return m_array; + ASSERT(m_type == KeyType::Array); + return WTF::get>>(m_value); } const String& string() const { - ASSERT(m_type == StringType); - return m_string; + ASSERT(m_type == KeyType::String); + return WTF::get(m_value); } double date() const { - ASSERT(m_type == DateType); - return m_number; + ASSERT(m_type == KeyType::Date); + return WTF::get(m_value); } double number() const { - ASSERT(m_type == NumberType); - return m_number; + ASSERT(m_type == KeyType::Number); + return WTF::get(m_value); + } + + const ThreadSafeDataBuffer& binary() const + { + ASSERT(m_type == KeyType::Binary); + return WTF::get(m_value); } - int compare(const IDBKey* other) const; - bool isLessThan(const IDBKey* other) const; - bool isEqual(const IDBKey* other) const; + int compare(const IDBKey& other) const; + bool isLessThan(const IDBKey& other) const; + bool isEqual(const IDBKey& other) const; size_t sizeEstimate() const { return m_sizeEstimate; } - static int compareTypes(Type a, Type b) + static int compareTypes(KeyType a, KeyType b) { return b - a; } @@ -147,16 +154,24 @@ public: using RefCounted::ref; using RefCounted::deref; +#if !LOG_DISABLED + String loggingString() const; +#endif + private: - IDBKey() : m_type(InvalidType), m_number(0), m_sizeEstimate(OverheadSize) { } - IDBKey(Type type, double number) : m_type(type), m_number(number), m_sizeEstimate(OverheadSize + sizeof(double)) { } - explicit IDBKey(const String& value) : m_type(StringType), m_string(value), m_number(0), m_sizeEstimate(OverheadSize + value.length() * sizeof(UChar)) { } - IDBKey(const KeyArray& keyArray, size_t arraySize) : m_type(ArrayType), m_array(keyArray), m_number(0), m_sizeEstimate(OverheadSize + arraySize) { } + IDBKey() + : m_type(KeyType::Invalid) + , m_sizeEstimate(OverheadSize) + { + } + + IDBKey(KeyType, double number); + explicit IDBKey(const String& value); + IDBKey(const Vector>& keyArray, size_t arraySize); + explicit IDBKey(const ThreadSafeDataBuffer&); - const Type m_type; - const KeyArray m_array; - const String m_string; - const double m_number; + const KeyType m_type; + Variant>, String, double, ThreadSafeDataBuffer> m_value; const size_t m_sizeEstimate; @@ -164,8 +179,43 @@ private: enum { OverheadSize = 16 }; }; +inline int compareBinaryKeyData(const Vector& a, const Vector& b) +{ + size_t length = std::min(a.size(), b.size()); + + for (size_t i = 0; i < length; ++i) { + if (a[i] > b[i]) + return 1; + if (a[i] < b[i]) + return -1; + } + + if (a.size() == b.size()) + return 0; + + if (a.size() > b.size()) + return 1; + + return -1; } -#endif // ENABLE(INDEXED_DATABASE) +inline int compareBinaryKeyData(const ThreadSafeDataBuffer& a, const ThreadSafeDataBuffer& b) +{ + auto* aData = a.data(); + auto* bData = b.data(); + + // Covers the cases where both pointers are null as well as both pointing to the same buffer. + if (aData == bData) + return 0; -#endif // IDBKey_h + if (aData && !bData) + return 1; + if (!aData && bData) + return -1; + + return compareBinaryKeyData(*aData, *bData); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp b/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp index 46ca3fc0c..3d5abf383 100644 --- a/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp @@ -29,68 +29,74 @@ #if ENABLE(INDEXED_DATABASE) #include "KeyedCoding.h" +#include namespace WebCore { IDBKeyData::IDBKeyData(const IDBKey* key) - : type(IDBKey::InvalidType) - , numberValue(0) - , isNull(false) + : m_type(KeyType::Invalid) { if (!key) { - isNull = true; + m_isNull = true; return; } - type = key->type(); - numberValue = 0; + m_type = key->type(); - switch (type) { - case IDBKey::InvalidType: + switch (m_type) { + case KeyType::Invalid: break; - case IDBKey::ArrayType: - for (auto key2 : key->array()) - arrayValue.append(IDBKeyData(key2.get())); + case KeyType::Array: { + m_value = Vector(); + auto& array = WTF::get>(m_value); + for (auto& key2 : key->array()) + array.append(IDBKeyData(key2.get())); break; - case IDBKey::StringType: - stringValue = key->string(); + } + case KeyType::Binary: + m_value = key->binary(); break; - case IDBKey::DateType: - numberValue = key->date(); + case KeyType::String: + m_value = key->string(); break; - case IDBKey::NumberType: - numberValue = key->number(); + case KeyType::Date: + m_value = key->date(); break; - case IDBKey::MinType: - ASSERT_NOT_REACHED(); + case KeyType::Number: + m_value = key->number(); + break; + case KeyType::Max: + case KeyType::Min: break; } } -PassRefPtr IDBKeyData::maybeCreateIDBKey() const +RefPtr IDBKeyData::maybeCreateIDBKey() const { - if (isNull) + if (m_isNull) return nullptr; - switch (type) { - case IDBKey::InvalidType: + switch (m_type) { + case KeyType::Invalid: return IDBKey::createInvalid(); - case IDBKey::ArrayType: - { - IDBKey::KeyArray array; - for (auto keyData : arrayValue) { - array.append(keyData.maybeCreateIDBKey()); - ASSERT(array.last()); - } - return IDBKey::createArray(array); + case KeyType::Array: { + Vector> array; + for (auto& keyData : WTF::get>(m_value)) { + array.append(keyData.maybeCreateIDBKey()); + ASSERT(array.last()); } - case IDBKey::StringType: - return IDBKey::createString(stringValue); - case IDBKey::DateType: - return IDBKey::createDate(numberValue); - case IDBKey::NumberType: - return IDBKey::createNumber(numberValue); - case IDBKey::MinType: + return IDBKey::createArray(array); + } + case KeyType::Binary: + return IDBKey::createBinary(WTF::get(m_value)); + case KeyType::String: + return IDBKey::createString(WTF::get(m_value)); + case KeyType::Date: + return IDBKey::createDate(WTF::get(m_value)); + case KeyType::Number: + return IDBKey::createNumber(WTF::get(m_value)); + case KeyType::Max: + case KeyType::Min: ASSERT_NOT_REACHED(); return nullptr; } @@ -99,74 +105,354 @@ PassRefPtr IDBKeyData::maybeCreateIDBKey() const return nullptr; } +IDBKeyData::IDBKeyData(const IDBKeyData& that, IsolatedCopyTag) +{ + isolatedCopy(that, *this); +} + IDBKeyData IDBKeyData::isolatedCopy() const { - IDBKeyData result; - result.type = type; - result.isNull = isNull; - - switch (type) { - case IDBKey::InvalidType: - return result; - case IDBKey::ArrayType: - for (auto key : arrayValue) - result.arrayValue.append(key.isolatedCopy()); - return result; - case IDBKey::StringType: - result.stringValue = stringValue.isolatedCopy(); - return result; - case IDBKey::DateType: - case IDBKey::NumberType: - result.numberValue = numberValue; - return result; - case IDBKey::MinType: - ASSERT_NOT_REACHED(); - return result; + return { *this, IsolatedCopy }; +} + +void IDBKeyData::isolatedCopy(const IDBKeyData& source, IDBKeyData& destination) +{ + destination.m_type = source.m_type; + destination.m_isNull = source.m_isNull; + + switch (source.m_type) { + case KeyType::Invalid: + return; + case KeyType::Array: { + destination.m_value = Vector(); + auto& destinationArray = WTF::get>(destination.m_value); + for (auto& key : WTF::get>(source.m_value)) + destinationArray.append(key.isolatedCopy()); + return; + } + case KeyType::Binary: + destination.m_value = WTF::get(source.m_value); + return; + case KeyType::String: + destination.m_value = WTF::get(source.m_value).isolatedCopy(); + return; + case KeyType::Date: + case KeyType::Number: + destination.m_value = WTF::get(source.m_value); + return; + case KeyType::Max: + case KeyType::Min: + return; } ASSERT_NOT_REACHED(); - return result; } void IDBKeyData::encode(KeyedEncoder& encoder) const { - encoder.encodeBool("null", isNull); - if (isNull) + encoder.encodeBool("null", m_isNull); + if (m_isNull) return; - encoder.encodeEnum("type", type); + encoder.encodeEnum("type", m_type); - switch (type) { - case IDBKey::InvalidType: + switch (m_type) { + case KeyType::Invalid: return; - case IDBKey::ArrayType: - encoder.encodeObjects("array", arrayValue.begin(), arrayValue.end(), [](KeyedEncoder& encoder, const IDBKeyData& key) { - encoder.encodeObject("idbKeyData", key, [](KeyedEncoder& encoder, const IDBKeyData& key) { - key.encode(encoder); - }); + case KeyType::Array: { + auto& array = WTF::get>(m_value); + encoder.encodeObjects("array", array.begin(), array.end(), [](KeyedEncoder& encoder, const IDBKeyData& key) { + key.encode(encoder); }); return; - case IDBKey::StringType: - encoder.encodeString("string", stringValue); + } + case KeyType::Binary: { + auto* data = WTF::get(m_value).data(); + encoder.encodeBool("hasBinary", !!data); + if (data) + encoder.encodeBytes("binary", data->data(), data->size()); + return; + } + case KeyType::String: + encoder.encodeString("string", WTF::get(m_value)); return; - case IDBKey::DateType: - case IDBKey::NumberType: - encoder.encodeDouble("number", numberValue); + case KeyType::Date: + case KeyType::Number: + encoder.encodeDouble("number", WTF::get(m_value)); return; - case IDBKey::MinType: - ASSERT_NOT_REACHED(); + case KeyType::Max: + case KeyType::Min: return; } ASSERT_NOT_REACHED(); } -bool IDBKeyData::decode(KeyedDecoder&, IDBKeyData&) +bool IDBKeyData::decode(KeyedDecoder& decoder, IDBKeyData& result) +{ + if (!decoder.decodeBool("null", result.m_isNull)) + return false; + + if (result.m_isNull) + return true; + + auto enumFunction = [](int64_t value) { + return value == KeyType::Max + || value == KeyType::Invalid + || value == KeyType::Array + || value == KeyType::Binary + || value == KeyType::String + || value == KeyType::Date + || value == KeyType::Number + || value == KeyType::Min; + }; + if (!decoder.decodeEnum("type", result.m_type, enumFunction)) + return false; + + if (result.m_type == KeyType::Invalid) + return true; + + if (result.m_type == KeyType::Max) + return true; + + if (result.m_type == KeyType::Min) + return true; + + if (result.m_type == KeyType::String) { + result.m_value = String(); + return decoder.decodeString("string", WTF::get(result.m_value)); + } + + if (result.m_type == KeyType::Number || result.m_type == KeyType::Date) { + result.m_value = 0.0; + return decoder.decodeDouble("number", WTF::get(result.m_value)); + } + + if (result.m_type == KeyType::Binary) { + result.m_value = ThreadSafeDataBuffer(); + + bool hasBinaryData; + if (!decoder.decodeBool("hasBinary", hasBinaryData)) + return false; + + if (!hasBinaryData) + return true; + + Vector bytes; + if (!decoder.decodeBytes("binary", bytes)) + return false; + + result.m_value = ThreadSafeDataBuffer::adoptVector(bytes); + return true; + } + + ASSERT(result.m_type == KeyType::Array); + + auto arrayFunction = [](KeyedDecoder& decoder, IDBKeyData& result) { + return decode(decoder, result); + }; + + result.m_value = Vector(); + return decoder.decodeObjects("array", WTF::get>(result.m_value), arrayFunction); +} + +int IDBKeyData::compare(const IDBKeyData& other) const +{ + if (m_type == KeyType::Invalid) { + if (other.m_type != KeyType::Invalid) + return -1; + if (other.m_type == KeyType::Invalid) + return 0; + } else if (other.m_type == KeyType::Invalid) + return 1; + + // The IDBKey::m_type enum is in reverse sort order. + if (m_type != other.m_type) + return m_type < other.m_type ? 1 : -1; + + // The types are the same, so handle actual value comparison. + switch (m_type) { + case KeyType::Invalid: + // Invalid type should have been fully handled above + ASSERT_NOT_REACHED(); + return 0; + case KeyType::Array: { + auto& array = WTF::get>(m_value); + auto& otherArray = WTF::get>(other.m_value); + for (size_t i = 0; i < array.size() && i < otherArray.size(); ++i) { + if (int result = array[i].compare(otherArray[i])) + return result; + } + if (array.size() < otherArray.size()) + return -1; + if (array.size() > otherArray.size()) + return 1; + return 0; + } + case KeyType::Binary: + return compareBinaryKeyData(WTF::get(m_value), WTF::get(other.m_value)); + case KeyType::String: + return codePointCompare(WTF::get(m_value), WTF::get(other.m_value)); + case KeyType::Date: + case KeyType::Number: { + auto number = WTF::get(m_value); + auto otherNumber = WTF::get(other.m_value); + + if (number == otherNumber) + return 0; + return number > otherNumber ? 1 : -1; + } + case KeyType::Max: + case KeyType::Min: + return 0; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +#if !LOG_DISABLED +String IDBKeyData::loggingString() const { - // FIXME: Implement when IDB Get support is implemented () - return false; + if (m_isNull) + return ""; + + String result; + + switch (m_type) { + case KeyType::Invalid: + return ""; + case KeyType::Array: { + StringBuilder builder; + builder.appendLiteral(" - { "); + auto& array = WTF::get>(m_value); + for (size_t i = 0; i < array.size(); ++i) { + builder.append(array[i].loggingString()); + if (i < array.size() - 1) + builder.appendLiteral(", "); + } + builder.appendLiteral(" }"); + result = builder.toString(); + break; + } + case KeyType::Binary: { + StringBuilder builder; + builder.append(" - "); + + auto* data = WTF::get(m_value).data(); + if (!data) { + builder.append("(null)"); + result = builder.toString(); + break; + } + + size_t i = 0; + for (; i < 8 && i < data->size(); ++i) + builder.append(String::format("%02x", data->at(i))); + + if (data->size() > 8) + builder.append("..."); + + result = builder.toString(); + break; + } + case KeyType::String: + result = " - " + WTF::get(m_value); + break; + case KeyType::Date: + return String::format(" - %f", WTF::get(m_value)); + case KeyType::Number: + return String::format(" - %f", WTF::get(m_value)); + case KeyType::Max: + return ""; + case KeyType::Min: + return ""; + } + + if (result.length() > 150) { + result.truncate(147); + result.append(WTF::ASCIILiteral("...")); + } + + return result; } +#endif +void IDBKeyData::setArrayValue(const Vector& value) +{ + *this = IDBKeyData(); + m_value = value; + m_type = KeyType::Array; + m_isNull = false; } +void IDBKeyData::setBinaryValue(const ThreadSafeDataBuffer& value) +{ + *this = IDBKeyData(); + m_value = value; + m_type = KeyType::Binary; + m_isNull = false; +} + +void IDBKeyData::setStringValue(const String& value) +{ + *this = IDBKeyData(); + m_value = value; + m_type = KeyType::String; + m_isNull = false; +} + +void IDBKeyData::setDateValue(double value) +{ + *this = IDBKeyData(); + m_value = value; + m_type = KeyType::Date; + m_isNull = false; +} + +void IDBKeyData::setNumberValue(double value) +{ + *this = IDBKeyData(); + m_value = value; + m_type = KeyType::Number; + m_isNull = false; +} + +IDBKeyData IDBKeyData::deletedValue() +{ + IDBKeyData result; + result.m_isNull = false; + result.m_isDeletedValue = true; + return result; +} + +bool IDBKeyData::operator<(const IDBKeyData& rhs) const +{ + return compare(rhs) < 0; +} + +bool IDBKeyData::operator==(const IDBKeyData& other) const +{ + if (m_type != other.m_type || m_isNull != other.m_isNull || m_isDeletedValue != other.m_isDeletedValue) + return false; + switch (m_type) { + case KeyType::Invalid: + case KeyType::Max: + case KeyType::Min: + return true; + case KeyType::Number: + case KeyType::Date: + return WTF::get(m_value) == WTF::get(other.m_value); + case KeyType::String: + return WTF::get(m_value) == WTF::get(other.m_value); + case KeyType::Binary: + return WTF::get(m_value) == WTF::get(other.m_value); + case KeyType::Array: + return WTF::get>(m_value) == WTF::get>(other.m_value); + } + RELEASE_ASSERT_NOT_REACHED(); +} + +} // namespace WebCore + #endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyData.h b/Source/WebCore/Modules/indexeddb/IDBKeyData.h index 6a4f64a06..98f0b7359 100644 --- a/Source/WebCore/Modules/indexeddb/IDBKeyData.h +++ b/Source/WebCore/Modules/indexeddb/IDBKeyData.h @@ -23,43 +23,282 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBKeyData_h -#define IDBKeyData_h +#pragma once #if ENABLE(INDEXED_DATABASE) #include "IDBKey.h" +#include +#include namespace WebCore { class KeyedDecoder; class KeyedEncoder; -struct IDBKeyData { +class IDBKeyData { +public: IDBKeyData() - : type(IDBKey::InvalidType) - , numberValue(0) - , isNull(true) + : m_type(KeyType::Invalid) + , m_isNull(true) { } - IDBKeyData(const IDBKey*); + WEBCORE_EXPORT IDBKeyData(const IDBKey*); - PassRefPtr maybeCreateIDBKey() const; + enum IsolatedCopyTag { IsolatedCopy }; + IDBKeyData(const IDBKeyData&, IsolatedCopyTag); + + static IDBKeyData minimum() + { + IDBKeyData result; + result.m_type = KeyType::Min; + result.m_isNull = false; + return result; + } + + static IDBKeyData maximum() + { + IDBKeyData result; + result.m_type = KeyType::Max; + result.m_isNull = false; + return result; + } + + WEBCORE_EXPORT RefPtr maybeCreateIDBKey() const; IDBKeyData isolatedCopy() const; - void encode(KeyedEncoder&) const; - static bool decode(KeyedDecoder&, IDBKeyData&); + WEBCORE_EXPORT void encode(KeyedEncoder&) const; + WEBCORE_EXPORT static bool decode(KeyedDecoder&, IDBKeyData&); + + // compare() has the same semantics as strcmp(). + // - Returns negative if this IDBKeyData is less than other. + // - Returns positive if this IDBKeyData is greater than other. + // - Returns zero if this IDBKeyData is equal to other. + WEBCORE_EXPORT int compare(const IDBKeyData& other) const; + + void setArrayValue(const Vector&); + void setBinaryValue(const ThreadSafeDataBuffer&); + void setStringValue(const String&); + void setDateValue(double); + WEBCORE_EXPORT void setNumberValue(double); + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBKeyData&); + +#if !LOG_DISABLED + WEBCORE_EXPORT String loggingString() const; +#endif + + bool isNull() const { return m_isNull; } + bool isValid() const { return m_type != KeyType::Invalid; } + KeyType type() const { return m_type; } + + bool operator<(const IDBKeyData&) const; + bool operator>(const IDBKeyData& other) const + { + return !(*this < other) && !(*this == other); + } + + bool operator<=(const IDBKeyData& other) const + { + return !(*this > other); + } + + bool operator>=(const IDBKeyData& other) const + { + return !(*this < other); + } + + bool operator==(const IDBKeyData& other) const; + bool operator!=(const IDBKeyData& other) const + { + return !(*this == other); + } + + unsigned hash() const + { + Vector hashCodes; + hashCodes.append(static_cast(m_type)); + hashCodes.append(m_isNull ? 1 : 0); + hashCodes.append(m_isDeletedValue ? 1 : 0); + switch (m_type) { + case KeyType::Invalid: + case KeyType::Max: + case KeyType::Min: + break; + case KeyType::Number: + case KeyType::Date: + hashCodes.append(StringHasher::hashMemory(&WTF::get(m_value))); + break; + case KeyType::String: + hashCodes.append(StringHash::hash(WTF::get(m_value))); + break; + case KeyType::Binary: { + auto* data = WTF::get(m_value).data(); + if (!data) + hashCodes.append(0); + else + hashCodes.append(StringHasher::hashMemory(data->data(), data->size())); + break; + } + case KeyType::Array: + for (auto& key : WTF::get>(m_value)) + hashCodes.append(key.hash()); + break; + } + + return StringHasher::hashMemory(hashCodes.data(), hashCodes.size() * sizeof(unsigned)); + } - IDBKey::Type type; - Vector arrayValue; - String stringValue; - double numberValue; - bool isNull; + static IDBKeyData deletedValue(); + bool isDeletedValue() const { return m_isDeletedValue; } + + String string() const + { + ASSERT(m_type == KeyType::String); + return WTF::get(m_value); + } + + double date() const + { + ASSERT(m_type == KeyType::Date); + return WTF::get(m_value); + } + + double number() const + { + ASSERT(m_type == KeyType::Number); + return WTF::get(m_value); + } + + const ThreadSafeDataBuffer& binary() const + { + ASSERT(m_type == KeyType::Binary); + return WTF::get(m_value); + } + + const Vector& array() const + { + ASSERT(m_type == KeyType::Array); + return WTF::get>(m_value); + } + +private: + static void isolatedCopy(const IDBKeyData& source, IDBKeyData& destination); + + KeyType m_type; + Variant, String, double, ThreadSafeDataBuffer> m_value; + + bool m_isNull { false }; + bool m_isDeletedValue { false }; }; +struct IDBKeyDataHash { + static unsigned hash(const IDBKeyData& a) { return a.hash(); } + static bool equal(const IDBKeyData& a, const IDBKeyData& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +struct IDBKeyDataHashTraits : public WTF::CustomHashTraits { + static const bool emptyValueIsZero = false; + static const bool hasIsEmptyValueFunction = true; + + static void constructDeletedValue(IDBKeyData& key) + { + key = IDBKeyData::deletedValue(); + } + + static bool isDeletedValue(const IDBKeyData& key) + { + return key.isDeletedValue(); + } + + static IDBKeyData emptyValue() + { + return IDBKeyData(); + } + + static bool isEmptyValue(const IDBKeyData& key) + { + return key.isNull(); + } +}; + +template +void IDBKeyData::encode(Encoder& encoder) const +{ + encoder << m_isNull; + if (m_isNull) + return; + + encoder.encodeEnum(m_type); + + switch (m_type) { + case KeyType::Invalid: + case KeyType::Max: + case KeyType::Min: + break; + case KeyType::Array: + encoder << WTF::get>(m_value); + break; + case KeyType::Binary: + encoder << WTF::get(m_value); + break; + case KeyType::String: + encoder << WTF::get(m_value); + break; + case KeyType::Date: + case KeyType::Number: + encoder << WTF::get(m_value); + break; + } +} + +template +bool IDBKeyData::decode(Decoder& decoder, IDBKeyData& keyData) +{ + if (!decoder.decode(keyData.m_isNull)) + return false; + + if (keyData.m_isNull) + return true; + + if (!decoder.decodeEnum(keyData.m_type)) + return false; + + switch (keyData.m_type) { + case KeyType::Invalid: + case KeyType::Max: + case KeyType::Min: + break; + case KeyType::Array: + keyData.m_value = Vector(); + if (!decoder.decode(WTF::get>(keyData.m_value))) + return false; + break; + case KeyType::Binary: + keyData.m_value = ThreadSafeDataBuffer(); + if (!decoder.decode(WTF::get(keyData.m_value))) + return false; + break; + case KeyType::String: + keyData.m_value = String(); + if (!decoder.decode(WTF::get(keyData.m_value))) + return false; + break; + case KeyType::Date: + case KeyType::Number: + keyData.m_value = 0.0; + if (!decoder.decode(WTF::get(keyData.m_value))) + return false; + break; + } + + return true; +} + } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) -#endif // IDBKeyData_h diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyPath.cpp b/Source/WebCore/Modules/indexeddb/IDBKeyPath.cpp index 2b46ff81d..e76d4f9ce 100644 --- a/Source/WebCore/Modules/indexeddb/IDBKeyPath.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBKeyPath.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2016-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,14 +25,13 @@ */ #include "config.h" -#include "IDBKeyPath.h" #if ENABLE(INDEXED_DATABASE) +#include "IDBKeyPath.h" -#include "KeyedCoding.h" #include #include -#include +#include namespace WebCore { @@ -46,8 +46,7 @@ public: explicit IDBKeyPathLexer(const String& s) : m_string(s) - , m_ptr(s.deprecatedCharacters()) - , m_end(s.deprecatedCharacters() + s.length()) + , m_remainingText(s) , m_currentTokenType(TokenError) { } @@ -65,23 +64,23 @@ public: private: TokenType lex(String&); TokenType lexIdentifier(String&); + String m_currentElement; - String m_string; - const UChar* m_ptr; - const UChar* m_end; + const String m_string; + StringView m_remainingText; TokenType m_currentTokenType; }; IDBKeyPathLexer::TokenType IDBKeyPathLexer::lex(String& element) { - if (m_ptr >= m_end) + if (m_remainingText.isEmpty()) return TokenEnd; - ASSERT_WITH_SECURITY_IMPLICATION(m_ptr < m_end); - if (*m_ptr == '.') { - ++m_ptr; + if (m_remainingText[0] == '.') { + m_remainingText = m_remainingText.substring(1); return TokenDot; } + return lexIdentifier(element); } @@ -109,16 +108,16 @@ static inline bool isIdentifierCharacter(UChar c) IDBKeyPathLexer::TokenType IDBKeyPathLexer::lexIdentifier(String& element) { - const UChar* start = m_ptr; - if (m_ptr < m_end && isIdentifierStartCharacter(*m_ptr)) - ++m_ptr; + StringView start = m_remainingText; + if (!m_remainingText.isEmpty() && isIdentifierStartCharacter(m_remainingText[0])) + m_remainingText = m_remainingText.substring(1); else return TokenError; - while (m_ptr < m_end && isIdentifierCharacter(*m_ptr)) - ++m_ptr; + while (!m_remainingText.isEmpty() && isIdentifierCharacter(m_remainingText[0])) + m_remainingText = m_remainingText.substring(1); - element = String(start, m_ptr - start); + element = start.substring(0, start.length() - m_remainingText.length()).toString(); return TokenIdentifier; } @@ -127,7 +126,7 @@ static bool IDBIsValidKeyPath(const String& keyPath) IDBKeyPathParseError error; Vector keyPathElements; IDBParseKeyPath(keyPath, keyPathElements, error); - return error == IDBKeyPathParseErrorNone; + return error == IDBKeyPathParseError::None; } void IDBParseKeyPath(const String& keyPath, Vector& elements, IDBKeyPathParseError& error) @@ -148,7 +147,7 @@ void IDBParseKeyPath(const String& keyPath, Vector& elements, IDBKeyPath else if (tokenType == IDBKeyPathLexer::TokenEnd) state = End; else { - error = IDBKeyPathParseErrorStart; + error = IDBKeyPathParseError::Start; return; } @@ -167,7 +166,7 @@ void IDBParseKeyPath(const String& keyPath, Vector& elements, IDBKeyPath else if (tokenType == IDBKeyPathLexer::TokenEnd) state = End; else { - error = IDBKeyPathParseErrorIdentifier; + error = IDBKeyPathParseError::Identifier; return; } break; @@ -180,130 +179,74 @@ void IDBParseKeyPath(const String& keyPath, Vector& elements, IDBKeyPath if (tokenType == IDBKeyPathLexer::TokenIdentifier) state = Identifier; else { - error = IDBKeyPathParseErrorDot; + error = IDBKeyPathParseError::Dot; return; } break; } case End: { - error = IDBKeyPathParseErrorNone; + error = IDBKeyPathParseError::None; return; } } } } -IDBKeyPath::IDBKeyPath(const String& string) - : m_type(StringType) - , m_string(string) -{ - ASSERT(!m_string.isNull()); -} - -IDBKeyPath::IDBKeyPath(const Vector& array) - : m_type(ArrayType) - , m_array(array) +bool isIDBKeyPathValid(const IDBKeyPath& keyPath) { -#ifndef NDEBUG - for (size_t i = 0; i < m_array.size(); ++i) - ASSERT(!m_array[i].isNull()); -#endif -} - -bool IDBKeyPath::isValid() const -{ - switch (m_type) { - case NullType: - return false; - - case StringType: - return IDBIsValidKeyPath(m_string); - - case ArrayType: - if (m_array.isEmpty()) + auto visitor = WTF::makeVisitor([](const String& string) { + return IDBIsValidKeyPath(string); + }, [](const Vector& vector) { + if (vector.isEmpty()) return false; - for (size_t i = 0; i < m_array.size(); ++i) { - if (!IDBIsValidKeyPath(m_array[i])) + for (auto& key : vector) { + if (!IDBIsValidKeyPath(key)) return false; } return true; - } - ASSERT_NOT_REACHED(); - return false; + }); + return WTF::visit(visitor, keyPath); } -bool IDBKeyPath::operator==(const IDBKeyPath& other) const +IDBKeyPath isolatedCopy(const IDBKeyPath& keyPath) { - if (m_type != other.m_type) - return false; - - switch (m_type) { - case NullType: - return true; - case StringType: - return m_string == other.m_string; - case ArrayType: - return m_array == other.m_array; - } - ASSERT_NOT_REACHED(); - return false; + auto visitor = WTF::makeVisitor([](const String& string) -> IDBKeyPath { + return string.isolatedCopy(); + }, [](const Vector& vector) -> IDBKeyPath { + Vector vectorCopy; + vectorCopy.reserveInitialCapacity(vector.size()); + for (auto& string : vector) + vectorCopy.uncheckedAppend(string.isolatedCopy()); + return vectorCopy; + }); + + return WTF::visit(visitor, keyPath); } -IDBKeyPath IDBKeyPath::isolatedCopy() const -{ - IDBKeyPath result; - result.m_type = m_type; - result.m_string = m_string.isolatedCopy(); - - result.m_array.reserveInitialCapacity(m_array.size()); - for (size_t i = 0; i < m_array.size(); ++i) - result.m_array.uncheckedAppend(m_array[i].isolatedCopy()); - - return result; -} - -void IDBKeyPath::encode(KeyedEncoder& encoder) const -{ - encoder.encodeEnum("type", m_type); - switch (m_type) { - case IDBKeyPath::NullType: - break; - case IDBKeyPath::StringType: - encoder.encodeString("string", m_string); - break; - case IDBKeyPath::ArrayType: - encoder.encodeObjects("array", m_array.begin(), m_array.end(), [](WebCore::KeyedEncoder& encoder, const String& string) { - encoder.encodeString("string", string); - }); - break; - default: - ASSERT_NOT_REACHED(); - }; -} - -bool IDBKeyPath::decode(KeyedDecoder& decoder, IDBKeyPath& result) +#ifndef NDEBUG +String loggingString(const IDBKeyPath& path) { - auto enumFunction = [](int64_t value) { - return value == NullType || value == StringType || value == ArrayType; - }; - - if (!decoder.decodeVerifiedEnum("type", result.m_type, enumFunction)) - return false; - - if (result.m_type == NullType) - return true; - - if (result.m_type == StringType) - return decoder.decodeString("string", result.m_string); - - ASSERT(result.m_type == ArrayType); + auto visitor = WTF::makeVisitor([](const String& string) { + return makeString("< ", string, " >"); + }, [](const Vector& strings) { + if (strings.isEmpty()) + return String("< >"); + + StringBuilder builder; + builder.append("< "); + for (size_t i = 0; i < strings.size() - 1; ++i) { + builder.append(strings[i]); + builder.append(", "); + } + builder.append(strings.last()); + builder.append(" >"); - auto arrayFunction = [](KeyedDecoder& decoder, String& result) { - return decoder.decodeString("string", result); - }; + return builder.toString(); + }); - return decoder.decodeObjects("array", result.m_array, arrayFunction); + return WTF::visit(visitor, path); } +#endif } // namespace WebCore diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyPath.h b/Source/WebCore/Modules/indexeddb/IDBKeyPath.h index 6dcd7deac..c5a04b3d2 100644 --- a/Source/WebCore/Modules/indexeddb/IDBKeyPath.h +++ b/Source/WebCore/Modules/indexeddb/IDBKeyPath.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2016-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,71 +24,39 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBKeyPath_h -#define IDBKeyPath_h +#pragma once #if ENABLE(INDEXED_DATABASE) +#include #include #include namespace WebCore { -class KeyedDecoder; -class KeyedEncoder; +using IDBKeyPath = WTF::Variant>; +bool isIDBKeyPathValid(const IDBKeyPath&); -enum IDBKeyPathParseError { - IDBKeyPathParseErrorNone, - IDBKeyPathParseErrorStart, - IDBKeyPathParseErrorIdentifier, - IDBKeyPathParseErrorDot, +enum class IDBKeyPathParseError { + None, + Start, + Identifier, + Dot, }; void IDBParseKeyPath(const String&, Vector&, IDBKeyPathParseError&); - -class IDBKeyPath { -public: - IDBKeyPath() : m_type(NullType) { } - explicit IDBKeyPath(const String&); - explicit IDBKeyPath(const Vector& array); - - enum Type { - NullType = 0, - StringType, - ArrayType - }; - - Type type() const { return m_type; } - - const Vector& array() const - { - ASSERT(m_type == ArrayType); - return m_array; - } - - const String& string() const - { - ASSERT(m_type == StringType); - return m_string; - } - - bool isNull() const { return m_type == NullType; } - bool isValid() const; - bool operator==(const IDBKeyPath& other) const; - - IDBKeyPath isolatedCopy() const; - - void encode(KeyedEncoder&) const; - static bool decode(KeyedDecoder&, IDBKeyPath&); - -private: - Type m_type; - String m_string; - Vector m_array; -}; +IDBKeyPath isolatedCopy(const IDBKeyPath&); +inline std::optional isolatedCopy(const std::optional& variant) +{ + if (!variant) + return { }; + return isolatedCopy(variant.value()); +} + +#ifndef NDEBUG +String loggingString(const IDBKeyPath&); +#endif } // namespace WebCore #endif - -#endif // IDBKeyPath_h diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyRange.cpp b/Source/WebCore/Modules/indexeddb/IDBKeyRange.cpp index 3d54fc2b2..c6511acc8 100644 --- a/Source/WebCore/Modules/indexeddb/IDBKeyRange.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBKeyRange.cpp @@ -26,118 +26,118 @@ #include "config.h" #include "IDBKeyRange.h" -#include "DOMRequestState.h" +#if ENABLE(INDEXED_DATABASE) + #include "IDBBindingUtilities.h" #include "IDBDatabaseException.h" #include "IDBKey.h" +#include "IDBKeyData.h" +#include "ScriptExecutionContext.h" +#include -#if ENABLE(INDEXED_DATABASE) +using namespace JSC; namespace WebCore { -PassRefPtr IDBKeyRange::create(PassRefPtr prpKey) +Ref IDBKeyRange::create(RefPtr&& lower, RefPtr&& upper, bool isLowerOpen, bool isUpperOpen) { - RefPtr key = prpKey; - return adoptRef(new IDBKeyRange(key, key, LowerBoundClosed, UpperBoundClosed)); + return adoptRef(*new IDBKeyRange(WTFMove(lower), WTFMove(upper), isLowerOpen, isUpperOpen)); } -IDBKeyRange::IDBKeyRange(PassRefPtr lower, PassRefPtr upper, LowerBoundType lowerType, UpperBoundType upperType) - : m_lower(lower) - , m_upper(upper) - , m_lowerType(lowerType) - , m_upperType(upperType) +Ref IDBKeyRange::create(RefPtr&& key) { + auto upper = key; + return create(WTFMove(key), WTFMove(upper), false, false); } -Deprecated::ScriptValue IDBKeyRange::lowerValue(ScriptExecutionContext* context) const +IDBKeyRange::IDBKeyRange(RefPtr&& lower, RefPtr&& upper, bool isLowerOpen, bool isUpperOpen) + : m_lower(WTFMove(lower)) + , m_upper(WTFMove(upper)) + , m_isLowerOpen(isLowerOpen) + , m_isUpperOpen(isUpperOpen) { - DOMRequestState requestState(context); - return idbKeyToScriptValue(&requestState, m_lower); } -Deprecated::ScriptValue IDBKeyRange::upperValue(ScriptExecutionContext* context) const +IDBKeyRange::~IDBKeyRange() { - DOMRequestState requestState(context); - return idbKeyToScriptValue(&requestState, m_upper); } -PassRefPtr IDBKeyRange::only(PassRefPtr prpKey, ExceptionCode& ec) +ExceptionOr> IDBKeyRange::only(RefPtr&& key) { - RefPtr key = prpKey; - if (!key || !key->isValid()) { - ec = IDBDatabaseException::DataError; - return 0; - } + if (!key || !key->isValid()) + return Exception { IDBDatabaseException::DataError }; - return IDBKeyRange::create(key, key, LowerBoundClosed, UpperBoundClosed); + return create(WTFMove(key)); } -PassRefPtr IDBKeyRange::only(ScriptExecutionContext* context, const Deprecated::ScriptValue& keyValue, ExceptionCode& ec) +ExceptionOr> IDBKeyRange::only(ExecState& state, JSValue keyValue) { - DOMRequestState requestState(context); - RefPtr key = scriptValueToIDBKey(&requestState, keyValue); - if (!key || !key->isValid()) { - ec = IDBDatabaseException::DataError; - return 0; - } - - return IDBKeyRange::create(key, key, LowerBoundClosed, UpperBoundClosed); + return only(scriptValueToIDBKey(state, keyValue)); } -PassRefPtr IDBKeyRange::lowerBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& boundValue, bool open, ExceptionCode& ec) +ExceptionOr> IDBKeyRange::lowerBound(ExecState& state, JSValue boundValue, bool open) { - DOMRequestState requestState(context); - RefPtr bound = scriptValueToIDBKey(&requestState, boundValue); - if (!bound || !bound->isValid()) { - ec = IDBDatabaseException::DataError; - return 0; - } + auto bound = scriptValueToIDBKey(state, boundValue); + if (!bound->isValid()) + return Exception { IDBDatabaseException::DataError }; - return IDBKeyRange::create(bound, 0, open ? LowerBoundOpen : LowerBoundClosed, UpperBoundOpen); + return create(WTFMove(bound), nullptr, open, true); } -PassRefPtr IDBKeyRange::upperBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& boundValue, bool open, ExceptionCode& ec) +ExceptionOr> IDBKeyRange::upperBound(ExecState& state, JSValue boundValue, bool open) { - DOMRequestState requestState(context); - RefPtr bound = scriptValueToIDBKey(&requestState, boundValue); - if (!bound || !bound->isValid()) { - ec = IDBDatabaseException::DataError; - return 0; - } + auto bound = scriptValueToIDBKey(state, boundValue); + if (!bound->isValid()) + return Exception { IDBDatabaseException::DataError }; - return IDBKeyRange::create(0, bound, LowerBoundOpen, open ? UpperBoundOpen : UpperBoundClosed); + return create(nullptr, WTFMove(bound), true, open); } -PassRefPtr IDBKeyRange::bound(ScriptExecutionContext* context, const Deprecated::ScriptValue& lowerValue, const Deprecated::ScriptValue& upperValue, bool lowerOpen, bool upperOpen, ExceptionCode& ec) +ExceptionOr> IDBKeyRange::bound(ExecState& state, JSValue lowerValue, JSValue upperValue, bool lowerOpen, bool upperOpen) { - DOMRequestState requestState(context); - RefPtr lower = scriptValueToIDBKey(&requestState, lowerValue); - RefPtr upper = scriptValueToIDBKey(&requestState, upperValue); + auto lower = scriptValueToIDBKey(state, lowerValue); + auto upper = scriptValueToIDBKey(state, upperValue); - if (!lower || !lower->isValid() || !upper || !upper->isValid()) { - ec = IDBDatabaseException::DataError; - return 0; - } - if (upper->isLessThan(lower.get())) { - ec = IDBDatabaseException::DataError; - return 0; - } - if (upper->isEqual(lower.get()) && (lowerOpen || upperOpen)) { - ec = IDBDatabaseException::DataError; - return 0; - } + if (!lower->isValid() || !upper->isValid()) + return Exception { IDBDatabaseException::DataError }; + if (upper->isLessThan(lower.get())) + return Exception { IDBDatabaseException::DataError }; + if (upper->isEqual(lower.get()) && (lowerOpen || upperOpen)) + return Exception { IDBDatabaseException::DataError }; - return IDBKeyRange::create(lower, upper, lowerOpen ? LowerBoundOpen : LowerBoundClosed, upperOpen ? UpperBoundOpen : UpperBoundClosed); + return create(WTFMove(lower), WTFMove(upper), lowerOpen, upperOpen); } bool IDBKeyRange::isOnlyKey() const { - if (m_lowerType != LowerBoundClosed || m_upperType != UpperBoundClosed) - return false; + return m_lower && m_upper && !m_isLowerOpen && !m_isUpperOpen && m_lower->isEqual(*m_upper); +} + +ExceptionOr IDBKeyRange::includes(JSC::ExecState& state, JSC::JSValue keyValue) +{ + auto key = scriptValueToIDBKey(state, keyValue); + if (!key->isValid()) + return Exception { IDBDatabaseException::DataError, "Failed to execute 'includes' on 'IDBKeyRange': The passed-in value is not a valid IndexedDB key." }; + + if (m_lower) { + int compare = m_lower->compare(key.get()); + + if (compare > 0) + return false; + if (m_isLowerOpen && !compare) + return false; + } + + if (m_upper) { + int compare = m_upper->compare(key.get()); + + if (compare < 0) + return false; + if (m_isUpperOpen && !compare) + return false; + } - ASSERT(m_lower); - ASSERT(m_upper); - return m_lower->isEqual(m_upper.get()); + return true; } } // namespace WebCore diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyRange.h b/Source/WebCore/Modules/indexeddb/IDBKeyRange.h index e551cf4d3..0f13f97b0 100644 --- a/Source/WebCore/Modules/indexeddb/IDBKeyRange.h +++ b/Source/WebCore/Modules/indexeddb/IDBKeyRange.h @@ -23,73 +23,57 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBKeyRange_h -#define IDBKeyRange_h +#pragma once #if ENABLE(INDEXED_DATABASE) -#include "Dictionary.h" -#include "IDBKey.h" +#include "ExceptionOr.h" #include "ScriptWrappable.h" -#include #include +#include + +namespace JSC { +class ExecState; +class JSValue; +} namespace WebCore { -typedef int ExceptionCode; +class IDBKey; +class ScriptExecutionContext; class IDBKeyRange : public ScriptWrappable, public RefCounted { public: - enum LowerBoundType { - LowerBoundOpen, - LowerBoundClosed - }; - enum UpperBoundType { - UpperBoundOpen, - UpperBoundClosed - }; - - static PassRefPtr create(PassRefPtr lower, PassRefPtr upper, LowerBoundType lowerType, UpperBoundType upperType) - { - return adoptRef(new IDBKeyRange(lower, upper, lowerType, upperType)); - } - static PassRefPtr create(PassRefPtr prpKey); - ~IDBKeyRange() { } + static Ref create(RefPtr&& lower, RefPtr&& upper, bool isLowerOpen, bool isUpperOpen); + static Ref create(RefPtr&&); + ~IDBKeyRange(); - PassRefPtr lower() const { return m_lower; } - PassRefPtr upper() const { return m_upper; } + IDBKey* lower() const { return m_lower.get(); } + IDBKey* upper() const { return m_upper.get(); } + bool lowerOpen() const { return m_isLowerOpen; } + bool upperOpen() const { return m_isUpperOpen; } - Deprecated::ScriptValue lowerValue(ScriptExecutionContext*) const; - Deprecated::ScriptValue upperValue(ScriptExecutionContext*) const; - bool lowerOpen() const { return m_lowerType == LowerBoundOpen; } - bool upperOpen() const { return m_upperType == UpperBoundOpen; } + static ExceptionOr> only(RefPtr&& value); + static ExceptionOr> only(JSC::ExecState&, JSC::JSValue key); - static PassRefPtr only(PassRefPtr value, ExceptionCode&); - static PassRefPtr only(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&); + static ExceptionOr> lowerBound(JSC::ExecState&, JSC::JSValue bound, bool open); + static ExceptionOr> upperBound(JSC::ExecState&, JSC::JSValue bound, bool open); - static PassRefPtr lowerBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& bound, ExceptionCode& ec) { return lowerBound(context, bound, false, ec); } - static PassRefPtr lowerBound(ScriptExecutionContext*, const Deprecated::ScriptValue& bound, bool open, ExceptionCode&); + static ExceptionOr> bound(JSC::ExecState&, JSC::JSValue lower, JSC::JSValue upper, bool lowerOpen, bool upperOpen); - static PassRefPtr upperBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& bound, ExceptionCode& ec) { return upperBound(context, bound, false, ec); } - static PassRefPtr upperBound(ScriptExecutionContext*, const Deprecated::ScriptValue& bound, bool open, ExceptionCode&); + ExceptionOr includes(JSC::ExecState&, JSC::JSValue key); - static PassRefPtr bound(ScriptExecutionContext* context, const Deprecated::ScriptValue& lower, const Deprecated::ScriptValue& upper, ExceptionCode& ec) { return bound(context, lower, upper, false, false, ec); } - static PassRefPtr bound(ScriptExecutionContext* context, const Deprecated::ScriptValue& lower, const Deprecated::ScriptValue& upper, bool lowerOpen, ExceptionCode& ec) { return bound(context, lower, upper, lowerOpen, false, ec); } - static PassRefPtr bound(ScriptExecutionContext*, const Deprecated::ScriptValue& lower, const Deprecated::ScriptValue& upper, bool lowerOpen, bool upperOpen, ExceptionCode&); - - bool isOnlyKey() const; + WEBCORE_EXPORT bool isOnlyKey() const; private: - IDBKeyRange(PassRefPtr lower, PassRefPtr upper, LowerBoundType lowerType, UpperBoundType upperType); + IDBKeyRange(RefPtr&& lower, RefPtr&& upper, bool isLowerOpen, bool isUpperOpen); RefPtr m_lower; RefPtr m_upper; - LowerBoundType m_lowerType; - UpperBoundType m_upperType; + bool m_isLowerOpen; + bool m_isUpperOpen; }; } // namespace WebCore #endif - -#endif // IDBKeyRange_h diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyRange.idl b/Source/WebCore/Modules/indexeddb/IDBKeyRange.idl index c8367f749..8927ec9f7 100644 --- a/Source/WebCore/Modules/indexeddb/IDBKeyRange.idl +++ b/Source/WebCore/Modules/indexeddb/IDBKeyRange.idl @@ -25,17 +25,17 @@ [ Conditional=INDEXED_DATABASE, - JSNoStaticTables, ImplementationLacksVTable, ] interface IDBKeyRange { - [ImplementedAs=lowerValue,CallWith=ScriptExecutionContext] readonly attribute any lower; - [ImplementedAs=upperValue,CallWith=ScriptExecutionContext] readonly attribute any upper; + [OverrideIDLType=IDLIDBKey] readonly attribute any lower; + [OverrideIDLType=IDLIDBKey] readonly attribute any upper; readonly attribute boolean lowerOpen; readonly attribute boolean upperOpen; - [CallWith=ScriptExecutionContext, RaisesException] static IDBKeyRange only(any value); - [CallWith=ScriptExecutionContext, RaisesException] static IDBKeyRange lowerBound(any lower, optional boolean open); - [CallWith=ScriptExecutionContext, RaisesException] static IDBKeyRange upperBound(any upper, optional boolean open); - [CallWith=ScriptExecutionContext, RaisesException] static IDBKeyRange bound(any lower, any upper, optional boolean lowerOpen, optional boolean upperOpen); -}; + [CallWith=ScriptState, MayThrowException] static IDBKeyRange only(any value); + [CallWith=ScriptState, MayThrowException] static IDBKeyRange lowerBound(any lower, optional boolean open = false); + [CallWith=ScriptState, MayThrowException] static IDBKeyRange upperBound(any upper, optional boolean open = false); + [CallWith=ScriptState, MayThrowException] static IDBKeyRange bound(any lower, any upper, optional boolean lowerOpen = false, optional boolean upperOpen = false); + [CallWith=ScriptState, MayThrowException] boolean includes(any key); +}; diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.cpp b/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.cpp index af1fb296b..7132edb81 100644 --- a/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.cpp @@ -28,8 +28,28 @@ #if ENABLE(INDEXED_DATABASE) +#include "IDBKey.h" + namespace WebCore { +IDBKeyRangeData::IDBKeyRangeData(IDBKey* key) + : isNull(!key) + , lowerKey(key) + , upperKey(key) + , lowerOpen(false) + , upperOpen(false) +{ +} + +IDBKeyRangeData::IDBKeyRangeData(const IDBKeyData& keyData) + : isNull(keyData.isNull()) + , lowerKey(keyData) + , upperKey(keyData) + , lowerOpen(false) + , upperOpen(false) +{ +} + IDBKeyRangeData IDBKeyRangeData::isolatedCopy() const { IDBKeyRangeData result; @@ -43,13 +63,68 @@ IDBKeyRangeData IDBKeyRangeData::isolatedCopy() const return result; } -PassRefPtr IDBKeyRangeData::maybeCreateIDBKeyRange() const +RefPtr IDBKeyRangeData::maybeCreateIDBKeyRange() const { if (isNull) return nullptr; - return IDBKeyRange::create(lowerKey.maybeCreateIDBKey(), upperKey.maybeCreateIDBKey(), lowerOpen ? IDBKeyRange::LowerBoundOpen : IDBKeyRange::LowerBoundClosed, upperOpen ? IDBKeyRange::UpperBoundOpen : IDBKeyRange::UpperBoundClosed); + return IDBKeyRange::create(lowerKey.maybeCreateIDBKey(), upperKey.maybeCreateIDBKey(), lowerOpen, upperOpen); +} + +bool IDBKeyRangeData::isExactlyOneKey() const +{ + if (isNull || lowerOpen || upperOpen || !upperKey.isValid() || !lowerKey.isValid()) + return false; + + return !lowerKey.compare(upperKey); +} + +bool IDBKeyRangeData::containsKey(const IDBKeyData& key) const +{ + if (lowerKey.isValid()) { + auto compare = lowerKey.compare(key); + if (compare > 0) + return false; + if (lowerOpen && !compare) + return false; + } + if (upperKey.isValid()) { + auto compare = upperKey.compare(key); + if (compare < 0) + return false; + if (upperOpen && !compare) + return false; + } + + return true; +} + +bool IDBKeyRangeData::isValid() const +{ + if (isNull) + return false; + + if (!lowerKey.isValid() && !lowerKey.isNull()) + return false; + + if (!upperKey.isValid() && !upperKey.isNull()) + return false; + + return true; +} + +#if !LOG_DISABLED +String IDBKeyRangeData::loggingString() const +{ + auto result = makeString(lowerOpen ? "( " : "[ ", lowerKey.loggingString(), ", ", upperKey.loggingString(), upperOpen ? " )" : " ]"); + if (result.length() > 400) { + result.truncate(397); + result.append(WTF::ASCIILiteral("...")); + } + + return result; } +#endif } // namespace WebCore diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.h b/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.h index d35a54393..12ecd4de3 100644 --- a/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.h +++ b/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBKeyRangeData_h -#define IDBKeyRangeData_h +#pragma once #if ENABLE(INDEXED_DATABASE) @@ -33,6 +32,8 @@ namespace WebCore { +class IDBKey; + struct IDBKeyRangeData { IDBKeyRangeData() : isNull(true) @@ -41,6 +42,18 @@ struct IDBKeyRangeData { { } + static IDBKeyRangeData allKeys() + { + IDBKeyRangeData result; + result.isNull = false; + result.lowerKey = IDBKeyData::minimum(); + result.upperKey = IDBKeyData::maximum(); + return result; + } + + IDBKeyRangeData(IDBKey*); + IDBKeyRangeData(const IDBKeyData&); + IDBKeyRangeData(IDBKeyRange* keyRange) : isNull(!keyRange) , lowerOpen(false) @@ -49,15 +62,22 @@ struct IDBKeyRangeData { if (isNull) return; - lowerKey = keyRange->lower().get(); - upperKey = keyRange->upper().get(); + lowerKey = keyRange->lower(); + upperKey = keyRange->upper(); lowerOpen = keyRange->lowerOpen(); upperOpen = keyRange->upperOpen(); } IDBKeyRangeData isolatedCopy() const; - PassRefPtr maybeCreateIDBKeyRange() const; + WEBCORE_EXPORT RefPtr maybeCreateIDBKeyRange() const; + + WEBCORE_EXPORT bool isExactlyOneKey() const; + bool containsKey(const IDBKeyData&) const; + bool isValid() const; + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBKeyRangeData&); bool isNull; @@ -66,9 +86,46 @@ struct IDBKeyRangeData { bool lowerOpen; bool upperOpen; + +#if !LOG_DISABLED + String loggingString() const; +#endif }; +template +void IDBKeyRangeData::encode(Encoder& encoder) const +{ + encoder << isNull; + if (isNull) + return; + + encoder << upperKey << lowerKey << upperOpen << lowerOpen; +} + +template +bool IDBKeyRangeData::decode(Decoder& decoder, IDBKeyRangeData& keyRange) +{ + if (!decoder.decode(keyRange.isNull)) + return false; + + if (keyRange.isNull) + return true; + + if (!decoder.decode(keyRange.upperKey)) + return false; + + if (!decoder.decode(keyRange.lowerKey)) + return false; + + if (!decoder.decode(keyRange.upperOpen)) + return false; + + if (!decoder.decode(keyRange.lowerOpen)) + return false; + + return true; +} + } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) -#endif // IDBKeyRangeData_h diff --git a/Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp b/Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp index 8d735ad6f..592c67d3b 100644 --- a/Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -29,565 +29,674 @@ #if ENABLE(INDEXED_DATABASE) #include "DOMStringList.h" -#include "IDBAny.h" +#include "Document.h" +#include "ExceptionCode.h" #include "IDBBindingUtilities.h" -#include "IDBCursorWithValue.h" +#include "IDBCursor.h" #include "IDBDatabase.h" #include "IDBDatabaseException.h" +#include "IDBError.h" +#include "IDBGetRecordData.h" #include "IDBIndex.h" #include "IDBKey.h" -#include "IDBKeyPath.h" -#include "IDBKeyRange.h" +#include "IDBKeyRangeData.h" +#include "IDBRequest.h" #include "IDBTransaction.h" +#include "IndexedDB.h" #include "Logging.h" +#include "Page.h" #include "ScriptExecutionContext.h" +#include "ScriptState.h" #include "SerializedScriptValue.h" -#include "SharedBuffer.h" +#include +#include + +using namespace JSC; namespace WebCore { -IDBObjectStore::IDBObjectStore(const IDBObjectStoreMetadata& metadata, IDBTransaction* transaction) - : m_metadata(metadata) +IDBObjectStore::IDBObjectStore(ScriptExecutionContext& context, const IDBObjectStoreInfo& info, IDBTransaction& transaction) + : ActiveDOMObject(&context) + , m_info(info) + , m_originalInfo(info) , m_transaction(transaction) - , m_deleted(false) { - ASSERT(m_transaction); - // We pass a reference to this object before it can be adopted. - relaxAdoptionRequirement(); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + suspendIfNeeded(); } -PassRefPtr IDBObjectStore::indexNames() const +IDBObjectStore::~IDBObjectStore() { - LOG(StorageAPI, "IDBObjectStore::indexNames"); - RefPtr indexNames = DOMStringList::create(); - for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) - indexNames->append(it->value.name); - indexNames->sort(); - return indexNames.release(); + ASSERT(currentThread() == m_transaction.database().originThreadID()); } -PassRefPtr IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr keyRange, ExceptionCode& ec) +const char* IDBObjectStore::activeDOMObjectName() const { - LOG(StorageAPI, "IDBObjectStore::get"); - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!keyRange) { - ec = IDBDatabaseException::DataError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - RefPtr request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - backendDB()->get(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, keyRange, false, request); - return request.release(); + return "IDBObjectStore"; +} + +bool IDBObjectStore::canSuspendForDocumentSuspension() const +{ + return false; +} + +bool IDBObjectStore::hasPendingActivity() const +{ + return !m_transaction.isFinished(); } -PassRefPtr IDBObjectStore::get(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) +const String& IDBObjectStore::name() const { - RefPtr keyRange = IDBKeyRange::only(context, key, ec); - if (ec) - return 0; - return get(context, keyRange.release(), ec); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + return m_info.name(); } -static void generateIndexKeysForValue(DOMRequestState* requestState, const IDBIndexMetadata& indexMetadata, const Deprecated::ScriptValue& objectValue, IDBObjectStore::IndexKeys* indexKeys) +ExceptionOr IDBObjectStore::setName(const String& name) { - ASSERT(indexKeys); - RefPtr indexKey = createIDBKeyFromScriptValueAndKeyPath(requestState, objectValue, indexMetadata.keyPath); + ASSERT(currentThread() == m_transaction.database().originThreadID()); - if (!indexKey) - return; + if (m_deleted) + return Exception { INVALID_STATE_ERR, ASCIILiteral("Failed set property 'name' on 'IDBObjectStore': The object store has been deleted.") }; - if (!indexMetadata.multiEntry || indexKey->type() != IDBKey::ArrayType) { - if (!indexKey->isValid()) - return; + if (!m_transaction.isVersionChange()) + return Exception { INVALID_STATE_ERR, ASCIILiteral("Failed set property 'name' on 'IDBObjectStore': The object store's transaction is not a version change transaction.") }; - indexKeys->append(indexKey); - } else { - ASSERT(indexMetadata.multiEntry); - ASSERT(indexKey->type() == IDBKey::ArrayType); - indexKey = IDBKey::createMultiEntryArray(indexKey->array()); + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed set property 'name' on 'IDBObjectStore': The object store's transaction is not active.") }; + + if (m_info.name() == name) + return { }; + + if (m_transaction.database().info().hasObjectStore(name)) + return Exception { IDBDatabaseException::ConstraintError, makeString("Failed set property 'name' on 'IDBObjectStore': The database already has an object store named '", name, "'.") }; + + m_transaction.database().renameObjectStore(*this, name); + m_info.rename(name); + + return { }; +} + +const std::optional& IDBObjectStore::keyPath() const +{ + ASSERT(currentThread() == m_transaction.database().originThreadID()); + return m_info.keyPath(); +} - for (size_t i = 0; i < indexKey->array().size(); ++i) - indexKeys->append(indexKey->array()[i]); +RefPtr IDBObjectStore::indexNames() const +{ + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + RefPtr indexNames = DOMStringList::create(); + + if (!m_deleted) { + for (auto& name : m_info.indexNames()) + indexNames->append(name); + indexNames->sort(); } + + return indexNames; } -PassRefPtr IDBObjectStore::add(JSC::ExecState* state, Deprecated::ScriptValue& value, const Deprecated::ScriptValue& key, ExceptionCode& ec) +IDBTransaction& IDBObjectStore::transaction() { - LOG(StorageAPI, "IDBObjectStore::add"); - return put(IDBDatabaseBackend::AddOnly, IDBAny::create(this), state, value, key, ec); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + return m_transaction; } -PassRefPtr IDBObjectStore::add(JSC::ExecState* state, Deprecated::ScriptValue& value, ExceptionCode& ec) +bool IDBObjectStore::autoIncrement() const { - LOG(StorageAPI, "IDBObjectStore::add"); - return put(IDBDatabaseBackend::AddOnly, IDBAny::create(this), state, value, static_cast(0), ec); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + return m_info.autoIncrement(); } -PassRefPtr IDBObjectStore::put(JSC::ExecState* state, Deprecated::ScriptValue& value, const Deprecated::ScriptValue& key, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::openCursor(ExecState& execState, RefPtr range, IDBCursorDirection direction) { - LOG(StorageAPI, "IDBObjectStore::put"); - return put(IDBDatabaseBackend::AddOrUpdate, IDBAny::create(this), state, value, key, ec); + LOG(IndexedDB, "IDBObjectStore::openCursor"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBObjectStore': The object store has been deleted.") }; + + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBObjectStore': The transaction is inactive or finished.") }; + + auto info = IDBCursorInfo::objectStoreCursor(m_transaction, m_info.identifier(), range.get(), direction, IndexedDB::CursorType::KeyAndValue); + return m_transaction.requestOpenCursor(execState, *this, info); } -PassRefPtr IDBObjectStore::put(JSC::ExecState* state, Deprecated::ScriptValue& value, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::openCursor(ExecState& execState, JSValue key, IDBCursorDirection direction) { - LOG(StorageAPI, "IDBObjectStore::put"); - return put(IDBDatabaseBackend::AddOrUpdate, IDBAny::create(this), state, value, static_cast(0), ec); + auto onlyResult = IDBKeyRange::only(execState, key); + if (onlyResult.hasException()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBObjectStore': The parameter is not a valid key.") }; + + return openCursor(execState, onlyResult.releaseReturnValue(), direction); } -PassRefPtr IDBObjectStore::put(IDBDatabaseBackend::PutMode putMode, PassRefPtr source, JSC::ExecState* state, Deprecated::ScriptValue& value, const Deprecated::ScriptValue& keyValue, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::openKeyCursor(ExecState& execState, RefPtr range, IDBCursorDirection direction) { - ScriptExecutionContext* context = scriptExecutionContextFromExecState(state); - DOMRequestState requestState(context); - RefPtr key = scriptValueToIDBKey(&requestState, keyValue); - return put(putMode, source, state, value, key.release(), ec); + LOG(IndexedDB, "IDBObjectStore::openCursor"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBObjectStore': The object store has been deleted.") }; + + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBObjectStore': The transaction is inactive or finished.") }; + + auto info = IDBCursorInfo::objectStoreCursor(m_transaction, m_info.identifier(), range.get(), direction, IndexedDB::CursorType::KeyOnly); + return m_transaction.requestOpenCursor(execState, *this, info); } -PassRefPtr IDBObjectStore::put(IDBDatabaseBackend::PutMode putMode, PassRefPtr source, JSC::ExecState* state, Deprecated::ScriptValue& value, PassRefPtr prpKey, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::openKeyCursor(ExecState& execState, JSValue key, IDBCursorDirection direction) { - RefPtr key = prpKey; - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - if (m_transaction->isReadOnly()) { - ec = IDBDatabaseException::ReadOnlyError; - return 0; - } + auto onlyResult = IDBKeyRange::only(execState, key); + if (onlyResult.hasException()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBObjectStore': The parameter is not a valid key or key range.") }; - // FIXME: Expose the JS engine exception state through ScriptState. - bool didThrow = false; - RefPtr serializedValue = SerializedScriptValue::serialize(value, state, nullptr, nullptr, didThrow); - if (didThrow) { - // Setting an explicit ExceptionCode here would defer handling the already thrown exception. - return 0; - } + return openKeyCursor(execState, onlyResult.releaseReturnValue(), direction); +} - if (serializedValue->hasBlobURLs()) { - // FIXME: Add Blob/File/FileList support - ec = IDBDatabaseException::DataCloneError; - return 0; - } +ExceptionOr> IDBObjectStore::get(ExecState& execState, JSValue key) +{ + LOG(IndexedDB, "IDBObjectStore::get"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); - const IDBKeyPath& keyPath = m_metadata.keyPath; - const bool usesInLineKeys = !keyPath.isNull(); - const bool hasKeyGenerator = autoIncrement(); + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The object store has been deleted.") }; - ScriptExecutionContext* context = scriptExecutionContextFromExecState(state); - DOMRequestState requestState(context); + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The transaction is inactive or finished.") }; - if (putMode != IDBDatabaseBackend::CursorUpdate && usesInLineKeys && key) { - ec = IDBDatabaseException::DataError; - return 0; - } - if (!usesInLineKeys && !hasKeyGenerator && !key) { - ec = IDBDatabaseException::DataError; - return 0; - } - if (usesInLineKeys) { - RefPtr keyPathKey = createIDBKeyFromScriptValueAndKeyPath(&requestState, value, keyPath); - if (keyPathKey && !keyPathKey->isValid()) { - ec = IDBDatabaseException::DataError; - return 0; - } - if (!hasKeyGenerator && !keyPathKey) { - ec = IDBDatabaseException::DataError; - return 0; - } - if (hasKeyGenerator && !keyPathKey) { - if (!canInjectIDBKeyIntoScriptValue(&requestState, value, keyPath)) { - ec = IDBDatabaseException::DataError; - return 0; - } - } - if (keyPathKey) - key = keyPathKey; - } - if (key && !key->isValid()) { - ec = IDBDatabaseException::DataError; - return 0; - } + auto idbKey = scriptValueToIDBKey(execState, key); + if (!idbKey->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The parameter is not a valid key.") }; - Vector indexIds; - Vector indexKeys; - for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) { - IndexKeys keys; - generateIndexKeysForValue(&requestState, it->value, value, &keys); - indexIds.append(it->key); - indexKeys.append(keys); - } + return m_transaction.requestGetRecord(execState, *this, { idbKey.ptr(), IDBGetRecordDataType::KeyAndValue }); +} + +ExceptionOr> IDBObjectStore::get(ExecState& execState, IDBKeyRange* keyRange) +{ + LOG(IndexedDB, "IDBObjectStore::get"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The object store has been deleted.") }; + + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError }; - RefPtr request = IDBRequest::create(context, source, m_transaction.get()); - Vector valueBytes = serializedValue->toWireBytes(); - // This is a hack to account for disagreements about whether SerializedScriptValue should deal in Vector or Vector. - // See https://lists.webkit.org/pipermail/webkit-dev/2013-February/023682.html - Vector* valueBytesSigned = reinterpret_cast*>(&valueBytes); - RefPtr valueBuffer = SharedBuffer::adoptVector(*valueBytesSigned); - backendDB()->put(m_transaction->id(), id(), valueBuffer, key.release(), static_cast(putMode), request, indexIds, indexKeys); - return request.release(); + IDBKeyRangeData keyRangeData(keyRange); + if (!keyRangeData.isValid()) + return Exception { IDBDatabaseException::DataError }; + + return m_transaction.requestGetRecord(execState, *this, { keyRangeData, IDBGetRecordDataType::KeyAndValue }); } -PassRefPtr IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr keyRange, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::getKey(ExecState& execState, JSValue key) { - LOG(StorageAPI, "IDBObjectStore::delete"); - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - if (m_transaction->isReadOnly()) { - ec = IDBDatabaseException::ReadOnlyError; - return 0; - } - if (!keyRange) { - ec = IDBDatabaseException::DataError; - return 0; - } + LOG(IndexedDB, "IDBObjectStore::getKey"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The object store has been deleted.") }; - RefPtr request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - backendDB()->deleteRange(m_transaction->id(), id(), keyRange, request); - return request.release(); + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The transaction is inactive or finished.") }; + + auto idbKey = scriptValueToIDBKey(execState, key); + if (!idbKey->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The parameter is not a valid key.") }; + + return m_transaction.requestGetRecord(execState, *this, { idbKey.ptr(), IDBGetRecordDataType::KeyOnly }); } -PassRefPtr IDBObjectStore::deleteFunction(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::getKey(ExecState& execState, IDBKeyRange* keyRange) { - RefPtr keyRange = IDBKeyRange::only(context, key, ec); - if (ec) - return 0; - return deleteFunction(context, keyRange.release(), ec); + LOG(IndexedDB, "IDBObjectStore::getKey"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The object store has been deleted.") }; + + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The transaction is inactive or finished.") }; + + IDBKeyRangeData keyRangeData(keyRange); + if (!keyRangeData.isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The parameter is not a valid key range.") }; + + return m_transaction.requestGetRecord(execState, *this, { keyRangeData, IDBGetRecordDataType::KeyOnly }); } -PassRefPtr IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::add(ExecState& execState, JSValue value, JSValue key) { - LOG(StorageAPI, "IDBObjectStore::clear"); - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - if (m_transaction->isReadOnly()) { - ec = IDBDatabaseException::ReadOnlyError; - return 0; - } + RefPtr idbKey; + if (!key.isUndefined()) + idbKey = scriptValueToIDBKey(execState, key); + return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::NoOverwrite, InlineKeyCheck::Perform); +} - RefPtr request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - backendDB()->clearObjectStore(m_transaction->id(), id(), request); - return request.release(); +ExceptionOr> IDBObjectStore::put(ExecState& execState, JSValue value, JSValue key) +{ + RefPtr idbKey; + if (!key.isUndefined()) + idbKey = scriptValueToIDBKey(execState, key); + return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::Overwrite, InlineKeyCheck::Perform); } -namespace { -// This class creates the index keys for a given index by extracting -// them from the SerializedScriptValue, for all the existing values in -// the objectStore. It only needs to be kept alive by virtue of being -// a listener on an IDBRequest object, in the same way that JavaScript -// cursor success handlers are kept alive. -class IndexPopulator : public EventListener { -public: - static PassRefPtr create(PassRefPtr backend, int64_t transactionId, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata) - { - return adoptRef(new IndexPopulator(backend, transactionId, objectStoreId, indexMetadata)); +ExceptionOr> IDBObjectStore::putForCursorUpdate(ExecState& state, JSValue value, JSValue key) +{ + return putOrAdd(state, value, scriptValueToIDBKey(state, key), IndexedDB::ObjectStoreOverwriteMode::OverwriteForCursor, InlineKeyCheck::DoNotPerform); +} + +ExceptionOr> IDBObjectStore::putOrAdd(ExecState& state, JSValue value, RefPtr key, IndexedDB::ObjectStoreOverwriteMode overwriteMode, InlineKeyCheck inlineKeyCheck) +{ + VM& vm = state.vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + + LOG(IndexedDB, "IDBObjectStore::putOrAdd"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + auto context = scriptExecutionContextFromExecState(&state); + if (!context) + return Exception { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to store record in object store because it does not have a valid script execution context") }; + + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to store record in an IDBObjectStore: The object store has been deleted.") }; + + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to store record in an IDBObjectStore: The transaction is inactive or finished.") }; + + if (m_transaction.isReadOnly()) + return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to store record in an IDBObjectStore: The transaction is read-only.") }; + + auto serializedValue = SerializedScriptValue::create(state, value); + if (UNLIKELY(scope.exception())) { + // Clear the DOM exception from the serializer so we can give a more targeted exception. + scope.clearException(); + + return Exception { IDBDatabaseException::DataCloneError, ASCIILiteral("Failed to store record in an IDBObjectStore: An object could not be cloned.") }; } - virtual bool operator==(const EventListener& other) - { - return this == &other; + bool privateBrowsingEnabled = false; + if (context->isDocument()) { + if (auto* page = static_cast(context)->page()) + privateBrowsingEnabled = page->sessionID().isEphemeral(); } -private: - IndexPopulator(PassRefPtr backend, int64_t transactionId, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata) - : EventListener(CPPEventListenerType) - , m_databaseBackend(backend) - , m_transactionId(transactionId) - , m_objectStoreId(objectStoreId) - , m_indexMetadata(indexMetadata) - { + if (serializedValue->hasBlobURLs() && privateBrowsingEnabled) { + // https://bugs.webkit.org/show_bug.cgi?id=156347 - Support Blobs in private browsing. + return Exception { IDBDatabaseException::DataCloneError, ASCIILiteral("Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.") }; } - virtual void handleEvent(ScriptExecutionContext*, Event* event) - { - ASSERT(event->type() == eventNames().successEvent); - EventTarget* target = event->target(); - IDBRequest* request = static_cast(target); - - RefPtr cursorAny = request->result(ASSERT_NO_EXCEPTION); - RefPtr cursor; - if (cursorAny->type() == IDBAny::IDBCursorWithValueType) - cursor = cursorAny->idbCursorWithValue(); - - Vector indexIds; - indexIds.append(m_indexMetadata.id); - if (cursor) { - cursor->continueFunction(static_cast(0), ASSERT_NO_EXCEPTION); - - RefPtr primaryKey = cursor->idbPrimaryKey(); - Deprecated::ScriptValue value = cursor->value(); - - IDBObjectStore::IndexKeys indexKeys; - generateIndexKeysForValue(request->requestState(), m_indexMetadata, value, &indexKeys); - - Vector indexKeysList; - indexKeysList.append(indexKeys); - - m_databaseBackend->setIndexKeys(m_transactionId, m_objectStoreId, primaryKey, indexIds, indexKeysList); - } else { - // Now that we are done indexing, tell the backend to go - // back to processing tasks of type NormalTask. - m_databaseBackend->setIndexesReady(m_transactionId, m_objectStoreId, indexIds); - m_databaseBackend.clear(); + if (key && !key->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to store record in an IDBObjectStore: The parameter is not a valid key.") }; + + bool usesInlineKeys = !!m_info.keyPath(); + bool usesKeyGenerator = autoIncrement(); + if (usesInlineKeys && inlineKeyCheck == InlineKeyCheck::Perform) { + if (key) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to store record in an IDBObjectStore: The object store uses in-line keys and the key parameter was provided.") }; + + RefPtr keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, m_info.keyPath().value()); + if (keyPathKey && !keyPathKey->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to store record in an IDBObjectStore: Evaluating the object store's key path yielded a value that is not a valid key.") }; + + if (!keyPathKey) { + if (!usesKeyGenerator) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to store record in an IDBObjectStore: Evaluating the object store's key path did not yield a value.") }; + if (!canInjectIDBKeyIntoScriptValue(state, value, m_info.keyPath().value())) + return Exception { IDBDatabaseException::DataError }; } - } + if (keyPathKey) { + ASSERT(!key); + key = keyPathKey; + } + } else if (!usesKeyGenerator && !key) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to store record in an IDBObjectStore: The object store uses out-of-line keys and has no key generator and the key parameter was not provided.") }; - RefPtr m_databaseBackend; - const int64_t m_transactionId; - const int64_t m_objectStoreId; - const IDBIndexMetadata m_indexMetadata; -}; + return m_transaction.requestPutOrAdd(state, *this, key.get(), *serializedValue, overwriteMode); } -PassRefPtr IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, const Dictionary& options, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::deleteFunction(ExecState& execState, IDBKeyRange* keyRange) { - bool unique = false; - options.get("unique", unique); + return doDelete(execState, keyRange); +} + +ExceptionOr> IDBObjectStore::doDelete(ExecState& execState, IDBKeyRange* keyRange) +{ + LOG(IndexedDB, "IDBObjectStore::deleteFunction"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before + // the exception for an object store being deleted. + // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies. + // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit. + // Until this is sorted out, we'll agree with the test and the majority share browsers. + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The object store has been deleted.") }; + + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The transaction is inactive or finished.") }; - bool multiEntry = false; - options.get("multiEntry", multiEntry); + if (m_transaction.isReadOnly()) + return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The transaction is read-only.") }; - return createIndex(context, name, keyPath, unique, multiEntry, ec); + IDBKeyRangeData keyRangeData(keyRange); + if (!keyRangeData.isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key range.") }; + + return m_transaction.requestDeleteRecord(execState, *this, keyRangeData); } -PassRefPtr IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::deleteFunction(ExecState& execState, JSValue key) { - LOG(StorageAPI, "IDBObjectStore::createIndex"); - if (!m_transaction->isVersionChange() || m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - if (!keyPath.isValid()) { - ec = IDBDatabaseException::SyntaxError; - return 0; - } - if (name.isNull()) { - ec = TypeError; - return 0; - } - if (containsIndex(name)) { - ec = IDBDatabaseException::ConstraintError; - return 0; - } + Ref idbKey = scriptValueToIDBKey(execState, key); + if (!idbKey->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key.") }; + return doDelete(execState, IDBKeyRange::create(WTFMove(idbKey)).ptr()); +} - if (keyPath.type() == IDBKeyPath::ArrayType && multiEntry) { - ec = IDBDatabaseException::InvalidAccessError; - return 0; - } +ExceptionOr> IDBObjectStore::clear(ExecState& execState) +{ + LOG(IndexedDB, "IDBObjectStore::clear"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before + // the exception for an object store being deleted. + // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies. + // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit. + // Until this is sorted out, we'll agree with the test and the majority share browsers. + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'clear' on 'IDBObjectStore': The object store has been deleted.") }; + + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'clear' on 'IDBObjectStore': The transaction is inactive or finished.") }; - int64_t indexId = m_metadata.maxIndexId + 1; - backendDB()->createIndex(m_transaction->id(), id(), indexId, name, keyPath, unique, multiEntry); + if (m_transaction.isReadOnly()) + return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'clear' on 'IDBObjectStore': The transaction is read-only.") }; - ++m_metadata.maxIndexId; + return m_transaction.requestClearObjectStore(execState, *this); +} + +ExceptionOr> IDBObjectStore::createIndex(ExecState&, const String& name, IDBKeyPath&& keyPath, const IndexParameters& parameters) +{ + LOG(IndexedDB, "IDBObjectStore::createIndex %s (keyPath: %s, unique: %i, multiEntry: %i)", name.utf8().data(), loggingString(keyPath).utf8().data(), parameters.unique, parameters.multiEntry); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + if (!m_transaction.isVersionChange()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The database is not running a version change transaction.") }; + + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The object store has been deleted.") }; + + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The transaction is inactive.")}; - IDBIndexMetadata metadata(name, indexId, keyPath, unique, multiEntry); - RefPtr index = IDBIndex::create(metadata, this, m_transaction.get()); - m_indexMap.set(name, index); - m_metadata.indexes.set(indexId, metadata); + if (m_info.hasIndex(name)) + return Exception { IDBDatabaseException::ConstraintError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': An index with the specified name already exists.") }; - ASSERT(!ec); - if (ec) - return 0; + if (!isIDBKeyPathValid(keyPath)) + return Exception { IDBDatabaseException::SyntaxError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument contains an invalid key path.") }; - RefPtr indexRequest = openCursor(context, static_cast(0), IDBCursor::directionNext(), IDBDatabaseBackend::PreemptiveTask, ec); - ASSERT(!ec); - if (ec) - return 0; - indexRequest->preventPropagation(); + if (name.isNull()) + return Exception { TypeError }; - // This is kept alive by being the success handler of the request, which is in turn kept alive by the owning transaction. - RefPtr indexPopulator = IndexPopulator::create(backendDB(), m_transaction->id(), id(), metadata); - indexRequest->setOnsuccess(indexPopulator); + if (parameters.multiEntry && WTF::holds_alternative>(keyPath)) + return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument was an array and the multiEntry option is true.") }; - return index.release(); + // Install the new Index into the ObjectStore's info. + IDBIndexInfo info = m_info.createNewIndex(name, WTFMove(keyPath), parameters.unique, parameters.multiEntry); + m_transaction.database().didCreateIndexInfo(info); + + // Create the actual IDBObjectStore from the transaction, which also schedules the operation server side. + auto index = m_transaction.createIndex(*this, info); + + Ref referencedIndex { *index }; + + Locker locker(m_referencedIndexLock); + m_referencedIndexes.set(name, WTFMove(index)); + + return WTFMove(referencedIndex); } -PassRefPtr IDBObjectStore::index(const String& name, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::index(const String& indexName) { - LOG(StorageAPI, "IDBObjectStore::index"); - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (m_transaction->isFinished()) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } + LOG(IndexedDB, "IDBObjectStore::index"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); - IDBIndexMap::iterator it = m_indexMap.find(name); - if (it != m_indexMap.end()) - return it->value; + if (!scriptExecutionContext()) + return Exception { IDBDatabaseException::InvalidStateError }; // FIXME: Is this code tested? Is iteven reachable? - int64_t indexId = findIndexId(name); - if (indexId == IDBIndexMetadata::InvalidId) { - ec = IDBDatabaseException::NotFoundError; - return 0; - } + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'index' on 'IDBObjectStore': The object store has been deleted.") }; + + if (m_transaction.isFinishedOrFinishing()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'index' on 'IDBObjectStore': The transaction is finished.") }; + + Locker locker(m_referencedIndexLock); + auto iterator = m_referencedIndexes.find(indexName); + if (iterator != m_referencedIndexes.end()) + return Ref { *iterator->value }; + + auto* info = m_info.infoForExistingIndex(indexName); + if (!info) + return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'index' on 'IDBObjectStore': The specified index was not found.") }; + + auto index = std::make_unique(*scriptExecutionContext(), *info, *this); - const IDBIndexMetadata* indexMetadata(0); - for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) { - if (it->value.name == name) { - indexMetadata = &it->value; - break; + Ref referencedIndex { *index }; + + m_referencedIndexes.set(indexName, WTFMove(index)); + + return WTFMove(referencedIndex); +} + +ExceptionOr IDBObjectStore::deleteIndex(const String& name) +{ + LOG(IndexedDB, "IDBObjectStore::deleteIndex %s", name.utf8().data()); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The object store has been deleted.") }; + + if (!m_transaction.isVersionChange()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The database is not running a version change transaction.") }; + + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The transaction is inactive or finished.") }; + + if (!m_info.hasIndex(name)) + return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The specified index was not found.") }; + + auto* info = m_info.infoForExistingIndex(name); + ASSERT(info); + m_transaction.database().didDeleteIndexInfo(*info); + + m_info.deleteIndex(name); + + { + Locker locker(m_referencedIndexLock); + if (auto index = m_referencedIndexes.take(name)) { + index->markAsDeleted(); + m_deletedIndexes.add(index->info().identifier(), WTFMove(index)); } } - ASSERT(indexMetadata); - ASSERT(indexMetadata->id != IDBIndexMetadata::InvalidId); - RefPtr index = IDBIndex::create(*indexMetadata, this, m_transaction.get()); - m_indexMap.set(name, index); - return index.release(); + m_transaction.deleteIndex(m_info.identifier(), name); + + return { }; } -void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::count(ExecState& execState, JSValue key) { - LOG(StorageAPI, "IDBObjectStore::deleteIndex"); - if (!m_transaction->isVersionChange() || m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return; - } - int64_t indexId = findIndexId(name); - if (indexId == IDBIndexMetadata::InvalidId) { - ec = IDBDatabaseException::NotFoundError; - return; - } + LOG(IndexedDB, "IDBObjectStore::count"); - backendDB()->deleteIndex(m_transaction->id(), id(), indexId); + Ref idbKey = scriptValueToIDBKey(execState, key); + if (!idbKey->isValid()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'count' on 'IDBObjectStore': The parameter is not a valid key.") }; - m_metadata.indexes.remove(indexId); - IDBIndexMap::iterator it = m_indexMap.find(name); - if (it != m_indexMap.end()) { - it->value->markDeleted(); - m_indexMap.remove(name); - } + return doCount(execState, IDBKeyRangeData(idbKey.ptr())); } -PassRefPtr IDBObjectStore::openCursor(ScriptExecutionContext* context, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::count(ExecState& execState, IDBKeyRange* range) { - return openCursor(context, static_cast(0), ec); + LOG(IndexedDB, "IDBObjectStore::count"); + + return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys()); } -PassRefPtr IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr keyRange, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::doCount(ExecState& execState, const IDBKeyRangeData& range) { - return openCursor(context, keyRange, IDBCursor::directionNext(), ec); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before + // the exception for an object store being deleted. + // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies. + // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit. + // Until this is sorted out, we'll agree with the test and the majority share browsers. + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'count' on 'IDBObjectStore': The object store has been deleted.") }; + + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'count' on 'IDBObjectStore': The transaction is inactive or finished.") }; + + if (!range.isValid()) + return Exception { IDBDatabaseException::DataError }; + + return m_transaction.requestCount(execState, *this, range); } -PassRefPtr IDBObjectStore::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::getAll(ExecState& execState, RefPtr range, std::optional count) { - return openCursor(context, key, IDBCursor::directionNext(), ec); + LOG(IndexedDB, "IDBObjectStore::getAll"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getAll' on 'IDBObjectStore': The object store has been deleted.") }; + + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getAll' on 'IDBObjectStore': The transaction is inactive or finished.") }; + + return m_transaction.requestGetAllObjectStoreRecords(execState, *this, range.get(), IndexedDB::GetAllType::Values, count); } -PassRefPtr IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr range, const String& direction, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::getAll(ExecState& execState, JSValue key, std::optional count) { - return openCursor(context, range, direction, IDBDatabaseBackend::NormalTask, ec); + auto onlyResult = IDBKeyRange::only(execState, key); + if (onlyResult.hasException()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getAll' on 'IDBObjectStore': The parameter is not a valid key.") }; + + return getAll(execState, onlyResult.releaseReturnValue(), count); } -PassRefPtr IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr range, const String& directionString, IDBDatabaseBackend::TaskType taskType, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::getAllKeys(ExecState& execState, RefPtr range, std::optional count) { - LOG(StorageAPI, "IDBObjectStore::openCursor"); - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; - } - IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec); - if (ec) - return 0; + LOG(IndexedDB, "IDBObjectStore::getAllKeys"); + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + if (m_deleted) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBObjectStore': The object store has been deleted.") }; - RefPtr request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - request->setCursorDetails(IndexedDB::CursorType::KeyAndValue, direction); + if (!m_transaction.isActive()) + return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBObjectStore': The transaction is inactive or finished.") }; - backendDB()->openCursor(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, range, direction, false, static_cast(taskType), request); - return request.release(); + return m_transaction.requestGetAllObjectStoreRecords(execState, *this, range.get(), IndexedDB::GetAllType::Keys, count); } -PassRefPtr IDBObjectStore::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode& ec) +ExceptionOr> IDBObjectStore::getAllKeys(ExecState& execState, JSValue key, std::optional count) { - RefPtr keyRange = IDBKeyRange::only(context, key, ec); - if (ec) - return 0; - return openCursor(context, keyRange.release(), direction, ec); + auto onlyResult = IDBKeyRange::only(execState, key); + if (onlyResult.hasException()) + return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBObjectStore': The parameter is not a valid key.") }; + + return getAllKeys(execState, onlyResult.releaseReturnValue(), count); } -PassRefPtr IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr range, ExceptionCode& ec) +void IDBObjectStore::markAsDeleted() { - LOG(StorageAPI, "IDBObjectStore::count"); - if (m_deleted) { - ec = IDBDatabaseException::InvalidStateError; - return 0; + ASSERT(currentThread() == m_transaction.database().originThreadID()); + m_deleted = true; +} + +void IDBObjectStore::rollbackForVersionChangeAbort() +{ + ASSERT(currentThread() == m_transaction.database().originThreadID()); + + String currentName = m_info.name(); + m_info = m_originalInfo; + + auto& databaseInfo = transaction().database().info(); + auto* objectStoreInfo = databaseInfo.infoForExistingObjectStore(m_info.identifier()); + if (!objectStoreInfo) { + m_info.rename(currentName); + m_deleted = true; + } else { + m_deleted = false; + + HashSet indexesToRemove; + for (auto indexIdentifier : objectStoreInfo->indexMap().keys()) { + if (!objectStoreInfo->hasIndex(indexIdentifier)) + indexesToRemove.add(indexIdentifier); + } + + for (auto indexIdentifier : indexesToRemove) + m_info.deleteIndex(indexIdentifier); } - if (!m_transaction->isActive()) { - ec = IDBDatabaseException::TransactionInactiveError; - return 0; + + Locker locker(m_referencedIndexLock); + + Vector identifiersToRemove; + for (auto& iterator : m_deletedIndexes) { + if (m_info.hasIndex(iterator.key)) { + auto name = iterator.value->info().name(); + m_referencedIndexes.set(name, WTFMove(iterator.value)); + identifiersToRemove.append(iterator.key); + } } - RefPtr request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - backendDB()->count(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, range, request); - return request.release(); + + for (auto identifier : identifiersToRemove) + m_deletedIndexes.remove(identifier); + + for (auto& index : m_referencedIndexes.values()) + index->rollbackInfoForVersionChangeAbort(); } -PassRefPtr IDBObjectStore::count(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) +void IDBObjectStore::visitReferencedIndexes(SlotVisitor& visitor) const { - RefPtr keyRange = IDBKeyRange::only(context, key, ec); - if (ec) - return 0; - return count(context, keyRange.release(), ec); + Locker locker(m_referencedIndexLock); + for (auto& index : m_referencedIndexes.values()) + visitor.addOpaqueRoot(index.get()); + for (auto& index : m_deletedIndexes.values()) + visitor.addOpaqueRoot(index.get()); } -void IDBObjectStore::transactionFinished() +void IDBObjectStore::renameReferencedIndex(IDBIndex& index, const String& newName) { - ASSERT(m_transaction->isFinished()); + LOG(IndexedDB, "IDBObjectStore::renameReferencedIndex"); + + auto* indexInfo = m_info.infoForExistingIndex(index.info().identifier()); + ASSERT(indexInfo); + indexInfo->rename(newName); + + ASSERT(m_referencedIndexes.contains(index.info().name())); + ASSERT(!m_referencedIndexes.contains(newName)); + ASSERT(m_referencedIndexes.get(index.info().name()) == &index); - // Break reference cycles. - m_indexMap.clear(); + m_referencedIndexes.set(newName, m_referencedIndexes.take(index.info().name())); } -int64_t IDBObjectStore::findIndexId(const String& name) const +void IDBObjectStore::ref() { - for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) { - if (it->value.name == name) { - ASSERT(it->key != IDBIndexMetadata::InvalidId); - return it->key; - } - } - return IDBIndexMetadata::InvalidId; + m_transaction.ref(); } -IDBDatabaseBackend* IDBObjectStore::backendDB() const +void IDBObjectStore::deref() { - return m_transaction->backendDB(); + m_transaction.deref(); } } // namespace WebCore diff --git a/Source/WebCore/Modules/indexeddb/IDBObjectStore.h b/Source/WebCore/Modules/indexeddb/IDBObjectStore.h index da65a054f..3be56901b 100644 --- a/Source/WebCore/Modules/indexeddb/IDBObjectStore.h +++ b/Source/WebCore/Modules/indexeddb/IDBObjectStore.h @@ -1,131 +1,139 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBObjectStore_h -#define IDBObjectStore_h - -#include "Dictionary.h" -#include "IDBCursor.h" -#include "IDBDatabaseMetadata.h" -#include "IDBIndex.h" -#include "IDBKey.h" -#include "IDBKeyRange.h" -#include "IDBRequest.h" -#include "IDBTransaction.h" -#include "ScriptWrappable.h" -#include "SerializedScriptValue.h" -#include -#include -#include -#include +#pragma once #if ENABLE(INDEXED_DATABASE) +#include "ActiveDOMObject.h" +#include "ExceptionOr.h" +#include "IDBCursorDirection.h" +#include "IDBKeyPath.h" +#include "IDBObjectStoreInfo.h" +#include + +namespace JSC { +class ExecState; +class JSValue; +class SlotVisitor; +} + namespace WebCore { class DOMStringList; -class IDBAny; +class IDBIndex; +class IDBKey; +class IDBKeyRange; +class IDBRequest; +class IDBTransaction; + +struct IDBKeyRangeData; -class IDBObjectStore : public ScriptWrappable, public RefCounted { +namespace IndexedDB { +enum class ObjectStoreOverwriteMode; +} + +class IDBObjectStore final : public ActiveDOMObject { public: - static PassRefPtr create(const IDBObjectStoreMetadata& metadata, IDBTransaction* transaction) - { - return adoptRef(new IDBObjectStore(metadata, transaction)); - } - ~IDBObjectStore() { } - - // Implement the IDBObjectStore IDL - int64_t id() const { return m_metadata.id; } - const String name() const { return m_metadata.name; } - PassRefPtr keyPathAny() const { return IDBAny::create(m_metadata.keyPath); } - const IDBKeyPath keyPath() const { return m_metadata.keyPath; } - PassRefPtr indexNames() const; - PassRefPtr transaction() const { return m_transaction; } - bool autoIncrement() const { return m_metadata.autoIncrement; } - - PassRefPtr add(JSC::ExecState*, Deprecated::ScriptValue&, ExceptionCode&); - PassRefPtr put(JSC::ExecState*, Deprecated::ScriptValue&, ExceptionCode&); - PassRefPtr openCursor(ScriptExecutionContext*, ExceptionCode&); - PassRefPtr openCursor(ScriptExecutionContext*, PassRefPtr, ExceptionCode&); - PassRefPtr openCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&); - PassRefPtr openCursor(ScriptExecutionContext*, PassRefPtr, const String& direction, ExceptionCode&); - PassRefPtr openCursor(ScriptExecutionContext*, PassRefPtr, const String& direction, IDBDatabaseBackend::TaskType, ExceptionCode&); - PassRefPtr openCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode&); - - PassRefPtr get(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&); - PassRefPtr get(ScriptExecutionContext*, PassRefPtr, ExceptionCode&); - PassRefPtr add(JSC::ExecState*, Deprecated::ScriptValue&, const Deprecated::ScriptValue& key, ExceptionCode&); - PassRefPtr put(JSC::ExecState*, Deprecated::ScriptValue&, const Deprecated::ScriptValue& key, ExceptionCode&); - PassRefPtr deleteFunction(ScriptExecutionContext*, PassRefPtr, ExceptionCode&); - PassRefPtr deleteFunction(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&); - PassRefPtr clear(ScriptExecutionContext*, ExceptionCode&); - - PassRefPtr createIndex(ScriptExecutionContext* context, const String& name, const String& keyPath, const Dictionary& options, ExceptionCode& ec) { return createIndex(context, name, IDBKeyPath(keyPath), options, ec); } - PassRefPtr createIndex(ScriptExecutionContext* context, const String& name, const Vector& keyPath, const Dictionary& options, ExceptionCode& ec) { return createIndex(context, name, IDBKeyPath(keyPath), options, ec); } - PassRefPtr createIndex(ScriptExecutionContext*, const String& name, const IDBKeyPath&, const Dictionary&, ExceptionCode&); - PassRefPtr createIndex(ScriptExecutionContext*, const String& name, const IDBKeyPath&, bool unique, bool multiEntry, ExceptionCode&); - - PassRefPtr index(const String& name, ExceptionCode&); - void deleteIndex(const String& name, ExceptionCode&); - - PassRefPtr count(ScriptExecutionContext* context, ExceptionCode& ec) { return count(context, static_cast(0), ec); } - PassRefPtr count(ScriptExecutionContext*, PassRefPtr, ExceptionCode&); - PassRefPtr count(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&); - - PassRefPtr put(IDBDatabaseBackend::PutMode, PassRefPtr source, JSC::ExecState*, Deprecated::ScriptValue&, const Deprecated::ScriptValue& key, ExceptionCode&); - PassRefPtr put(IDBDatabaseBackend::PutMode, PassRefPtr source, JSC::ExecState*, Deprecated::ScriptValue&, PassRefPtr, ExceptionCode&); - void markDeleted() { m_deleted = true; } - void transactionFinished(); - - IDBObjectStoreMetadata metadata() const { return m_metadata; } - void setMetadata(const IDBObjectStoreMetadata& metadata) { m_metadata = metadata; } - - typedef Vector> IndexKeys; - typedef HashMap IndexKeyMap; - - IDBDatabaseBackend* backendDB() const; + IDBObjectStore(ScriptExecutionContext&, const IDBObjectStoreInfo&, IDBTransaction&); + ~IDBObjectStore(); + + const String& name() const; + ExceptionOr setName(const String&); + const std::optional& keyPath() const; + RefPtr indexNames() const; + IDBTransaction& transaction(); + bool autoIncrement() const; + + struct IndexParameters { + bool unique; + bool multiEntry; + }; + + ExceptionOr> openCursor(JSC::ExecState&, RefPtr, IDBCursorDirection); + ExceptionOr> openCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection); + ExceptionOr> openKeyCursor(JSC::ExecState&, RefPtr, IDBCursorDirection); + ExceptionOr> openKeyCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection); + ExceptionOr> get(JSC::ExecState&, JSC::JSValue key); + ExceptionOr> get(JSC::ExecState&, IDBKeyRange*); + ExceptionOr> getKey(JSC::ExecState&, JSC::JSValue key); + ExceptionOr> getKey(JSC::ExecState&, IDBKeyRange*); + ExceptionOr> add(JSC::ExecState&, JSC::JSValue, JSC::JSValue key); + ExceptionOr> put(JSC::ExecState&, JSC::JSValue, JSC::JSValue key); + ExceptionOr> deleteFunction(JSC::ExecState&, IDBKeyRange*); + ExceptionOr> deleteFunction(JSC::ExecState&, JSC::JSValue key); + ExceptionOr> clear(JSC::ExecState&); + ExceptionOr> createIndex(JSC::ExecState&, const String& name, IDBKeyPath&&, const IndexParameters&); + ExceptionOr> index(const String& name); + ExceptionOr deleteIndex(const String& name); + ExceptionOr> count(JSC::ExecState&, IDBKeyRange*); + ExceptionOr> count(JSC::ExecState&, JSC::JSValue key); + ExceptionOr> getAll(JSC::ExecState&, RefPtr, std::optional count); + ExceptionOr> getAll(JSC::ExecState&, JSC::JSValue key, std::optional count); + ExceptionOr> getAllKeys(JSC::ExecState&, RefPtr, std::optional count); + ExceptionOr> getAllKeys(JSC::ExecState&, JSC::JSValue key, std::optional count); + + ExceptionOr> putForCursorUpdate(JSC::ExecState&, JSC::JSValue, JSC::JSValue key); + + void markAsDeleted(); + bool isDeleted() const { return m_deleted; } + + const IDBObjectStoreInfo& info() const { return m_info; } + + void rollbackForVersionChangeAbort(); + + void ref(); + void deref(); + + void visitReferencedIndexes(JSC::SlotVisitor&) const; + void renameReferencedIndex(IDBIndex&, const String& newName); private: - IDBObjectStore(const IDBObjectStoreMetadata&, IDBTransaction*); + enum class InlineKeyCheck { Perform, DoNotPerform }; + ExceptionOr> putOrAdd(JSC::ExecState&, JSC::JSValue, RefPtr, IndexedDB::ObjectStoreOverwriteMode, InlineKeyCheck); + ExceptionOr> doCount(JSC::ExecState&, const IDBKeyRangeData&); + ExceptionOr> doDelete(JSC::ExecState&, IDBKeyRange*); + + const char* activeDOMObjectName() const final; + bool canSuspendForDocumentSuspension() const final; + bool hasPendingActivity() const final; - int64_t findIndexId(const String& name) const; - bool containsIndex(const String& name) const - { - return findIndexId(name) != IDBIndexMetadata::InvalidId; - } + IDBObjectStoreInfo m_info; + IDBObjectStoreInfo m_originalInfo; - IDBObjectStoreMetadata m_metadata; - RefPtr m_transaction; - bool m_deleted; + // IDBObjectStore objects are always owned by their referencing IDBTransaction. + // ObjectStores will never outlive transactions so its okay to keep a raw C++ reference here. + IDBTransaction& m_transaction; - typedef HashMap> IDBIndexMap; - IDBIndexMap m_indexMap; + bool m_deleted { false }; + + mutable Lock m_referencedIndexLock; + HashMap> m_referencedIndexes; + HashMap> m_deletedIndexes; }; } // namespace WebCore -#endif - -#endif // IDBObjectStore_h +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBObjectStore.idl b/Source/WebCore/Modules/indexeddb/IDBObjectStore.idl index ae6350d57..96b00063d 100644 --- a/Source/WebCore/Modules/indexeddb/IDBObjectStore.idl +++ b/Source/WebCore/Modules/indexeddb/IDBObjectStore.idl @@ -23,30 +23,47 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// NOTE: This type is specified as 'any' in the IndexedDB specification, but is always +// constrained to this union. +typedef (DOMString or sequence) IDBKeyPath; + [ Conditional=INDEXED_DATABASE, - JSNoStaticTables, - ImplementationLacksVTable, + GenerateIsReachable=Impl, + JSCustomMarkFunction, + SkipVTableValidation, ] interface IDBObjectStore { - [TreatReturnedNullStringAs=Null] readonly attribute DOMString name; - [ImplementedAs=keyPathAny] readonly attribute IDBAny keyPath; + [SetterMayThrowException] attribute DOMString name; + readonly attribute IDBKeyPath? keyPath; readonly attribute DOMStringList indexNames; readonly attribute IDBTransaction transaction; readonly attribute boolean autoIncrement; - [CallWith=ScriptState, RaisesException] IDBRequest put(any value, optional any key); - [CallWith=ScriptState, RaisesException] IDBRequest add(any value, optional any key); - [CallWith=ScriptExecutionContext, ImplementedAs=deleteFunction, RaisesException] IDBRequest delete(IDBKeyRange? keyRange); - [CallWith=ScriptExecutionContext, ImplementedAs=deleteFunction, RaisesException] IDBRequest delete(any key); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest get(IDBKeyRange? key); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest get(any key); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest clear(); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openCursor(optional IDBKeyRange? range, optional DOMString direction); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openCursor(any key, optional DOMString direction); - [CallWith=ScriptExecutionContext, Custom, RaisesException] IDBIndex createIndex(DOMString name, sequence keyPath, optional Dictionary options); - [CallWith=ScriptExecutionContext, Custom, RaisesException] IDBIndex createIndex(DOMString name, DOMString keyPath, optional Dictionary options); - [RaisesException] IDBIndex index(DOMString name); - [RaisesException] void deleteIndex(DOMString name); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest count(optional IDBKeyRange? range); - [CallWith=ScriptExecutionContext, RaisesException] IDBRequest count(any key); + [CallWith=ScriptState, MayThrowException] IDBRequest put(any value, optional any key); + [CallWith=ScriptState, MayThrowException] IDBRequest add(any value, optional any key); + [CallWith=ScriptState, ImplementedAs=deleteFunction, MayThrowException] IDBRequest delete(IDBKeyRange? keyRange); + [CallWith=ScriptState, ImplementedAs=deleteFunction, MayThrowException] IDBRequest delete(any key); + [CallWith=ScriptState, MayThrowException] IDBRequest get(IDBKeyRange? key); + [CallWith=ScriptState, MayThrowException] IDBRequest get(any key); + [CallWith=ScriptState, MayThrowException] IDBRequest getKey(IDBKeyRange? key); + [CallWith=ScriptState, MayThrowException] IDBRequest getKey(any key); + [CallWith=ScriptState, MayThrowException] IDBRequest clear(); + [CallWith=ScriptState, MayThrowException] IDBRequest openCursor(optional IDBKeyRange? range = null, optional IDBCursorDirection direction = "next"); + [CallWith=ScriptState, MayThrowException] IDBRequest openCursor(any key, optional IDBCursorDirection direction = "next"); + [CallWith=ScriptState, MayThrowException] IDBRequest openKeyCursor(optional IDBKeyRange? range = null, optional IDBCursorDirection direction = "next"); + [CallWith=ScriptState, MayThrowException] IDBRequest openKeyCursor(any key, optional IDBCursorDirection direction = "next"); + [CallWith=ScriptState, MayThrowException] IDBRequest getAll(optional IDBKeyRange? range = null, [EnforceRange] optional unsigned long count); + [CallWith=ScriptState, MayThrowException] IDBRequest getAll(any key, [EnforceRange] optional unsigned long count); + [CallWith=ScriptState, MayThrowException] IDBRequest getAllKeys(optional IDBKeyRange? range = null, [EnforceRange] optional unsigned long count); + [CallWith=ScriptState, MayThrowException] IDBRequest getAllKeys(any key, [EnforceRange] optional unsigned long count); + [CallWith=ScriptState, MayThrowException] IDBIndex createIndex(DOMString name, (DOMString or sequence) keyPath, optional IDBIndexParameters options); + [MayThrowException] IDBIndex index(DOMString name); + [MayThrowException] void deleteIndex(DOMString name); + [CallWith=ScriptState, MayThrowException] IDBRequest count(optional IDBKeyRange? range = null); + [CallWith=ScriptState, MayThrowException] IDBRequest count(any key); +}; + +dictionary IDBIndexParameters { + boolean unique = false; + boolean multiEntry = false; }; diff --git a/Source/WebCore/Modules/indexeddb/IDBObjectStoreMetadata.h b/Source/WebCore/Modules/indexeddb/IDBObjectStoreMetadata.h deleted file mode 100644 index a1500a4a6..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBObjectStoreMetadata.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBObjectStoreMetadata_h -#define IDBObjectStoreMetadata_h - -#include "IDBIndexMetadata.h" - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -struct IDBObjectStoreMetadata { - IDBObjectStoreMetadata() - { - } - - IDBObjectStoreMetadata(const String& name, int64_t id, const IDBKeyPath& keyPath, bool autoIncrement, int64_t maxIndexId) - : name(name) - , id(id) - , keyPath(keyPath) - , autoIncrement(autoIncrement) - , maxIndexId(maxIndexId) - { - } - - String name; - int64_t id; - IDBKeyPath keyPath; - bool autoIncrement; - int64_t maxIndexId; - - static const int64_t InvalidId = -1; - - typedef HashMap IndexMap; - IndexMap indexes; - - IDBObjectStoreMetadata isolatedCopy() const; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBObjectStoreMetadata_h diff --git a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.cpp b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.cpp index e929c6c2b..6b79850be 100644 --- a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2012 Google Inc. All rights reserved. + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -28,128 +28,206 @@ #if ENABLE(INDEXED_DATABASE) +#include "DOMError.h" +#include "EventNames.h" +#include "IDBConnectionProxy.h" +#include "IDBConnectionToServer.h" #include "IDBDatabase.h" -#include "IDBDatabaseCallbacksImpl.h" -#include "IDBPendingTransactionMonitor.h" +#include "IDBError.h" +#include "IDBRequestCompletionEvent.h" +#include "IDBResultData.h" +#include "IDBTransaction.h" #include "IDBVersionChangeEvent.h" #include "Logging.h" #include "ScriptExecutionContext.h" namespace WebCore { -PassRefPtr IDBOpenDBRequest::create(ScriptExecutionContext* context, PassRefPtr callbacks, int64_t transactionId, uint64_t version, IndexedDB::VersionNullness versionNullness) +Ref IDBOpenDBRequest::createDeleteRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBDatabaseIdentifier& databaseIdentifier) { - RefPtr request(adoptRef(new IDBOpenDBRequest(context, callbacks, transactionId, version, versionNullness))); - request->suspendIfNeeded(); - return request.release(); + return adoptRef(*new IDBOpenDBRequest(context, connectionProxy, databaseIdentifier, 0, IndexedDB::RequestType::Delete)); } -IDBOpenDBRequest::IDBOpenDBRequest(ScriptExecutionContext* context, PassRefPtr callbacks, int64_t transactionId, uint64_t version, IndexedDB::VersionNullness versionNullness) - : IDBRequest(context, IDBAny::createNull(), IDBDatabaseBackend::NormalTask, 0) - , m_databaseCallbacks(callbacks) - , m_transactionId(transactionId) +Ref IDBOpenDBRequest::createOpenRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBDatabaseIdentifier& databaseIdentifier, uint64_t version) +{ + return adoptRef(*new IDBOpenDBRequest(context, connectionProxy, databaseIdentifier, version, IndexedDB::RequestType::Open)); +} + +IDBOpenDBRequest::IDBOpenDBRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBDatabaseIdentifier& databaseIdentifier, uint64_t version, IndexedDB::RequestType requestType) + : IDBRequest(context, connectionProxy) + , m_databaseIdentifier(databaseIdentifier) , m_version(version) - , m_versionNullness(versionNullness) { - ASSERT(!m_result); + m_requestType = requestType; } IDBOpenDBRequest::~IDBOpenDBRequest() { + ASSERT(currentThread() == originThreadID()); } -EventTargetInterface IDBOpenDBRequest::eventTargetInterface() const +void IDBOpenDBRequest::onError(const IDBResultData& data) { - return IDBOpenDBRequestEventTargetInterfaceType; + ASSERT(currentThread() == originThreadID()); + + m_domError = DOMError::create(data.error().name(), data.error().message()); + enqueueEvent(IDBRequestCompletionEvent::create(eventNames().errorEvent, true, true, *this)); } -void IDBOpenDBRequest::onBlocked(uint64_t oldVersion) +void IDBOpenDBRequest::versionChangeTransactionDidFinish() { - LOG(StorageAPI, "IDBOpenDBRequest::onBlocked()"); - if (!shouldEnqueueEvent()) - return; - - enqueueEvent(IDBVersionChangeEvent::create(oldVersion, m_version, m_versionNullness, eventNames().blockedEvent)); + ASSERT(currentThread() == originThreadID()); + + // 3.3.7 "versionchange" transaction steps + // When the transaction is finished, after firing complete/abort on the transaction, immediately set request's transaction property to null. + m_shouldExposeTransactionToDOM = false; } -void IDBOpenDBRequest::onUpgradeNeeded(uint64_t oldVersion, PassRefPtr prpDatabaseBackend, const IDBDatabaseMetadata& metadata) +void IDBOpenDBRequest::fireSuccessAfterVersionChangeCommit() { - LOG(StorageAPI, "IDBOpenDBRequest::onUpgradeNeeded()"); - if (m_contextStopped || !scriptExecutionContext()) { - RefPtr db = prpDatabaseBackend; - db->abort(m_transactionId); - db->close(m_databaseCallbacks); - return; - } - if (!shouldEnqueueEvent()) - return; + LOG(IndexedDB, "IDBOpenDBRequest::fireSuccessAfterVersionChangeCommit() - %s", resourceIdentifier().loggingString().utf8().data()); + + ASSERT(currentThread() == originThreadID()); + ASSERT(hasPendingActivity()); + m_transaction->addRequest(*this); - ASSERT(m_databaseCallbacks); + auto event = IDBRequestCompletionEvent::create(eventNames().successEvent, false, false, *this); + m_openDatabaseSuccessEvent = &event.get(); - RefPtr databaseBackend = prpDatabaseBackend; + enqueueEvent(WTFMove(event)); +} - RefPtr idbDatabase = IDBDatabase::create(scriptExecutionContext(), databaseBackend, m_databaseCallbacks); - idbDatabase->setMetadata(metadata); - m_databaseCallbacks->connect(idbDatabase.get()); - m_databaseCallbacks = 0; +void IDBOpenDBRequest::fireErrorAfterVersionChangeCompletion() +{ + LOG(IndexedDB, "IDBOpenDBRequest::fireErrorAfterVersionChangeCompletion() - %s", resourceIdentifier().loggingString().utf8().data()); - IDBDatabaseMetadata oldMetadata(metadata); - oldMetadata.version = oldVersion; + ASSERT(currentThread() == originThreadID()); + ASSERT(hasPendingActivity()); - m_transaction = IDBTransaction::create(scriptExecutionContext(), m_transactionId, idbDatabase.get(), this, oldMetadata); - m_result = IDBAny::create(idbDatabase.release()); + IDBError idbError(IDBDatabaseException::AbortError); + m_domError = DOMError::create(idbError.name(), idbError.message()); + setResultToUndefined(); - if (m_versionNullness == IndexedDB::VersionNullness::Null) - m_version = 1; - enqueueEvent(IDBVersionChangeEvent::create(oldVersion, m_version, m_versionNullness, eventNames().upgradeneededEvent)); + m_transaction->addRequest(*this); + enqueueEvent(IDBRequestCompletionEvent::create(eventNames().errorEvent, true, true, *this)); } -void IDBOpenDBRequest::onSuccess(PassRefPtr prpBackend, const IDBDatabaseMetadata& metadata) +void IDBOpenDBRequest::cancelForStop() { - LOG(StorageAPI, "IDBOpenDBRequest::onSuccess()"); - if (!shouldEnqueueEvent()) - return; + connectionProxy().openDBRequestCancelled({ connectionProxy(), *this }); +} - RefPtr backend = prpBackend; - RefPtr idbDatabase; - if (m_result) { - idbDatabase = m_result->idbDatabase(); - ASSERT(idbDatabase); - ASSERT(!m_databaseCallbacks); - } else { - ASSERT(m_databaseCallbacks); - idbDatabase = IDBDatabase::create(scriptExecutionContext(), backend.release(), m_databaseCallbacks); - m_databaseCallbacks->connect(idbDatabase.get()); - m_databaseCallbacks = 0; - m_result = IDBAny::create(idbDatabase.get()); - } - idbDatabase->setMetadata(metadata); - enqueueEvent(Event::create(eventNames().successEvent, false, false)); +bool IDBOpenDBRequest::dispatchEvent(Event& event) +{ + ASSERT(currentThread() == originThreadID()); + + bool result = IDBRequest::dispatchEvent(event); + + if (m_transaction && m_transaction->isVersionChange() && (event.type() == eventNames().errorEvent || event.type() == eventNames().successEvent)) + m_transaction->database().connectionProxy().didFinishHandlingVersionChangeTransaction(m_transaction->database().databaseConnectionIdentifier(), *m_transaction); + + return result; } -bool IDBOpenDBRequest::shouldEnqueueEvent() const +void IDBOpenDBRequest::onSuccess(const IDBResultData& resultData) { - if (m_contextStopped || !scriptExecutionContext()) - return false; - ASSERT(m_readyState == PENDING || m_readyState == DONE); - if (m_requestAborted) - return false; - return true; + LOG(IndexedDB, "IDBOpenDBRequest::onSuccess()"); + + ASSERT(currentThread() == originThreadID()); + + setResult(IDBDatabase::create(*scriptExecutionContext(), connectionProxy(), resultData)); + m_readyState = ReadyState::Done; + + enqueueEvent(IDBRequestCompletionEvent::create(eventNames().successEvent, false, false, *this)); } -bool IDBOpenDBRequest::dispatchEvent(PassRefPtr event) +void IDBOpenDBRequest::onUpgradeNeeded(const IDBResultData& resultData) { - // If the connection closed between onUpgradeNeeded and the delivery of the "success" event, - // an "error" event should be fired instead. - if (event->type() == eventNames().successEvent && m_result->type() == IDBAny::IDBDatabaseType && m_result->idbDatabase()->isClosePending()) { - m_result.clear(); - onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "The connection was closed.")); - return false; + ASSERT(currentThread() == originThreadID()); + + Ref database = IDBDatabase::create(*scriptExecutionContext(), connectionProxy(), resultData); + Ref transaction = database->startVersionChangeTransaction(resultData.transactionInfo(), *this); + + ASSERT(transaction->info().mode() == IDBTransactionMode::Versionchange); + ASSERT(transaction->originalDatabaseInfo()); + + uint64_t oldVersion = transaction->originalDatabaseInfo()->version(); + uint64_t newVersion = transaction->info().newVersion(); + + LOG(IndexedDB, "IDBOpenDBRequest::onUpgradeNeeded() - current version is %" PRIu64 ", new is %" PRIu64, oldVersion, newVersion); + + setResult(WTFMove(database)); + m_readyState = ReadyState::Done; + m_transaction = WTFMove(transaction); + m_transaction->addRequest(*this); + + enqueueEvent(IDBVersionChangeEvent::create(oldVersion, newVersion, eventNames().upgradeneededEvent)); +} + +void IDBOpenDBRequest::onDeleteDatabaseSuccess(const IDBResultData& resultData) +{ + ASSERT(currentThread() == originThreadID()); + + uint64_t oldVersion = resultData.databaseInfo().version(); + + LOG(IndexedDB, "IDBOpenDBRequest::onDeleteDatabaseSuccess() - current version is %" PRIu64, oldVersion); + + m_readyState = ReadyState::Done; + setResultToUndefined(); + + enqueueEvent(IDBVersionChangeEvent::create(oldVersion, 0, eventNames().successEvent)); +} + +void IDBOpenDBRequest::requestCompleted(const IDBResultData& data) +{ + LOG(IndexedDB, "IDBOpenDBRequest::requestCompleted"); + + ASSERT(currentThread() == originThreadID()); + + // If an Open request was completed after the page has navigated, leaving this request + // with a stopped script execution context, we need to message back to the server so it + // doesn't hang waiting on a database connection or transaction that will never exist. + if (m_contextStopped) { + switch (data.type()) { + case IDBResultType::OpenDatabaseSuccess: + connectionProxy().abortOpenAndUpgradeNeeded(data.databaseConnectionIdentifier(), IDBResourceIdentifier::emptyValue()); + break; + case IDBResultType::OpenDatabaseUpgradeNeeded: + connectionProxy().abortOpenAndUpgradeNeeded(data.databaseConnectionIdentifier(), data.transactionInfo().identifier()); + break; + default: + break; + } + + return; } - return IDBRequest::dispatchEvent(event); + switch (data.type()) { + case IDBResultType::Error: + onError(data); + break; + case IDBResultType::OpenDatabaseSuccess: + onSuccess(data); + break; + case IDBResultType::OpenDatabaseUpgradeNeeded: + onUpgradeNeeded(data); + break; + case IDBResultType::DeleteDatabaseSuccess: + onDeleteDatabaseSuccess(data); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } +} + +void IDBOpenDBRequest::requestBlocked(uint64_t oldVersion, uint64_t newVersion) +{ + ASSERT(currentThread() == originThreadID()); + + LOG(IndexedDB, "IDBOpenDBRequest::requestBlocked"); + enqueueEvent(IDBVersionChangeEvent::create(oldVersion, newVersion, eventNames().blockedEvent)); } } // namespace WebCore -#endif +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.h b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.h index 86b1a0fbb..a15a1dc34 100644 --- a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.h +++ b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.h @@ -1,72 +1,74 @@ /* - * Copyright (C) 2012 Google Inc. All rights reserved. + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBOpenDBRequest_h -#define IDBOpenDBRequest_h +#pragma once #if ENABLE(INDEXED_DATABASE) +#include "IDBDatabaseIdentifier.h" #include "IDBRequest.h" -#include "IndexedDB.h" namespace WebCore { -class IDBDatabaseCallbacks; +class IDBResultData; -class IDBOpenDBRequest : public IDBRequest { +class IDBOpenDBRequest final : public IDBRequest { public: - static PassRefPtr create(ScriptExecutionContext*, PassRefPtr, int64_t transactionId, uint64_t version, IndexedDB::VersionNullness); + static Ref createDeleteRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBDatabaseIdentifier&); + static Ref createOpenRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBDatabaseIdentifier&, uint64_t version); + virtual ~IDBOpenDBRequest(); + + const IDBDatabaseIdentifier& databaseIdentifier() const { return m_databaseIdentifier; } + uint64_t version() const { return m_version; } + + void requestCompleted(const IDBResultData&); + void requestBlocked(uint64_t oldVersion, uint64_t newVersion); - using IDBRequest::onSuccess; + void versionChangeTransactionDidFinish(); + void fireSuccessAfterVersionChangeCommit(); + void fireErrorAfterVersionChangeCompletion(); - virtual void onBlocked(uint64_t existingVersion) override; - virtual void onUpgradeNeeded(uint64_t oldVersion, PassRefPtr, const IDBDatabaseMetadata&) override; - virtual void onSuccess(PassRefPtr, const IDBDatabaseMetadata&) override; +private: + IDBOpenDBRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBDatabaseIdentifier&, uint64_t version, IndexedDB::RequestType); - // EventTarget - virtual EventTargetInterface eventTargetInterface() const; - virtual bool dispatchEvent(PassRefPtr) override; + bool dispatchEvent(Event&) final; - DEFINE_ATTRIBUTE_EVENT_LISTENER(blocked); - DEFINE_ATTRIBUTE_EVENT_LISTENER(upgradeneeded); + void cancelForStop() final; -protected: - virtual bool shouldEnqueueEvent() const override; + void onError(const IDBResultData&); + void onSuccess(const IDBResultData&); + void onUpgradeNeeded(const IDBResultData&); + void onDeleteDatabaseSuccess(const IDBResultData&); -private: - IDBOpenDBRequest(ScriptExecutionContext*, PassRefPtr, int64_t transactionId, uint64_t version, IndexedDB::VersionNullness); + bool isOpenDBRequest() const final { return true; } - RefPtr m_databaseCallbacks; - const int64_t m_transactionId; - uint64_t m_version; - IndexedDB::VersionNullness m_versionNullness; + IDBDatabaseIdentifier m_databaseIdentifier; + uint64_t m_version { 0 }; }; } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBOpenDBRequest_h diff --git a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.idl b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.idl index e5479cce2..dd6e62a23 100644 --- a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.idl +++ b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.idl @@ -25,11 +25,10 @@ [ Conditional=INDEXED_DATABASE, - EventTarget, - JSNoStaticTables, JSGenerateToJSObject, - JSGenerateToNativeObject + JSGenerateToNativeObject, + SkipVTableValidation, ] interface IDBOpenDBRequest : IDBRequest { - attribute EventListener onblocked; - attribute EventListener onupgradeneeded; + attribute EventHandler onblocked; + attribute EventHandler onupgradeneeded; }; diff --git a/Source/WebCore/Modules/indexeddb/IDBOperation.h b/Source/WebCore/Modules/indexeddb/IDBOperation.h deleted file mode 100644 index d88d2ce21..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBOperation.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBOperation_h -#define IDBOperation_h - -#include - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -class IDBOperation : public RefCounted { -public: - virtual ~IDBOperation() { } - virtual void perform(std::function completionCallback) = 0; -}; - -class IDBSynchronousOperation : public RefCounted { -public: - virtual ~IDBSynchronousOperation() { } - virtual void perform() = 0; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBOperation_h diff --git a/Source/WebCore/Modules/indexeddb/IDBPendingDeleteCall.h b/Source/WebCore/Modules/indexeddb/IDBPendingDeleteCall.h deleted file mode 100644 index 22bda5d49..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBPendingDeleteCall.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBPendingDeleteCall_h -#define IDBPendingDeleteCall_h - -#include "IDBCallbacks.h" -#include -#include - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -class IDBPendingDeleteCall { -public: - static PassOwnPtr create(PassRefPtr callbacks) - { - return adoptPtr(new IDBPendingDeleteCall(callbacks)); - } - IDBCallbacks* callbacks() { return m_callbacks.get(); } - -private: - IDBPendingDeleteCall(PassRefPtr callbacks) - : m_callbacks(callbacks) - { - } - RefPtr m_callbacks; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBPendingDeleteCall_h diff --git a/Source/WebCore/Modules/indexeddb/IDBPendingOpenCall.h b/Source/WebCore/Modules/indexeddb/IDBPendingOpenCall.h deleted file mode 100644 index 193ceeada..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBPendingOpenCall.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBPendingOpenCall_h -#define IDBPendingOpenCall_h - -#include -#include - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -class IDBCallbacks; -class IDBDatabaseCallbacks; - -class IDBPendingOpenCall { -public: - static PassOwnPtr create(IDBCallbacks& callbacks, IDBDatabaseCallbacks& databaseCallbacks, int64_t transactionId, uint64_t version) - { - return adoptPtr(new IDBPendingOpenCall(callbacks, databaseCallbacks, transactionId, version)); - } - IDBCallbacks* callbacks() { return m_callbacks.get(); } - IDBDatabaseCallbacks* databaseCallbacks() { return m_databaseCallbacks.get(); } - uint64_t version() { return m_version; } - int64_t transactionId() const { return m_transactionId; } - -private: - IDBPendingOpenCall(IDBCallbacks& callbacks, IDBDatabaseCallbacks& databaseCallbacks, int64_t transactionId, uint64_t version) - : m_callbacks(&callbacks) - , m_databaseCallbacks(&databaseCallbacks) - , m_version(version) - , m_transactionId(transactionId) - { - } - RefPtr m_callbacks; - RefPtr m_databaseCallbacks; - uint64_t m_version; - const int64_t m_transactionId; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBPendingOpenCall_h diff --git a/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.cpp b/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.cpp deleted file mode 100644 index e6741bdd3..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBPendingTransactionMonitor.h" -#include "IDBTransaction.h" -#include -#include - -using WTF::ThreadSpecific; - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -typedef Vector> TransactionList; - -static ThreadSpecific& transactions() -{ - // FIXME: Move the Vector to ScriptExecutionContext to avoid dealing with - // thread-local storage. - static std::once_flag onceFlag; - static ThreadSpecific* transactions; - std::call_once(onceFlag, []{ - transactions = new ThreadSpecific; - }); - - return *transactions; -} - -void IDBPendingTransactionMonitor::addNewTransaction(PassRefPtr transaction) -{ - transactions()->append(transaction); -} - -void IDBPendingTransactionMonitor::deactivateNewTransactions() -{ - ThreadSpecific& list = transactions(); - for (size_t i = 0; i < list->size(); ++i) { - RefPtr transaction = list->at(i); - transaction->setActive(false); - } - // FIXME: Exercise this call to clear() in a layout test. - list->clear(); -} - -}; -#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.h b/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.h deleted file mode 100644 index fef55fe3c..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBPendingTransactionMonitor_h -#define IDBPendingTransactionMonitor_h - -#if ENABLE(INDEXED_DATABASE) - -#include -#include - -namespace WebCore { - -class IDBTransaction; - -// This class keeps track of the transactions created during the current -// Javascript execution context. Transactions have an internal |active| flag -// which is set to true on creation, but must be set to false when control -// returns to the event loop. - -class IDBPendingTransactionMonitor { - WTF_MAKE_NONCOPYABLE(IDBPendingTransactionMonitor); -public: - static void addNewTransaction(PassRefPtr); - static void deactivateNewTransactions(); - -private: - IDBPendingTransactionMonitor(); -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBPendingTransactionMonitor_h diff --git a/Source/WebCore/Modules/indexeddb/IDBRecordIdentifier.h b/Source/WebCore/Modules/indexeddb/IDBRecordIdentifier.h index 7ca7783e7..e80d593a1 100644 --- a/Source/WebCore/Modules/indexeddb/IDBRecordIdentifier.h +++ b/Source/WebCore/Modules/indexeddb/IDBRecordIdentifier.h @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -25,8 +25,8 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBRecordIdentifier_h -#define IDBRecordIdentifier_h + +#pragma once #include #include @@ -37,14 +37,14 @@ namespace WebCore { class IDBRecordIdentifier : public RefCounted { public: - static PassRefPtr create(const Vector& encodedPrimaryKey, int64_t version) + static Ref create(const Vector& encodedPrimaryKey, int64_t version) { - return adoptRef(new IDBRecordIdentifier(encodedPrimaryKey, version)); + return adoptRef(*new IDBRecordIdentifier(encodedPrimaryKey, version)); } - static PassRefPtr create() + static Ref create() { - return adoptRef(new IDBRecordIdentifier); + return adoptRef(*new IDBRecordIdentifier); } const Vector& encodedPrimaryKey() const { return m_encodedPrimaryKey; } @@ -74,6 +74,4 @@ private: } // namespace WebCore -#endif - -#endif // IDBRecordIdentifier_h +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBRequest.cpp b/Source/WebCore/Modules/indexeddb/IDBRequest.cpp index 1bc170647..8b84cd19c 100644 --- a/Source/WebCore/Modules/indexeddb/IDBRequest.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBRequest.cpp @@ -1,29 +1,26 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -31,519 +28,527 @@ #if ENABLE(INDEXED_DATABASE) -#include "EventException.h" -#include "EventListener.h" +#include "DOMError.h" +#include "Event.h" #include "EventNames.h" #include "EventQueue.h" -#include "ExceptionCodePlaceholder.h" #include "IDBBindingUtilities.h" -#include "IDBCursorBackend.h" -#include "IDBCursorWithValue.h" +#include "IDBConnectionProxy.h" +#include "IDBCursor.h" #include "IDBDatabase.h" +#include "IDBDatabaseException.h" #include "IDBEventDispatcher.h" -#include "IDBTransaction.h" +#include "IDBIndex.h" +#include "IDBKeyData.h" +#include "IDBObjectStore.h" +#include "IDBResultData.h" +#include "JSDOMConvert.h" #include "Logging.h" +#include "ScopeGuard.h" #include "ScriptExecutionContext.h" +#include "ThreadSafeDataBuffer.h" +#include +#include + +using namespace JSC; namespace WebCore { -PassRefPtr IDBRequest::create(ScriptExecutionContext* context, PassRefPtr source, IDBTransaction* transaction) +Ref IDBRequest::create(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction) { - RefPtr request(adoptRef(new IDBRequest(context, source, IDBDatabaseBackend::NormalTask, transaction))); - request->suspendIfNeeded(); - // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions. - if (transaction) - transaction->registerRequest(request.get()); - return request.release(); + return adoptRef(*new IDBRequest(context, objectStore, transaction)); } -PassRefPtr IDBRequest::create(ScriptExecutionContext* context, PassRefPtr source, IDBDatabaseBackend::TaskType taskType, IDBTransaction* transaction) +Ref IDBRequest::create(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction) { - RefPtr request(adoptRef(new IDBRequest(context, source, taskType, transaction))); - request->suspendIfNeeded(); - // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions. - if (transaction) - transaction->registerRequest(request.get()); - return request.release(); + return adoptRef(*new IDBRequest(context, cursor, transaction)); } -IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr source, IDBDatabaseBackend::TaskType taskType, IDBTransaction* transaction) - : ActiveDOMObject(context) - , m_result(0) - , m_errorCode(0) - , m_contextStopped(false) - , m_transaction(transaction) - , m_readyState(PENDING) - , m_requestAborted(false) - , m_source(source) - , m_taskType(taskType) - , m_hasPendingActivity(true) - , m_cursorType(IndexedDB::CursorType::KeyAndValue) - , m_cursorDirection(IndexedDB::CursorDirection::Next) - , m_cursorFinished(false) - , m_pendingCursor(0) - , m_didFireUpgradeNeededEvent(false) - , m_preventPropagation(false) - , m_requestState(context) +Ref IDBRequest::create(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction) { + return adoptRef(*new IDBRequest(context, index, transaction)); } -IDBRequest::~IDBRequest() +Ref IDBRequest::createObjectStoreGet(ScriptExecutionContext& context, IDBObjectStore& objectStore, IndexedDB::ObjectStoreRecordType type, IDBTransaction& transaction) { - ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !scriptExecutionContext()); + return adoptRef(*new IDBRequest(context, objectStore, type, transaction)); } -PassRefPtr IDBRequest::result(ExceptionCode& ec) const +Ref IDBRequest::createIndexGet(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType requestedRecordType, IDBTransaction& transaction) { - if (m_readyState != DONE) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - return m_result; + return adoptRef(*new IDBRequest(context, index, requestedRecordType, transaction)); } -PassRefPtr IDBRequest::error(ExceptionCode& ec) const +IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy) + : IDBActiveDOMObject(&context) + , m_resourceIdentifier(connectionProxy) + , m_connectionProxy(connectionProxy) { - if (m_readyState != DONE) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - return m_error; + suspendIfNeeded(); } -unsigned short IDBRequest::errorCode(ExceptionCode& ec) const +IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction) + : IDBActiveDOMObject(&context) + , m_transaction(&transaction) + , m_resourceIdentifier(transaction.connectionProxy()) + , m_source(&objectStore) + , m_connectionProxy(transaction.database().connectionProxy()) { - if (m_readyState != DONE) { - ec = IDBDatabaseException::InvalidStateError; - return 0; - } - return m_errorCode; + suspendIfNeeded(); } -PassRefPtr IDBRequest::source() const +IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction) + : IDBActiveDOMObject(&context) + , m_transaction(&transaction) + , m_resourceIdentifier(transaction.connectionProxy()) + , m_pendingCursor(&cursor) + , m_connectionProxy(transaction.database().connectionProxy()) { - return m_source; + suspendIfNeeded(); + + WTF::switchOn(cursor.source(), + [this] (const auto& value) { this->m_source = IDBRequest::Source { value }; } + ); + + cursor.setRequest(*this); } -PassRefPtr IDBRequest::transaction() const +IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction) + : IDBActiveDOMObject(&context) + , m_transaction(&transaction) + , m_resourceIdentifier(transaction.connectionProxy()) + , m_source(&index) + , m_connectionProxy(transaction.database().connectionProxy()) { - return m_transaction; + suspendIfNeeded(); } -const String& IDBRequest::readyState() const +IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBObjectStore& objectStore, IndexedDB::ObjectStoreRecordType type, IDBTransaction& transaction) + : IDBActiveDOMObject(&context) + , m_transaction(&transaction) + , m_resourceIdentifier(transaction.connectionProxy()) + , m_source(&objectStore) + , m_requestedObjectStoreRecordType(type) + , m_connectionProxy(transaction.database().connectionProxy()) { - ASSERT(m_readyState == PENDING || m_readyState == DONE); - DEFINE_STATIC_LOCAL(AtomicString, pending, ("pending", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(AtomicString, done, ("done", AtomicString::ConstructFromLiteral)); - - if (m_readyState == PENDING) - return pending; + suspendIfNeeded(); +} - return done; +IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType requestedRecordType, IDBTransaction& transaction) + : IDBRequest(context, index, transaction) +{ + m_requestedIndexRecordType = requestedRecordType; } -void IDBRequest::markEarlyDeath() +IDBRequest::~IDBRequest() { - ASSERT(m_readyState == PENDING); - m_readyState = EarlyDeath; - if (m_transaction) - m_transaction->unregisterRequest(this); + ASSERT(currentThread() == originThreadID()); + + if (m_result) { + WTF::switchOn(m_result.value(), + [] (RefPtr& cursor) { cursor->clearRequest(); }, + [] (const auto&) { } + ); + } } -void IDBRequest::abort() +ExceptionOr> IDBRequest::result() const { - ASSERT(!m_requestAborted); - if (m_contextStopped || !scriptExecutionContext()) - return; - ASSERT(m_readyState == PENDING || m_readyState == DONE); - if (m_readyState == DONE) - return; + if (!isDone()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to read the 'result' property from 'IDBRequest': The request has not finished.") }; - // Enqueued events may be the only reference to this object. - RefPtr self(this); + return std::optional { m_result }; +} - EventQueue& eventQueue = scriptExecutionContext()->eventQueue(); - for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { - bool removed = eventQueue.cancelEvent(*m_enqueuedEvents[i]); - ASSERT_UNUSED(removed, removed); - } - m_enqueuedEvents.clear(); +ExceptionOr IDBRequest::error() const +{ + ASSERT(currentThread() == originThreadID()); + + if (!isDone()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to read the 'error' property from 'IDBRequest': The request has not finished.") }; - m_errorCode = 0; - m_error.clear(); - m_errorMessage = String(); - m_result.clear(); - onError(IDBDatabaseError::create(IDBDatabaseException::AbortError)); - m_requestAborted = true; + return m_domError.get(); } -void IDBRequest::setCursorDetails(IndexedDB::CursorType cursorType, IndexedDB::CursorDirection direction) +void IDBRequest::setSource(IDBCursor& cursor) { - ASSERT(m_readyState == PENDING); - ASSERT(!m_pendingCursor); - m_cursorType = cursorType; - m_cursorDirection = direction; + ASSERT(currentThread() == originThreadID()); + ASSERT(!m_cursorRequestNotifier); + + m_source = Source { &cursor }; + m_cursorRequestNotifier = std::make_unique([this]() { + ASSERT(WTF::holds_alternative>(m_source.value())); + WTF::get>(m_source.value())->decrementOutstandingRequestCount(); + }); } -void IDBRequest::setPendingCursor(PassRefPtr cursor) +void IDBRequest::setVersionChangeTransaction(IDBTransaction& transaction) { - ASSERT(m_readyState == DONE); - ASSERT(scriptExecutionContext()); - ASSERT(m_transaction); - ASSERT(!m_pendingCursor); - ASSERT(cursor == getResultCursor()); + ASSERT(currentThread() == originThreadID()); + ASSERT(!m_transaction); + ASSERT(transaction.isVersionChange()); + ASSERT(!transaction.isFinishedOrFinishing()); - m_pendingCursor = cursor; - m_result.clear(); - m_readyState = PENDING; - m_errorCode = 0; - m_error.clear(); - m_errorMessage = String(); - m_transaction->registerRequest(this); + m_transaction = &transaction; } -PassRefPtr IDBRequest::getResultCursor() +RefPtr IDBRequest::transaction() const { - if (!m_result) + ASSERT(currentThread() == originThreadID()); + return m_shouldExposeTransactionToDOM ? m_transaction : nullptr; +} + +uint64_t IDBRequest::sourceObjectStoreIdentifier() const +{ + ASSERT(currentThread() == originThreadID()); + + if (!m_source) return 0; - if (m_result->type() == IDBAny::IDBCursorType) - return m_result->idbCursor(); - if (m_result->type() == IDBAny::IDBCursorWithValueType) - return m_result->idbCursorWithValue(); - return 0; + + return WTF::switchOn(m_source.value(), + [] (const RefPtr& objectStore) { return objectStore->info().identifier(); }, + [] (const RefPtr& index) { return index->info().objectStoreIdentifier(); }, + [] (const RefPtr&) { return 0; } + ); } -void IDBRequest::setResultCursor(PassRefPtr cursor, PassRefPtr key, PassRefPtr primaryKey, const Deprecated::ScriptValue& value) +uint64_t IDBRequest::sourceIndexIdentifier() const { - ASSERT(m_readyState == PENDING); - m_cursorKey = key; - m_cursorPrimaryKey = primaryKey; - m_cursorValue = value; + ASSERT(currentThread() == originThreadID()); - if (m_cursorType == IndexedDB::CursorType::KeyOnly) { - m_result = IDBAny::create(cursor); - return; - } + if (!m_source) + return 0; - m_result = IDBAny::create(IDBCursorWithValue::fromCursor(cursor)); + return WTF::switchOn(m_source.value(), + [] (const RefPtr&) -> uint64_t { return 0; }, + [] (const RefPtr& index) -> uint64_t { return index->info().identifier(); }, + [] (const RefPtr&) -> uint64_t { return 0; } + ); } -void IDBRequest::finishCursor() +IndexedDB::ObjectStoreRecordType IDBRequest::requestedObjectStoreRecordType() const { - m_cursorFinished = true; - if (m_readyState != PENDING) - m_hasPendingActivity = false; + ASSERT(currentThread() == originThreadID()); + + return m_requestedObjectStoreRecordType; } -bool IDBRequest::shouldEnqueueEvent() const +IndexedDB::IndexRecordType IDBRequest::requestedIndexRecordType() const { - if (m_contextStopped || !scriptExecutionContext()) - return false; - ASSERT(m_readyState == PENDING || m_readyState == DONE); - if (m_requestAborted) - return false; - ASSERT(m_readyState == PENDING); - ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result); - return true; + ASSERT(currentThread() == originThreadID()); + ASSERT(m_source); + ASSERT(WTF::holds_alternative>(m_source.value())); + + return m_requestedIndexRecordType; } -void IDBRequest::onError(PassRefPtr error) +EventTargetInterface IDBRequest::eventTargetInterface() const { - LOG(StorageAPI, "IDBRequest::onError()"); - if (!shouldEnqueueEvent()) - return; + ASSERT(currentThread() == originThreadID()); - m_errorCode = error->code(); - m_errorMessage = error->message(); - m_error = DOMError::create(IDBDatabaseException::getErrorName(error->idbCode())); - m_pendingCursor.clear(); - enqueueEvent(Event::create(eventNames().errorEvent, true, true)); + return IDBRequestEventTargetInterfaceType; } -static PassRefPtr createSuccessEvent() +const char* IDBRequest::activeDOMObjectName() const { - return Event::create(eventNames().successEvent, false, false); + ASSERT(currentThread() == originThreadID()); + + return "IDBRequest"; } -void IDBRequest::onSuccess(PassRefPtr domStringList) +bool IDBRequest::canSuspendForDocumentSuspension() const { - LOG(StorageAPI, "IDBRequest::onSuccess(DOMStringList)"); - if (!shouldEnqueueEvent()) - return; + ASSERT(currentThread() == originThreadID()); + return false; +} - m_result = IDBAny::create(domStringList); - enqueueEvent(createSuccessEvent()); +bool IDBRequest::hasPendingActivity() const +{ + ASSERT(currentThread() == originThreadID() || mayBeGCThread()); + return m_hasPendingActivity; } -void IDBRequest::onSuccess(PassRefPtr backend, PassRefPtr key, PassRefPtr primaryKey, PassRefPtr buffer) +void IDBRequest::stop() { - LOG(StorageAPI, "IDBRequest::onSuccess(IDBCursor)"); - if (!shouldEnqueueEvent()) - return; + ASSERT(currentThread() == originThreadID()); + ASSERT(!m_contextStopped); - DOMRequestState::Scope scope(m_requestState); - Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer); - ASSERT(!m_pendingCursor); - RefPtr cursor; - switch (m_cursorType) { - case IndexedDB::CursorType::KeyOnly: - cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get()); - break; - case IndexedDB::CursorType::KeyAndValue: - cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get()); - break; - default: - ASSERT_NOT_REACHED(); - } - setResultCursor(cursor, key, primaryKey, value); + cancelForStop(); + + removeAllEventListeners(); - enqueueEvent(createSuccessEvent()); + m_contextStopped = true; } -void IDBRequest::onSuccess(PassRefPtr idbKey) +void IDBRequest::cancelForStop() { - LOG(StorageAPI, "IDBRequest::onSuccess(IDBKey)"); - if (!shouldEnqueueEvent()) - return; - - if (idbKey && idbKey->isValid()) { - DOMRequestState::Scope scope(m_requestState); - m_result = IDBAny::create(idbKeyToScriptValue(requestState(), idbKey)); - } else - m_result = IDBAny::createInvalid(); - enqueueEvent(createSuccessEvent()); + // The base IDBRequest class has nothing additional to do here. } -void IDBRequest::onSuccess(PassRefPtr valueBuffer) +void IDBRequest::enqueueEvent(Ref&& event) { - LOG(StorageAPI, "IDBRequest::onSuccess(SharedBuffer)"); - if (!shouldEnqueueEvent()) + ASSERT(currentThread() == originThreadID()); + if (!scriptExecutionContext() || m_contextStopped) return; - DOMRequestState::Scope scope(m_requestState); - Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer); - onSuccessInternal(value); + event->setTarget(this); + scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event)); } -#ifndef NDEBUG -static PassRefPtr effectiveObjectStore(PassRefPtr source) +bool IDBRequest::dispatchEvent(Event& event) { - if (source->type() == IDBAny::IDBObjectStoreType) - return source->idbObjectStore(); - if (source->type() == IDBAny::IDBIndexType) - return source->idbIndex()->objectStore(); + LOG(IndexedDB, "IDBRequest::dispatchEvent - %s (%p)", event.type().string().utf8().data(), this); + + ASSERT(currentThread() == originThreadID()); + ASSERT(m_hasPendingActivity); + ASSERT(!m_contextStopped); + + if (event.type() != eventNames().blockedEvent) + m_readyState = ReadyState::Done; + + Vector> targets; + targets.append(this); + + if (&event == m_openDatabaseSuccessEvent) + m_openDatabaseSuccessEvent = nullptr; + else if (m_transaction && !m_transaction->isFinished()) { + targets.append(m_transaction); + targets.append(m_transaction->db()); + } + + m_hasPendingActivity = false; + + m_cursorRequestNotifier = nullptr; + + bool dontPreventDefault; + { + TransactionActivator activator(m_transaction.get()); + dontPreventDefault = IDBEventDispatcher::dispatch(event, targets); + } + + // IDBEventDispatcher::dispatch() might have set the pending activity flag back to true, suggesting the request will be reused. + // We might also re-use the request if this event was the upgradeneeded event for an IDBOpenDBRequest. + if (!m_hasPendingActivity) + m_hasPendingActivity = isOpenDBRequest() && (event.type() == eventNames().upgradeneededEvent || event.type() == eventNames().blockedEvent); + + // The request should only remain in the transaction's request list if it represents a pending cursor operation, or this is an open request that was blocked. + if (m_transaction && !m_pendingCursor && event.type() != eventNames().blockedEvent) + m_transaction->removeRequest(*this); - ASSERT_NOT_REACHED(); - return 0; + if (dontPreventDefault && event.type() == eventNames().errorEvent && m_transaction && !m_transaction->isFinishedOrFinishing()) { + ASSERT(m_domError); + m_transaction->abortDueToFailedRequest(*m_domError); + } + + if (m_transaction) + m_transaction->finishedDispatchEventForRequest(*this); + + return dontPreventDefault; } -#endif -void IDBRequest::onSuccess(PassRefPtr valueBuffer, PassRefPtr prpPrimaryKey, const IDBKeyPath& keyPath) +void IDBRequest::uncaughtExceptionInEventHandler() { - LOG(StorageAPI, "IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)"); - if (!shouldEnqueueEvent()) - return; + LOG(IndexedDB, "IDBRequest::uncaughtExceptionInEventHandler"); -#ifndef NDEBUG - ASSERT(keyPath == effectiveObjectStore(m_source)->keyPath()); -#endif - DOMRequestState::Scope scope(m_requestState); - Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer); + ASSERT(currentThread() == originThreadID()); - RefPtr primaryKey = prpPrimaryKey; -#ifndef NDEBUG - RefPtr expectedKey = createIDBKeyFromScriptValueAndKeyPath(requestState(), value, keyPath); - ASSERT(!expectedKey || expectedKey->isEqual(primaryKey.get())); -#endif - bool injected = injectIDBKeyIntoScriptValue(requestState(), primaryKey, value, keyPath); - ASSERT_UNUSED(injected, injected); - onSuccessInternal(value); + if (m_transaction && m_idbError.code() != IDBDatabaseException::AbortError) + m_transaction->abortDueToFailedRequest(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::AbortError), ASCIILiteral("IDBTransaction will abort due to uncaught exception in an event handler"))); } -void IDBRequest::onSuccess(int64_t value) +void IDBRequest::setResult(const IDBKeyData& keyData) { - LOG(StorageAPI, "IDBRequest::onSuccess(int64_t)"); - if (!shouldEnqueueEvent()) + ASSERT(currentThread() == originThreadID()); + + auto* context = scriptExecutionContext(); + if (!context) return; - return onSuccessInternal(SerializedScriptValue::numberValue(value)); -} -void IDBRequest::onSuccess() -{ - LOG(StorageAPI, "IDBRequest::onSuccess()"); - if (!shouldEnqueueEvent()) + auto* state = context->execState(); + if (!state) return; - return onSuccessInternal(SerializedScriptValue::undefinedValue()); + + // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object + // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject. + VM& vm = context->vm(); + JSLockHolder lock(vm); + m_result = Result { JSC::Strong { vm, toJS(*state, *jsCast(state->lexicalGlobalObject()), keyData) } }; } -void IDBRequest::onSuccessInternal(PassRefPtr value) +void IDBRequest::setResult(const Vector& keyDatas) { - ASSERT(!m_contextStopped); - DOMRequestState::Scope scope(m_requestState); - return onSuccessInternal(deserializeIDBValue(requestState(), value)); + ASSERT(currentThread() == originThreadID()); + + auto* context = scriptExecutionContext(); + if (!context) + return; + + auto* state = context->execState(); + if (!state) + return; + + // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object + // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject. + VM& vm = context->vm(); + JSLockHolder lock(vm); + m_result = Result { JSC::Strong { vm, toJS>(*state, *jsCast(state->lexicalGlobalObject()), keyDatas) } }; } -void IDBRequest::onSuccessInternal(const Deprecated::ScriptValue& value) +void IDBRequest::setResult(const Vector& values) { - m_result = IDBAny::create(value); - if (m_pendingCursor) { - m_pendingCursor->close(); - m_pendingCursor.clear(); - } - enqueueEvent(createSuccessEvent()); + ASSERT(currentThread() == originThreadID()); + + auto* context = scriptExecutionContext(); + if (!context) + return; + + auto* state = context->execState(); + if (!state) + return; + + // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object + // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject. + VM& vm = context->vm(); + JSLockHolder lock(vm); + m_result = Result { JSC::Strong { vm, toJS>(*state, *jsCast(state->lexicalGlobalObject()), values) } }; } -void IDBRequest::onSuccess(PassRefPtr key, PassRefPtr primaryKey, PassRefPtr buffer) +void IDBRequest::setResult(uint64_t number) { - LOG(StorageAPI, "IDBRequest::onSuccess(key, primaryKey, value)"); - if (!shouldEnqueueEvent()) + ASSERT(currentThread() == originThreadID()); + + auto* context = scriptExecutionContext(); + if (!context) return; - DOMRequestState::Scope scope(m_requestState); - Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer); - ASSERT(m_pendingCursor); - setResultCursor(m_pendingCursor.release(), key, primaryKey, value); - enqueueEvent(createSuccessEvent()); + m_result = Result { JSC::Strong { context->vm(), toJS(number) } }; } -bool IDBRequest::hasPendingActivity() const +void IDBRequest::setResultToStructuredClone(const IDBValue& value) { - // FIXME: In an ideal world, we should return true as long as anyone has a or can - // get a handle to us and we have event listeners. This is order to handle - // user generated events properly. - return m_hasPendingActivity && !m_contextStopped; + ASSERT(currentThread() == originThreadID()); + + LOG(IndexedDB, "IDBRequest::setResultToStructuredClone"); + + auto* context = scriptExecutionContext(); + if (!context) + return; + + auto* state = context->execState(); + if (!state) + return; + + // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object + // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject. + VM& vm = context->vm(); + JSLockHolder lock(vm); + m_result = Result { JSC::Strong { vm, toJS(*state, *jsCast(state->lexicalGlobalObject()), value) } }; } -void IDBRequest::stop() +void IDBRequest::setResultToUndefined() { - if (m_contextStopped) + ASSERT(currentThread() == originThreadID()); + + auto* context = scriptExecutionContext(); + if (!context) return; - m_contextStopped = true; - m_requestState.clear(); - if (m_readyState == PENDING) - markEarlyDeath(); + m_result = Result { JSC::Strong { context->vm(), JSC::jsUndefined() } }; } -EventTargetInterface IDBRequest::eventTargetInterface() const +IDBCursor* IDBRequest::resultCursor() { - return IDBRequestEventTargetInterfaceType; + ASSERT(currentThread() == originThreadID()); + + if (!m_result) + return nullptr; + + return WTF::switchOn(m_result.value(), + [] (const RefPtr& cursor) -> IDBCursor* { return cursor.get(); }, + [] (const auto&) -> IDBCursor* { return nullptr; } + ); } -bool IDBRequest::dispatchEvent(PassRefPtr event) +void IDBRequest::willIterateCursor(IDBCursor& cursor) { - LOG(StorageAPI, "IDBRequest::dispatchEvent"); - ASSERT(m_readyState == PENDING); - ASSERT(!m_contextStopped); - ASSERT(m_hasPendingActivity); - ASSERT(m_enqueuedEvents.size()); + ASSERT(currentThread() == originThreadID()); + ASSERT(isDone()); ASSERT(scriptExecutionContext()); - ASSERT(event->target() == this); - ASSERT_WITH_MESSAGE(m_readyState < DONE, "When dispatching event %s, m_readyState < DONE(%d), was %d", event->type().string().utf8().data(), DONE, m_readyState); - - DOMRequestState::Scope scope(m_requestState); + ASSERT(m_transaction); + ASSERT(!m_pendingCursor); + ASSERT(&cursor == resultCursor()); + ASSERT(!m_cursorRequestNotifier); - if (event->type() != eventNames().blockedEvent) - m_readyState = DONE; + m_pendingCursor = &cursor; + m_hasPendingActivity = true; + m_result = std::nullopt; + m_readyState = ReadyState::Pending; + m_domError = nullptr; + m_idbError = { }; - for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { - if (m_enqueuedEvents[i].get() == event.get()) - m_enqueuedEvents.remove(i); - } + m_cursorRequestNotifier = std::make_unique([this]() { + m_pendingCursor->decrementOutstandingRequestCount(); + }); +} - Vector> targets; - targets.append(this); - if (m_transaction && !m_preventPropagation) { - targets.append(m_transaction); - // If there ever are events that are associated with a database but - // that do not have a transaction, then this will not work and we need - // this object to actually hold a reference to the database (to ensure - // it stays alive). - targets.append(m_transaction->db()); - } +void IDBRequest::didOpenOrIterateCursor(const IDBResultData& resultData) +{ + ASSERT(currentThread() == originThreadID()); + ASSERT(m_pendingCursor); - // Cursor properties should not updated until the success event is being dispatched. - RefPtr cursorToNotify; - if (event->type() == eventNames().successEvent) { - cursorToNotify = getResultCursor(); - if (cursorToNotify) { - cursorToNotify->setValueReady(requestState(), m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue); - m_cursorValue.clear(); - } - } + m_result = std::nullopt; - if (event->type() == eventNames().upgradeneededEvent) { - ASSERT(!m_didFireUpgradeNeededEvent); - m_didFireUpgradeNeededEvent = true; + if (resultData.type() == IDBResultType::IterateCursorSuccess || resultData.type() == IDBResultType::OpenCursorSuccess) { + m_pendingCursor->setGetResult(*this, resultData.getResult()); + if (resultData.getResult().isDefined()) + m_result = Result { m_pendingCursor }; } - // FIXME: When we allow custom event dispatching, this will probably need to change. - ASSERT_WITH_MESSAGE(event->type() == eventNames().successEvent || event->type() == eventNames().errorEvent || event->type() == eventNames().blockedEvent || event->type() == eventNames().upgradeneededEvent, "event type was %s", event->type().string().utf8().data()); - const bool setTransactionActive = m_transaction && (event->type() == eventNames().successEvent || event->type() == eventNames().upgradeneededEvent || (event->type() == eventNames().errorEvent && m_errorCode != IDBDatabaseException::AbortError)); - - if (setTransactionActive) - m_transaction->setActive(true); + m_cursorRequestNotifier = nullptr; + m_pendingCursor = nullptr; - bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets); + completeRequestAndDispatchEvent(resultData); +} - if (m_transaction) { - if (m_readyState == DONE) - m_transaction->unregisterRequest(this); +void IDBRequest::completeRequestAndDispatchEvent(const IDBResultData& resultData) +{ + ASSERT(currentThread() == originThreadID()); - // Possibly abort the transaction. This must occur after unregistering (so this request - // doesn't receive a second error) and before deactivating (which might trigger commit). - if (event->type() == eventNames().errorEvent && dontPreventDefault && !m_requestAborted) { - m_transaction->setError(m_error, m_errorMessage); - m_transaction->abort(IGNORE_EXCEPTION); - } + m_readyState = ReadyState::Done; - // If this was the last request in the transaction's list, it may commit here. - if (setTransactionActive) - m_transaction->setActive(false); - } + m_idbError = resultData.error(); + if (!m_idbError.isNull()) + onError(); + else + onSuccess(); +} - if (cursorToNotify) - cursorToNotify->postSuccessHandlerCallback(); +void IDBRequest::onError() +{ + LOG(IndexedDB, "IDBRequest::onError"); - if (m_readyState == DONE && (!cursorToNotify || m_cursorFinished) && event->type() != eventNames().upgradeneededEvent) - m_hasPendingActivity = false; + ASSERT(currentThread() == originThreadID()); + ASSERT(!m_idbError.isNull()); - return dontPreventDefault; + m_domError = DOMError::create(m_idbError.name(), m_idbError.message()); + enqueueEvent(Event::create(eventNames().errorEvent, true, true)); } -void IDBRequest::uncaughtExceptionInEventHandler() +void IDBRequest::onSuccess() { - if (m_transaction && !m_requestAborted) { - m_transaction->setError(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::AbortError)), "Uncaught exception in event handler."); - m_transaction->abort(IGNORE_EXCEPTION); - } -} + LOG(IndexedDB, "IDBRequest::onSuccess"); + ASSERT(currentThread() == originThreadID()); -void IDBRequest::transactionDidFinishAndDispatch() -{ - ASSERT(m_transaction); - ASSERT(m_transaction->isVersionChange()); - ASSERT(m_readyState == DONE); - ASSERT(scriptExecutionContext()); - m_transaction.clear(); - m_readyState = PENDING; + enqueueEvent(Event::create(eventNames().successEvent, false, false)); } -void IDBRequest::enqueueEvent(PassRefPtr event) +void IDBRequest::setResult(Ref&& database) { - ASSERT(m_readyState == PENDING || m_readyState == DONE); - - if (m_contextStopped || !scriptExecutionContext()) - return; - - ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().string().utf8().data(), m_readyState); - - event->setTarget(this); + ASSERT(currentThread() == originThreadID()); - if (scriptExecutionContext()->eventQueue().enqueueEvent(event.get())) - m_enqueuedEvents.append(event); + m_result = Result { RefPtr { WTFMove(database) } }; } } // namespace WebCore -#endif +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBRequest.h b/Source/WebCore/Modules/indexeddb/IDBRequest.h index 0b5a4be5d..3949889d7 100644 --- a/Source/WebCore/Modules/indexeddb/IDBRequest.h +++ b/Source/WebCore/Modules/indexeddb/IDBRequest.h @@ -1,169 +1,180 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBRequest_h -#define IDBRequest_h +#pragma once #if ENABLE(INDEXED_DATABASE) -#include "ActiveDOMObject.h" -#include "DOMError.h" -#include "DOMRequestState.h" -#include "DOMStringList.h" -#include "Event.h" -#include "EventListener.h" -#include "EventNames.h" #include "EventTarget.h" -#include "IDBAny.h" -#include "IDBCallbacks.h" -#include "IDBCursor.h" -#include "IDBDatabaseBackend.h" -#include "IDBDatabaseCallbacks.h" -#include "ScriptWrappable.h" +#include "ExceptionOr.h" +#include "IDBActiveDOMObject.h" +#include "IDBError.h" +#include "IDBResourceIdentifier.h" +#include "IndexedDB.h" +#include namespace WebCore { +class DOMError; +class Event; +class IDBCursor; +class IDBDatabase; +class IDBIndex; +class IDBKeyData; +class IDBObjectStore; +class IDBResultData; class IDBTransaction; +class IDBValue; +class ScopeGuard; +class ThreadSafeDataBuffer; -typedef int ExceptionCode; +namespace IDBClient { +class IDBConnectionProxy; +class IDBConnectionToServer; +} -class IDBRequest : public ScriptWrappable, public IDBCallbacks, public EventTargetWithInlineData, public ActiveDOMObject { +class IDBRequest : public EventTargetWithInlineData, public IDBActiveDOMObject, public RefCounted { public: - static PassRefPtr create(ScriptExecutionContext*, PassRefPtr source, IDBTransaction*); - static PassRefPtr create(ScriptExecutionContext*, PassRefPtr source, IDBDatabaseBackend::TaskType, IDBTransaction*); + static Ref create(ScriptExecutionContext&, IDBObjectStore&, IDBTransaction&); + static Ref create(ScriptExecutionContext&, IDBCursor&, IDBTransaction&); + static Ref create(ScriptExecutionContext&, IDBIndex&, IDBTransaction&); + static Ref createObjectStoreGet(ScriptExecutionContext&, IDBObjectStore&, IndexedDB::ObjectStoreRecordType, IDBTransaction&); + static Ref createIndexGet(ScriptExecutionContext&, IDBIndex&, IndexedDB::IndexRecordType, IDBTransaction&); + + const IDBResourceIdentifier& resourceIdentifier() const { return m_resourceIdentifier; } + virtual ~IDBRequest(); - PassRefPtr result(ExceptionCode&) const; - unsigned short errorCode(ExceptionCode&) const; - PassRefPtr error(ExceptionCode&) const; - PassRefPtr source() const; - PassRefPtr transaction() const; - void preventPropagation() { m_preventPropagation = true; } + using Result = Variant, RefPtr, JSC::Strong>; + ExceptionOr> result() const; + + using Source = Variant, RefPtr, RefPtr>; + const std::optional& source() const { return m_source; } - // Defined in the IDL - enum ReadyState { - PENDING = 1, - DONE = 2, - EarlyDeath = 3 - }; + ExceptionOr error() const; - const String& readyState() const; + RefPtr transaction() const; + + enum class ReadyState { Pending, Done }; + ReadyState readyState() const { return m_readyState; } - DEFINE_ATTRIBUTE_EVENT_LISTENER(success); - DEFINE_ATTRIBUTE_EVENT_LISTENER(error); + bool isDone() const { return m_readyState == ReadyState::Done; } - void markEarlyDeath(); - void setCursorDetails(IndexedDB::CursorType, IndexedDB::CursorDirection); - void setPendingCursor(PassRefPtr); - void finishCursor(); - void abort(); + uint64_t sourceObjectStoreIdentifier() const; + uint64_t sourceIndexIdentifier() const; + IndexedDB::ObjectStoreRecordType requestedObjectStoreRecordType() const; + IndexedDB::IndexRecordType requestedIndexRecordType() const; - // IDBCallbacks - virtual void onError(PassRefPtr); - virtual void onSuccess(PassRefPtr); - virtual void onSuccess(PassRefPtr, PassRefPtr, PassRefPtr primaryKey, PassRefPtr); - virtual void onSuccess(PassRefPtr); - virtual void onSuccess(PassRefPtr); - virtual void onSuccess(PassRefPtr, PassRefPtr, const IDBKeyPath&); - virtual void onSuccess(int64_t); - virtual void onSuccess(); - virtual void onSuccess(PassRefPtr, PassRefPtr primaryKey, PassRefPtr); - virtual void onSuccessWithPrefetch(const Vector>&, const Vector>&, const Vector>&) { ASSERT_NOT_REACHED(); } // Not implemented. Callback should not reach the renderer side. + ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); } - // ActiveDOMObject - virtual bool hasPendingActivity() const override; + using RefCounted::ref; + using RefCounted::deref; - // EventTarget - virtual EventTargetInterface eventTargetInterface() const override; - virtual ScriptExecutionContext* scriptExecutionContext() const override final { return ActiveDOMObject::scriptExecutionContext(); } - virtual void uncaughtExceptionInEventHandler() override final; + void completeRequestAndDispatchEvent(const IDBResultData&); - using EventTarget::dispatchEvent; - virtual bool dispatchEvent(PassRefPtr) override; + void setResult(const IDBKeyData&); + void setResult(const Vector&); + void setResult(const Vector&); + void setResult(uint64_t); + void setResultToStructuredClone(const IDBValue&); + void setResultToUndefined(); - void transactionDidFinishAndDispatch(); + void willIterateCursor(IDBCursor&); + void didOpenOrIterateCursor(const IDBResultData&); - using RefCounted::ref; - using RefCounted::deref; + const IDBCursor* pendingCursor() const { return m_pendingCursor.get(); } - IDBDatabaseBackend::TaskType taskType() { return m_taskType; } + void setSource(IDBCursor&); + void setVersionChangeTransaction(IDBTransaction&); - DOMRequestState* requestState() { return &m_requestState; } + IndexedDB::RequestType requestType() const { return m_requestType; } + + bool hasPendingActivity() const final; protected: - IDBRequest(ScriptExecutionContext*, PassRefPtr source, IDBDatabaseBackend::TaskType, IDBTransaction*); - void enqueueEvent(PassRefPtr); - virtual bool shouldEnqueueEvent() const; - void onSuccessInternal(PassRefPtr); - void onSuccessInternal(const Deprecated::ScriptValue&); - - RefPtr m_result; - unsigned short m_errorCode; - String m_errorMessage; - RefPtr m_error; - bool m_contextStopped; + IDBRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&); + + void enqueueEvent(Ref&&); + bool dispatchEvent(Event&) override; + + void setResult(Ref&&); + + IDBClient::IDBConnectionProxy& connectionProxy() { return m_connectionProxy.get(); } + + // FIXME: Protected data members aren't great for maintainability. + // Consider adding protected helper functions and making these private. + ReadyState m_readyState { ReadyState::Pending }; RefPtr m_transaction; - ReadyState m_readyState; - bool m_requestAborted; // May be aborted by transaction then receive async onsuccess; ignore vs. assert. + bool m_shouldExposeTransactionToDOM { true }; + RefPtr m_domError; + IndexedDB::RequestType m_requestType { IndexedDB::RequestType::Other }; + bool m_contextStopped { false }; + Event* m_openDatabaseSuccessEvent { nullptr }; private: - // ActiveDOMObject - virtual void stop() override; + IDBRequest(ScriptExecutionContext&, IDBObjectStore&, IDBTransaction&); + IDBRequest(ScriptExecutionContext&, IDBCursor&, IDBTransaction&); + IDBRequest(ScriptExecutionContext&, IDBIndex&, IDBTransaction&); + IDBRequest(ScriptExecutionContext&, IDBObjectStore&, IndexedDB::ObjectStoreRecordType, IDBTransaction&); + IDBRequest(ScriptExecutionContext&, IDBIndex&, IndexedDB::IndexRecordType, IDBTransaction&); + + EventTargetInterface eventTargetInterface() const override; + + const char* activeDOMObjectName() const final; + bool canSuspendForDocumentSuspension() const final; + void stop() final; + virtual void cancelForStop(); + + void refEventTarget() final { RefCounted::ref(); } + void derefEventTarget() final { RefCounted::deref(); } + void uncaughtExceptionInEventHandler() final; - // EventTarget - virtual void refEventTarget() override final { ref(); } - virtual void derefEventTarget() override final { deref(); } + virtual bool isOpenDBRequest() const { return false; } - PassRefPtr getResultCursor(); - void setResultCursor(PassRefPtr, PassRefPtr, PassRefPtr primaryKey, const Deprecated::ScriptValue&); + void onError(); + void onSuccess(); - RefPtr m_source; - const IDBDatabaseBackend::TaskType m_taskType; + IDBCursor* resultCursor(); - bool m_hasPendingActivity; - Vector> m_enqueuedEvents; + IDBError m_idbError; + IDBResourceIdentifier m_resourceIdentifier; + + std::optional m_result; + std::optional m_source; + + bool m_hasPendingActivity { true }; + IndexedDB::ObjectStoreRecordType m_requestedObjectStoreRecordType { IndexedDB::ObjectStoreRecordType::ValueOnly }; + IndexedDB::IndexRecordType m_requestedIndexRecordType { IndexedDB::IndexRecordType::Key }; - // Only used if the result type will be a cursor. - IndexedDB::CursorType m_cursorType; - IndexedDB::CursorDirection m_cursorDirection; - bool m_cursorFinished; RefPtr m_pendingCursor; - RefPtr m_cursorKey; - RefPtr m_cursorPrimaryKey; - Deprecated::ScriptValue m_cursorValue; - bool m_didFireUpgradeNeededEvent; - bool m_preventPropagation; - DOMRequestState m_requestState; + std::unique_ptr m_cursorRequestNotifier; + + Ref m_connectionProxy; }; } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBRequest_h diff --git a/Source/WebCore/Modules/indexeddb/IDBRequest.idl b/Source/WebCore/Modules/indexeddb/IDBRequest.idl index c2c7e52e4..c1ec27b3b 100644 --- a/Source/WebCore/Modules/indexeddb/IDBRequest.idl +++ b/Source/WebCore/Modules/indexeddb/IDBRequest.idl @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -28,19 +28,22 @@ */ [ - Conditional=INDEXED_DATABASE, ActiveDOMObject, - EventTarget, - JSNoStaticTables, - JSGenerateToJSObject, - JSGenerateToNativeObject + Conditional=INDEXED_DATABASE, + GenerateIsReachable=Impl, + SkipVTableValidation, ] interface IDBRequest : EventTarget { - [GetterRaisesException] readonly attribute IDBAny result; - [GetterRaisesException] readonly attribute DOMError error; - readonly attribute IDBAny source; + [GetterMayThrowException] readonly attribute (IDBCursor or IDBDatabase or any)? result; + [GetterMayThrowException] readonly attribute DOMError? error; + readonly attribute (IDBObjectStore or IDBIndex or IDBCursor)? source; readonly attribute IDBTransaction transaction; - readonly attribute DOMString readyState; + readonly attribute IDBRequestReadyState readyState; + + attribute EventHandler onsuccess; + attribute EventHandler onerror; +}; - attribute EventListener onsuccess; - attribute EventListener onerror; +enum IDBRequestReadyState { + "pending", + "done" }; diff --git a/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.cpp b/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.cpp new file mode 100644 index 000000000..100a41e38 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBRequestCompletionEvent.h" + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +IDBRequestCompletionEvent::IDBRequestCompletionEvent(const AtomicString& type, bool canBubble, bool cancelable, IDBRequest& request) + : Event(type, canBubble, cancelable) + , m_request(request) +{ +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.h b/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.h new file mode 100644 index 000000000..64666435a --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "Event.h" +#include "IDBRequest.h" + +namespace WebCore { + +class IDBRequestCompletionEvent : public Event { +public: + static Ref create(const AtomicString& type, bool canBubble, bool cancelable, IDBRequest& request) + { + return adoptRef(*new IDBRequestCompletionEvent(type, canBubble, cancelable, request)); + } + +private: + IDBRequestCompletionEvent(const AtomicString& type, bool canBubble, bool cancelable, IDBRequest&); + + Ref m_request; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBServerConnection.h b/Source/WebCore/Modules/indexeddb/IDBServerConnection.h deleted file mode 100644 index 104a2069b..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBServerConnection.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBServerConnection_h -#define IDBServerConnection_h - -#include "IDBCursorBackendOperations.h" -#include "IDBDatabaseMetadata.h" -#include "IDBGetResult.h" -#include "IDBTransactionBackendOperations.h" -#include "IndexedDB.h" -#include -#include -#include -#include - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -class IDBDatabaseError; -class IDBKey; -class IDBTransactionBackend; - -struct IDBOpenCursorResult; -struct IDBIndexMetadata; -struct IDBObjectStoreMetadata; - -// This interface provides a single asynchronous layer between the web-facing frontend -// and the I/O performing backend of IndexedDatabase. -// If an operation's completion needs to be confirmed that must be done through use of a callback function. -class IDBServerConnection : public RefCounted { -public: - virtual ~IDBServerConnection() { } - - virtual bool isClosed() = 0; - - typedef std::function BoolCallbackFunction; - - // Factory-level operations - virtual void deleteDatabase(const String& name, BoolCallbackFunction successCallback) = 0; - - // Database-level operations - typedef std::function GetIDBDatabaseMetadataFunction; - virtual void getOrEstablishIDBDatabaseMetadata(GetIDBDatabaseMetadataFunction) = 0; - virtual void close() = 0; - - // Transaction-level operations - virtual void openTransaction(int64_t transactionID, const HashSet& objectStoreIds, IndexedDB::TransactionMode, BoolCallbackFunction successCallback) = 0; - virtual void beginTransaction(int64_t transactionID, std::function completionCallback) = 0; - virtual void commitTransaction(int64_t transactionID, BoolCallbackFunction successCallback) = 0; - virtual void resetTransaction(int64_t transactionID, std::function completionCallback) = 0; - virtual void rollbackTransaction(int64_t transactionID, std::function completionCallback) = 0; - - virtual void setIndexKeys(int64_t transactionID, int64_t databaseID, int64_t objectStoreID, const IDBObjectStoreMetadata&, IDBKey& primaryKey, const Vector& indexIDs, const Vector>>& indexKeys, std::function)> completionCallback) = 0; - - virtual void createObjectStore(IDBTransactionBackend&, const CreateObjectStoreOperation&, std::function)> completionCallback) = 0; - virtual void createIndex(IDBTransactionBackend&, const CreateIndexOperation&, std::function)> completionCallback) = 0; - virtual void deleteIndex(IDBTransactionBackend&, const DeleteIndexOperation&, std::function)> completionCallback) = 0; - virtual void get(IDBTransactionBackend&, const GetOperation&, std::function)> completionCallback) = 0; - virtual void put(IDBTransactionBackend&, const PutOperation&, std::function, PassRefPtr)> completionCallback) = 0; - virtual void openCursor(IDBTransactionBackend&, const OpenCursorOperation&, std::function)> completionCallback) = 0; - virtual void count(IDBTransactionBackend&, const CountOperation&, std::function)> completionCallback) = 0; - virtual void deleteRange(IDBTransactionBackend&, const DeleteRangeOperation&, std::function)> completionCallback) = 0; - virtual void clearObjectStore(IDBTransactionBackend&, const ClearObjectStoreOperation&, std::function)> completionCallback) = 0; - virtual void deleteObjectStore(IDBTransactionBackend&, const DeleteObjectStoreOperation&, std::function)> completionCallback) = 0; - virtual void changeDatabaseVersion(IDBTransactionBackend&, const IDBDatabaseBackend::VersionChangeOperation&, std::function)> completionCallback) = 0; - - // Cursor-level operations - virtual void cursorAdvance(IDBCursorBackend&, const CursorAdvanceOperation&, std::function, PassRefPtr, PassRefPtr, PassRefPtr)> completionCallback) = 0; - virtual void cursorIterate(IDBCursorBackend&, const CursorIterationOperation&, std::function, PassRefPtr, PassRefPtr, PassRefPtr)> completionCallback) = 0; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBServerConnection_h diff --git a/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp b/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp index 5d2f3f875..1ee73d28d 100644 --- a/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -28,404 +28,1378 @@ #if ENABLE(INDEXED_DATABASE) -#include "EventException.h" +#include "DOMError.h" +#include "DOMStringList.h" +#include "DOMWindow.h" +#include "Event.h" +#include "EventNames.h" #include "EventQueue.h" -#include "ExceptionCodePlaceholder.h" +#include "IDBCursorWithValue.h" #include "IDBDatabase.h" #include "IDBDatabaseException.h" +#include "IDBError.h" #include "IDBEventDispatcher.h" +#include "IDBGetRecordData.h" #include "IDBIndex.h" +#include "IDBIterateCursorData.h" +#include "IDBKeyData.h" +#include "IDBKeyRangeData.h" #include "IDBObjectStore.h" #include "IDBOpenDBRequest.h" -#include "IDBPendingTransactionMonitor.h" +#include "IDBRequest.h" +#include "IDBResultData.h" +#include "IDBValue.h" +#include "JSDOMWindowBase.h" #include "Logging.h" -#include "ScriptCallStack.h" #include "ScriptExecutionContext.h" +#include "ScriptState.h" +#include "SerializedScriptValue.h" +#include "TransactionOperation.h" +#include + +using namespace JSC; namespace WebCore { -PassRefPtr IDBTransaction::create(ScriptExecutionContext* context, int64_t id, const Vector& objectStoreNames, IndexedDB::TransactionMode mode, IDBDatabase* db) +Ref IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info) { - IDBOpenDBRequest* openDBRequest = 0; - RefPtr transaction(adoptRef(new IDBTransaction(context, id, objectStoreNames, mode, db, openDBRequest, IDBDatabaseMetadata()))); - transaction->suspendIfNeeded(); - return transaction.release(); + return adoptRef(*new IDBTransaction(database, info, nullptr)); } -PassRefPtr IDBTransaction::create(ScriptExecutionContext* context, int64_t id, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata) +Ref IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest& request) { - RefPtr transaction(adoptRef(new IDBTransaction(context, id, Vector(), IndexedDB::TransactionMode::VersionChange, db, openDBRequest, previousMetadata))); - transaction->suspendIfNeeded(); - return transaction.release(); + return adoptRef(*new IDBTransaction(database, info, &request)); } -const AtomicString& IDBTransaction::modeReadOnly() +IDBTransaction::IDBTransaction(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest* request) + : IDBActiveDOMObject(database.scriptExecutionContext()) + , m_database(database) + , m_info(info) + , m_pendingOperationTimer(*this, &IDBTransaction::pendingOperationTimerFired) + , m_completedOperationTimer(*this, &IDBTransaction::completedOperationTimerFired) + , m_openDBRequest(request) + , m_currentlyCompletingRequest(request) + { - DEFINE_STATIC_LOCAL(AtomicString, readonly, ("readonly", AtomicString::ConstructFromLiteral)); - return readonly; + LOG(IndexedDB, "IDBTransaction::IDBTransaction - %s", m_info.loggingString().utf8().data()); + ASSERT(currentThread() == m_database->originThreadID()); + + if (m_info.mode() == IDBTransactionMode::Versionchange) { + ASSERT(m_openDBRequest); + m_openDBRequest->setVersionChangeTransaction(*this); + m_startedOnServer = true; + } else { + activate(); + + auto* context = scriptExecutionContext(); + ASSERT(context); + + RefPtr self; + JSC::VM& vm = context->vm(); + vm.whenIdle([self, this]() { + deactivate(); + }); + + establishOnServer(); + } + + suspendIfNeeded(); } -const AtomicString& IDBTransaction::modeReadWrite() +IDBTransaction::~IDBTransaction() { - DEFINE_STATIC_LOCAL(AtomicString, readwrite, ("readwrite", AtomicString::ConstructFromLiteral)); - return readwrite; + ASSERT(currentThread() == m_database->originThreadID()); } -const AtomicString& IDBTransaction::modeVersionChange() +IDBClient::IDBConnectionProxy& IDBTransaction::connectionProxy() { - DEFINE_STATIC_LOCAL(AtomicString, versionchange, ("versionchange", AtomicString::ConstructFromLiteral)); - return versionchange; + return m_database->connectionProxy(); } -const AtomicString& IDBTransaction::modeReadOnlyLegacy() +Ref IDBTransaction::objectStoreNames() const { - DEFINE_STATIC_LOCAL(AtomicString, readonly, ("0", AtomicString::ConstructFromLiteral)); - return readonly; + ASSERT(currentThread() == m_database->originThreadID()); + + const Vector names = isVersionChange() ? m_database->info().objectStoreNames() : m_info.objectStores(); + + Ref objectStoreNames = DOMStringList::create(); + for (auto& name : names) + objectStoreNames->append(name); + + objectStoreNames->sort(); + return objectStoreNames; } -const AtomicString& IDBTransaction::modeReadWriteLegacy() +IDBDatabase* IDBTransaction::db() { - DEFINE_STATIC_LOCAL(AtomicString, readwrite, ("1", AtomicString::ConstructFromLiteral)); - return readwrite; + ASSERT(currentThread() == m_database->originThreadID()); + return m_database.ptr(); } +DOMError* IDBTransaction::error() const +{ + ASSERT(currentThread() == m_database->originThreadID()); + return m_domError.get(); +} -IDBTransaction::IDBTransaction(ScriptExecutionContext* context, int64_t id, const Vector& objectStoreNames, IndexedDB::TransactionMode mode, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata) - : ActiveDOMObject(context) - , m_id(id) - , m_database(db) - , m_objectStoreNames(objectStoreNames) - , m_openDBRequest(openDBRequest) - , m_mode(mode) - , m_state(Active) - , m_hasPendingActivity(true) - , m_contextStopped(false) - , m_previousMetadata(previousMetadata) +ExceptionOr> IDBTransaction::objectStore(const String& objectStoreName) { - if (mode == IndexedDB::TransactionMode::VersionChange) { - // Not active until the callback. - m_state = Inactive; + LOG(IndexedDB, "IDBTransaction::objectStore"); + ASSERT(currentThread() == m_database->originThreadID()); + + if (!scriptExecutionContext()) + return Exception { IDBDatabaseException::InvalidStateError }; + + if (isFinishedOrFinishing()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The transaction finished.") }; + + Locker locker(m_referencedObjectStoreLock); + + auto iterator = m_referencedObjectStores.find(objectStoreName); + if (iterator != m_referencedObjectStores.end()) + return Ref { *iterator->value }; + + bool found = false; + for (auto& objectStore : m_info.objectStores()) { + if (objectStore == objectStoreName) { + found = true; + break; + } } - // We pass a reference of this object before it can be adopted. - relaxAdoptionRequirement(); - if (m_state == Active) - IDBPendingTransactionMonitor::addNewTransaction(this); - m_database->transactionCreated(this); + auto* info = m_database->info().infoForExistingObjectStore(objectStoreName); + if (!info) + return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found.") }; + + // Version change transactions are scoped to every object store in the database. + if (!info || (!found && !isVersionChange())) + return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found.") }; + + auto objectStore = std::make_unique(*scriptExecutionContext(), *info, *this); + auto* rawObjectStore = objectStore.get(); + m_referencedObjectStores.set(objectStoreName, WTFMove(objectStore)); + + return Ref(*rawObjectStore); } -IDBTransaction::~IDBTransaction() + +void IDBTransaction::abortDueToFailedRequest(DOMError& error) { - ASSERT(m_state == Finished || m_contextStopped); - ASSERT(m_requestList.isEmpty() || m_contextStopped); + LOG(IndexedDB, "IDBTransaction::abortDueToFailedRequest"); + ASSERT(currentThread() == m_database->originThreadID()); + + if (isFinishedOrFinishing()) + return; + + m_domError = &error; + internalAbort(); +} + +void IDBTransaction::transitionedToFinishing(IndexedDB::TransactionState state) +{ + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT(!isFinishedOrFinishing()); + m_state = state; + ASSERT(isFinishedOrFinishing()); } -const String& IDBTransaction::mode() const +ExceptionOr IDBTransaction::abort() { - return modeToString(m_mode); + LOG(IndexedDB, "IDBTransaction::abort"); + ASSERT(currentThread() == m_database->originThreadID()); + + if (isFinishedOrFinishing()) + return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'abort' on 'IDBTransaction': The transaction is inactive or finished.") }; + + internalAbort(); + + return { }; } -void IDBTransaction::setError(PassRefPtr error, const String& errorMessage) +void IDBTransaction::internalAbort() { - ASSERT(m_state != Finished); - ASSERT(error); + LOG(IndexedDB, "IDBTransaction::internalAbort"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(!isFinishedOrFinishing()); + + m_database->willAbortTransaction(*this); + + if (isVersionChange()) { + Locker locker(m_referencedObjectStoreLock); + + auto& info = m_database->info(); + Vector identifiersToRemove; + for (auto& iterator : m_deletedObjectStores) { + if (info.infoForExistingObjectStore(iterator.key)) { + auto name = iterator.value->info().name(); + m_referencedObjectStores.set(name, WTFMove(iterator.value)); + identifiersToRemove.append(iterator.key); + } + } + + for (auto identifier : identifiersToRemove) + m_deletedObjectStores.remove(identifier); - // The first error to be set is the true cause of the - // transaction abort. - if (!m_error) { - m_error = error; - m_errorMessage = errorMessage; + for (auto& objectStore : m_referencedObjectStores.values()) + objectStore->rollbackForVersionChangeAbort(); } + + transitionedToFinishing(IndexedDB::TransactionState::Aborting); + + m_abortQueue.swap(m_pendingTransactionOperationQueue); + + LOG(IndexedDBOperations, "IDB abort-on-server operation: Transaction %s", info().identifier().loggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, nullptr, &IDBTransaction::abortOnServerAndCancelRequests)); } -PassRefPtr IDBTransaction::objectStore(const String& name, ExceptionCode& ec) +void IDBTransaction::abortInProgressOperations(const IDBError& error) { - if (m_state == Finished) { - ec = IDBDatabaseException::InvalidStateError; - return 0; + LOG(IndexedDB, "IDBTransaction::abortInProgressOperations"); + + Vector> inProgressAbortVector; + inProgressAbortVector.reserveInitialCapacity(m_transactionOperationsInProgressQueue.size()); + while (!m_transactionOperationsInProgressQueue.isEmpty()) + inProgressAbortVector.uncheckedAppend(m_transactionOperationsInProgressQueue.takeFirst()); + + for (auto& operation : inProgressAbortVector) { + m_transactionOperationsInProgressQueue.append(operation.get()); + m_currentlyCompletingRequest = nullptr; + operation->doComplete(IDBResultData::error(operation->identifier(), error)); } - IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); - if (it != m_objectStoreMap.end()) - return it->value; + Vector> completedOnServerAbortVector; + completedOnServerAbortVector.reserveInitialCapacity(m_completedOnServerQueue.size()); + while (!m_completedOnServerQueue.isEmpty()) + completedOnServerAbortVector.uncheckedAppend(m_completedOnServerQueue.takeFirst().first); - if (!isVersionChange() && !m_objectStoreNames.contains(name)) { - ec = IDBDatabaseException::NotFoundError; - return 0; + for (auto& operation : completedOnServerAbortVector) { + m_currentlyCompletingRequest = nullptr; + operation->doComplete(IDBResultData::error(operation->identifier(), error)); } - int64_t objectStoreId = m_database->findObjectStoreId(name); - if (objectStoreId == IDBObjectStoreMetadata::InvalidId) { - ASSERT(isVersionChange()); - ec = IDBDatabaseException::NotFoundError; - return 0; + connectionProxy().forgetActiveOperations(inProgressAbortVector); +} + +void IDBTransaction::abortOnServerAndCancelRequests(IDBClient::TransactionOperation& operation) +{ + LOG(IndexedDB, "IDBTransaction::abortOnServerAndCancelRequests"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(m_pendingTransactionOperationQueue.isEmpty()); + + m_database->connectionProxy().abortTransaction(*this); + + ASSERT(m_transactionOperationMap.contains(operation.identifier())); + ASSERT(m_transactionOperationsInProgressQueue.last() == &operation); + m_transactionOperationMap.remove(operation.identifier()); + m_transactionOperationsInProgressQueue.removeLast(); + + m_currentlyCompletingRequest = nullptr; + + IDBError error(IDBDatabaseException::AbortError); + + abortInProgressOperations(error); + + for (auto& operation : m_abortQueue) { + m_currentlyCompletingRequest = nullptr; + m_transactionOperationsInProgressQueue.append(operation.get()); + operation->doComplete(IDBResultData::error(operation->identifier(), error)); } - const IDBDatabaseMetadata& metadata = m_database->metadata(); + // Since we're aborting, it should be impossible to have queued any further operations. + ASSERT(m_pendingTransactionOperationQueue.isEmpty()); +} - RefPtr objectStore = IDBObjectStore::create(metadata.objectStores.get(objectStoreId), this); - objectStoreCreated(name, objectStore); - return objectStore.release(); +const char* IDBTransaction::activeDOMObjectName() const +{ + ASSERT(currentThread() == m_database->originThreadID()); + return "IDBTransaction"; } -void IDBTransaction::objectStoreCreated(const String& name, PassRefPtr prpObjectStore) +bool IDBTransaction::canSuspendForDocumentSuspension() const { - ASSERT(m_state != Finished); - RefPtr objectStore = prpObjectStore; - m_objectStoreMap.set(name, objectStore); - if (isVersionChange()) - m_objectStoreCleanupMap.set(objectStore, objectStore->metadata()); + ASSERT(currentThread() == m_database->originThreadID()); + return false; } -void IDBTransaction::objectStoreDeleted(const String& name) +bool IDBTransaction::hasPendingActivity() const { - ASSERT(m_state != Finished); - ASSERT(isVersionChange()); - IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); - if (it != m_objectStoreMap.end()) { - RefPtr objectStore = it->value; - m_objectStoreMap.remove(name); - objectStore->markDeleted(); - m_objectStoreCleanupMap.set(objectStore, objectStore->metadata()); - m_deletedObjectStores.add(objectStore); - } + ASSERT(currentThread() == m_database->originThreadID() || mayBeGCThread()); + return !m_contextStopped && m_state != IndexedDB::TransactionState::Finished; } -void IDBTransaction::setActive(bool active) +void IDBTransaction::stop() { - ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to setActive(%s)", active ? "true" : "false"); - if (m_state == Finishing) + LOG(IndexedDB, "IDBTransaction::stop - %s", m_info.loggingString().utf8().data()); + ASSERT(currentThread() == m_database->originThreadID()); + + // IDBDatabase::stop() calls IDBTransaction::stop() for each of its active transactions. + // Since the order of calling ActiveDOMObject::stop() is random, we might already have been stopped. + if (m_contextStopped) return; - ASSERT(active != (m_state == Active)); - m_state = active ? Active : Inactive; - if (!active && m_requestList.isEmpty()) - backendDB()->commit(m_id); + removeAllEventListeners(); + + m_contextStopped = true; + + if (isFinishedOrFinishing()) + return; + + internalAbort(); +} + +bool IDBTransaction::isActive() const +{ + ASSERT(currentThread() == m_database->originThreadID()); + return m_state == IndexedDB::TransactionState::Active; } -void IDBTransaction::abort(ExceptionCode& ec) +bool IDBTransaction::isFinishedOrFinishing() const { - if (m_state == Finishing || m_state == Finished) { - ec = IDBDatabaseException::InvalidStateError; + ASSERT(currentThread() == m_database->originThreadID()); + + return m_state == IndexedDB::TransactionState::Committing + || m_state == IndexedDB::TransactionState::Aborting + || m_state == IndexedDB::TransactionState::Finished; +} + +void IDBTransaction::addRequest(IDBRequest& request) +{ + ASSERT(currentThread() == m_database->originThreadID()); + m_openRequests.add(&request); +} + +void IDBTransaction::removeRequest(IDBRequest& request) +{ + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(m_openRequests.contains(&request)); + m_openRequests.remove(&request); +} + +void IDBTransaction::scheduleOperation(RefPtr&& operation) +{ + ASSERT(!m_transactionOperationMap.contains(operation->identifier())); + ASSERT(currentThread() == m_database->originThreadID()); + + m_pendingTransactionOperationQueue.append(operation); + m_transactionOperationMap.set(operation->identifier(), WTFMove(operation)); + + schedulePendingOperationTimer(); +} + +void IDBTransaction::schedulePendingOperationTimer() +{ + ASSERT(currentThread() == m_database->originThreadID()); + + if (!m_pendingOperationTimer.isActive()) + m_pendingOperationTimer.startOneShot(0); +} + +void IDBTransaction::pendingOperationTimerFired() +{ + LOG(IndexedDB, "IDBTransaction::pendingOperationTimerFired (%p)", this); + ASSERT(currentThread() == m_database->originThreadID()); + + if (!m_startedOnServer) + return; + + // If the last in-progress operation we've sent to the server is not an IDBRequest operation, + // then we have to wait until it completes before sending any more. + if (!m_transactionOperationsInProgressQueue.isEmpty() && !m_transactionOperationsInProgressQueue.last()->nextRequestCanGoToServer()) return; - } - m_state = Finishing; + // We want to batch operations together without spinning the runloop for performance, + // but don't want to affect responsiveness of the main thread. + // This number is a good compromise in ad-hoc testing. + static const size_t operationBatchLimit = 128; + + for (size_t iterations = 0; !m_pendingTransactionOperationQueue.isEmpty() && iterations < operationBatchLimit; ++iterations) { + auto operation = m_pendingTransactionOperationQueue.takeFirst(); + m_transactionOperationsInProgressQueue.append(operation.get()); + operation->perform(); + + if (!operation->nextRequestCanGoToServer()) + break; - while (!m_requestList.isEmpty()) { - RefPtr request = *m_requestList.begin(); - m_requestList.remove(request); - request->abort(); } - RefPtr selfRef = this; - backendDB()->abort(m_id); + if (!m_transactionOperationMap.isEmpty() || !m_openRequests.isEmpty()) + return; + + if (!isFinishedOrFinishing()) + commit(); } -IDBTransaction::OpenCursorNotifier::OpenCursorNotifier(PassRefPtr transaction, IDBCursor* cursor) - : m_transaction(transaction), - m_cursor(cursor) +void IDBTransaction::operationCompletedOnServer(const IDBResultData& data, IDBClient::TransactionOperation& operation) { - m_transaction->registerOpenCursor(m_cursor); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(currentThread() == operation.originThreadID()); + + m_completedOnServerQueue.append({ &operation, data }); + scheduleCompletedOperationTimer(); } -IDBTransaction::OpenCursorNotifier::~OpenCursorNotifier() +void IDBTransaction::scheduleCompletedOperationTimer() { - if (m_cursor) - m_transaction->unregisterOpenCursor(m_cursor); + ASSERT(currentThread() == m_database->originThreadID()); + + if (!m_completedOperationTimer.isActive()) + m_completedOperationTimer.startOneShot(0); } -void IDBTransaction::OpenCursorNotifier::cursorFinished() +void IDBTransaction::completedOperationTimerFired() { - if (m_cursor) { - m_transaction->unregisterOpenCursor(m_cursor); - m_cursor = 0; - m_transaction.clear(); - } + LOG(IndexedDB, "IDBTransaction::completedOperationTimerFired (%p)", this); + ASSERT(currentThread() == m_database->originThreadID()); + + if (m_completedOnServerQueue.isEmpty() || m_currentlyCompletingRequest) + return; + + auto iterator = m_completedOnServerQueue.takeFirst(); + iterator.first->doComplete(iterator.second); + + if (!m_completedOnServerQueue.isEmpty() && !m_currentlyCompletingRequest) + scheduleCompletedOperationTimer(); +} + +void IDBTransaction::completeNoncursorRequest(IDBRequest& request, const IDBResultData& result) +{ + ASSERT(!m_currentlyCompletingRequest); + + request.completeRequestAndDispatchEvent(result); + + m_currentlyCompletingRequest = &request; } -void IDBTransaction::registerOpenCursor(IDBCursor* cursor) +void IDBTransaction::completeCursorRequest(IDBRequest& request, const IDBResultData& result) { - m_openCursors.add(cursor); + ASSERT(!m_currentlyCompletingRequest); + + request.didOpenOrIterateCursor(result); + + m_currentlyCompletingRequest = &request; } -void IDBTransaction::unregisterOpenCursor(IDBCursor* cursor) +void IDBTransaction::finishedDispatchEventForRequest(IDBRequest& request) { - m_openCursors.remove(cursor); + if (isFinishedOrFinishing()) + return; + + ASSERT_UNUSED(request, !m_currentlyCompletingRequest || m_currentlyCompletingRequest == &request); + + m_currentlyCompletingRequest = nullptr; + scheduleCompletedOperationTimer(); } -void IDBTransaction::closeOpenCursors() +void IDBTransaction::commit() { - HashSet cursors; - cursors.swap(m_openCursors); - for (HashSet::iterator i = cursors.begin(); i != cursors.end(); ++i) - (*i)->close(); + LOG(IndexedDB, "IDBTransaction::commit"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(!isFinishedOrFinishing()); + + transitionedToFinishing(IndexedDB::TransactionState::Committing); + m_database->willCommitTransaction(*this); + + LOG(IndexedDBOperations, "IDB commit operation: Transaction %s", info().identifier().loggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, nullptr, &IDBTransaction::commitOnServer)); } -void IDBTransaction::registerRequest(IDBRequest* request) +void IDBTransaction::commitOnServer(IDBClient::TransactionOperation& operation) { - ASSERT(request); - ASSERT(m_state == Active); - m_requestList.add(request); + LOG(IndexedDB, "IDBTransaction::commitOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->connectionProxy().commitTransaction(*this); + + ASSERT(!m_transactionOperationsInProgressQueue.isEmpty()); + ASSERT(m_transactionOperationsInProgressQueue.last() == &operation); + m_transactionOperationsInProgressQueue.removeLast(); + + ASSERT(m_transactionOperationMap.contains(operation.identifier())); + m_transactionOperationMap.remove(operation.identifier()); } -void IDBTransaction::unregisterRequest(IDBRequest* request) +void IDBTransaction::finishAbortOrCommit() { - ASSERT(request); - // If we aborted the request, it will already have been removed. - m_requestList.remove(request); + ASSERT(m_state != IndexedDB::TransactionState::Finished); + ASSERT(currentThread() == m_database->originThreadID()); + + m_state = IndexedDB::TransactionState::Finished; } -void IDBTransaction::onAbort(PassRefPtr prpError) +void IDBTransaction::didStart(const IDBError& error) { - LOG(StorageAPI, "IDBTransaction::onAbort"); - RefPtr error = prpError; - ASSERT(m_state != Finished); + LOG(IndexedDB, "IDBTransaction::didStart"); + ASSERT(currentThread() == m_database->originThreadID()); - if (m_state != Finishing) { - ASSERT(error.get()); - setError(DOMError::create(error->name()), error->message()); + m_database->didStartTransaction(*this); - // Abort was not triggered by front-end, so outstanding requests must - // be aborted now. - while (!m_requestList.isEmpty()) { - RefPtr request = *m_requestList.begin(); - m_requestList.remove(request); - request->abort(); - } - m_state = Finishing; + m_startedOnServer = true; + + // It's possible the transaction failed to start on the server. + // That equates to an abort. + if (!error.isNull()) { + didAbort(error); + return; } + schedulePendingOperationTimer(); +} + +void IDBTransaction::notifyDidAbort(const IDBError& error) +{ + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->didAbortTransaction(*this); + m_idbError = error; + fireOnAbort(); + if (isVersionChange()) { - for (IDBObjectStoreMetadataMap::iterator it = m_objectStoreCleanupMap.begin(); it != m_objectStoreCleanupMap.end(); ++it) - it->key->setMetadata(it->value); - m_database->setMetadata(m_previousMetadata); - m_database->close(); + ASSERT(m_openDBRequest); + m_openDBRequest->fireErrorAfterVersionChangeCompletion(); } - m_objectStoreCleanupMap.clear(); - closeOpenCursors(); +} - // Enqueue events before notifying database, as database may close which enqueues more events and order matters. - enqueueEvent(Event::create(eventNames().abortEvent, true, false)); - m_database->transactionFinished(this); +void IDBTransaction::didAbort(const IDBError& error) +{ + LOG(IndexedDB, "IDBTransaction::didAbort"); + ASSERT(currentThread() == m_database->originThreadID()); + + if (m_state == IndexedDB::TransactionState::Finished) + return; + + notifyDidAbort(error); + + finishAbortOrCommit(); } -void IDBTransaction::onComplete() +void IDBTransaction::didCommit(const IDBError& error) { - LOG(StorageAPI, "IDBTransaction::onComplete"); - ASSERT(m_state != Finished); - m_state = Finishing; - m_objectStoreCleanupMap.clear(); - closeOpenCursors(); + LOG(IndexedDB, "IDBTransaction::didCommit"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(m_state == IndexedDB::TransactionState::Committing); + + if (error.isNull()) { + m_database->didCommitTransaction(*this); + fireOnComplete(); + } else { + m_database->willAbortTransaction(*this); + notifyDidAbort(error); + } - // Enqueue events before notifying database, as database may close which enqueues more events and order matters. + finishAbortOrCommit(); +} + +void IDBTransaction::fireOnComplete() +{ + LOG(IndexedDB, "IDBTransaction::fireOnComplete"); + ASSERT(currentThread() == m_database->originThreadID()); enqueueEvent(Event::create(eventNames().completeEvent, false, false)); - m_database->transactionFinished(this); } -bool IDBTransaction::hasPendingActivity() const +void IDBTransaction::fireOnAbort() { - // FIXME: In an ideal world, we should return true as long as anyone has a or can - // get a handle to us or any child request object and any of those have - // event listeners. This is in order to handle user generated events properly. - return m_hasPendingActivity && !m_contextStopped; + LOG(IndexedDB, "IDBTransaction::fireOnAbort"); + ASSERT(currentThread() == m_database->originThreadID()); + enqueueEvent(Event::create(eventNames().abortEvent, true, false)); } -IndexedDB::TransactionMode IDBTransaction::stringToMode(const String& modeString, ExceptionCode& ec) +void IDBTransaction::enqueueEvent(Ref&& event) { - if (modeString.isNull() - || modeString == IDBTransaction::modeReadOnly()) - return IndexedDB::TransactionMode::ReadOnly; - if (modeString == IDBTransaction::modeReadWrite()) - return IndexedDB::TransactionMode::ReadWrite; + ASSERT(m_state != IndexedDB::TransactionState::Finished); + ASSERT(currentThread() == m_database->originThreadID()); + + if (!scriptExecutionContext() || m_contextStopped) + return; - ec = TypeError; - return IndexedDB::TransactionMode::ReadOnly; + event->setTarget(this); + scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event)); } -const AtomicString& IDBTransaction::modeToString(IndexedDB::TransactionMode mode) +bool IDBTransaction::dispatchEvent(Event& event) { - switch (mode) { - case IndexedDB::TransactionMode::ReadOnly: - return IDBTransaction::modeReadOnly(); - break; + LOG(IndexedDB, "IDBTransaction::dispatchEvent"); - case IndexedDB::TransactionMode::ReadWrite: - return IDBTransaction::modeReadWrite(); - break; + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(scriptExecutionContext()); + ASSERT(!m_contextStopped); + ASSERT(event.target() == this); + ASSERT(event.type() == eventNames().completeEvent || event.type() == eventNames().abortEvent); - case IndexedDB::TransactionMode::VersionChange: - return IDBTransaction::modeVersionChange(); - break; + Vector> targets; + targets.append(this); + targets.append(db()); + + bool result = IDBEventDispatcher::dispatch(event, targets); + + if (isVersionChange()) { + ASSERT(m_openDBRequest); + m_openDBRequest->versionChangeTransactionDidFinish(); + + if (event.type() == eventNames().completeEvent) { + if (m_database->isClosingOrClosed()) + m_openDBRequest->fireErrorAfterVersionChangeCompletion(); + else + m_openDBRequest->fireSuccessAfterVersionChangeCommit(); + } + + m_openDBRequest = nullptr; } - ASSERT_NOT_REACHED(); - return IDBTransaction::modeReadOnly(); + return result; } -bool IDBTransaction::dispatchEvent(PassRefPtr event) +Ref IDBTransaction::createObjectStore(const IDBObjectStoreInfo& info) { - LOG(StorageAPI, "IDBTransaction::dispatchEvent"); - ASSERT(m_state != Finished); - ASSERT(m_hasPendingActivity); + LOG(IndexedDB, "IDBTransaction::createObjectStore"); + ASSERT(isVersionChange()); ASSERT(scriptExecutionContext()); - ASSERT(event->target() == this); - m_state = Finished; + ASSERT(currentThread() == m_database->originThreadID()); - // Break reference cycles. - for (IDBObjectStoreMap::iterator it = m_objectStoreMap.begin(); it != m_objectStoreMap.end(); ++it) - it->value->transactionFinished(); - m_objectStoreMap.clear(); - for (IDBObjectStoreSet::iterator it = m_deletedObjectStores.begin(); it != m_deletedObjectStores.end(); ++it) - (*it)->transactionFinished(); - m_deletedObjectStores.clear(); + Locker locker(m_referencedObjectStoreLock); - Vector> targets; - targets.append(this); - targets.append(db()); + auto objectStore = std::make_unique(*scriptExecutionContext(), info, *this); + auto* rawObjectStore = objectStore.get(); + m_referencedObjectStores.set(info.name(), WTFMove(objectStore)); + + LOG(IndexedDBOperations, "IDB create object store operation: %s", info.condensedLoggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didCreateObjectStoreOnServer, &IDBTransaction::createObjectStoreOnServer, info)); + + return *rawObjectStore; +} + +void IDBTransaction::createObjectStoreOnServer(IDBClient::TransactionOperation& operation, const IDBObjectStoreInfo& info) +{ + LOG(IndexedDB, "IDBTransaction::createObjectStoreOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(isVersionChange()); + + m_database->connectionProxy().createObjectStore(operation, info); +} + +void IDBTransaction::didCreateObjectStoreOnServer(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didCreateObjectStoreOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::CreateObjectStoreSuccess || resultData.type() == IDBResultType::Error); +} + +void IDBTransaction::renameObjectStore(IDBObjectStore& objectStore, const String& newName) +{ + LOG(IndexedDB, "IDBTransaction::renameObjectStore"); + + Locker locker(m_referencedObjectStoreLock); + + ASSERT(isVersionChange()); + ASSERT(scriptExecutionContext()); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT(m_referencedObjectStores.contains(objectStore.info().name())); + ASSERT(!m_referencedObjectStores.contains(newName)); + ASSERT(m_referencedObjectStores.get(objectStore.info().name()) == &objectStore); - // FIXME: When we allow custom event dispatching, this will probably need to change. - ASSERT(event->type() == eventNames().completeEvent || event->type() == eventNames().abortEvent); - bool returnValue = IDBEventDispatcher::dispatch(event.get(), targets); - // FIXME: Try to construct a test where |this| outlives openDBRequest and we - // get a crash. - if (m_openDBRequest) { - ASSERT(isVersionChange()); - m_openDBRequest->transactionDidFinishAndDispatch(); + uint64_t objectStoreIdentifier = objectStore.info().identifier(); + + LOG(IndexedDBOperations, "IDB rename object store operation: %s to %s", objectStore.info().condensedLoggingString().utf8().data(), newName.utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didRenameObjectStoreOnServer, &IDBTransaction::renameObjectStoreOnServer, objectStoreIdentifier, newName)); + + m_referencedObjectStores.set(newName, m_referencedObjectStores.take(objectStore.info().name())); +} + +void IDBTransaction::renameObjectStoreOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& newName) +{ + LOG(IndexedDB, "IDBTransaction::renameObjectStoreOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(isVersionChange()); + + m_database->connectionProxy().renameObjectStore(operation, objectStoreIdentifier, newName); +} + +void IDBTransaction::didRenameObjectStoreOnServer(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didRenameObjectStoreOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::RenameObjectStoreSuccess || resultData.type() == IDBResultType::Error); +} + +std::unique_ptr IDBTransaction::createIndex(IDBObjectStore& objectStore, const IDBIndexInfo& info) +{ + LOG(IndexedDB, "IDBTransaction::createIndex"); + ASSERT(isVersionChange()); + ASSERT(currentThread() == m_database->originThreadID()); + + if (!scriptExecutionContext()) + return nullptr; + + LOG(IndexedDBOperations, "IDB create index operation: %s under object store %s", info.condensedLoggingString().utf8().data(), objectStore.info().condensedLoggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didCreateIndexOnServer, &IDBTransaction::createIndexOnServer, info)); + + return std::make_unique(*scriptExecutionContext(), info, objectStore); +} + +void IDBTransaction::createIndexOnServer(IDBClient::TransactionOperation& operation, const IDBIndexInfo& info) +{ + LOG(IndexedDB, "IDBTransaction::createIndexOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(isVersionChange()); + + m_database->connectionProxy().createIndex(operation, info); +} + +void IDBTransaction::didCreateIndexOnServer(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didCreateIndexOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + if (resultData.type() == IDBResultType::CreateIndexSuccess) + return; + + ASSERT(resultData.type() == IDBResultType::Error); + + // This operation might have failed because the transaction is already aborting. + if (m_state == IndexedDB::TransactionState::Aborting) + return; + + // Otherwise, failure to create an index forced abortion of the transaction. + abortDueToFailedRequest(DOMError::create(IDBDatabaseException::getErrorName(resultData.error().code()), resultData.error().message())); +} + +void IDBTransaction::renameIndex(IDBIndex& index, const String& newName) +{ + LOG(IndexedDB, "IDBTransaction::renameIndex"); + Locker locker(m_referencedObjectStoreLock); + + ASSERT(isVersionChange()); + ASSERT(scriptExecutionContext()); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT(m_referencedObjectStores.contains(index.objectStore().info().name())); + ASSERT(m_referencedObjectStores.get(index.objectStore().info().name()) == &index.objectStore()); + + index.objectStore().renameReferencedIndex(index, newName); + + uint64_t objectStoreIdentifier = index.objectStore().info().identifier(); + uint64_t indexIdentifier = index.info().identifier(); + + LOG(IndexedDBOperations, "IDB rename index operation: %s to %s under object store %" PRIu64, index.info().condensedLoggingString().utf8().data(), newName.utf8().data(), index.info().objectStoreIdentifier()); + scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didRenameIndexOnServer, &IDBTransaction::renameIndexOnServer, objectStoreIdentifier, indexIdentifier, newName)); +} + +void IDBTransaction::renameIndexOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const uint64_t& indexIdentifier, const String& newName) +{ + LOG(IndexedDB, "IDBTransaction::renameIndexOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(isVersionChange()); + + m_database->connectionProxy().renameIndex(operation, objectStoreIdentifier, indexIdentifier, newName); +} + +void IDBTransaction::didRenameIndexOnServer(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didRenameIndexOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::RenameIndexSuccess || resultData.type() == IDBResultType::Error); +} + +Ref IDBTransaction::requestOpenCursor(ExecState& state, IDBObjectStore& objectStore, const IDBCursorInfo& info) +{ + LOG(IndexedDB, "IDBTransaction::requestOpenCursor"); + ASSERT(currentThread() == m_database->originThreadID()); + + if (info.cursorType() == IndexedDB::CursorType::KeyOnly) + return doRequestOpenCursor(state, IDBCursor::create(*this, objectStore, info)); + + return doRequestOpenCursor(state, IDBCursorWithValue::create(*this, objectStore, info)); +} + +Ref IDBTransaction::requestOpenCursor(ExecState& state, IDBIndex& index, const IDBCursorInfo& info) +{ + LOG(IndexedDB, "IDBTransaction::requestOpenCursor"); + ASSERT(currentThread() == m_database->originThreadID()); + + if (info.cursorType() == IndexedDB::CursorType::KeyOnly) + return doRequestOpenCursor(state, IDBCursor::create(*this, index, info)); + + return doRequestOpenCursor(state, IDBCursorWithValue::create(*this, index, info)); +} + +Ref IDBTransaction::doRequestOpenCursor(ExecState& state, Ref&& cursor) +{ + ASSERT(isActive()); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state)); + + auto request = IDBRequest::create(*scriptExecutionContext(), cursor.get(), *this); + addRequest(request.get()); + + LOG(IndexedDBOperations, "IDB open cursor operation: %s", cursor->info().loggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didOpenCursorOnServer, &IDBTransaction::openCursorOnServer, cursor->info())); + + return request; +} + +void IDBTransaction::openCursorOnServer(IDBClient::TransactionOperation& operation, const IDBCursorInfo& info) +{ + LOG(IndexedDB, "IDBTransaction::openCursorOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->connectionProxy().openCursor(operation, info); +} + +void IDBTransaction::didOpenCursorOnServer(IDBRequest& request, const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didOpenCursorOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + completeCursorRequest(request, resultData); +} + +void IDBTransaction::iterateCursor(IDBCursor& cursor, const IDBIterateCursorData& data) +{ + LOG(IndexedDB, "IDBTransaction::iterateCursor"); + ASSERT(isActive()); + ASSERT(cursor.request()); + ASSERT(currentThread() == m_database->originThreadID()); + + addRequest(*cursor.request()); + + LOG(IndexedDBOperations, "IDB iterate cursor operation: %s %s", cursor.info().loggingString().utf8().data(), data.loggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, *cursor.request(), &IDBTransaction::didIterateCursorOnServer, &IDBTransaction::iterateCursorOnServer, data)); +} + +// FIXME: changes here +void IDBTransaction::iterateCursorOnServer(IDBClient::TransactionOperation& operation, const IDBIterateCursorData& data) +{ + LOG(IndexedDB, "IDBTransaction::iterateCursorOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->connectionProxy().iterateCursor(operation, data); +} + +void IDBTransaction::didIterateCursorOnServer(IDBRequest& request, const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didIterateCursorOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + completeCursorRequest(request, resultData); +} + +Ref IDBTransaction::requestGetAllObjectStoreRecords(JSC::ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& keyRangeData, IndexedDB::GetAllType getAllType, std::optional count) +{ + LOG(IndexedDB, "IDBTransaction::requestGetAllObjectStoreRecords"); + ASSERT(isActive()); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state)); + + auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this); + addRequest(request.get()); + + IDBGetAllRecordsData getAllRecordsData { keyRangeData, getAllType, count, objectStore.info().identifier(), 0 }; + + LOG(IndexedDBOperations, "IDB get all object store records operation: %s", getAllRecordsData.loggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetAllRecordsOnServer, &IDBTransaction::getAllRecordsOnServer, getAllRecordsData)); + + return request; +} + +Ref IDBTransaction::requestGetAllIndexRecords(JSC::ExecState& state, IDBIndex& index, const IDBKeyRangeData& keyRangeData, IndexedDB::GetAllType getAllType, std::optional count) +{ + LOG(IndexedDB, "IDBTransaction::requestGetAllIndexRecords"); + ASSERT(isActive()); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state)); + + auto request = IDBRequest::create(*scriptExecutionContext(), index, *this); + addRequest(request.get()); + + IDBGetAllRecordsData getAllRecordsData { keyRangeData, getAllType, count, index.objectStore().info().identifier(), index.info().identifier() }; + + LOG(IndexedDBOperations, "IDB get all index records operation: %s", getAllRecordsData.loggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetAllRecordsOnServer, &IDBTransaction::getAllRecordsOnServer, getAllRecordsData)); + + return request; +} + +void IDBTransaction::getAllRecordsOnServer(IDBClient::TransactionOperation& operation, const IDBGetAllRecordsData& getAllRecordsData) +{ + LOG(IndexedDB, "IDBTransaction::getAllRecordsOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->connectionProxy().getAllRecords(operation, getAllRecordsData); +} + +void IDBTransaction::didGetAllRecordsOnServer(IDBRequest& request, const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didGetAllRecordsOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + if (resultData.type() == IDBResultType::Error) { + completeNoncursorRequest(request, resultData); + return; + } + + ASSERT(resultData.type() == IDBResultType::GetAllRecordsSuccess); + + auto& getAllResult = resultData.getAllResult(); + switch (getAllResult.type()) { + case IndexedDB::GetAllType::Keys: + request.setResult(getAllResult.keys()); + break; + case IndexedDB::GetAllType::Values: + request.setResult(getAllResult.values()); + break; } - m_hasPendingActivity = false; - return returnValue; + + completeNoncursorRequest(request, resultData); } -bool IDBTransaction::canSuspend() const +Ref IDBTransaction::requestGetRecord(ExecState& state, IDBObjectStore& objectStore, const IDBGetRecordData& getRecordData) { - // FIXME: Technically we can suspend before the first request is schedule - // and after the complete/abort event is enqueued. - return m_state == Finished; + LOG(IndexedDB, "IDBTransaction::requestGetRecord"); + ASSERT(isActive()); + ASSERT(!getRecordData.keyRangeData.isNull); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state)); + + IndexedDB::ObjectStoreRecordType type = getRecordData.type == IDBGetRecordDataType::KeyAndValue ? IndexedDB::ObjectStoreRecordType::ValueOnly : IndexedDB::ObjectStoreRecordType::KeyOnly; + + auto request = IDBRequest::createObjectStoreGet(*scriptExecutionContext(), objectStore, type, *this); + addRequest(request.get()); + + LOG(IndexedDBOperations, "IDB get record operation: %s %s", objectStore.info().condensedLoggingString().utf8().data(), getRecordData.loggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetRecordOnServer, &IDBTransaction::getRecordOnServer, getRecordData)); + + return request; } -void IDBTransaction::stop() +Ref IDBTransaction::requestGetValue(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range) { - m_contextStopped = true; + LOG(IndexedDB, "IDBTransaction::requestGetValue"); + ASSERT(currentThread() == m_database->originThreadID()); + + return requestIndexRecord(state, index, IndexedDB::IndexRecordType::Value, range); +} + +Ref IDBTransaction::requestGetKey(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range) +{ + LOG(IndexedDB, "IDBTransaction::requestGetValue"); + ASSERT(currentThread() == m_database->originThreadID()); - abort(IGNORE_EXCEPTION); + return requestIndexRecord(state, index, IndexedDB::IndexRecordType::Key, range); } -void IDBTransaction::enqueueEvent(PassRefPtr event) +Ref IDBTransaction::requestIndexRecord(ExecState& state, IDBIndex& index, IndexedDB::IndexRecordType type, const IDBKeyRangeData& range) { - ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to enqueue an event of type %s.", event->type().string().utf8().data()); - if (m_contextStopped || !scriptExecutionContext()) + LOG(IndexedDB, "IDBTransaction::requestGetValue"); + ASSERT(isActive()); + ASSERT(!range.isNull); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state)); + + auto request = IDBRequest::createIndexGet(*scriptExecutionContext(), index, type, *this); + addRequest(request.get()); + + IDBGetRecordData getRecordData = { range, IDBGetRecordDataType::KeyAndValue }; + + LOG(IndexedDBOperations, "IDB get index record operation: %s %s", index.info().condensedLoggingString().utf8().data(), getRecordData.loggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetRecordOnServer, &IDBTransaction::getRecordOnServer, getRecordData)); + + return request; +} + +void IDBTransaction::getRecordOnServer(IDBClient::TransactionOperation& operation, const IDBGetRecordData& getRecordData) +{ + LOG(IndexedDB, "IDBTransaction::getRecordOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->connectionProxy().getRecord(operation, getRecordData); +} + +void IDBTransaction::didGetRecordOnServer(IDBRequest& request, const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didGetRecordOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + if (resultData.type() == IDBResultType::Error) { + completeNoncursorRequest(request, resultData); return; + } - event->setTarget(this); - scriptExecutionContext()->eventQueue().enqueueEvent(event); + ASSERT(resultData.type() == IDBResultType::GetRecordSuccess); + + bool useResultKey = request.sourceIndexIdentifier() && request.requestedIndexRecordType() == IndexedDB::IndexRecordType::Key; + if (!useResultKey) + useResultKey = request.requestedObjectStoreRecordType() == IndexedDB::ObjectStoreRecordType::KeyOnly; + + const IDBGetResult& result = resultData.getResult(); + + if (useResultKey) { + if (!result.keyData().isNull()) + request.setResult(result.keyData()); + else + request.setResultToUndefined(); + } else { + if (resultData.getResult().value().data().data()) + request.setResultToStructuredClone(resultData.getResult().value()); + else + request.setResultToUndefined(); + } + + completeNoncursorRequest(request, resultData); +} + +Ref IDBTransaction::requestCount(ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& range) +{ + LOG(IndexedDB, "IDBTransaction::requestCount (IDBObjectStore)"); + ASSERT(isActive()); + ASSERT(!range.isNull); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state)); + + auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this); + addRequest(request.get()); + + LOG(IndexedDBOperations, "IDB object store count operation: %s, range %s", objectStore.info().condensedLoggingString().utf8().data(), range.loggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetCountOnServer, &IDBTransaction::getCountOnServer, range)); + + return request; +} + +Ref IDBTransaction::requestCount(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range) +{ + LOG(IndexedDB, "IDBTransaction::requestCount (IDBIndex)"); + ASSERT(isActive()); + ASSERT(!range.isNull); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state)); + + auto request = IDBRequest::create(*scriptExecutionContext(), index, *this); + addRequest(request.get()); + + LOG(IndexedDBOperations, "IDB index count operation: %s, range %s", index.info().condensedLoggingString().utf8().data(), range.loggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetCountOnServer, &IDBTransaction::getCountOnServer, range)); + + return request; +} + +void IDBTransaction::getCountOnServer(IDBClient::TransactionOperation& operation, const IDBKeyRangeData& keyRange) +{ + LOG(IndexedDB, "IDBTransaction::getCountOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->connectionProxy().getCount(operation, keyRange); +} + +void IDBTransaction::didGetCountOnServer(IDBRequest& request, const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didGetCountOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + request.setResult(resultData.resultInteger()); + completeNoncursorRequest(request, resultData); } -IDBDatabaseBackend* IDBTransaction::backendDB() const +Ref IDBTransaction::requestDeleteRecord(ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& range) { - return db()->backend(); + LOG(IndexedDB, "IDBTransaction::requestDeleteRecord"); + ASSERT(isActive()); + ASSERT(!range.isNull); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state)); + + auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this); + addRequest(request.get()); + + LOG(IndexedDBOperations, "IDB delete record operation: %s, range %s", objectStore.info().condensedLoggingString().utf8().data(), range.loggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didDeleteRecordOnServer, &IDBTransaction::deleteRecordOnServer, range)); + return request; +} + +void IDBTransaction::deleteRecordOnServer(IDBClient::TransactionOperation& operation, const IDBKeyRangeData& keyRange) +{ + LOG(IndexedDB, "IDBTransaction::deleteRecordOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->connectionProxy().deleteRecord(operation, keyRange); +} + +void IDBTransaction::didDeleteRecordOnServer(IDBRequest& request, const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didDeleteRecordOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + request.setResultToUndefined(); + completeNoncursorRequest(request, resultData); +} + +Ref IDBTransaction::requestClearObjectStore(ExecState& state, IDBObjectStore& objectStore) +{ + LOG(IndexedDB, "IDBTransaction::requestClearObjectStore"); + ASSERT(isActive()); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state)); + + auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this); + addRequest(request.get()); + + uint64_t objectStoreIdentifier = objectStore.info().identifier(); + + LOG(IndexedDBOperations, "IDB clear object store operation: %s", objectStore.info().condensedLoggingString().utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didClearObjectStoreOnServer, &IDBTransaction::clearObjectStoreOnServer, objectStoreIdentifier)); + + return request; +} + +void IDBTransaction::clearObjectStoreOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier) +{ + LOG(IndexedDB, "IDBTransaction::clearObjectStoreOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->connectionProxy().clearObjectStore(operation, objectStoreIdentifier); +} + +void IDBTransaction::didClearObjectStoreOnServer(IDBRequest& request, const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didClearObjectStoreOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + request.setResultToUndefined(); + completeNoncursorRequest(request, resultData); +} + +Ref IDBTransaction::requestPutOrAdd(ExecState& state, IDBObjectStore& objectStore, IDBKey* key, SerializedScriptValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode) +{ + LOG(IndexedDB, "IDBTransaction::requestPutOrAdd"); + ASSERT(isActive()); + ASSERT(!isReadOnly()); + ASSERT(objectStore.info().autoIncrement() || key); + ASSERT(currentThread() == m_database->originThreadID()); + + ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state)); + + auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this); + addRequest(request.get()); + + LOG(IndexedDBOperations, "IDB putOrAdd operation: %s key: %s", objectStore.info().condensedLoggingString().utf8().data(), key ? key->loggingString().utf8().data() : ""); + scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didPutOrAddOnServer, &IDBTransaction::putOrAddOnServer, key, &value, overwriteMode)); + + return request; +} + +void IDBTransaction::putOrAddOnServer(IDBClient::TransactionOperation& operation, RefPtr key, RefPtr value, const IndexedDB::ObjectStoreOverwriteMode& overwriteMode) +{ + LOG(IndexedDB, "IDBTransaction::putOrAddOnServer"); + ASSERT(currentThread() == originThreadID()); + ASSERT(!isReadOnly()); + ASSERT(value); + + if (!value->hasBlobURLs()) { + m_database->connectionProxy().putOrAdd(operation, key.get(), *value, overwriteMode); + return; + } + + // Due to current limitations on our ability to post tasks back to a worker thread, + // workers currently write blobs to disk synchronously. + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=157958 - Make this asynchronous after refactoring allows it. + if (!isMainThread()) { + auto idbValue = value->writeBlobsToDiskForIndexedDBSynchronously(); + if (idbValue.data().data()) + m_database->connectionProxy().putOrAdd(operation, key.get(), idbValue, overwriteMode); + else { + // If the IDBValue doesn't have any data, then something went wrong writing the blobs to disk. + // In that case, we cannot successfully store this record, so we callback with an error. + RefPtr protectedOperation(&operation); + auto result = IDBResultData::error(operation.identifier(), { IDBDatabaseException::UnknownError, ASCIILiteral("Error preparing Blob/File data to be stored in object store") }); + scriptExecutionContext()->postTask([protectedOperation = WTFMove(protectedOperation), result = WTFMove(result)](ScriptExecutionContext&) { + protectedOperation->doComplete(result); + }); + } + return; + } + + // Since this request won't actually go to the server until the blob writes are complete, + // stop future requests from going to the server ahead of it. + operation.setNextRequestCanGoToServer(false); + + value->writeBlobsToDiskForIndexedDB([protectedThis = makeRef(*this), this, protectedOperation = Ref(operation), keyData = IDBKeyData(key.get()).isolatedCopy(), overwriteMode](const IDBValue& idbValue) mutable { + ASSERT(currentThread() == originThreadID()); + ASSERT(isMainThread()); + if (idbValue.data().data()) { + m_database->connectionProxy().putOrAdd(protectedOperation.get(), WTFMove(keyData), idbValue, overwriteMode); + return; + } + + // If the IDBValue doesn't have any data, then something went wrong writing the blobs to disk. + // In that case, we cannot successfully store this record, so we callback with an error. + auto result = IDBResultData::error(protectedOperation->identifier(), { IDBDatabaseException::UnknownError, ASCIILiteral("Error preparing Blob/File data to be stored in object store") }); + callOnMainThread([protectedThis = WTFMove(protectedThis), protectedOperation = WTFMove(protectedOperation), result = WTFMove(result)]() mutable { + protectedOperation->doComplete(result); + }); + }); +} + +void IDBTransaction::didPutOrAddOnServer(IDBRequest& request, const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didPutOrAddOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + if (auto* result = resultData.resultKey()) + request.setResult(*result); + else + request.setResultToUndefined(); + completeNoncursorRequest(request, resultData); +} + +void IDBTransaction::deleteObjectStore(const String& objectStoreName) +{ + LOG(IndexedDB, "IDBTransaction::deleteObjectStore"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(isVersionChange()); + + Locker locker(m_referencedObjectStoreLock); + + if (auto objectStore = m_referencedObjectStores.take(objectStoreName)) { + objectStore->markAsDeleted(); + auto identifier = objectStore->info().identifier(); + m_deletedObjectStores.set(identifier, WTFMove(objectStore)); + } + + LOG(IndexedDBOperations, "IDB delete object store operation: %s", objectStoreName.utf8().data()); + scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didDeleteObjectStoreOnServer, &IDBTransaction::deleteObjectStoreOnServer, objectStoreName)); +} + +void IDBTransaction::deleteObjectStoreOnServer(IDBClient::TransactionOperation& operation, const String& objectStoreName) +{ + LOG(IndexedDB, "IDBTransaction::deleteObjectStoreOnServer"); + ASSERT(isVersionChange()); + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->connectionProxy().deleteObjectStore(operation, objectStoreName); } +void IDBTransaction::didDeleteObjectStoreOnServer(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didDeleteObjectStoreOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteObjectStoreSuccess || resultData.type() == IDBResultType::Error); } +void IDBTransaction::deleteIndex(uint64_t objectStoreIdentifier, const String& indexName) +{ + LOG(IndexedDB, "IDBTransaction::deleteIndex"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(isVersionChange()); + + LOG(IndexedDBOperations, "IDB delete index operation: %s (%" PRIu64 ")", indexName.utf8().data(), objectStoreIdentifier); + scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didDeleteIndexOnServer, &IDBTransaction::deleteIndexOnServer, objectStoreIdentifier, indexName)); +} + +void IDBTransaction::deleteIndexOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& indexName) +{ + LOG(IndexedDB, "IDBTransaction::deleteIndexOnServer"); + ASSERT(isVersionChange()); + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->connectionProxy().deleteIndex(operation, objectStoreIdentifier, indexName); +} + +void IDBTransaction::didDeleteIndexOnServer(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBTransaction::didDeleteIndexOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteIndexSuccess || resultData.type() == IDBResultType::Error); +} + +void IDBTransaction::operationCompletedOnClient(IDBClient::TransactionOperation& operation) +{ + LOG(IndexedDB, "IDBTransaction::operationCompletedOnClient"); + + ASSERT(currentThread() == m_database->originThreadID()); + ASSERT(currentThread() == operation.originThreadID()); + ASSERT(m_transactionOperationMap.get(operation.identifier()) == &operation); + ASSERT(m_transactionOperationsInProgressQueue.first() == &operation); + + m_transactionOperationMap.remove(operation.identifier()); + m_transactionOperationsInProgressQueue.removeFirst(); + + schedulePendingOperationTimer(); +} + +void IDBTransaction::establishOnServer() +{ + LOG(IndexedDB, "IDBTransaction::establishOnServer"); + ASSERT(currentThread() == m_database->originThreadID()); + + m_database->connectionProxy().establishTransaction(*this); +} + +void IDBTransaction::activate() +{ + ASSERT(currentThread() == m_database->originThreadID()); + + if (isFinishedOrFinishing()) + return; + + m_state = IndexedDB::TransactionState::Active; +} + +void IDBTransaction::deactivate() +{ + ASSERT(currentThread() == m_database->originThreadID()); + + if (m_state == IndexedDB::TransactionState::Active) + m_state = IndexedDB::TransactionState::Inactive; + + schedulePendingOperationTimer(); +} + +void IDBTransaction::connectionClosedFromServer(const IDBError& error) +{ + LOG(IndexedDB, "IDBTransaction::connectionClosedFromServer - %s", error.message().utf8().data()); + + m_state = IndexedDB::TransactionState::Aborting; + + abortInProgressOperations(error); + + Vector> operations; + copyValuesToVector(m_transactionOperationMap, operations); + + for (auto& operation : operations) { + m_currentlyCompletingRequest = nullptr; + m_transactionOperationsInProgressQueue.append(operation.get()); + ASSERT(m_transactionOperationsInProgressQueue.first() == operation.get()); + operation->doComplete(IDBResultData::error(operation->identifier(), error)); + } + + connectionProxy().forgetActiveOperations(operations); + + m_pendingTransactionOperationQueue.clear(); + m_abortQueue.clear(); + m_transactionOperationMap.clear(); + + m_idbError = error; + m_domError = error.toDOMError(); + fireOnAbort(); +} + +void IDBTransaction::visitReferencedObjectStores(JSC::SlotVisitor& visitor) const +{ + Locker locker(m_referencedObjectStoreLock); + for (auto& objectStore : m_referencedObjectStores.values()) + visitor.addOpaqueRoot(objectStore.get()); + for (auto& objectStore : m_deletedObjectStores.values()) + visitor.addOpaqueRoot(objectStore.get()); +} + +} // namespace WebCore + #endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBTransaction.h b/Source/WebCore/Modules/indexeddb/IDBTransaction.h index f6e4ad98c..af96c4cd9 100644 --- a/Source/WebCore/Modules/indexeddb/IDBTransaction.h +++ b/Source/WebCore/Modules/indexeddb/IDBTransaction.h @@ -1,174 +1,285 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBTransaction_h -#define IDBTransaction_h +#pragma once #if ENABLE(INDEXED_DATABASE) -#include "ActiveDOMObject.h" -#include "DOMError.h" -#include "Event.h" -#include "EventListener.h" -#include "EventNames.h" #include "EventTarget.h" -#include "IDBDatabaseMetadata.h" +#include "IDBActiveDOMObject.h" +#include "IDBError.h" +#include "IDBGetAllRecordsData.h" +#include "IDBGetRecordData.h" +#include "IDBKeyRangeData.h" +#include "IDBOpenDBRequest.h" +#include "IDBTransactionInfo.h" +#include "IDBTransactionMode.h" #include "IndexedDB.h" -#include "ScriptWrappable.h" -#include -#include +#include "Timer.h" +#include +#include namespace WebCore { +class DOMError; +class DOMStringList; class IDBCursor; +class IDBCursorInfo; class IDBDatabase; -class IDBDatabaseBackend; -class IDBDatabaseError; +class IDBIndex; +class IDBIndexInfo; +class IDBKey; +class IDBKeyData; class IDBObjectStore; -class IDBOpenDBRequest; -struct IDBObjectStoreMetadata; +class IDBObjectStoreInfo; +class IDBResultData; +class SerializedScriptValue; -class IDBTransaction final : public ScriptWrappable, public RefCounted, public EventTargetWithInlineData, public ActiveDOMObject { -public: - static PassRefPtr create(ScriptExecutionContext*, int64_t, const Vector& objectStoreNames, IndexedDB::TransactionMode, IDBDatabase*); - static PassRefPtr create(ScriptExecutionContext*, int64_t, IDBDatabase*, IDBOpenDBRequest*, const IDBDatabaseMetadata& previousMetadata); - virtual ~IDBTransaction(); - - static const AtomicString& modeReadOnly(); - static const AtomicString& modeReadWrite(); - static const AtomicString& modeVersionChange(); - static const AtomicString& modeReadOnlyLegacy(); - static const AtomicString& modeReadWriteLegacy(); - - static IndexedDB::TransactionMode stringToMode(const String&, ExceptionCode&); - static const AtomicString& modeToString(IndexedDB::TransactionMode); - - IDBDatabaseBackend* backendDB() const; - - int64_t id() const { return m_id; } - bool isActive() const { return m_state == Active; } - bool isFinished() const { return m_state == Finished; } - bool isReadOnly() const { return m_mode == IndexedDB::TransactionMode::ReadOnly; } - bool isVersionChange() const { return m_mode == IndexedDB::TransactionMode::VersionChange; } - - // Implement the IDBTransaction IDL - const String& mode() const; - IDBDatabase* db() const { return m_database.get(); } - PassRefPtr error() const { return m_error; } - PassRefPtr objectStore(const String& name, ExceptionCode&); - void abort(ExceptionCode&); - - class OpenCursorNotifier { - public: - OpenCursorNotifier(PassRefPtr, IDBCursor*); - ~OpenCursorNotifier(); - void cursorFinished(); - private: - RefPtr m_transaction; - IDBCursor* m_cursor; - }; - - void registerRequest(IDBRequest*); - void unregisterRequest(IDBRequest*); - void objectStoreCreated(const String&, PassRefPtr); - void objectStoreDeleted(const String&); - void setActive(bool); - void setError(PassRefPtr, const String& errorMessage); - - DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); - DEFINE_ATTRIBUTE_EVENT_LISTENER(complete); - DEFINE_ATTRIBUTE_EVENT_LISTENER(error); - - void onAbort(PassRefPtr); - void onComplete(); - - // EventTarget - virtual EventTargetInterface eventTargetInterface() const override { return IDBTransactionEventTargetInterfaceType; } - virtual ScriptExecutionContext* scriptExecutionContext() const override { return ActiveDOMObject::scriptExecutionContext(); } +struct IDBIterateCursorData; +struct IDBKeyRangeData; + +namespace IDBClient { +class IDBConnectionProxy; +class TransactionOperation; +} +class IDBTransaction : public ThreadSafeRefCounted, public EventTargetWithInlineData, public IDBActiveDOMObject { +public: + static Ref create(IDBDatabase&, const IDBTransactionInfo&); + static Ref create(IDBDatabase&, const IDBTransactionInfo&, IDBOpenDBRequest&); + + ~IDBTransaction() final; + + // IDBTransaction IDL + Ref objectStoreNames() const; + IDBTransactionMode mode() const { return m_info.mode(); } + IDBDatabase* db(); + DOMError* error() const; + ExceptionOr> objectStore(const String& name); + ExceptionOr abort(); + + EventTargetInterface eventTargetInterface() const final { return IDBTransactionEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); } + void refEventTarget() final { ThreadSafeRefCounted::ref(); } + void derefEventTarget() final { ThreadSafeRefCounted::deref(); } using EventTarget::dispatchEvent; - virtual bool dispatchEvent(PassRefPtr) override; + bool dispatchEvent(Event&) final; + + using ThreadSafeRefCounted::ref; + using ThreadSafeRefCounted::deref; + + const char* activeDOMObjectName() const final; + bool canSuspendForDocumentSuspension() const final; + bool hasPendingActivity() const final; + void stop() final; + + const IDBTransactionInfo& info() const { return m_info; } + IDBDatabase& database() { return m_database.get(); } + const IDBDatabase& database() const { return m_database.get(); } + IDBDatabaseInfo* originalDatabaseInfo() const { return m_info.originalDatabaseInfo(); } + + void didStart(const IDBError&); + void didAbort(const IDBError&); + void didCommit(const IDBError&); + + bool isVersionChange() const { return mode() == IDBTransactionMode::Versionchange; } + bool isReadOnly() const { return mode() == IDBTransactionMode::Readonly; } + bool isActive() const; + + Ref createObjectStore(const IDBObjectStoreInfo&); + void renameObjectStore(IDBObjectStore&, const String& newName); + std::unique_ptr createIndex(IDBObjectStore&, const IDBIndexInfo&); + void renameIndex(IDBIndex&, const String& newName); + + Ref requestPutOrAdd(JSC::ExecState&, IDBObjectStore&, IDBKey*, SerializedScriptValue&, IndexedDB::ObjectStoreOverwriteMode); + Ref requestGetRecord(JSC::ExecState&, IDBObjectStore&, const IDBGetRecordData&); + Ref requestGetAllObjectStoreRecords(JSC::ExecState&, IDBObjectStore&, const IDBKeyRangeData&, IndexedDB::GetAllType, std::optional count); + Ref requestGetAllIndexRecords(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&, IndexedDB::GetAllType, std::optional count); + Ref requestDeleteRecord(JSC::ExecState&, IDBObjectStore&, const IDBKeyRangeData&); + Ref requestClearObjectStore(JSC::ExecState&, IDBObjectStore&); + Ref requestCount(JSC::ExecState&, IDBObjectStore&, const IDBKeyRangeData&); + Ref requestCount(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&); + Ref requestGetValue(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&); + Ref requestGetKey(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&); + Ref requestOpenCursor(JSC::ExecState&, IDBObjectStore&, const IDBCursorInfo&); + Ref requestOpenCursor(JSC::ExecState&, IDBIndex&, const IDBCursorInfo&); + void iterateCursor(IDBCursor&, const IDBIterateCursorData&); + + void deleteObjectStore(const String& objectStoreName); + void deleteIndex(uint64_t objectStoreIdentifier, const String& indexName); - // ActiveDOMObject - virtual bool hasPendingActivity() const override; + void addRequest(IDBRequest&); + void removeRequest(IDBRequest&); - using RefCounted::ref; - using RefCounted::deref; + void abortDueToFailedRequest(DOMError&); + + void activate(); + void deactivate(); + + void operationCompletedOnServer(const IDBResultData&, IDBClient::TransactionOperation&); + void operationCompletedOnClient(IDBClient::TransactionOperation&); + + void finishedDispatchEventForRequest(IDBRequest&); + + bool isFinishedOrFinishing() const; + bool isFinished() const { return m_state == IndexedDB::TransactionState::Finished; } + + IDBClient::IDBConnectionProxy& connectionProxy(); + + void connectionClosedFromServer(const IDBError&); + + void visitReferencedObjectStores(JSC::SlotVisitor&) const; private: - IDBTransaction(ScriptExecutionContext*, int64_t, const Vector&, IndexedDB::TransactionMode, IDBDatabase*, IDBOpenDBRequest*, const IDBDatabaseMetadata&); + IDBTransaction(IDBDatabase&, const IDBTransactionInfo&, IDBOpenDBRequest*); + + void commit(); + + void internalAbort(); + void notifyDidAbort(const IDBError&); + void finishAbortOrCommit(); + void abortInProgressOperations(const IDBError&); + + void scheduleOperation(RefPtr&&); + void pendingOperationTimerFired(); + void completedOperationTimerFired(); + + void fireOnComplete(); + void fireOnAbort(); + void enqueueEvent(Ref&&); + + Ref requestIndexRecord(JSC::ExecState&, IDBIndex&, IndexedDB::IndexRecordType, const IDBKeyRangeData&); - void enqueueEvent(PassRefPtr); - void closeOpenCursors(); + void commitOnServer(IDBClient::TransactionOperation&); + void abortOnServerAndCancelRequests(IDBClient::TransactionOperation&); - void registerOpenCursor(IDBCursor*); - void unregisterOpenCursor(IDBCursor*); + void createObjectStoreOnServer(IDBClient::TransactionOperation&, const IDBObjectStoreInfo&); + void didCreateObjectStoreOnServer(const IDBResultData&); - // ActiveDOMObject - virtual bool canSuspend() const override; - virtual void stop() override; + void renameObjectStoreOnServer(IDBClient::TransactionOperation&, const uint64_t& objectStoreIdentifier, const String& newName); + void didRenameObjectStoreOnServer(const IDBResultData&); - // EventTarget - virtual void refEventTarget() override { ref(); } - virtual void derefEventTarget() override { deref(); } + void createIndexOnServer(IDBClient::TransactionOperation&, const IDBIndexInfo&); + void didCreateIndexOnServer(const IDBResultData&); - enum State { - Inactive, // Created or started, but not in an event callback - Active, // Created or started, in creation scope or an event callback - Finishing, // In the process of aborting or completing. - Finished, // No more events will fire and no new requests may be filed. - }; + void renameIndexOnServer(IDBClient::TransactionOperation&, const uint64_t& objectStoreIdentifier, const uint64_t& indexIdentifier, const String& newName); + void didRenameIndexOnServer(const IDBResultData&); - int64_t m_id; - RefPtr m_database; - const Vector m_objectStoreNames; - IDBOpenDBRequest* m_openDBRequest; - const IndexedDB::TransactionMode m_mode; - State m_state; - bool m_hasPendingActivity; - bool m_contextStopped; - RefPtr m_error; - String m_errorMessage; + void clearObjectStoreOnServer(IDBClient::TransactionOperation&, const uint64_t& objectStoreIdentifier); + void didClearObjectStoreOnServer(IDBRequest&, const IDBResultData&); - ListHashSet> m_requestList; + void putOrAddOnServer(IDBClient::TransactionOperation&, RefPtr, RefPtr, const IndexedDB::ObjectStoreOverwriteMode&); + void didPutOrAddOnServer(IDBRequest&, const IDBResultData&); - typedef HashMap> IDBObjectStoreMap; - IDBObjectStoreMap m_objectStoreMap; + void getRecordOnServer(IDBClient::TransactionOperation&, const IDBGetRecordData&); + void didGetRecordOnServer(IDBRequest&, const IDBResultData&); - typedef HashSet> IDBObjectStoreSet; - IDBObjectStoreSet m_deletedObjectStores; + void getAllRecordsOnServer(IDBClient::TransactionOperation&, const IDBGetAllRecordsData&); + void didGetAllRecordsOnServer(IDBRequest&, const IDBResultData&); - typedef HashMap, IDBObjectStoreMetadata> IDBObjectStoreMetadataMap; - IDBObjectStoreMetadataMap m_objectStoreCleanupMap; - IDBDatabaseMetadata m_previousMetadata; + void getCountOnServer(IDBClient::TransactionOperation&, const IDBKeyRangeData&); + void didGetCountOnServer(IDBRequest&, const IDBResultData&); - HashSet m_openCursors; + void deleteRecordOnServer(IDBClient::TransactionOperation&, const IDBKeyRangeData&); + void didDeleteRecordOnServer(IDBRequest&, const IDBResultData&); + + void deleteObjectStoreOnServer(IDBClient::TransactionOperation&, const String& objectStoreName); + void didDeleteObjectStoreOnServer(const IDBResultData&); + + void deleteIndexOnServer(IDBClient::TransactionOperation&, const uint64_t& objectStoreIdentifier, const String& indexName); + void didDeleteIndexOnServer(const IDBResultData&); + + Ref doRequestOpenCursor(JSC::ExecState&, Ref&&); + void openCursorOnServer(IDBClient::TransactionOperation&, const IDBCursorInfo&); + void didOpenCursorOnServer(IDBRequest&, const IDBResultData&); + + void iterateCursorOnServer(IDBClient::TransactionOperation&, const IDBIterateCursorData&); + void didIterateCursorOnServer(IDBRequest&, const IDBResultData&); + + void transitionedToFinishing(IndexedDB::TransactionState); + + void establishOnServer(); + + void completeNoncursorRequest(IDBRequest&, const IDBResultData&); + void completeCursorRequest(IDBRequest&, const IDBResultData&); + + void schedulePendingOperationTimer(); + void scheduleCompletedOperationTimer(); + + Ref m_database; + IDBTransactionInfo m_info; + + IndexedDB::TransactionState m_state { IndexedDB::TransactionState::Inactive }; + bool m_startedOnServer { false }; + + IDBError m_idbError; + RefPtr m_domError; + + Timer m_pendingOperationTimer; + Timer m_completedOperationTimer; + std::unique_ptr m_activationTimer; + + RefPtr m_openDBRequest; + + Deque> m_pendingTransactionOperationQueue; + Deque m_transactionOperationsInProgressQueue; + Deque, IDBResultData>> m_completedOnServerQueue; + Deque> m_abortQueue; + + HashMap> m_transactionOperationMap; + + mutable Lock m_referencedObjectStoreLock; + HashMap> m_referencedObjectStores; + HashMap> m_deletedObjectStores; + + HashSet> m_openRequests; + RefPtr m_currentlyCompletingRequest; + + bool m_contextStopped { false }; +}; + +class TransactionActivator { + WTF_MAKE_NONCOPYABLE(TransactionActivator); +public: + TransactionActivator(IDBTransaction* transaction) + : m_transaction(transaction) + { + if (m_transaction) + m_transaction->activate(); + } + + ~TransactionActivator() + { + if (m_transaction) + m_transaction->deactivate(); + } + +private: + IDBTransaction* m_transaction; }; } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBTransaction_h diff --git a/Source/WebCore/Modules/indexeddb/IDBTransaction.idl b/Source/WebCore/Modules/indexeddb/IDBTransaction.idl index b770f892f..769e919b4 100644 --- a/Source/WebCore/Modules/indexeddb/IDBTransaction.idl +++ b/Source/WebCore/Modules/indexeddb/IDBTransaction.idl @@ -25,21 +25,20 @@ */ [ - Conditional=INDEXED_DATABASE, ActiveDOMObject, - EventTarget, - JSNoStaticTables, - JSGenerateToJSObject, - JSGenerateToNativeObject, + Conditional=INDEXED_DATABASE, + JSCustomMarkFunction, + SkipVTableValidation, ] interface IDBTransaction : EventTarget { - readonly attribute DOMString mode; + readonly attribute DOMStringList objectStoreNames; + readonly attribute IDBTransactionMode mode; readonly attribute IDBDatabase db; readonly attribute DOMError error; - [RaisesException] IDBObjectStore objectStore (DOMString name); - [RaisesException] void abort (); + [MayThrowException] IDBObjectStore objectStore(DOMString name); + [MayThrowException] void abort(); - attribute EventListener onabort; - attribute EventListener oncomplete; - attribute EventListener onerror; + attribute EventHandler onabort; + attribute EventHandler oncomplete; + attribute EventHandler onerror; }; diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.cpp b/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.cpp deleted file mode 100644 index bfb808815..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBTransactionBackend.h" - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBCursorBackend.h" -#include "IDBDatabaseBackend.h" -#include "IDBDatabaseCallbacks.h" -#include "IDBDatabaseException.h" -#include "IDBFactoryBackendInterface.h" -#include "IDBKeyRange.h" -#include "IDBServerConnection.h" -#include "IDBTransactionBackendOperations.h" -#include "IDBTransactionCoordinator.h" -#include "Logging.h" - -namespace WebCore { - -PassRefPtr IDBTransactionBackend::create(IDBDatabaseBackend* databaseBackend, int64_t id, PassRefPtr callbacks, const Vector& objectStoreIds, IndexedDB::TransactionMode mode) -{ - HashSet objectStoreHashSet; - for (size_t i = 0; i < objectStoreIds.size(); ++i) - objectStoreHashSet.add(objectStoreIds[i]); - - return adoptRef(new IDBTransactionBackend(databaseBackend, id, callbacks, objectStoreHashSet, mode)); -} - -IDBTransactionBackend::IDBTransactionBackend(IDBDatabaseBackend* databaseBackend, int64_t id, PassRefPtr callbacks, const HashSet& objectStoreIds, IndexedDB::TransactionMode mode) - : m_objectStoreIds(objectStoreIds) - , m_mode(mode) - , m_state(Unopened) - , m_commitPending(false) - , m_callbacks(callbacks) - , m_database(databaseBackend) - , m_taskTimer(this, &IDBTransactionBackend::taskTimerFired) - , m_pendingPreemptiveEvents(0) - , m_id(id) -{ - // We pass a reference of this object before it can be adopted. - relaxAdoptionRequirement(); - - m_database->transactionCoordinator()->didCreateTransaction(this); - - RefPtr backend(this); - m_database->serverConnection().openTransaction(id, objectStoreIds, mode, [backend](bool success) { - if (!success) { - callOnMainThread([backend]() { - backend->abort(); - }); - return; - } - - backend->m_state = Unused; - if (backend->hasPendingTasks()) - backend->start(); - }); -} - -IDBTransactionBackend::~IDBTransactionBackend() -{ - // It shouldn't be possible for this object to get deleted until it's either complete or aborted. - ASSERT(m_state == Finished); -} - -void IDBTransactionBackend::scheduleTask(IDBDatabaseBackend::TaskType type, PassRefPtr task, PassRefPtr abortTask) -{ - if (m_state == Finished) - return; - - if (type == IDBDatabaseBackend::NormalTask) - m_taskQueue.append(task); - else - m_preemptiveTaskQueue.append(task); - - if (abortTask) - m_abortTaskQueue.prepend(abortTask); - - if (m_state == Unopened) - return; - - if (m_state == Unused) - start(); - else if (m_state == Running && !m_taskTimer.isActive()) - m_taskTimer.startOneShot(0); -} - -void IDBTransactionBackend::abort() -{ - abort(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error (unknown cause)")); -} - -void IDBTransactionBackend::abort(PassRefPtr error) -{ - LOG(StorageAPI, "IDBTransactionBackend::abort"); - if (m_state == Finished) - return; - - bool wasRunning = m_state == Running; - - // The last reference to this object may be released while performing the - // abort steps below. We therefore take a self reference to keep ourselves - // alive while executing this method. - Ref protect(*this); - - m_state = Finished; - m_taskTimer.stop(); - - if (wasRunning) - m_database->serverConnection().rollbackTransaction(m_id, []() { }); - - // Run the abort tasks, if any. - while (!m_abortTaskQueue.isEmpty()) { - RefPtr task(m_abortTaskQueue.takeFirst()); - task->perform(); - } - - // Backing store resources (held via cursors) must be released before script callbacks - // are fired, as the script callbacks may release references and allow the backing store - // itself to be released, and order is critical. - closeOpenCursors(); - - m_database->serverConnection().resetTransaction(m_id, []() { }); - - // Transactions must also be marked as completed before the front-end is notified, as - // the transaction completion unblocks operations like closing connections. - m_database->transactionCoordinator()->didFinishTransaction(this); - ASSERT(!m_database->transactionCoordinator()->isActive(this)); - m_database->transactionFinished(this); - - if (m_callbacks) - m_callbacks->onAbort(id(), error); - - m_database->transactionFinishedAndAbortFired(this); - - m_database = 0; -} - -bool IDBTransactionBackend::isTaskQueueEmpty() const -{ - return m_preemptiveTaskQueue.isEmpty() && m_taskQueue.isEmpty(); -} - -bool IDBTransactionBackend::hasPendingTasks() const -{ - return m_pendingPreemptiveEvents || !isTaskQueueEmpty(); -} - -void IDBTransactionBackend::registerOpenCursor(IDBCursorBackend* cursor) -{ - m_openCursors.add(cursor); -} - -void IDBTransactionBackend::unregisterOpenCursor(IDBCursorBackend* cursor) -{ - m_openCursors.remove(cursor); -} - -void IDBTransactionBackend::run() -{ - // TransactionCoordinator has started this transaction. Schedule a timer - // to process the first task. - ASSERT(m_state == StartPending || m_state == Running); - ASSERT(!m_taskTimer.isActive()); - - m_taskTimer.startOneShot(0); -} - -void IDBTransactionBackend::start() -{ - ASSERT(m_state == Unused); - - m_state = StartPending; - m_database->transactionCoordinator()->didStartTransaction(this); - m_database->transactionStarted(this); -} - -void IDBTransactionBackend::commit() -{ - LOG(StorageAPI, "IDBTransactionBackend::commit (Transaction %lli)", static_cast(m_id)); - - // In multiprocess ports, front-end may have requested a commit but an abort has already - // been initiated asynchronously by the back-end. - if (m_state == Finished) - return; - - ASSERT(m_state == Unused || m_state == Running); - m_commitPending = true; - - // Front-end has requested a commit, but there may be tasks like createIndex which - // are considered synchronous by the front-end but are processed asynchronously. - if (hasPendingTasks()) { - LOG(StorageAPI, "IDBTransactionBackend::commit - Not committing now, transaction still has pending tasks (Transaction %lli)", static_cast(m_id)); - return; - } - - // The last reference to this object may be released while performing the - // commit steps below. We therefore take a self reference to keep ourselves - // alive while executing this method. - RefPtr backend(this); - - bool unused = m_state == Unused; - m_state = Finished; - - bool committed = unused; - - m_database->serverConnection().commitTransaction(m_id, [backend, this, committed, unused](bool success) mutable { - committed |= success; - - // Backing store resources (held via cursors) must be released before script callbacks - // are fired, as the script callbacks may release references and allow the backing store - // itself to be released, and order is critical. - closeOpenCursors(); - - m_database->serverConnection().resetTransaction(m_id, []() { }); - - // Transactions must also be marked as completed before the front-end is notified, as - // the transaction completion unblocks operations like closing connections. - if (!unused) - m_database->transactionCoordinator()->didFinishTransaction(this); - m_database->transactionFinished(this); - - if (committed) { - m_callbacks->onComplete(id()); - m_database->transactionFinishedAndCompleteFired(this); - } else { - m_callbacks->onAbort(id(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error committing transaction.")); - m_database->transactionFinishedAndAbortFired(this); - } - - m_database = 0; - }); -} - -void IDBTransactionBackend::taskTimerFired(Timer&) -{ - LOG(StorageAPI, "IDBTransactionBackend::taskTimerFired"); - - if (m_state == StartPending) { - m_database->serverConnection().beginTransaction(m_id, []() { }); - m_state = Running; - } - - // The last reference to this object may be released while performing a task. - // Take a self reference to keep this object alive so that tasks can - // successfully make their completion callbacks. - RefPtr self(this); - - TaskQueue* taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue; - if (!taskQueue->isEmpty() && m_state != Finished) { - ASSERT(m_state == Running); - RefPtr task(taskQueue->takeFirst()); - task->perform([self, this, task]() { - m_taskTimer.startOneShot(0); - }); - - return; - } - - // If there are no pending tasks, we haven't already committed/aborted, - // and the front-end requested a commit, it is now safe to do so. - if (!hasPendingTasks() && m_state != Finished && m_commitPending) - commit(); -} - -void IDBTransactionBackend::closeOpenCursors() -{ - for (HashSet::iterator i = m_openCursors.begin(); i != m_openCursors.end(); ++i) - (*i)->close(); - m_openCursors.clear(); -} - -void IDBTransactionBackend::scheduleCreateObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata) -{ - scheduleTask(CreateObjectStoreOperation::create(this, objectStoreMetadata), CreateObjectStoreAbortOperation::create(this, objectStoreMetadata.id)); -} - -void IDBTransactionBackend::scheduleDeleteObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata) -{ - scheduleTask(DeleteObjectStoreOperation::create(this, objectStoreMetadata), DeleteObjectStoreAbortOperation::create(this, objectStoreMetadata)); -} - -void IDBTransactionBackend::scheduleVersionChangeOperation(int64_t requestedVersion, PassRefPtr callbacks, PassRefPtr databaseCallbacks, const IDBDatabaseMetadata& metadata) -{ - scheduleTask(IDBDatabaseBackend::VersionChangeOperation::create(this, requestedVersion, callbacks, databaseCallbacks), IDBDatabaseBackend::VersionChangeAbortOperation::create(this, String::number(metadata.version), metadata.version)); -} - -void IDBTransactionBackend::scheduleCreateIndexOperation(int64_t objectStoreId, const IDBIndexMetadata& indexMetadata) -{ - scheduleTask(CreateIndexOperation::create(this, objectStoreId, indexMetadata), CreateIndexAbortOperation::create(this, objectStoreId, indexMetadata.id)); -} - -void IDBTransactionBackend::scheduleDeleteIndexOperation(int64_t objectStoreId, const IDBIndexMetadata& indexMetadata) -{ - scheduleTask(DeleteIndexOperation::create(this, objectStoreId, indexMetadata), DeleteIndexAbortOperation::create(this, objectStoreId, indexMetadata)); -} - -void IDBTransactionBackend::scheduleGetOperation(const IDBDatabaseMetadata& metadata, int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, IndexedDB::CursorType cursorType, PassRefPtr callbacks) -{ - scheduleTask(GetOperation::create(this, metadata, objectStoreId, indexId, keyRange, cursorType, callbacks)); -} - -void IDBTransactionBackend::schedulePutOperation(const IDBObjectStoreMetadata& objectStoreMetadata, PassRefPtr value, PassRefPtr key, IDBDatabaseBackend::PutMode putMode, PassRefPtr callbacks, const Vector& indexIds, const Vector& indexKeys) -{ - scheduleTask(PutOperation::create(this, objectStoreMetadata, value, key, putMode, callbacks, indexIds, indexKeys)); -} - -void IDBTransactionBackend::scheduleSetIndexesReadyOperation(size_t indexCount) -{ - scheduleTask(IDBDatabaseBackend::PreemptiveTask, SetIndexesReadyOperation::create(this, indexCount)); -} - -void IDBTransactionBackend::scheduleOpenCursorOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, PassRefPtr callbacks) -{ - scheduleTask(OpenCursorOperation::create(this, objectStoreId, indexId, keyRange, direction, cursorType, taskType, callbacks)); -} - -void IDBTransactionBackend::scheduleCountOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, PassRefPtr callbacks) -{ - scheduleTask(CountOperation::create(this, objectStoreId, indexId, keyRange, callbacks)); -} - -void IDBTransactionBackend::scheduleDeleteRangeOperation(int64_t objectStoreId, PassRefPtr keyRange, PassRefPtr callbacks) -{ - scheduleTask(DeleteRangeOperation::create(this, objectStoreId, keyRange, callbacks)); -} - -void IDBTransactionBackend::scheduleClearObjectStoreOperation(int64_t objectStoreId, PassRefPtr callbacks) -{ - scheduleTask(ClearObjectStoreOperation::create(this, objectStoreId, callbacks)); -} - -}; - -#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.h b/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.h deleted file mode 100644 index bb200dde3..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBTransactionBackend_h -#define IDBTransactionBackend_h - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBDatabaseBackend.h" -#include "IDBDatabaseError.h" -#include "IDBOperation.h" -#include "Timer.h" -#include -#include -#include - -namespace WebCore { - -class IDBCursorBackend; -class IDBDatabaseCallbacks; - -class IDBTransactionBackend : public RefCounted { -public: - static PassRefPtr create(IDBDatabaseBackend*, int64_t transactionId, PassRefPtr, const Vector& objectStoreIds, IndexedDB::TransactionMode); - ~IDBTransactionBackend(); - - void commit(); - void abort(); - void abort(PassRefPtr); - - void run(); - IndexedDB::TransactionMode mode() const { return m_mode; } - const HashSet& scope() const { return m_objectStoreIds; } - - void scheduleTask(PassRefPtr task, PassRefPtr abortTask = nullptr) { scheduleTask(IDBDatabaseBackend::NormalTask, task, abortTask); } - void scheduleTask(IDBDatabaseBackend::TaskType, PassRefPtr, PassRefPtr abortTask = nullptr); - - void registerOpenCursor(IDBCursorBackend*); - void unregisterOpenCursor(IDBCursorBackend*); - - void addPreemptiveEvent() { m_pendingPreemptiveEvents++; } - void didCompletePreemptiveEvent() { m_pendingPreemptiveEvents--; ASSERT(m_pendingPreemptiveEvents >= 0); } - - IDBDatabaseBackend& database() const { return *m_database; } - - void scheduleCreateObjectStoreOperation(const IDBObjectStoreMetadata&); - void scheduleDeleteObjectStoreOperation(const IDBObjectStoreMetadata&); - void scheduleVersionChangeOperation(int64_t requestedVersion, PassRefPtr, PassRefPtr, const IDBDatabaseMetadata&); - void scheduleCreateIndexOperation(int64_t objectStoreId, const IDBIndexMetadata&); - void scheduleDeleteIndexOperation(int64_t objectStoreId, const IDBIndexMetadata&); - void scheduleGetOperation(const IDBDatabaseMetadata&, int64_t objectStoreId, int64_t indexId, PassRefPtr, IndexedDB::CursorType, PassRefPtr); - void schedulePutOperation(const IDBObjectStoreMetadata&, PassRefPtr value, PassRefPtr, IDBDatabaseBackend::PutMode, PassRefPtr, const Vector& indexIds, const Vector&); - void scheduleSetIndexesReadyOperation(size_t indexCount); - void scheduleOpenCursorOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr, IndexedDB::CursorDirection, IndexedDB::CursorType, IDBDatabaseBackend::TaskType, PassRefPtr); - void scheduleCountOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr, PassRefPtr); - void scheduleDeleteRangeOperation(int64_t objectStoreId, PassRefPtr, PassRefPtr); - void scheduleClearObjectStoreOperation(int64_t objectStoreId, PassRefPtr); - - int64_t id() const { return m_id; } - -private: - IDBTransactionBackend(IDBDatabaseBackend*, int64_t id, PassRefPtr, const HashSet& objectStoreIds, IndexedDB::TransactionMode); - - enum State { - Unopened, // Backing store transaction not yet created. - Unused, // Backing store transaction created, but no tasks yet. - StartPending, // Enqueued tasks, but backing store transaction not yet started. - Running, // Backing store transaction started but not yet finished. - Finished, // Either aborted or committed. - }; - - void start(); - - bool isTaskQueueEmpty() const; - bool hasPendingTasks() const; - - void taskTimerFired(Timer&); - void closeOpenCursors(); - - const HashSet m_objectStoreIds; - const IndexedDB::TransactionMode m_mode; - - State m_state; - bool m_commitPending; - RefPtr m_callbacks; - RefPtr m_database; - - typedef Deque> TaskQueue; - TaskQueue m_taskQueue; - TaskQueue m_preemptiveTaskQueue; - Deque> m_abortTaskQueue; - - // FIXME: delete the timer once we have threads instead. - Timer m_taskTimer; - int m_pendingPreemptiveEvents; - - HashSet m_openCursors; - - int64_t m_id; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBTransactionBackend_h diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.cpp b/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.cpp deleted file mode 100644 index 4b6b0fc46..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBTransactionBackendOperations.h" - -#include "IDBCursorBackend.h" -#include "IDBDatabaseCallbacks.h" -#include "IDBKeyRange.h" -#include "IDBRecordIdentifier.h" -#include "IDBServerConnection.h" -#include "Logging.h" -#include - -#if ENABLE(INDEXED_DATABASE) - -#define STANDARD_DATABASE_ERROR_CALLBACK std::function)> operationCallback = \ - [operation, completionCallback](PassRefPtr error) { \ - if (error) \ - operation->m_transaction->abort(error); \ - completionCallback(); \ - }; - -namespace WebCore { - -void CreateObjectStoreOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "CreateObjectStoreOperation"); - - RefPtr operation(this); - STANDARD_DATABASE_ERROR_CALLBACK; - - m_transaction->database().serverConnection().createObjectStore(*m_transaction, *this, operationCallback); -} - -void CreateIndexOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "CreateIndexOperation"); - - RefPtr operation(this); - STANDARD_DATABASE_ERROR_CALLBACK; - - m_transaction->database().serverConnection().createIndex(*m_transaction, *this, operationCallback); -} - -void CreateIndexAbortOperation::perform() -{ - LOG(StorageAPI, "CreateIndexAbortOperation"); - m_transaction->database().removeIndex(m_objectStoreID, m_indexID); -} - -void DeleteIndexOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "DeleteIndexOperation"); - - RefPtr operation(this); - STANDARD_DATABASE_ERROR_CALLBACK; - - m_transaction->database().serverConnection().deleteIndex(*m_transaction, *this, operationCallback); -} - -void DeleteIndexAbortOperation::perform() -{ - LOG(StorageAPI, "DeleteIndexAbortOperation"); - m_transaction->database().addIndex(m_objectStoreID, m_indexMetadata, IDBIndexMetadata::InvalidId); -} - -void GetOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "GetOperation"); - - RefPtr operation(this); - STANDARD_DATABASE_ERROR_CALLBACK; - - m_transaction->database().serverConnection().get(*m_transaction, *this, [this, operation, operationCallback](const IDBGetResult& result, PassRefPtr prpError) { - RefPtr error = prpError; - - if (error) - m_callbacks->onError(error); - else { - if (!result.valueBuffer) { - if (result.keyData.isNull) - m_callbacks->onSuccess(); - else - m_callbacks->onSuccess(result.keyData.maybeCreateIDBKey()); - } else { - if (!result.keyData.isNull) - m_callbacks->onSuccess(result.valueBuffer, result.keyData.maybeCreateIDBKey(), result.keyPath); - else - m_callbacks->onSuccess(result.valueBuffer.get()); - } - } - - operationCallback(error.release()); - }); -} - -void PutOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "PutOperation"); - ASSERT(m_transaction->mode() != IndexedDB::TransactionMode::ReadOnly); - ASSERT(m_indexIDs.size() == m_indexKeys.size()); - - RefPtr operation(this); - STANDARD_DATABASE_ERROR_CALLBACK; - - m_transaction->database().serverConnection().put(*m_transaction, *this, [this, operation, operationCallback](PassRefPtr key, PassRefPtr prpError) { - RefPtr error = prpError; - if (key) { - ASSERT(!error); - m_callbacks->onSuccess(key); - } else { - ASSERT(error); - m_callbacks->onError(error); - } - operationCallback(error.release()); - }); -} - -void SetIndexesReadyOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "SetIndexesReadyOperation"); - - for (size_t i = 0; i < m_indexCount; ++i) - m_transaction->didCompletePreemptiveEvent(); - - callOnMainThread(completionCallback); -} - -void OpenCursorOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "OpenCursorOperation"); - - RefPtr operation(this); - auto callback = [this, operation, completionCallback](int64_t cursorID, PassRefPtr) { - // FIXME: When the LevelDB port fails to open a backing store cursor it calls onSuccess(nullptr); - // This seems nonsensical and might have to change soon, breaking them. - if (!cursorID) - m_callbacks->onSuccess(static_cast(0)); - else { - RefPtr cursor = IDBCursorBackend::create(cursorID, m_cursorType, m_taskType, *m_transaction, m_objectStoreID); - m_callbacks->onSuccess(cursor, cursor->key(), cursor->primaryKey(), cursor->value()); - } - - completionCallback(); - }; - - m_transaction->database().serverConnection().openCursor(*m_transaction, *this, callback); -} - -void CountOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "CountOperation"); - - RefPtr operation(this); - auto callback = [this, operation, completionCallback](int64_t count, PassRefPtr) { - // FIXME: The LevelDB port never had an error condition for the count operation. - // We probably need to support an error for the count operation, breaking the LevelDB port. - m_callbacks->onSuccess(count); - - completionCallback(); - }; - - m_transaction->database().serverConnection().count(*m_transaction, *this, callback); -} - -void DeleteRangeOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "DeleteRangeOperation"); - - RefPtr operation(this); - auto callback = [this, operation, completionCallback](PassRefPtr error) { - if (error) - m_callbacks->onError(error); - else - m_callbacks->onSuccess(); - - completionCallback(); - }; - - m_transaction->database().serverConnection().deleteRange(*m_transaction, *this, callback); -} - -void ClearObjectStoreOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "ClearObjectStoreOperation"); - - RefPtr operation(this); - - auto clearCallback = [this, operation, completionCallback](PassRefPtr prpError) { - RefPtr error = prpError; - - if (error) { - m_callbacks->onError(error); - m_transaction->abort(error.release()); - } else - m_callbacks->onSuccess(); - - completionCallback(); - }; - - m_transaction->database().serverConnection().clearObjectStore(*m_transaction, *this, clearCallback); -} - -void DeleteObjectStoreOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "DeleteObjectStoreOperation"); - - RefPtr operation(this); - STANDARD_DATABASE_ERROR_CALLBACK; - - m_transaction->database().serverConnection().deleteObjectStore(*m_transaction, *this, operationCallback); -} - -void IDBDatabaseBackend::VersionChangeOperation::perform(std::function completionCallback) -{ - LOG(StorageAPI, "VersionChangeOperation"); - - uint64_t oldVersion = m_transaction->database().metadata().version; - RefPtr operation(this); - ASSERT(static_cast(m_version) > oldVersion); - - std::function)> operationCallback = [oldVersion, operation, this, completionCallback](PassRefPtr prpError) { - RefPtr error = prpError; - if (error) { - m_callbacks->onError(error); - m_transaction->abort(error); - } else { - ASSERT(!m_transaction->database().hasPendingSecondHalfOpen()); - m_transaction->database().setCurrentVersion(m_version); - m_transaction->database().setPendingSecondHalfOpen(IDBPendingOpenCall::create(*m_callbacks, *m_databaseCallbacks, m_transaction->id(), m_version)); - m_callbacks->onUpgradeNeeded(oldVersion, &m_transaction->database(), m_transaction->database().metadata()); - } - completionCallback(); - }; - - m_transaction->database().serverConnection().changeDatabaseVersion(*m_transaction, *this, operationCallback); -} - -void CreateObjectStoreAbortOperation::perform() -{ - LOG(StorageAPI, "CreateObjectStoreAbortOperation"); - m_transaction->database().removeObjectStore(m_objectStoreID); -} - -void DeleteObjectStoreAbortOperation::perform() -{ - LOG(StorageAPI, "DeleteObjectStoreAbortOperation"); - m_transaction->database().addObjectStore(m_objectStoreMetadata, IDBObjectStoreMetadata::InvalidId); -} - -void IDBDatabaseBackend::VersionChangeAbortOperation::perform() -{ - LOG(StorageAPI, "VersionChangeAbortOperation"); - m_transaction->database().setCurrentVersion(m_previousIntVersion); -} - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.h b/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.h deleted file mode 100644 index 964cb85ba..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.h +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBTransactionBackendOperations_h -#define IDBTransactionBackendOperations_h - -#include "IDBDatabaseBackend.h" -#include "IDBOperation.h" -#include "IDBTransactionBackend.h" - -#if ENABLE(INDEXED_DATABASE) - -namespace WebCore { - -class IDBServerConnection; - -class CreateObjectStoreOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata) - { - return adoptRef(new CreateObjectStoreOperation(transaction, objectStoreMetadata)); - } - virtual void perform(std::function successCallback) override final; - - const IDBObjectStoreMetadata& objectStoreMetadata() const { return m_objectStoreMetadata; } - -private: - CreateObjectStoreOperation(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata) - : m_transaction(transaction) - , m_objectStoreMetadata(objectStoreMetadata) - { - } - - RefPtr m_transaction; - const IDBObjectStoreMetadata m_objectStoreMetadata; -}; - -class DeleteObjectStoreOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata) - { - return adoptRef(new DeleteObjectStoreOperation(transaction, objectStoreMetadata)); - } - virtual void perform(std::function successCallback) override final; - - IDBTransactionBackend* transaction() const { return m_transaction.get(); } - const IDBObjectStoreMetadata& objectStoreMetadata() const { return m_objectStoreMetadata; } - -private: - DeleteObjectStoreOperation(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata) - : m_transaction(transaction) - , m_objectStoreMetadata(objectStoreMetadata) - { - } - - RefPtr m_transaction; - const IDBObjectStoreMetadata m_objectStoreMetadata; -}; - -class IDBDatabaseBackend::VersionChangeOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, int64_t version, PassRefPtr callbacks, PassRefPtr databaseCallbacks) - { - return adoptRef(new VersionChangeOperation(transaction, version, callbacks, databaseCallbacks)); - } - virtual void perform(std::function successCallback) override final; - - IDBTransactionBackend* transaction() const { return m_transaction.get(); } - int64_t version() const { return m_version; } - IDBDatabaseCallbacks* databaseCallbacks() const { return m_databaseCallbacks.get(); } - -private: - VersionChangeOperation(IDBTransactionBackend* transaction, int64_t version, PassRefPtr callbacks, PassRefPtr databaseCallbacks) - : m_transaction(transaction) - , m_version(version) - , m_callbacks(callbacks) - , m_databaseCallbacks(databaseCallbacks) - { - } - - RefPtr m_transaction; - int64_t m_version; - RefPtr m_callbacks; - RefPtr m_databaseCallbacks; -}; - -class CreateObjectStoreAbortOperation : public IDBSynchronousOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, int64_t objectStoreId) - { - return adoptRef(new CreateObjectStoreAbortOperation(transaction, objectStoreId)); - } - virtual void perform() override final; -private: - CreateObjectStoreAbortOperation(IDBTransactionBackend* transaction, int64_t objectStoreId) - : m_transaction(transaction) - , m_objectStoreID(objectStoreId) - { - } - - RefPtr m_transaction; - const int64_t m_objectStoreID; -}; - -class DeleteObjectStoreAbortOperation : public IDBSynchronousOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStore) - { - return adoptRef(new DeleteObjectStoreAbortOperation(transaction, objectStore)); - } - virtual void perform() override final; -private: - DeleteObjectStoreAbortOperation(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata) - : m_transaction(transaction) - , m_objectStoreMetadata(objectStoreMetadata) - { - } - - RefPtr m_transaction; - IDBObjectStoreMetadata m_objectStoreMetadata; -}; - -class IDBDatabaseBackend::VersionChangeAbortOperation : public IDBSynchronousOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, const String& previousVersion, int64_t previousIntVersion) - { - return adoptRef(new VersionChangeAbortOperation(transaction, previousVersion, previousIntVersion)); - } - virtual void perform() override final; -private: - VersionChangeAbortOperation(IDBTransactionBackend* transaction, const String& previousVersion, int64_t previousIntVersion) - : m_transaction(transaction) - , m_previousVersion(previousVersion) - , m_previousIntVersion(previousIntVersion) - { - } - - RefPtr m_transaction; - String m_previousVersion; - int64_t m_previousIntVersion; -}; - -class CreateIndexOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata) - { - return adoptRef(new CreateIndexOperation(transaction, objectStoreId, indexMetadata)); - } - virtual void perform(std::function successCallback) override final; - - int64_t objectStoreID() const { return m_objectStoreID; } - const IDBIndexMetadata& idbIndexMetadata() const { return m_indexMetadata; } - -private: - CreateIndexOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata) - : m_transaction(transaction) - , m_objectStoreID(objectStoreId) - , m_indexMetadata(indexMetadata) - { - } - - RefPtr m_transaction; - const int64_t m_objectStoreID; - const IDBIndexMetadata m_indexMetadata; -}; - -class CreateIndexAbortOperation : public IDBSynchronousOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId) - { - return adoptRef(new CreateIndexAbortOperation(transaction, objectStoreId, indexId)); - } - virtual void perform() override final; -private: - CreateIndexAbortOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId) - : m_transaction(transaction) - , m_objectStoreID(objectStoreId) - , m_indexID(indexId) - { - } - - const RefPtr m_transaction; - const int64_t m_objectStoreID; - const int64_t m_indexID; -}; - -class DeleteIndexOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata) - { - return adoptRef(new DeleteIndexOperation(transaction, objectStoreId, indexMetadata)); - } - virtual void perform(std::function successCallback) override final; - - int64_t objectStoreID() const { return m_objectStoreID; } - const IDBIndexMetadata& idbIndexMetadata() const { return m_indexMetadata; } - -private: - DeleteIndexOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata) - : m_transaction(transaction) - , m_objectStoreID(objectStoreId) - , m_indexMetadata(indexMetadata) - { - } - - RefPtr m_transaction; - const int64_t m_objectStoreID; - const IDBIndexMetadata m_indexMetadata; -}; - -class DeleteIndexAbortOperation : public IDBSynchronousOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata) - { - return adoptRef(new DeleteIndexAbortOperation(transaction, objectStoreId, indexMetadata)); - } - virtual void perform() override final; -private: - DeleteIndexAbortOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata) - : m_transaction(transaction) - , m_objectStoreID(objectStoreId) - , m_indexMetadata(indexMetadata) - { - } - - RefPtr m_transaction; - const int64_t m_objectStoreID; - const IDBIndexMetadata m_indexMetadata; -}; - -class GetOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, const IDBDatabaseMetadata& metadata, int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, IndexedDB::CursorType cursorType, PassRefPtr callbacks) - { - return adoptRef(new GetOperation(transaction, metadata, objectStoreId, indexId, keyRange, cursorType, callbacks)); - } - virtual void perform(std::function successCallback) override final; - - int64_t objectStoreID() const { return m_objectStoreID; } - int64_t indexID() const { return m_indexID; } - IndexedDB::CursorType cursorType() const { return m_cursorType; } - IDBKeyRange* keyRange() const { return m_keyRange.get(); } - bool autoIncrement() const { return m_autoIncrement; } - IDBKeyPath keyPath() const { return m_keyPath; } - -private: - GetOperation(IDBTransactionBackend* transaction, const IDBDatabaseMetadata& metadata, int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, IndexedDB::CursorType cursorType, PassRefPtr callbacks) - : m_transaction(transaction) - , m_objectStoreID(objectStoreId) - , m_indexID(indexId) - , m_keyPath(metadata.objectStores.get(objectStoreId).keyPath) - , m_autoIncrement(metadata.objectStores.get(objectStoreId).autoIncrement) - , m_keyRange(keyRange) - , m_cursorType(cursorType) - , m_callbacks(callbacks) - { - ASSERT(metadata.objectStores.contains(objectStoreId)); - ASSERT(metadata.objectStores.get(objectStoreId).id == objectStoreId); - } - - RefPtr m_transaction; - const int64_t m_objectStoreID; - const int64_t m_indexID; - const IDBKeyPath m_keyPath; - const bool m_autoIncrement; - const RefPtr m_keyRange; - const IndexedDB::CursorType m_cursorType; - const RefPtr m_callbacks; -}; - -class PutOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStore, PassRefPtr value, PassRefPtr key, IDBDatabaseBackend::PutMode putMode, PassRefPtr callbacks, const Vector& indexIds, const Vector& indexKeys) - { - return adoptRef(new PutOperation(transaction, objectStore, value, key, putMode, callbacks, indexIds, indexKeys)); - } - virtual void perform(std::function successCallback) override final; - - IDBDatabaseBackend::PutMode putMode() const { return m_putMode; } - const IDBObjectStoreMetadata& objectStore() const { return m_objectStore; } - IDBKey* key() const { return m_key.get(); } - const Vector& indexIDs() const { return m_indexIDs; } - const Vector& indexKeys() const { return m_indexKeys; } - SharedBuffer* value() const { return m_value.get(); } - -private: - PutOperation(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStore, PassRefPtr& value, PassRefPtr key, IDBDatabaseBackend::PutMode putMode, PassRefPtr callbacks, const Vector& indexIds, const Vector& indexKeys) - : m_transaction(transaction) - , m_objectStore(objectStore) - , m_value(value) - , m_key(key) - , m_putMode(putMode) - , m_callbacks(callbacks) - , m_indexIDs(indexIds) - , m_indexKeys(indexKeys) - { - } - - RefPtr m_transaction; - const IDBObjectStoreMetadata m_objectStore; - const RefPtr m_value; - const RefPtr m_key; - const IDBDatabaseBackend::PutMode m_putMode; - const RefPtr m_callbacks; - const Vector m_indexIDs; - const Vector m_indexKeys; -}; - -class SetIndexesReadyOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, size_t indexCount) - { - return adoptRef(new SetIndexesReadyOperation(transaction, indexCount)); - } - virtual void perform(std::function successCallback) override final; -private: - SetIndexesReadyOperation(IDBTransactionBackend* transaction, size_t indexCount) - : m_transaction(transaction) - , m_indexCount(indexCount) - { - } - - RefPtr m_transaction; - const size_t m_indexCount; -}; - -class OpenCursorOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, PassRefPtr callbacks) - { - return adoptRef(new OpenCursorOperation(transaction, objectStoreId, indexId, keyRange, direction, cursorType, taskType, callbacks)); - } - virtual void perform(std::function successCallback) override final; - - int64_t transactionID() const { return m_transaction->id(); } - int64_t objectStoreID() const { return m_objectStoreID; } - int64_t indexID() const { return m_indexID; } - IndexedDB::CursorDirection direction() const { return m_direction; } - IndexedDB::CursorType cursorType() const { return m_cursorType; } - IDBDatabaseBackend::TaskType taskType() const { return m_taskType; } - IDBKeyRange* keyRange() const { return m_keyRange.get(); } - IndexedDB::CursorDirection cursorDirection() const { return m_direction; } - -private: - OpenCursorOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, PassRefPtr callbacks) - : m_transaction(transaction) - , m_objectStoreID(objectStoreId) - , m_indexID(indexId) - , m_keyRange(keyRange) - , m_direction(direction) - , m_cursorType(cursorType) - , m_taskType(taskType) - , m_callbacks(callbacks) - { - } - - RefPtr m_transaction; - const int64_t m_objectStoreID; - const int64_t m_indexID; - const PassRefPtr m_keyRange; - const IndexedDB::CursorDirection m_direction; - const IndexedDB::CursorType m_cursorType; - const IDBDatabaseBackend::TaskType m_taskType; - const RefPtr m_callbacks; -}; - -class CountOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, PassRefPtr callbacks) - { - return adoptRef(new CountOperation(transaction, objectStoreId, indexId, keyRange, callbacks)); - } - virtual void perform(std::function successCallback) override final; - - int64_t objectStoreID() const { return m_objectStoreID; } - int64_t indexID() const { return m_indexID; } - IDBKeyRange* keyRange() const { return m_keyRange.get(); } - -private: - CountOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId, PassRefPtr keyRange, PassRefPtr callbacks) - : m_transaction(transaction) - , m_objectStoreID(objectStoreId) - , m_indexID(indexId) - , m_keyRange(keyRange) - , m_callbacks(callbacks) - { - } - - RefPtr m_transaction; - const int64_t m_objectStoreID; - const int64_t m_indexID; - const RefPtr m_keyRange; - const RefPtr m_callbacks; -}; - -class DeleteRangeOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, int64_t objectStoreId, PassRefPtr keyRange, PassRefPtr callbacks) - { - return adoptRef(new DeleteRangeOperation(transaction, objectStoreId, keyRange, callbacks)); - } - virtual void perform(std::function successCallback) override final; - - int64_t objectStoreID() const { return m_objectStoreID; } - IDBKeyRange* keyRange() const { return m_keyRange.get(); } - -private: - DeleteRangeOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, PassRefPtr keyRange, PassRefPtr callbacks) - : m_transaction(transaction) - , m_objectStoreID(objectStoreId) - , m_keyRange(keyRange) - , m_callbacks(callbacks) - { - } - - RefPtr m_transaction; - const int64_t m_objectStoreID; - const RefPtr m_keyRange; - const RefPtr m_callbacks; -}; - -class ClearObjectStoreOperation : public IDBOperation { -public: - static PassRefPtr create(IDBTransactionBackend* transaction, int64_t objectStoreId, PassRefPtr callbacks) - { - return adoptRef(new ClearObjectStoreOperation(transaction, objectStoreId, callbacks)); - } - virtual void perform(std::function successCallback) override final; - - IDBTransactionBackend* transaction() const { return m_transaction.get(); } - int64_t objectStoreID() const { return m_objectStoreID; } - -private: - ClearObjectStoreOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, PassRefPtr callbacks) - : m_transaction(transaction) - , m_objectStoreID(objectStoreId) - , m_callbacks(callbacks) - { - } - - RefPtr m_transaction; - const int64_t m_objectStoreID; - const RefPtr m_callbacks; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBTransactionBackendOperations_h diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.cpp b/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.cpp deleted file mode 100644 index dd4f86fbf..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBTransactionCoordinator.h" - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBDatabaseCallbacks.h" -#include "IDBTransactionBackend.h" - -namespace WebCore { - -PassOwnPtr IDBTransactionCoordinator::create() -{ - return adoptPtr(new IDBTransactionCoordinator()); -} - -IDBTransactionCoordinator::IDBTransactionCoordinator() -{ -} - -IDBTransactionCoordinator::~IDBTransactionCoordinator() -{ -} - -void IDBTransactionCoordinator::didCreateTransaction(IDBTransactionBackend* transaction) -{ - ASSERT(!m_transactions.contains(transaction)); - m_transactions.add(transaction, transaction); -} - -void IDBTransactionCoordinator::didStartTransaction(IDBTransactionBackend* transaction) -{ - ASSERT(m_transactions.contains(transaction)); - - m_queuedTransactions.add(transaction); - processStartedTransactions(); -} - -void IDBTransactionCoordinator::didFinishTransaction(IDBTransactionBackend* transaction) -{ - ASSERT(m_transactions.contains(transaction)); - - if (m_queuedTransactions.contains(transaction)) { - ASSERT(!m_startedTransactions.contains(transaction)); - m_queuedTransactions.remove(transaction); - } else if (m_startedTransactions.contains(transaction)) - m_startedTransactions.remove(transaction); - - m_transactions.remove(transaction); - - processStartedTransactions(); -} - -#ifndef NDEBUG -// Verifies internal consistiency while returning whether anything is found. -bool IDBTransactionCoordinator::isActive(IDBTransactionBackend* transaction) -{ - bool found = false; - if (m_queuedTransactions.contains(transaction)) - found = true; - if (m_startedTransactions.contains(transaction)) { - ASSERT(!found); - found = true; - } - ASSERT(found == m_transactions.contains(transaction)); - return found; -} -#endif - -void IDBTransactionCoordinator::processStartedTransactions() -{ - if (m_queuedTransactions.isEmpty()) - return; - - ASSERT(m_startedTransactions.isEmpty() || (*m_startedTransactions.begin())->mode() != IndexedDB::TransactionMode::VersionChange); - - ListHashSet::const_iterator it = m_queuedTransactions.begin(); - while (it != m_queuedTransactions.end()) { - IDBTransactionBackend* transaction = *it; - ++it; - if (canRunTransaction(transaction)) { - m_queuedTransactions.remove(transaction); - m_startedTransactions.add(transaction); - transaction->run(); - } - } -} - -static bool doScopesOverlap(const HashSet& scope1, const HashSet& scope2) -{ - for (HashSet::const_iterator it = scope1.begin(); it != scope1.end(); ++it) { - if (scope2.contains(*it)) - return true; - } - return false; -} - -bool IDBTransactionCoordinator::canRunTransaction(IDBTransactionBackend* transaction) -{ - ASSERT(m_queuedTransactions.contains(transaction)); - switch (transaction->mode()) { - case IndexedDB::TransactionMode::VersionChange: - ASSERT(m_queuedTransactions.size() == 1); - ASSERT(m_startedTransactions.isEmpty()); - return true; - - case IndexedDB::TransactionMode::ReadOnly: - return true; - - case IndexedDB::TransactionMode::ReadWrite: - for (HashSet::const_iterator it = m_startedTransactions.begin(); it != m_startedTransactions.end(); ++it) { - if ((*it)->mode() == IndexedDB::TransactionMode::ReadWrite && doScopesOverlap(transaction->scope(), (*it)->scope())) - return false; - } - for (ListHashSet::const_iterator it = m_queuedTransactions.begin(); *it != transaction; ++it) { - ASSERT(it != m_queuedTransactions.end()); - if ((*it)->mode() == IndexedDB::TransactionMode::ReadWrite && doScopesOverlap(transaction->scope(), (*it)->scope())) - return false; - } - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -}; - -#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.h b/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.h deleted file mode 100644 index 19878091b..000000000 --- a/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBTransactionCoordinator_h -#define IDBTransactionCoordinator_h - -#if ENABLE(INDEXED_DATABASE) - -#include -#include -#include - -namespace WebCore { - -class IDBTransactionBackend; - -// Transactions are executed in the order the were created. -class IDBTransactionCoordinator { -public: - static PassOwnPtr create(); - virtual ~IDBTransactionCoordinator(); - - // Called by transactions as they start and finish. - void didCreateTransaction(IDBTransactionBackend*); - void didOpenBackingStoreTransaction(IDBTransactionBackend*); - void didStartTransaction(IDBTransactionBackend*); - void didFinishTransaction(IDBTransactionBackend*); - -#ifndef NDEBUG - bool isActive(IDBTransactionBackend*); -#endif - -private: - IDBTransactionCoordinator(); - - void processStartedTransactions(); - bool canRunTransaction(IDBTransactionBackend*); - - // This is just an efficient way to keep references to all transactions. - HashMap > m_transactions; - // Transactions in different states are grouped below. - ListHashSet m_queuedTransactions; - HashSet m_startedTransactions; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBTransactionCoordinator_h diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionMode.h b/Source/WebCore/Modules/indexeddb/IDBTransactionMode.h new file mode 100644 index 000000000..a089ccf3a --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBTransactionMode.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +enum class IDBTransactionMode { + Readonly, + Readwrite, + Versionchange +}; + +} + +#endif diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionMode.idl b/Source/WebCore/Modules/indexeddb/IDBTransactionMode.idl new file mode 100644 index 000000000..7a7eb9306 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBTransactionMode.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=INDEXED_DATABASE, +] enum IDBTransactionMode { + "readonly", + "readwrite", + "versionchange" +}; diff --git a/Source/WebCore/Modules/indexeddb/IDBValue.cpp b/Source/WebCore/Modules/indexeddb/IDBValue.cpp new file mode 100644 index 000000000..d824707c9 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBValue.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBValue.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "SerializedScriptValue.h" +#include + +namespace WebCore { + +IDBValue::IDBValue() +{ +} + +IDBValue::IDBValue(const SerializedScriptValue& scriptValue) + : m_data(ThreadSafeDataBuffer::copyVector(scriptValue.data())) + , m_blobURLs(scriptValue.blobURLsIsolatedCopy()) +{ +} + +IDBValue::IDBValue(const ThreadSafeDataBuffer& value) + : m_data(value) +{ +} + +IDBValue::IDBValue(const SerializedScriptValue& scriptValue, const Vector& blobURLs, const Vector& blobFilePaths) + : m_data(ThreadSafeDataBuffer::copyVector(scriptValue.data())) + , m_blobURLs(blobURLs) + , m_blobFilePaths(blobFilePaths) +{ + ASSERT(m_data.data()); +} + +IDBValue::IDBValue(const ThreadSafeDataBuffer& value, Vector&& blobURLs, Vector&& blobFilePaths) + : m_data(value) + , m_blobURLs(WTFMove(blobURLs)) + , m_blobFilePaths(WTFMove(blobFilePaths)) +{ +} + +IDBValue::IDBValue(const ThreadSafeDataBuffer& value, const Vector& blobURLs, const Vector& blobFilePaths) + : m_data(value) + , m_blobURLs(blobURLs) + , m_blobFilePaths(blobFilePaths) +{ +} + +void IDBValue::setAsIsolatedCopy(const IDBValue& other) +{ + ASSERT(m_blobURLs.isEmpty() && m_blobFilePaths.isEmpty()); + + m_data = other.m_data; + m_blobURLs = CrossThreadCopier>::copy(other.m_blobURLs); + m_blobFilePaths = CrossThreadCopier>::copy(other.m_blobFilePaths); +} + +IDBValue IDBValue::isolatedCopy() const +{ + IDBValue result; + result.setAsIsolatedCopy(*this); + return result; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBValue.h b/Source/WebCore/Modules/indexeddb/IDBValue.h new file mode 100644 index 000000000..2b2cfaca1 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/IDBValue.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once +#if ENABLE(INDEXED_DATABASE) + +#include "ThreadSafeDataBuffer.h" +#include + +namespace WebCore { + +class SerializedScriptValue; + +class IDBValue { +public: + WEBCORE_EXPORT IDBValue(); + IDBValue(const SerializedScriptValue&); + IDBValue(const ThreadSafeDataBuffer&); + IDBValue(const SerializedScriptValue&, const Vector& blobURLs, const Vector& blobFilePaths); + IDBValue(const ThreadSafeDataBuffer&, Vector&& blobURLs, Vector&& blobFilePaths); + IDBValue(const ThreadSafeDataBuffer&, const Vector& blobURLs, const Vector& blobFilePaths); + + void setAsIsolatedCopy(const IDBValue&); + IDBValue isolatedCopy() const; + + const ThreadSafeDataBuffer& data() const { return m_data; } + const Vector& blobURLs() const { return m_blobURLs; } + const Vector& blobFilePaths() const { return m_blobFilePaths; } + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBValue&); + +private: + ThreadSafeDataBuffer m_data; + Vector m_blobURLs; + Vector m_blobFilePaths; +}; + + +template +void IDBValue::encode(Encoder& encoder) const +{ + encoder << m_data; + encoder << m_blobURLs; + encoder << m_blobFilePaths; +} + +template +bool IDBValue::decode(Decoder& decoder, IDBValue& result) +{ + if (!decoder.decode(result.m_data)) + return false; + + if (!decoder.decode(result.m_blobURLs)) + return false; + + if (!decoder.decode(result.m_blobFilePaths)) + return false; + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.cpp b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.cpp index 94f3c01aa..e84a733c2 100644 --- a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.cpp +++ b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -30,15 +30,21 @@ namespace WebCore { -IDBVersionChangeEvent::IDBVersionChangeEvent(unsigned long long oldVersion, unsigned long long newVersion, IndexedDB::VersionNullness newVersionNullness, const AtomicString& eventType) - : Event(eventType, false /*canBubble*/, false /*cancelable*/) +IDBVersionChangeEvent::IDBVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion, const AtomicString& name) + : Event(name, false /*canBubble*/, false /*cancelable*/) + , m_requestIdentifier(requestIdentifier) , m_oldVersion(oldVersion) - , m_newVersion(newVersion) - , m_newVersionNullness(newVersionNullness) { + if (newVersion) + m_newVersion = newVersion; + else + m_newVersion = std::nullopt; } -IDBVersionChangeEvent::~IDBVersionChangeEvent() +IDBVersionChangeEvent::IDBVersionChangeEvent(const AtomicString& name, const Init& init, IsTrusted isTrusted) + : Event(name, init, isTrusted) + , m_oldVersion(init.oldVersion) + , m_newVersion(init.newVersion) { } @@ -49,4 +55,4 @@ EventInterface IDBVersionChangeEvent::eventInterface() const } // namespace WebCore -#endif +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.h b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.h index c8d200885..0801720bc 100644 --- a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.h +++ b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.h @@ -1,65 +1,82 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IDBVersionChangeEvent_h -#define IDBVersionChangeEvent_h +#pragma once #if ENABLE(INDEXED_DATABASE) #include "Event.h" -#include "IndexedDB.h" -#include -#include -#include +#include "IDBResourceIdentifier.h" +#include namespace WebCore { -class IDBVersionChangeEvent : public Event { +class IDBVersionChangeEvent final : public Event { public: - static PassRefPtr create(unsigned long long oldVersion = 0, unsigned long long newVersion = 0, IndexedDB::VersionNullness newVersionNullness = IndexedDB::VersionNullness::Null, const AtomicString& eventType = AtomicString()) + static Ref create(uint64_t oldVersion, uint64_t newVersion, const AtomicString& eventType) + { + return adoptRef(*new IDBVersionChangeEvent(IDBResourceIdentifier::emptyValue(), oldVersion, newVersion, eventType)); + } + + static Ref create(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion, const AtomicString& eventType) { - return adoptRef(new IDBVersionChangeEvent(oldVersion, newVersion, newVersionNullness, eventType)); + return adoptRef(*new IDBVersionChangeEvent(requestIdentifier, oldVersion, newVersion, eventType)); } - virtual ~IDBVersionChangeEvent(); + struct Init : EventInit { + uint64_t oldVersion { 0 }; + std::optional newVersion; + }; - virtual unsigned long long oldVersion() { return m_oldVersion; } - virtual unsigned long long newVersion(bool& isNull) { isNull = (m_newVersionNullness == IndexedDB::VersionNullness::Null); return m_newVersion; } + static Ref create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) + { + return adoptRef(*new IDBVersionChangeEvent(type, initializer, isTrusted)); + } - virtual EventInterface eventInterface() const; + const IDBResourceIdentifier& requestIdentifier() const { return m_requestIdentifier; } + + bool isVersionChangeEvent() const final { return true; } + + uint64_t oldVersion() const { return m_oldVersion; } + std::optional newVersion() const { return m_newVersion; } private: - IDBVersionChangeEvent(unsigned long long oldVersion, unsigned long long newVersion, IndexedDB::VersionNullness newVersionNullness, const AtomicString& eventType); + IDBVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion, const AtomicString& eventType); + IDBVersionChangeEvent(const AtomicString&, const Init&, IsTrusted); + + EventInterface eventInterface() const; - unsigned long long m_oldVersion; - unsigned long long m_newVersion; - IndexedDB::VersionNullness m_newVersionNullness; + IDBResourceIdentifier m_requestIdentifier; + uint64_t m_oldVersion; + std::optional m_newVersion; }; } // namespace WebCore -#endif // ENABLE(INDEXED_DATABASE) +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::IDBVersionChangeEvent) + static bool isType(const WebCore::Event& event) { return event.isVersionChangeEvent(); } +SPECIALIZE_TYPE_TRAITS_END() -#endif // IDBVersionChangeEvent_h +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.idl b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.idl index 49163e267..e696e5302 100644 --- a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.idl +++ b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.idl @@ -23,10 +23,16 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// FIXME: This should be exposed to workers as well. [ Conditional=INDEXED_DATABASE, - JSNoStaticTables, + Constructor(DOMString type, optional IDBVersionChangeEventInit eventInitDict), ] interface IDBVersionChangeEvent : Event { readonly attribute unsigned long long oldVersion; readonly attribute unsigned long long? newVersion; }; + +dictionary IDBVersionChangeEventInit : EventInit { + unsigned long long oldVersion = 0; + unsigned long long? newVersion = null; +}; diff --git a/Source/WebCore/Modules/indexeddb/IndexedDB.h b/Source/WebCore/Modules/indexeddb/IndexedDB.h index fac392621..0ed653e8f 100644 --- a/Source/WebCore/Modules/indexeddb/IndexedDB.h +++ b/Source/WebCore/Modules/indexeddb/IndexedDB.h @@ -23,8 +23,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IndexedDB_h -#define IndexedDB_h +#pragma once #if ENABLE(INDEXED_DATABASE) @@ -32,18 +31,19 @@ namespace WebCore { namespace IndexedDB { -enum class TransactionMode { - ReadOnly = 0, - ReadWrite = 1, - VersionChange = 2, +enum class TransactionState { + Active, + Inactive, + Committing, + Aborting, + Finished, }; -const unsigned TransactionModeMaximum = 2; enum class CursorDirection { - Next = 0, - NextNoDuplicate = 1, - Prev = 2, - PrevNoDuplicate = 3, + Next, + Nextunique, + Prev, + Prevunique, }; const unsigned CursorDirectionMaximum = 3; @@ -53,15 +53,57 @@ enum class CursorType { }; const unsigned CursorTypeMaximum = 1; +enum class CursorSource { + Index, + ObjectStore, +}; + enum class VersionNullness { Null, NonNull, }; +enum class ObjectStoreOverwriteMode { + Overwrite, + OverwriteForCursor, + NoOverwrite, +}; + +enum class IndexRecordType { + Key, + Value, +}; + +enum class ObjectStoreRecordType { + ValueOnly, + KeyOnly, +}; + +// In order of the least to the highest precedent in terms of sort order. +enum KeyType { + Max = -1, + Invalid = 0, + Array, + Binary, + String, + Date, + Number, + Min, +}; + +enum class RequestType { + Open, + Delete, + Other, +}; + +enum class GetAllType { + Keys, + Values, +}; + } // namespace IndexedDB } // namespace WebCore #endif // ENABLED(INDEXED_DATABASE) - -#endif // IndexedDB_h diff --git a/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.cpp b/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.cpp deleted file mode 100644 index a7cbcd797..000000000 --- a/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2012, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - */ - -#include "config.h" -#include "PageGroupIndexedDatabase.h" - -#if ENABLE(INDEXED_DATABASE) - -#include "GroupSettings.h" -#include "IDBFactoryBackendInterface.h" -#include "PageGroup.h" - -namespace WebCore { - -PageGroupIndexedDatabase::PageGroupIndexedDatabase(const String& databaseDirectoryIdentifier) - : m_databaseDirectoryIdentifier(databaseDirectoryIdentifier) -{ -} - -PageGroupIndexedDatabase::~PageGroupIndexedDatabase() -{ -} - -const char* PageGroupIndexedDatabase::supplementName() -{ - return "PageGroupIndexedDatabase"; -} - -PageGroupIndexedDatabase* PageGroupIndexedDatabase::from(PageGroup& group) -{ - PageGroupIndexedDatabase* supplement = static_cast(Supplement::from(&group, supplementName())); - if (!supplement) { - supplement = new PageGroupIndexedDatabase(group.groupSettings().indexedDBDatabasePath()); - provideTo(&group, supplementName(), adoptPtr(supplement)); - } - return supplement; -} - -IDBFactoryBackendInterface* PageGroupIndexedDatabase::factoryBackend() -{ - // Do not add page setting based access control here since this object is shared by all pages in - // the group and having per-page controls is misleading. - if (!m_factoryBackend) - m_factoryBackend = IDBFactoryBackendInterface::create(m_databaseDirectoryIdentifier); - return m_factoryBackend.get(); -} - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.h b/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.h deleted file mode 100644 index 92a25582d..000000000 --- a/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2012, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - */ - -#ifndef PageGroupIndexedDatabase_h -#define PageGroupIndexedDatabase_h - -#if ENABLE(INDEXED_DATABASE) - -#include "Supplementable.h" -#include - -namespace WebCore { - -class IDBFactoryBackendInterface; -class PageGroup; - -class PageGroupIndexedDatabase : public Supplement { -public: - virtual ~PageGroupIndexedDatabase(); - static PageGroupIndexedDatabase* from(PageGroup&); - - IDBFactoryBackendInterface* factoryBackend(); - -private: - explicit PageGroupIndexedDatabase(const String& databaseDirectoryIdentifier); - static const char* supplementName(); - - String m_databaseDirectoryIdentifier; - RefPtr m_factoryBackend; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // PageGroupIndexedDatabase_h diff --git a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.cpp b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.cpp index 2074b15c5..4ab415653 100644 --- a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.cpp +++ b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,19 +27,20 @@ #include "config.h" -#if ENABLE(INDEXED_DATABASE) +#if ENABLE(INDEXED_DATABASE_IN_WORKERS) #include "WorkerGlobalScopeIndexedDatabase.h" +#include "IDBConnectionProxy.h" #include "IDBFactory.h" -#include "IDBFactoryBackendInterface.h" +#include "IDBOpenDBRequest.h" #include "ScriptExecutionContext.h" -#include "SecurityOrigin.h" +#include "WorkerGlobalScope.h" namespace WebCore { -WorkerGlobalScopeIndexedDatabase::WorkerGlobalScopeIndexedDatabase(const String& databaseDirectoryIdentifier) - : m_databaseDirectoryIdentifier(databaseDirectoryIdentifier) +WorkerGlobalScopeIndexedDatabase::WorkerGlobalScopeIndexedDatabase(WorkerGlobalScope&, IDBClient::IDBConnectionProxy& connectionProxy) + : m_connectionProxy(connectionProxy) { } @@ -52,36 +53,35 @@ const char* WorkerGlobalScopeIndexedDatabase::supplementName() return "WorkerGlobalScopeIndexedDatabase"; } -WorkerGlobalScopeIndexedDatabase* WorkerGlobalScopeIndexedDatabase::from(ScriptExecutionContext* context) +WorkerGlobalScopeIndexedDatabase* WorkerGlobalScopeIndexedDatabase::from(WorkerGlobalScope& scope) { - WorkerGlobalScopeIndexedDatabase* supplement = static_cast(Supplement::from(context, supplementName())); + WorkerGlobalScopeIndexedDatabase* supplement = static_cast(Supplement::from(&scope, supplementName())); if (!supplement) { - String databaseDirectoryIdentifier; - WorkerGlobalScope* workerGlobalScope = static_cast(context); - const GroupSettings* groupSettings = workerGlobalScope->groupSettings(); - if (groupSettings) - databaseDirectoryIdentifier = groupSettings->indexedDBDatabasePath(); + auto* connectionProxy = scope.idbConnectionProxy(); + if (!connectionProxy) + return nullptr; - supplement = new WorkerGlobalScopeIndexedDatabase(databaseDirectoryIdentifier); - provideTo(context, supplementName(), adoptPtr(supplement)); + auto newSupplement = std::make_unique(scope, *connectionProxy); + supplement = newSupplement.get(); + provideTo(&scope, supplementName(), WTFMove(newSupplement)); } return supplement; } -IDBFactory* WorkerGlobalScopeIndexedDatabase::indexedDB(ScriptExecutionContext* context) +IDBFactory* WorkerGlobalScopeIndexedDatabase::indexedDB(WorkerGlobalScope& scope) { - return from(context)->indexedDB(); + auto* scopeIDB = from(scope); + return scopeIDB ? scopeIDB->indexedDB() : nullptr; } IDBFactory* WorkerGlobalScopeIndexedDatabase::indexedDB() { - if (!m_factoryBackend) - m_factoryBackend = IDBFactoryBackendInterface::create(m_databaseDirectoryIdentifier); if (!m_idbFactory) - m_idbFactory = IDBFactory::create(m_factoryBackend.get()); + m_idbFactory = IDBFactory::create(m_connectionProxy.get()); + return m_idbFactory.get(); } } // namespace WebCore -#endif // ENABLE(INDEXED_DATABASE) +#endif // ENABLE(INDEXED_DATABASE_IN_WORKERS) diff --git a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.h b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.h index c00b0ab56..20fe4654f 100644 --- a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.h +++ b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2013, 2014, 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,10 +24,9 @@ * */ -#ifndef WorkerGlobalScopeIndexedDatabase_h -#define WorkerGlobalScopeIndexedDatabase_h +#pragma once -#if ENABLE(INDEXED_DATABASE) +#if ENABLE(INDEXED_DATABASE_IN_WORKERS) #include "Supplementable.h" #include @@ -35,29 +34,29 @@ namespace WebCore { class IDBFactory; -class IDBFactoryBackendInterface; -class ScriptExecutionContext; +class WorkerGlobalScope; -class WorkerGlobalScopeIndexedDatabase : public Supplement { +namespace IDBClient { +class IDBConnectionProxy; +} + +class WorkerGlobalScopeIndexedDatabase : public Supplement { public: + explicit WorkerGlobalScopeIndexedDatabase(WorkerGlobalScope&, IDBClient::IDBConnectionProxy&); virtual ~WorkerGlobalScopeIndexedDatabase(); - static WorkerGlobalScopeIndexedDatabase* from(ScriptExecutionContext*); - static IDBFactory* indexedDB(ScriptExecutionContext*); + static IDBFactory* indexedDB(WorkerGlobalScope&); private: - explicit WorkerGlobalScopeIndexedDatabase(const String& databaseDirectoryIdentifier); - IDBFactory* indexedDB(); + static const char* supplementName(); + static WorkerGlobalScopeIndexedDatabase* from(WorkerGlobalScope&); - String m_databaseDirectoryIdentifier; - RefPtr m_factoryBackend; RefPtr m_idbFactory; + Ref m_connectionProxy; }; } // namespace WebCore -#endif // ENABLE(INDEXED_DATABASE) - -#endif // WorkerGlobalScopeIndexedDatabase_h +#endif // ENABLE(INDEXED_DATABASE_IN_WORKERS) diff --git a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.idl b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.idl index 9bee7303a..fc7f775d5 100644 --- a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.idl +++ b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2016 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,31 +25,8 @@ */ [ - Conditional=INDEXED_DATABASE, + Conditional=INDEXED_DATABASE_IN_WORKERS, ] partial interface WorkerGlobalScope { - [ImplementedAs=indexedDB] readonly attribute IDBFactory webkitIndexedDB; - - attribute IDBCursorConstructor webkitIDBCursor; - attribute IDBDatabaseConstructor webkitIDBDatabase; - attribute IDBFactoryConstructor webkitIDBFactory; - attribute IDBIndexConstructor webkitIDBIndex; - attribute IDBKeyRangeConstructor webkitIDBKeyRange; - attribute IDBObjectStoreConstructor webkitIDBObjectStore; - attribute IDBRequestConstructor webkitIDBRequest; - attribute IDBTransactionConstructor webkitIDBTransaction; - - readonly attribute IDBFactory indexedDB; - - attribute IDBCursorConstructor IDBCursor; - attribute IDBCursorWithValueConstructor IDBCursorWithValue; - attribute IDBDatabaseConstructor IDBDatabase; - attribute IDBFactoryConstructor IDBFactory; - attribute IDBIndexConstructor IDBIndex; - attribute IDBKeyRangeConstructor IDBKeyRange; - attribute IDBObjectStoreConstructor IDBObjectStore; - attribute IDBOpenDBRequestConstructor IDBOpenDBRequest; - attribute IDBRequestConstructor IDBRequest; - attribute IDBTransactionConstructor IDBTransaction; - attribute IDBVersionChangeEventConstructor IDBVersionChangeEvent; + [EnabledAtRuntime=IndexedDBWorkers] readonly attribute IDBFactory indexedDB; }; diff --git a/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.cpp b/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.cpp new file mode 100644 index 000000000..4db50c0b9 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.cpp @@ -0,0 +1,562 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBConnectionProxy.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorInfo.h" +#include "IDBDatabase.h" +#include "IDBGetRecordData.h" +#include "IDBIterateCursorData.h" +#include "IDBKeyRangeData.h" +#include "IDBOpenDBRequest.h" +#include "IDBRequestData.h" +#include "IDBResultData.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include + +namespace WebCore { +namespace IDBClient { + +IDBConnectionProxy::IDBConnectionProxy(IDBConnectionToServer& connection) + : m_connectionToServer(connection) + , m_serverConnectionIdentifier(connection.identifier()) +{ + ASSERT(isMainThread()); +} + +void IDBConnectionProxy::ref() +{ + m_connectionToServer.ref(); +} + +void IDBConnectionProxy::deref() +{ + m_connectionToServer.deref(); +} + +Ref IDBConnectionProxy::openDatabase(ScriptExecutionContext& context, const IDBDatabaseIdentifier& databaseIdentifier, uint64_t version) +{ + RefPtr request; + { + Locker locker(m_openDBRequestMapLock); + + request = IDBOpenDBRequest::createOpenRequest(context, *this, databaseIdentifier, version); + ASSERT(!m_openDBRequestMap.contains(request->resourceIdentifier())); + m_openDBRequestMap.set(request->resourceIdentifier(), request.get()); + } + + callConnectionOnMainThread(&IDBConnectionToServer::openDatabase, IDBRequestData(*this, *request)); + + return request.releaseNonNull(); +} + +Ref IDBConnectionProxy::deleteDatabase(ScriptExecutionContext& context, const IDBDatabaseIdentifier& databaseIdentifier) +{ + RefPtr request; + { + Locker locker(m_openDBRequestMapLock); + + request = IDBOpenDBRequest::createDeleteRequest(context, *this, databaseIdentifier); + ASSERT(!m_openDBRequestMap.contains(request->resourceIdentifier())); + m_openDBRequestMap.set(request->resourceIdentifier(), request.get()); + } + + callConnectionOnMainThread(&IDBConnectionToServer::deleteDatabase, IDBRequestData(*this, *request)); + + return request.releaseNonNull(); +} + +void IDBConnectionProxy::didOpenDatabase(const IDBResultData& resultData) +{ + completeOpenDBRequest(resultData); +} + +void IDBConnectionProxy::didDeleteDatabase(const IDBResultData& resultData) +{ + completeOpenDBRequest(resultData); +} + +void IDBConnectionProxy::completeOpenDBRequest(const IDBResultData& resultData) +{ + ASSERT(isMainThread()); + + RefPtr request; + { + Locker locker(m_openDBRequestMapLock); + request = m_openDBRequestMap.take(resultData.requestIdentifier()); + } + + if (!request) + return; + + request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestCompleted, resultData); +} + +void IDBConnectionProxy::createObjectStore(TransactionOperation& operation, const IDBObjectStoreInfo& info) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::createObjectStore, requestData, info); +} + +void IDBConnectionProxy::renameObjectStore(TransactionOperation& operation, uint64_t objectStoreIdentifier, const String& newName) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::renameObjectStore, requestData, objectStoreIdentifier, newName); +} + +void IDBConnectionProxy::renameIndex(TransactionOperation& operation, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::renameIndex, requestData, objectStoreIdentifier, indexIdentifier, newName); +} + +void IDBConnectionProxy::deleteObjectStore(TransactionOperation& operation, const String& objectStoreName) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::deleteObjectStore, requestData, objectStoreName); +} + +void IDBConnectionProxy::clearObjectStore(TransactionOperation& operation, uint64_t objectStoreIdentifier) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::clearObjectStore, requestData, objectStoreIdentifier); +} + +void IDBConnectionProxy::createIndex(TransactionOperation& operation, const IDBIndexInfo& info) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::createIndex, requestData, info); +} + +void IDBConnectionProxy::deleteIndex(TransactionOperation& operation, uint64_t objectStoreIdentifier, const String& indexName) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::deleteIndex, requestData, WTFMove(objectStoreIdentifier), indexName); +} + +void IDBConnectionProxy::putOrAdd(TransactionOperation& operation, IDBKeyData&& keyData, const IDBValue& value, const IndexedDB::ObjectStoreOverwriteMode mode) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::putOrAdd, requestData, keyData, value, mode); +} + +void IDBConnectionProxy::getRecord(TransactionOperation& operation, const IDBGetRecordData& getRecordData) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::getRecord, requestData, getRecordData); +} + +void IDBConnectionProxy::getAllRecords(TransactionOperation& operation, const IDBGetAllRecordsData& getAllRecordsData) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::getAllRecords, requestData, getAllRecordsData); +} + +void IDBConnectionProxy::getCount(TransactionOperation& operation, const IDBKeyRangeData& keyRange) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::getCount, requestData, keyRange); +} + +void IDBConnectionProxy::deleteRecord(TransactionOperation& operation, const IDBKeyRangeData& keyRange) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::deleteRecord, requestData, keyRange); +} + +void IDBConnectionProxy::openCursor(TransactionOperation& operation, const IDBCursorInfo& info) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::openCursor, requestData, info); +} + +void IDBConnectionProxy::iterateCursor(TransactionOperation& operation, const IDBIterateCursorData& data) +{ + const IDBRequestData requestData { operation }; + saveOperation(operation); + + callConnectionOnMainThread(&IDBConnectionToServer::iterateCursor, requestData, data); +} + +void IDBConnectionProxy::saveOperation(TransactionOperation& operation) +{ + Locker locker(m_transactionOperationLock); + + ASSERT(!m_activeOperations.contains(operation.identifier())); + m_activeOperations.set(operation.identifier(), &operation); +} + +void IDBConnectionProxy::completeOperation(const IDBResultData& resultData) +{ + RefPtr operation; + { + Locker locker(m_transactionOperationLock); + operation = m_activeOperations.take(resultData.requestIdentifier()); + } + + if (!operation) + return; + + operation->transitionToComplete(resultData, WTFMove(operation)); +} + +void IDBConnectionProxy::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) +{ + callConnectionOnMainThread(&IDBConnectionToServer::abortOpenAndUpgradeNeeded, databaseConnectionIdentifier, transactionIdentifier); +} + +void IDBConnectionProxy::fireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) +{ + RefPtr database; + { + Locker locker(m_databaseConnectionMapLock); + database = m_databaseConnectionMap.get(databaseConnectionIdentifier); + } + + if (!database) + return; + + database->performCallbackOnOriginThread(*database, &IDBDatabase::fireVersionChangeEvent, requestIdentifier, requestedVersion); +} + +void IDBConnectionProxy::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier) +{ + callConnectionOnMainThread(&IDBConnectionToServer::didFireVersionChangeEvent, databaseConnectionIdentifier, requestIdentifier); +} + +void IDBConnectionProxy::notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion) +{ + ASSERT(isMainThread()); + + RefPtr request; + { + Locker locker(m_openDBRequestMapLock); + request = m_openDBRequestMap.get(requestIdentifier); + } + + if (!request) + return; + + request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestBlocked, oldVersion, newVersion); +} + +void IDBConnectionProxy::openDBRequestCancelled(const IDBRequestData& requestData) +{ + callConnectionOnMainThread(&IDBConnectionToServer::openDBRequestCancelled, requestData); +} + +void IDBConnectionProxy::establishTransaction(IDBTransaction& transaction) +{ + { + Locker locker(m_transactionMapLock); + ASSERT(!hasRecordOfTransaction(transaction)); + m_pendingTransactions.set(transaction.info().identifier(), &transaction); + } + + callConnectionOnMainThread(&IDBConnectionToServer::establishTransaction, transaction.database().databaseConnectionIdentifier(), transaction.info()); +} + +void IDBConnectionProxy::didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + RefPtr transaction; + { + Locker locker(m_transactionMapLock); + transaction = m_pendingTransactions.take(transactionIdentifier); + } + + ASSERT(transaction); + + transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didStart, error); +} + +void IDBConnectionProxy::commitTransaction(IDBTransaction& transaction) +{ + { + Locker locker(m_transactionMapLock); + ASSERT(!m_committingTransactions.contains(transaction.info().identifier())); + m_committingTransactions.set(transaction.info().identifier(), &transaction); + } + + callConnectionOnMainThread(&IDBConnectionToServer::commitTransaction, transaction.info().identifier()); +} + +void IDBConnectionProxy::didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + RefPtr transaction; + { + Locker locker(m_transactionMapLock); + transaction = m_committingTransactions.take(transactionIdentifier); + } + + if (!transaction) + return; + + transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didCommit, error); +} + +void IDBConnectionProxy::abortTransaction(IDBTransaction& transaction) +{ + { + Locker locker(m_transactionMapLock); + ASSERT(!m_abortingTransactions.contains(transaction.info().identifier())); + m_abortingTransactions.set(transaction.info().identifier(), &transaction); + } + + callConnectionOnMainThread(&IDBConnectionToServer::abortTransaction, transaction.info().identifier()); +} + +void IDBConnectionProxy::didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + RefPtr transaction; + { + Locker locker(m_transactionMapLock); + transaction = m_abortingTransactions.take(transactionIdentifier); + } + + if (!transaction) + return; + + transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didAbort, error); +} + +bool IDBConnectionProxy::hasRecordOfTransaction(const IDBTransaction& transaction) const +{ + ASSERT(m_transactionMapLock.isLocked()); + + auto identifier = transaction.info().identifier(); + return m_pendingTransactions.contains(identifier) || m_committingTransactions.contains(identifier) || m_abortingTransactions.contains(identifier); +} + +void IDBConnectionProxy::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, IDBTransaction& transaction) +{ + callConnectionOnMainThread(&IDBConnectionToServer::didFinishHandlingVersionChangeTransaction, databaseConnectionIdentifier, transaction.info().identifier()); +} + +void IDBConnectionProxy::databaseConnectionPendingClose(IDBDatabase& database) +{ + callConnectionOnMainThread(&IDBConnectionToServer::databaseConnectionPendingClose, database.databaseConnectionIdentifier()); +} + +void IDBConnectionProxy::databaseConnectionClosed(IDBDatabase& database) +{ + callConnectionOnMainThread(&IDBConnectionToServer::databaseConnectionClosed, database.databaseConnectionIdentifier()); +} + +void IDBConnectionProxy::didCloseFromServer(uint64_t databaseConnectionIdentifier, const IDBError& error) +{ + RefPtr database; + { + Locker locker(m_databaseConnectionMapLock); + database = m_databaseConnectionMap.get(databaseConnectionIdentifier); + } + + // If the IDBDatabase object is gone, message back to the server so it doesn't hang + // waiting for a reply that will never come. + if (!database) { + m_connectionToServer.confirmDidCloseFromServer(databaseConnectionIdentifier); + return; + } + + database->performCallbackOnOriginThread(*database, &IDBDatabase::didCloseFromServer, error); +} + +void IDBConnectionProxy::confirmDidCloseFromServer(IDBDatabase& database) +{ + callConnectionOnMainThread(&IDBConnectionToServer::confirmDidCloseFromServer, database.databaseConnectionIdentifier()); +} + +void IDBConnectionProxy::connectionToServerLost(const IDBError& error) +{ + Vector databaseConnectionIdentifiers; + { + Locker locker(m_databaseConnectionMapLock); + copyKeysToVector(m_databaseConnectionMap, databaseConnectionIdentifiers); + } + + for (auto connectionIdentifier : databaseConnectionIdentifiers) { + RefPtr database; + { + Locker locker(m_databaseConnectionMapLock); + database = m_databaseConnectionMap.get(connectionIdentifier); + } + + if (!database) + continue; + + database->performCallbackOnOriginThread(*database, &IDBDatabase::connectionToServerLost, error); + } + + Vector openDBRequestIdentifiers; + { + Locker locker(m_openDBRequestMapLock); + copyKeysToVector(m_openDBRequestMap, openDBRequestIdentifiers); + } + + for (auto& requestIdentifier : openDBRequestIdentifiers) { + RefPtr request; + { + Locker locker(m_openDBRequestMapLock); + request = m_openDBRequestMap.get(requestIdentifier); + } + + if (!request) + continue; + + auto result = IDBResultData::error(requestIdentifier, error); + request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestCompleted, result); + } +} + +void IDBConnectionProxy::scheduleMainThreadTasks() +{ + Locker locker(m_mainThreadTaskLock); + if (m_mainThreadProtector) + return; + + m_mainThreadProtector = &m_connectionToServer; + callOnMainThread([this] { + handleMainThreadTasks(); + }); +} + +void IDBConnectionProxy::handleMainThreadTasks() +{ + RefPtr protector; + { + Locker locker(m_mainThreadTaskLock); + ASSERT(m_mainThreadProtector); + protector = WTFMove(m_mainThreadProtector); + } + + while (auto task = m_mainThreadQueue.tryGetMessage()) + task->performTask(); +} + +void IDBConnectionProxy::getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function&)> callback) +{ + // This method is only meant to be called by the web inspector on the main thread. + RELEASE_ASSERT(isMainThread()); + + m_connectionToServer.getAllDatabaseNames(mainFrameOrigin, openingOrigin, callback); +} + +void IDBConnectionProxy::registerDatabaseConnection(IDBDatabase& database) +{ + Locker locker(m_databaseConnectionMapLock); + + ASSERT(!m_databaseConnectionMap.contains(database.databaseConnectionIdentifier())); + m_databaseConnectionMap.set(database.databaseConnectionIdentifier(), &database); +} + +void IDBConnectionProxy::unregisterDatabaseConnection(IDBDatabase& database) +{ + Locker locker(m_databaseConnectionMapLock); + + ASSERT(!m_databaseConnectionMap.contains(database.databaseConnectionIdentifier()) || m_databaseConnectionMap.get(database.databaseConnectionIdentifier()) == &database); + m_databaseConnectionMap.remove(database.databaseConnectionIdentifier()); +} + +void IDBConnectionProxy::forgetActiveOperations(const Vector>& operations) +{ + Locker locker(m_transactionOperationLock); + + for (auto& operation : operations) + m_activeOperations.remove(operation->identifier()); +} + +template +void removeItemsMatchingCurrentThread(HashMap& map) +{ + auto currentThreadID = currentThread(); + + Vector keys; + keys.reserveInitialCapacity(map.size()); + for (auto& iterator : map) { + if (iterator.value->originThreadID() == currentThreadID) + keys.uncheckedAppend(iterator.key); + } + + for (auto& key : keys) + map.remove(key); +} + +void IDBConnectionProxy::forgetActivityForCurrentThread() +{ + ASSERT(!isMainThread()); + + { + Locker lock(m_databaseConnectionMapLock); + removeItemsMatchingCurrentThread(m_databaseConnectionMap); + } + { + Locker lock(m_openDBRequestMapLock); + removeItemsMatchingCurrentThread(m_openDBRequestMap); + } + { + Locker lock(m_transactionMapLock); + removeItemsMatchingCurrentThread(m_pendingTransactions); + removeItemsMatchingCurrentThread(m_committingTransactions); + removeItemsMatchingCurrentThread(m_abortingTransactions); + } + { + Locker lock(m_transactionOperationLock); + removeItemsMatchingCurrentThread(m_activeOperations); + } +} + +} // namesapce IDBClient +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.h b/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.h new file mode 100644 index 000000000..5c01ac34c --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBConnectionToServer.h" +#include "IDBResourceIdentifier.h" +#include "TransactionOperation.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WebCore { + +class IDBDatabase; +class IDBDatabaseIdentifier; +class IDBError; +class IDBOpenDBRequest; +class IDBResultData; +class IDBTransaction; +class ScriptExecutionContext; +class SecurityOrigin; + +struct IDBGetRecordData; +struct IDBIterateCursorData; + +namespace IDBClient { + +class IDBConnectionToServer; + +class IDBConnectionProxy { +public: + IDBConnectionProxy(IDBConnectionToServer&); + + Ref openDatabase(ScriptExecutionContext&, const IDBDatabaseIdentifier&, uint64_t version); + void didOpenDatabase(const IDBResultData&); + + Ref deleteDatabase(ScriptExecutionContext&, const IDBDatabaseIdentifier&); + void didDeleteDatabase(const IDBResultData&); + + void createObjectStore(TransactionOperation&, const IDBObjectStoreInfo&); + void deleteObjectStore(TransactionOperation&, const String& objectStoreName); + void clearObjectStore(TransactionOperation&, uint64_t objectStoreIdentifier); + void createIndex(TransactionOperation&, const IDBIndexInfo&); + void deleteIndex(TransactionOperation&, uint64_t objectStoreIdentifier, const String& indexName); + void putOrAdd(TransactionOperation&, IDBKeyData&&, const IDBValue&, const IndexedDB::ObjectStoreOverwriteMode); + void getRecord(TransactionOperation&, const IDBGetRecordData&); + void getAllRecords(TransactionOperation&, const IDBGetAllRecordsData&); + void getCount(TransactionOperation&, const IDBKeyRangeData&); + void deleteRecord(TransactionOperation&, const IDBKeyRangeData&); + void openCursor(TransactionOperation&, const IDBCursorInfo&); + void iterateCursor(TransactionOperation&, const IDBIterateCursorData&); + void renameObjectStore(TransactionOperation&, uint64_t objectStoreIdentifier, const String& newName); + void renameIndex(TransactionOperation&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName); + + void fireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion); + void didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier); + + void notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion); + void openDBRequestCancelled(const IDBRequestData&); + + void establishTransaction(IDBTransaction&); + void commitTransaction(IDBTransaction&); + void abortTransaction(IDBTransaction&); + + void didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&); + void didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&); + void didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&); + + void didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, IDBTransaction&); + void databaseConnectionPendingClose(IDBDatabase&); + void databaseConnectionClosed(IDBDatabase&); + + void didCloseFromServer(uint64_t databaseConnectionIdentifier, const IDBError&); + void confirmDidCloseFromServer(IDBDatabase&); + + void connectionToServerLost(const IDBError&); + + void abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier); + + void completeOperation(const IDBResultData&); + + uint64_t serverConnectionIdentifier() const { return m_serverConnectionIdentifier; } + + void ref(); + void deref(); + + void getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function&)>); + + void registerDatabaseConnection(IDBDatabase&); + void unregisterDatabaseConnection(IDBDatabase&); + + void forgetActiveOperations(const Vector>&); + void forgetActivityForCurrentThread(); + +private: + void completeOpenDBRequest(const IDBResultData&); + bool hasRecordOfTransaction(const IDBTransaction&) const; + + void saveOperation(TransactionOperation&); + + template + void callConnectionOnMainThread(void (IDBConnectionToServer::*method)(Parameters...), Arguments&&... arguments) + { + if (isMainThread()) + (m_connectionToServer.*method)(std::forward(arguments)...); + else + postMainThreadTask(m_connectionToServer, method, arguments...); + } + + template + void postMainThreadTask(Arguments&&... arguments) + { + auto task = createCrossThreadTask(arguments...); + m_mainThreadQueue.append(WTFMove(task)); + + scheduleMainThreadTasks(); + } + + void scheduleMainThreadTasks(); + void handleMainThreadTasks(); + + IDBConnectionToServer& m_connectionToServer; + uint64_t m_serverConnectionIdentifier; + + HashMap m_databaseConnectionMap; + Lock m_databaseConnectionMapLock; + + HashMap> m_openDBRequestMap; + Lock m_openDBRequestMapLock; + + HashMap> m_pendingTransactions; + HashMap> m_committingTransactions; + HashMap> m_abortingTransactions; + Lock m_transactionMapLock; + + HashMap> m_activeOperations; + Lock m_transactionOperationLock; + + CrossThreadQueue m_mainThreadQueue; + Lock m_mainThreadTaskLock; + RefPtr m_mainThreadProtector; +}; + +} // namespace IDBClient +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.cpp b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.cpp new file mode 100644 index 000000000..75c5773a5 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.cpp @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBConnectionToServer.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBConnectionProxy.h" +#include "IDBDatabase.h" +#include "IDBGetRecordData.h" +#include "IDBKeyRangeData.h" +#include "IDBOpenDBRequest.h" +#include "IDBRequestData.h" +#include "IDBResultData.h" +#include "Logging.h" +#include "TransactionOperation.h" +#include + +namespace WebCore { +namespace IDBClient { + +Ref IDBConnectionToServer::create(IDBConnectionToServerDelegate& delegate) +{ + return adoptRef(*new IDBConnectionToServer(delegate)); +} + +IDBConnectionToServer::IDBConnectionToServer(IDBConnectionToServerDelegate& delegate) + : m_delegate(delegate) + , m_proxy(std::make_unique(*this)) +{ +} + +uint64_t IDBConnectionToServer::identifier() const +{ + return m_delegate->identifier(); +} + +IDBConnectionProxy& IDBConnectionToServer::proxy() +{ + ASSERT(m_proxy); + return *m_proxy; +} + +void IDBConnectionToServer::deleteDatabase(const IDBRequestData& request) +{ + LOG(IndexedDB, "IDBConnectionToServer::deleteDatabase - %s", request.databaseIdentifier().debugString().utf8().data()); + m_delegate->deleteDatabase(request); +} + +void IDBConnectionToServer::didDeleteDatabase(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didDeleteDatabase"); + m_proxy->didDeleteDatabase(resultData); +} + +void IDBConnectionToServer::openDatabase(const IDBRequestData& request) +{ + LOG(IndexedDB, "IDBConnectionToServer::openDatabase - %s (%s) (%" PRIu64 ")", request.databaseIdentifier().debugString().utf8().data(), request.requestIdentifier().loggingString().utf8().data(), request.requestedVersion()); + m_delegate->openDatabase(request); +} + +void IDBConnectionToServer::didOpenDatabase(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didOpenDatabase"); + m_proxy->didOpenDatabase(resultData); +} + +void IDBConnectionToServer::createObjectStore(const IDBRequestData& requestData, const IDBObjectStoreInfo& info) +{ + LOG(IndexedDB, "IDBConnectionToServer::createObjectStore"); + ASSERT(isMainThread()); + + m_delegate->createObjectStore(requestData, info); +} + +void IDBConnectionToServer::didCreateObjectStore(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didCreateObjectStore"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::deleteObjectStore(const IDBRequestData& requestData, const String& objectStoreName) +{ + LOG(IndexedDB, "IDBConnectionToServer::deleteObjectStore"); + ASSERT(isMainThread()); + + m_delegate->deleteObjectStore(requestData, objectStoreName); +} + +void IDBConnectionToServer::didDeleteObjectStore(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didDeleteObjectStore"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::renameObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& newName) +{ + LOG(IndexedDB, "IDBConnectionToServer::renameObjectStore"); + ASSERT(isMainThread()); + + m_delegate->renameObjectStore(requestData, objectStoreIdentifier, newName); +} + +void IDBConnectionToServer::didRenameObjectStore(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didRenameObjectStore"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::clearObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier) +{ + LOG(IndexedDB, "IDBConnectionToServer::clearObjectStore"); + ASSERT(isMainThread()); + + m_delegate->clearObjectStore(requestData, objectStoreIdentifier); +} + +void IDBConnectionToServer::didClearObjectStore(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didClearObjectStore"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::createIndex(const IDBRequestData& requestData, const IDBIndexInfo& info) +{ + LOG(IndexedDB, "IDBConnectionToServer::createIndex"); + ASSERT(isMainThread()); + + m_delegate->createIndex(requestData, info); +} + +void IDBConnectionToServer::didCreateIndex(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didCreateIndex"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::deleteIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName) +{ + LOG(IndexedDB, "IDBConnectionToServer::deleteIndex"); + ASSERT(isMainThread()); + + m_delegate->deleteIndex(requestData, objectStoreIdentifier, indexName); +} + +void IDBConnectionToServer::didDeleteIndex(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didDeleteIndex"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::renameIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) +{ + LOG(IndexedDB, "IDBConnectionToServer::renameIndex"); + ASSERT(isMainThread()); + + m_delegate->renameIndex(requestData, objectStoreIdentifier, indexIdentifier, newName); +} + +void IDBConnectionToServer::didRenameIndex(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didRenameIndex"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& key, const IDBValue& value, const IndexedDB::ObjectStoreOverwriteMode overwriteMode) +{ + LOG(IndexedDB, "IDBConnectionToServer::putOrAdd"); + ASSERT(isMainThread()); + + m_delegate->putOrAdd(requestData, key, value, overwriteMode); +} + +void IDBConnectionToServer::didPutOrAdd(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didPutOrAdd"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData) +{ + LOG(IndexedDB, "IDBConnectionToServer::getRecord"); + ASSERT(isMainThread()); + ASSERT(!getRecordData.keyRangeData.isNull); + + m_delegate->getRecord(requestData, getRecordData); +} + +void IDBConnectionToServer::didGetRecord(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didGetRecord"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData) +{ + LOG(IndexedDB, "IDBConnectionToServer::getAllRecords"); + ASSERT(isMainThread()); + + m_delegate->getAllRecords(requestData, getAllRecordsData); +} + +void IDBConnectionToServer::didGetAllRecords(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didGetAllRecords"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData) +{ + LOG(IndexedDB, "IDBConnectionToServer::getCount"); + ASSERT(isMainThread()); + ASSERT(!keyRangeData.isNull); + + m_delegate->getCount(requestData, keyRangeData); +} + +void IDBConnectionToServer::didGetCount(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didGetCount"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData) +{ + LOG(IndexedDB, "IDBConnectionToServer::deleteRecord"); + ASSERT(isMainThread()); + ASSERT(!keyRangeData.isNull); + + m_delegate->deleteRecord(requestData, keyRangeData); +} + +void IDBConnectionToServer::didDeleteRecord(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didDeleteRecord"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info) +{ + LOG(IndexedDB, "IDBConnectionToServer::openCursor"); + ASSERT(isMainThread()); + + m_delegate->openCursor(requestData, info); +} + +void IDBConnectionToServer::didOpenCursor(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didOpenCursor"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data) +{ + LOG(IndexedDB, "IDBConnectionToServer::iterateCursor"); + ASSERT(isMainThread()); + + m_delegate->iterateCursor(requestData, data); +} + +void IDBConnectionToServer::didIterateCursor(const IDBResultData& resultData) +{ + LOG(IndexedDB, "IDBConnectionToServer::didIterateCursor"); + m_proxy->completeOperation(resultData); +} + +void IDBConnectionToServer::establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo& info) +{ + LOG(IndexedDB, "IDBConnectionToServer::establishTransaction"); + ASSERT(isMainThread()); + + m_delegate->establishTransaction(databaseConnectionIdentifier, info); +} + +void IDBConnectionToServer::commitTransaction(const IDBResourceIdentifier& transactionIdentifier) +{ + LOG(IndexedDB, "IDBConnectionToServer::commitTransaction"); + ASSERT(isMainThread()); + + m_delegate->commitTransaction(transactionIdentifier); +} + +void IDBConnectionToServer::didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + LOG(IndexedDB, "IDBConnectionToServer::didCommitTransaction"); + ASSERT(isMainThread()); + + m_proxy->didCommitTransaction(transactionIdentifier, error); +} + +void IDBConnectionToServer::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) +{ + LOG(IndexedDB, "IDBConnectionToServer::didFinishHandlingVersionChangeTransaction"); + ASSERT(isMainThread()); + + m_delegate->didFinishHandlingVersionChangeTransaction(databaseConnectionIdentifier, transactionIdentifier); +} + +void IDBConnectionToServer::abortTransaction(const IDBResourceIdentifier& transactionIdentifier) +{ + LOG(IndexedDB, "IDBConnectionToServer::abortTransaction"); + ASSERT(isMainThread()); + + m_delegate->abortTransaction(transactionIdentifier); +} + +void IDBConnectionToServer::didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + LOG(IndexedDB, "IDBConnectionToServer::didAbortTransaction"); + ASSERT(isMainThread()); + + m_proxy->didAbortTransaction(transactionIdentifier, error); +} + +void IDBConnectionToServer::fireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) +{ + LOG(IndexedDB, "IDBConnectionToServer::fireVersionChangeEvent"); + ASSERT(isMainThread()); + + m_proxy->fireVersionChangeEvent(databaseConnectionIdentifier, requestIdentifier, requestedVersion); +} + +void IDBConnectionToServer::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier) +{ + LOG(IndexedDB, "IDBConnectionToServer::didFireVersionChangeEvent"); + ASSERT(isMainThread()); + + m_delegate->didFireVersionChangeEvent(databaseConnectionIdentifier, requestIdentifier); +} + +void IDBConnectionToServer::didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + LOG(IndexedDB, "IDBConnectionToServer::didStartTransaction"); + ASSERT(isMainThread()); + + m_proxy->didStartTransaction(transactionIdentifier, error); +} + +void IDBConnectionToServer::didCloseFromServer(uint64_t databaseConnectionIdentifier, const IDBError& error) +{ + LOG(IndexedDB, "IDBConnectionToServer::didCloseFromServer"); + ASSERT(isMainThread()); + + m_proxy->didCloseFromServer(databaseConnectionIdentifier, error); +} + +void IDBConnectionToServer::confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier) +{ + LOG(IndexedDB, "IDBConnectionToServer::confirmDidCloseFromServer"); + ASSERT(isMainThread()); + + m_delegate->confirmDidCloseFromServer(databaseConnectionIdentifier); +} + +void IDBConnectionToServer::connectionToServerLost(const IDBError& error) +{ + LOG(IndexedDB, "IDBConnectionToServer::connectionToServerLost"); + ASSERT(isMainThread()); + + m_proxy->connectionToServerLost(error); +} + +void IDBConnectionToServer::notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion) +{ + LOG(IndexedDB, "IDBConnectionToServer::didStartTransaction"); + ASSERT(isMainThread()); + + m_proxy->notifyOpenDBRequestBlocked(requestIdentifier, oldVersion, newVersion); +} + +void IDBConnectionToServer::openDBRequestCancelled(const IDBRequestData& requestData) +{ + LOG(IndexedDB, "IDBConnectionToServer::openDBRequestCancelled"); + ASSERT(isMainThread()); + + m_delegate->openDBRequestCancelled(requestData); +} + +void IDBConnectionToServer::databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier) +{ + LOG(IndexedDB, "IDBConnectionToServer::databaseConnectionPendingClose"); + ASSERT(isMainThread()); + + m_delegate->databaseConnectionPendingClose(databaseConnectionIdentifier); +} + +void IDBConnectionToServer::databaseConnectionClosed(uint64_t databaseConnectionIdentifier) +{ + LOG(IndexedDB, "IDBConnectionToServer::databaseConnectionClosed"); + ASSERT(isMainThread()); + + m_delegate->databaseConnectionClosed(databaseConnectionIdentifier); +} + +void IDBConnectionToServer::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) +{ + LOG(IndexedDB, "IDBConnectionToServer::abortOpenAndUpgradeNeeded"); + ASSERT(isMainThread()); + + m_delegate->abortOpenAndUpgradeNeeded(databaseConnectionIdentifier, transactionIdentifier); +} + +void IDBConnectionToServer::getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function&)> callback) +{ + static uint64_t callbackID = 0; + + m_getAllDatabaseNamesCallbacks.add(++callbackID, WTFMove(callback)); + + m_delegate->getAllDatabaseNames(SecurityOriginData::fromSecurityOrigin(mainFrameOrigin), SecurityOriginData::fromSecurityOrigin(openingOrigin), callbackID); +} + +void IDBConnectionToServer::didGetAllDatabaseNames(uint64_t callbackID, const Vector& databaseNames) +{ + auto callback = m_getAllDatabaseNamesCallbacks.take(callbackID); + ASSERT(callback); + + callback(databaseNames); +} + +} // namespace IDBClient +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.h b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.h new file mode 100644 index 000000000..7651f4305 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBConnectionProxy.h" +#include "IDBConnectionToServerDelegate.h" +#include "IDBResourceIdentifier.h" +#include +#include +#include + +namespace WebCore { + +class IDBCursorInfo; +class IDBDatabase; +class IDBError; +class IDBObjectStoreInfo; +class IDBResultData; +class IDBValue; +class SecurityOrigin; + +struct IDBGetAllRecordsData; +struct IDBGetRecordData; +struct IDBIterateCursorData; + +namespace IDBClient { + +class IDBConnectionToServer : public ThreadSafeRefCounted { +public: + WEBCORE_EXPORT static Ref create(IDBConnectionToServerDelegate&); + + uint64_t identifier() const; + + IDBConnectionProxy& proxy(); + + void deleteDatabase(const IDBRequestData&); + WEBCORE_EXPORT void didDeleteDatabase(const IDBResultData&); + + void openDatabase(const IDBRequestData&); + WEBCORE_EXPORT void didOpenDatabase(const IDBResultData&); + + void createObjectStore(const IDBRequestData&, const IDBObjectStoreInfo&); + WEBCORE_EXPORT void didCreateObjectStore(const IDBResultData&); + + void deleteObjectStore(const IDBRequestData&, const String& objectStoreName); + WEBCORE_EXPORT void didDeleteObjectStore(const IDBResultData&); + + void renameObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& newName); + WEBCORE_EXPORT void didRenameObjectStore(const IDBResultData&); + + void clearObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier); + WEBCORE_EXPORT void didClearObjectStore(const IDBResultData&); + + void createIndex(const IDBRequestData&, const IDBIndexInfo&); + WEBCORE_EXPORT void didCreateIndex(const IDBResultData&); + + void deleteIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& indexName); + WEBCORE_EXPORT void didDeleteIndex(const IDBResultData&); + + void renameIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName); + WEBCORE_EXPORT void didRenameIndex(const IDBResultData&); + + void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, const IndexedDB::ObjectStoreOverwriteMode); + WEBCORE_EXPORT void didPutOrAdd(const IDBResultData&); + + void getRecord(const IDBRequestData&, const IDBGetRecordData&); + WEBCORE_EXPORT void didGetRecord(const IDBResultData&); + + void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&); + WEBCORE_EXPORT void didGetAllRecords(const IDBResultData&); + + void getCount(const IDBRequestData&, const IDBKeyRangeData&); + WEBCORE_EXPORT void didGetCount(const IDBResultData&); + + void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&); + WEBCORE_EXPORT void didDeleteRecord(const IDBResultData&); + + void openCursor(const IDBRequestData&, const IDBCursorInfo&); + WEBCORE_EXPORT void didOpenCursor(const IDBResultData&); + + void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&); + WEBCORE_EXPORT void didIterateCursor(const IDBResultData&); + + void commitTransaction(const IDBResourceIdentifier& transactionIdentifier); + WEBCORE_EXPORT void didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&); + + void didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier&); + + void abortTransaction(const IDBResourceIdentifier& transactionIdentifier); + WEBCORE_EXPORT void didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&); + + WEBCORE_EXPORT void fireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion); + void didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier); + + WEBCORE_EXPORT void didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&); + + WEBCORE_EXPORT void didCloseFromServer(uint64_t databaseConnectionIdentifier, const IDBError&); + void confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier); + + WEBCORE_EXPORT void connectionToServerLost(const IDBError&); + + WEBCORE_EXPORT void notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion); + void openDBRequestCancelled(const IDBRequestData&); + + void establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo&); + + void databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier); + void databaseConnectionClosed(uint64_t databaseConnectionIdentifier); + + // To be used when an IDBOpenDBRequest gets a new database connection, optionally with a + // versionchange transaction, but the page is already torn down. + void abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier); + + void getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function&)>); + WEBCORE_EXPORT void didGetAllDatabaseNames(uint64_t callbackID, const Vector& databaseNames); + +private: + IDBConnectionToServer(IDBConnectionToServerDelegate&); + + Ref m_delegate; + + HashMap&)>> m_getAllDatabaseNamesCallbacks; + + std::unique_ptr m_proxy; +}; + +} // namespace IDBClient +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServerDelegate.h b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServerDelegate.h new file mode 100644 index 000000000..a61c283a2 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServerDelegate.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include + +namespace WebCore { + +class IDBCursorInfo; +class IDBIndexInfo; +class IDBKeyData; +class IDBObjectStoreInfo; +class IDBRequestData; +class IDBResourceIdentifier; +class IDBTransactionInfo; +class IDBValue; + +struct IDBGetAllRecordsData; +struct IDBGetRecordData; +struct IDBIterateCursorData; +struct SecurityOriginData; + +namespace IndexedDB { +enum class ObjectStoreOverwriteMode; +} + +struct IDBKeyRangeData; + +namespace IDBClient { + +class IDBConnectionToServerDelegate { +public: + virtual ~IDBConnectionToServerDelegate() { } + + virtual uint64_t identifier() const = 0; + virtual void deleteDatabase(const IDBRequestData&) = 0; + virtual void openDatabase(const IDBRequestData&) = 0; + virtual void abortTransaction(const IDBResourceIdentifier&) = 0; + virtual void commitTransaction(const IDBResourceIdentifier&) = 0; + virtual void didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier&) = 0; + virtual void createObjectStore(const IDBRequestData&, const IDBObjectStoreInfo&) = 0; + virtual void deleteObjectStore(const IDBRequestData&, const String& objectStoreName) = 0; + virtual void renameObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& newName) = 0; + virtual void clearObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier) = 0; + virtual void createIndex(const IDBRequestData&, const IDBIndexInfo&) = 0; + virtual void deleteIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& indexName) = 0; + virtual void renameIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) = 0; + virtual void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, const IndexedDB::ObjectStoreOverwriteMode) = 0; + virtual void getRecord(const IDBRequestData&, const IDBGetRecordData&) = 0; + virtual void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&) = 0; + virtual void getCount(const IDBRequestData&, const IDBKeyRangeData&) = 0; + virtual void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&) = 0; + virtual void openCursor(const IDBRequestData&, const IDBCursorInfo&) = 0; + virtual void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&) = 0; + + virtual void establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo&) = 0; + virtual void databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier) = 0; + virtual void databaseConnectionClosed(uint64_t databaseConnectionIdentifier) = 0; + virtual void abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) = 0; + virtual void didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier) = 0; + virtual void openDBRequestCancelled(const IDBRequestData&) = 0; + virtual void confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier) = 0; + + virtual void getAllDatabaseNames(const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID) = 0; + + virtual void ref() = 0; + virtual void deref() = 0; +}; + +} // namespace IDBClient +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/client/TransactionOperation.cpp b/Source/WebCore/Modules/indexeddb/client/TransactionOperation.cpp new file mode 100644 index 000000000..8f3547522 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/client/TransactionOperation.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TransactionOperation.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursor.h" +#include + +namespace WebCore { +namespace IDBClient { + +TransactionOperation::TransactionOperation(IDBTransaction& transaction, IDBRequest& request) + : TransactionOperation(transaction) +{ + m_objectStoreIdentifier = request.sourceObjectStoreIdentifier(); + m_indexIdentifier = request.sourceIndexIdentifier(); + if (m_indexIdentifier) + m_indexRecordType = request.requestedIndexRecordType(); + if (auto* cursor = request.pendingCursor()) + m_cursorIdentifier = std::make_unique(cursor->info().identifier()); + + m_idbRequest = &request; +} + +} // namespace IDBClient +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/client/TransactionOperation.h b/Source/WebCore/Modules/indexeddb/client/TransactionOperation.h new file mode 100644 index 000000000..cd95befd5 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/client/TransactionOperation.h @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBRequest.h" +#include "IDBRequestData.h" +#include "IDBResourceIdentifier.h" +#include "IDBResultData.h" +#include "IDBTransaction.h" +#include +#include + +namespace WebCore { + +class IDBResultData; + +namespace IndexedDB { +enum class IndexRecordType; +} + +namespace IDBClient { + +class TransactionOperation : public ThreadSafeRefCounted { + friend IDBRequestData::IDBRequestData(TransactionOperation&); +public: + virtual ~TransactionOperation() + { + ASSERT(m_originThreadID == currentThread()); + } + + void perform() + { + ASSERT(m_originThreadID == currentThread()); + ASSERT(m_performFunction); + m_performFunction(); + m_performFunction = { }; + } + + void transitionToCompleteOnThisThread(const IDBResultData& data) + { + ASSERT(m_originThreadID == currentThread()); + m_transaction->operationCompletedOnServer(data, *this); + } + + void transitionToComplete(const IDBResultData& data, RefPtr&& lastRef) + { + ASSERT(isMainThread()); + + if (m_originThreadID == currentThread()) + transitionToCompleteOnThisThread(data); + else { + m_transaction->performCallbackOnOriginThread(*this, &TransactionOperation::transitionToCompleteOnThisThread, data); + m_transaction->callFunctionOnOriginThread([lastRef = WTFMove(lastRef)]() { + }); + } + } + + void doComplete(const IDBResultData& data) + { + ASSERT(m_originThreadID == currentThread()); + + // Due to race conditions between the server sending an "operation complete" message and the client + // forcefully aborting an operation, it's unavoidable that this method might be called twice. + // It's okay to handle that gracefully with an early return. + if (!m_completeFunction) + return; + + m_completeFunction(data); + m_transaction->operationCompletedOnClient(*this); + + // m_completeFunction might be holding the last ref to this TransactionOperation, + // so we need to do this trick to null it out without first destroying it. + std::function oldCompleteFunction; + std::swap(m_completeFunction, oldCompleteFunction); + } + + const IDBResourceIdentifier& identifier() const { return m_identifier; } + + ThreadIdentifier originThreadID() const { return m_originThreadID; } + + IDBRequest* idbRequest() { return m_idbRequest.get(); } + + bool nextRequestCanGoToServer() const { return m_nextRequestCanGoToServer && m_idbRequest; } + void setNextRequestCanGoToServer(bool nextRequestCanGoToServer) { m_nextRequestCanGoToServer = nextRequestCanGoToServer; } + +protected: + TransactionOperation(IDBTransaction& transaction) + : m_transaction(transaction) + , m_identifier(transaction.connectionProxy()) + { + } + + TransactionOperation(IDBTransaction&, IDBRequest&); + + Ref m_transaction; + IDBResourceIdentifier m_identifier; + uint64_t m_objectStoreIdentifier { 0 }; + uint64_t m_indexIdentifier { 0 }; + std::unique_ptr m_cursorIdentifier; + IndexedDB::IndexRecordType m_indexRecordType; + std::function m_performFunction; + std::function m_completeFunction; + +private: + IDBResourceIdentifier transactionIdentifier() const { return m_transaction->info().identifier(); } + uint64_t objectStoreIdentifier() const { return m_objectStoreIdentifier; } + uint64_t indexIdentifier() const { return m_indexIdentifier; } + IDBResourceIdentifier* cursorIdentifier() const { return m_cursorIdentifier.get(); } + IDBTransaction& transaction() { return m_transaction.get(); } + IndexedDB::IndexRecordType indexRecordType() const { return m_indexRecordType; } + + ThreadIdentifier m_originThreadID { currentThread() }; + RefPtr m_idbRequest; + bool m_nextRequestCanGoToServer { true }; +}; + +template +class TransactionOperationImpl final : public TransactionOperation { +public: + TransactionOperationImpl(IDBTransaction& transaction, void (IDBTransaction::*completeMethod)(const IDBResultData&), void (IDBTransaction::*performMethod)(TransactionOperation&, Arguments...), Arguments&&... arguments) + : TransactionOperation(transaction) + { + RefPtr protectedThis(this); + + ASSERT(performMethod); + m_performFunction = [protectedThis, this, performMethod, arguments...] { + (&m_transaction.get()->*performMethod)(*this, arguments...); + }; + + if (completeMethod) { + m_completeFunction = [protectedThis, this, completeMethod](const IDBResultData& resultData) { + if (completeMethod) + (&m_transaction.get()->*completeMethod)(resultData); + }; + } + } + + TransactionOperationImpl(IDBTransaction& transaction, IDBRequest& request, void (IDBTransaction::*completeMethod)(IDBRequest&, const IDBResultData&), void (IDBTransaction::*performMethod)(TransactionOperation&, Arguments...), Arguments&&... arguments) + : TransactionOperation(transaction, request) + { + RefPtr protectedThis(this); + + ASSERT(performMethod); + m_performFunction = [protectedThis, this, performMethod, arguments...] { + (&m_transaction.get()->*performMethod)(*this, arguments...); + }; + + if (completeMethod) { + RefPtr refRequest(&request); + m_completeFunction = [protectedThis, this, refRequest, completeMethod](const IDBResultData& resultData) { + if (completeMethod) + (&m_transaction.get()->*completeMethod)(*refRequest, resultData); + }; + } + } +}; + +inline RefPtr createTransactionOperation( + IDBTransaction& transaction, + void (IDBTransaction::*complete)(const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&)) +{ + auto operation = new TransactionOperationImpl<>(transaction, complete, perform); + return adoptRef(operation); +} + +template +RefPtr createTransactionOperation( + IDBTransaction& transaction, + void (IDBTransaction::*complete)(const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1), + const P1& parameter1) +{ + auto operation = new TransactionOperationImpl(transaction, complete, perform, parameter1); + return adoptRef(operation); +} + +template +RefPtr createTransactionOperation( + IDBTransaction& transaction, + void (IDBTransaction::*complete)(const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2), + const P1& parameter1, + const P2& parameter2) +{ + auto operation = new TransactionOperationImpl(transaction, complete, perform, parameter1, parameter2); + return adoptRef(operation); +} + +template +RefPtr createTransactionOperation( + IDBTransaction& transaction, + void (IDBTransaction::*complete)(const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2, MP3), + const P1& parameter1, + const P2& parameter2, + const P3& parameter3) +{ + auto operation = new TransactionOperationImpl(transaction, complete, perform, parameter1, parameter2, parameter3); + return adoptRef(operation); +} + +template +RefPtr createTransactionOperation( + IDBTransaction& transaction, + IDBRequest& request, + void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1), + const P1& parameter1) +{ + auto operation = new TransactionOperationImpl(transaction, request, complete, perform, parameter1); + return adoptRef(operation); +} + +template +RefPtr createTransactionOperation( + IDBTransaction& transaction, + IDBRequest& request, + void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2), + const P1& parameter1, + const P2& parameter2) +{ + auto operation = new TransactionOperationImpl(transaction, request, complete, perform, parameter1, parameter2); + return adoptRef(operation); +} + +template +RefPtr createTransactionOperation( + IDBTransaction& transaction, + IDBRequest& request, + void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&), + void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2, MP3), + const P1& parameter1, + const P2& parameter2, + const P3& parameter3) +{ + auto operation = new TransactionOperationImpl(transaction, request, complete, perform, parameter1, parameter2, parameter3); + return adoptRef(operation); +} + +} // namespace IDBClient +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.cpp deleted file mode 100644 index b3c5f6113..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBBackingStoreCursorLevelDB.h" - -#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -#include "IDBBackingStoreLevelDB.h" -#include "LevelDBTransaction.h" - -namespace WebCore { - -IDBBackingStoreCursorLevelDB::IDBBackingStoreCursorLevelDB(const IDBBackingStoreCursorLevelDB* other) - : m_transaction(other->m_transaction) - , m_cursorOptions(other->m_cursorOptions) - , m_currentKey(other->m_currentKey) - , m_recordIdentifier(IDBRecordIdentifier::create()) -{ - if (other->m_iterator) { - m_iterator = m_transaction->createIterator(); - - if (other->m_iterator->isValid()) { - m_iterator->seek(other->m_iterator->key()); - ASSERT(m_iterator->isValid()); - } - } - - m_recordIdentifier->reset(other->m_recordIdentifier->encodedPrimaryKey(), other->m_recordIdentifier->version()); -} - -bool IDBBackingStoreCursorLevelDB::firstSeek() -{ - m_iterator = m_transaction->createIterator(); - if (m_cursorOptions.forward) - m_iterator->seek(m_cursorOptions.lowKey); - else - m_iterator->seek(m_cursorOptions.highKey); - - return continueFunction(0, Ready); -} - -bool IDBBackingStoreCursorLevelDB::advance(unsigned long count) -{ - while (count--) { - if (!continueFunction()) - return false; - } - return true; -} - -bool IDBBackingStoreCursorLevelDB::continueFunction(const IDBKey* key, IteratorState nextState) -{ - RefPtr previousKey = m_currentKey; - - bool firstIteration = true; - - // When iterating with PrevNoDuplicate, spec requires that the - // value we yield for each key is the first duplicate in forwards - // order. - RefPtr lastDuplicateKey; - - bool forward = m_cursorOptions.forward; - - for (;;) { - if (nextState == Seek) { - // FIXME: Optimize seeking for reverse cursors as well. - if (firstIteration && key && forward) { - m_iterator->seek(encodeKey(*key)); - firstIteration = false; - } else if (forward) - m_iterator->next(); - else - m_iterator->prev(); - } else - nextState = Seek; // for subsequent iterations - - if (!m_iterator->isValid()) { - if (!forward && lastDuplicateKey.get()) { - // We need to walk forward because we hit the end of - // the data. - forward = true; - continue; - } - - return false; - } - - if (isPastBounds()) { - if (!forward && lastDuplicateKey.get()) { - // We need to walk forward because now we're beyond the - // bounds defined by the cursor. - forward = true; - continue; - } - - return false; - } - - if (!haveEnteredRange()) - continue; - - // The row may not load because there's a stale entry in the - // index. This is not fatal. - if (!loadCurrentRow()) - continue; - - if (key) { - if (forward) { - if (m_currentKey->isLessThan(key)) - continue; - } else { - if (key->isLessThan(m_currentKey.get())) - continue; - } - } - - if (m_cursorOptions.unique) { - - if (m_currentKey->isEqual(previousKey.get())) { - // We should never be able to walk forward all the way - // to the previous key. - ASSERT(!lastDuplicateKey.get()); - continue; - } - - if (!forward) { - if (!lastDuplicateKey.get()) { - lastDuplicateKey = m_currentKey; - continue; - } - - // We need to walk forward because we hit the boundary - // between key ranges. - if (!lastDuplicateKey->isEqual(m_currentKey.get())) { - forward = true; - continue; - } - - continue; - } - } - break; - } - - ASSERT(!lastDuplicateKey.get() || (forward && lastDuplicateKey->isEqual(m_currentKey.get()))); - return true; -} - -bool IDBBackingStoreCursorLevelDB::haveEnteredRange() const -{ - if (m_cursorOptions.forward) { - if (m_cursorOptions.lowOpen) - return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) > 0; - - return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) >= 0; - } - if (m_cursorOptions.highOpen) - return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) < 0; - - return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) <= 0; -} - -bool IDBBackingStoreCursorLevelDB::isPastBounds() const -{ - if (m_cursorOptions.forward) { - if (m_cursorOptions.highOpen) - return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) >= 0; - return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) > 0; - } - - if (m_cursorOptions.lowOpen) - return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) <= 0; - return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) < 0; -} - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.h deleted file mode 100644 index 972088478..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBBackingStoreCursorLevelDB_h -#define IDBBackingStoreCursorLevelDB_h - -#include "IDBKey.h" -#include "IDBRecordIdentifier.h" -#include "LevelDBIterator.h" - -#if ENABLE(INDEXED_DATABASE) -#if USE(LEVELDB) - -namespace WebCore { - -class LevelDBTransaction; -class SharedBuffer; - -class IDBBackingStoreCursorLevelDB : public RefCounted { -public: - enum IteratorState { - Ready = 0, - Seek - }; - - struct CursorOptions { - int64_t databaseId; - int64_t objectStoreId; - int64_t indexId; - Vector lowKey; - bool lowOpen; - Vector highKey; - bool highOpen; - bool forward; - bool unique; - }; - - virtual PassRefPtr key() const { return m_currentKey; } - virtual bool continueFunction(const IDBKey* = 0, IteratorState = Seek); - virtual bool advance(unsigned long); - bool firstSeek(); - - virtual PassRefPtr clone() = 0; - - virtual PassRefPtr primaryKey() const { return m_currentKey; } - virtual PassRefPtr value() const = 0; - virtual const IDBRecordIdentifier& recordIdentifier() const { return *m_recordIdentifier; } - virtual ~IDBBackingStoreCursorLevelDB() { } - virtual bool loadCurrentRow() = 0; - -protected: - IDBBackingStoreCursorLevelDB(int64_t cursorID, LevelDBTransaction* transaction, const CursorOptions& cursorOptions) - : m_cursorID(cursorID) - , m_transaction(transaction) - , m_cursorOptions(cursorOptions) - , m_recordIdentifier(IDBRecordIdentifier::create()) - { - } - explicit IDBBackingStoreCursorLevelDB(const IDBBackingStoreCursorLevelDB* other); - - virtual Vector encodeKey(const IDBKey&) = 0; - - bool isPastBounds() const; - bool haveEnteredRange() const; - - int64_t m_cursorID; - LevelDBTransaction* m_transaction; - const CursorOptions m_cursorOptions; - OwnPtr m_iterator; - RefPtr m_currentKey; - RefPtr m_recordIdentifier; -}; - -} // namespace WebCore - -#endif // USE(LEVELDB) -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBBackingStoreCursorLevelDB_h diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp deleted file mode 100644 index 0712bdf38..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp +++ /dev/null @@ -1,1931 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBBackingStoreLevelDB.h" - -#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -#include "FileSystem.h" -#include "HistogramSupport.h" -#include "IDBDatabaseMetadata.h" -#include "IDBIndexWriterLevelDB.h" -#include "IDBKey.h" -#include "IDBKeyPath.h" -#include "IDBKeyRange.h" -#include "IDBLevelDBCoding.h" -#include "IDBTransactionBackend.h" -#include "LevelDBComparator.h" -#include "LevelDBDatabase.h" -#include "LevelDBIterator.h" -#include "LevelDBSlice.h" -#include "LevelDBTransaction.h" -#include "Logging.h" -#include "SecurityOrigin.h" -#include "SharedBuffer.h" -#include - -namespace WebCore { - -using namespace IDBLevelDBCoding; - -const int64_t KeyGeneratorInitialNumber = 1; // From the IndexedDB specification. - -enum IDBBackingStoreLevelDBErrorSource { - // 0 - 2 are no longer used. - FindKeyInIndex = 3, - GetIDBDatabaseMetaData, - GetIndexes, - GetKeyGeneratorCurrentNumber, - GetObjectStores, - GetRecord, - KeyExistsInObjectStore, - LoadCurrentRow, - SetupMetadata, - GetPrimaryKeyViaIndex, - KeyExistsInIndex, - VersionExists, - DeleteObjectStore, - SetMaxObjectStoreId, - SetMaxIndexId, - GetNewDatabaseId, - GetNewVersionNumber, - CreateIDBDatabaseMetaData, - DeleteDatabase, - IDBLevelDBBackingStoreInternalErrorMax, -}; - -static void recordInternalError(const char* type, IDBBackingStoreLevelDBErrorSource location) -{ - String name = String::format("WebCore.IndexedDB.BackingStore.%sError", type); - HistogramSupport::histogramEnumeration(name.utf8().data(), location, IDBLevelDBBackingStoreInternalErrorMax); -} - -// Use to signal conditions that usually indicate developer error, but could be caused by data corruption. -// A macro is used instead of an inline function so that the assert and log report the line number. -#define REPORT_ERROR(type, location) \ - do { \ - LOG_ERROR("IndexedDB %s Error: %s", type, #location); \ - ASSERT_NOT_REACHED(); \ - recordInternalError(type, location); \ - } while (0) - -#define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location) -#define INTERNAL_CONSISTENCY_ERROR(location) REPORT_ERROR("Consistency", location) -#define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location) - -static void putBool(LevelDBTransaction* transaction, const LevelDBSlice& key, bool value) -{ - transaction->put(key, encodeBool(value)); -} - -template -static bool getInt(DBOrTransaction* db, const LevelDBSlice& key, int64_t& foundInt, bool& found) -{ - Vector result; - bool ok = db->safeGet(key, result, found); - if (!ok) - return false; - if (!found) - return true; - - foundInt = decodeInt(result.begin(), result.end()); - return true; -} - -static void putInt(LevelDBTransaction* transaction, const LevelDBSlice& key, int64_t value) -{ - ASSERT(value >= 0); - transaction->put(key, encodeInt(value)); -} - -template -WARN_UNUSED_RETURN static bool getVarInt(DBOrTransaction* db, const LevelDBSlice& key, int64_t& foundInt, bool& found) -{ - Vector result; - bool ok = db->safeGet(key, result, found); - if (!ok) - return false; - if (!found) - return true; - - found = decodeVarInt(result.begin(), result.end(), foundInt) == result.end(); - return true; -} - -static void putVarInt(LevelDBTransaction* transaction, const LevelDBSlice& key, int64_t value) -{ - transaction->put(key, encodeVarInt(value)); -} - -template -WARN_UNUSED_RETURN static bool getString(DBOrTransaction* db, const LevelDBSlice& key, String& foundString, bool& found) -{ - Vector result; - found = false; - bool ok = db->safeGet(key, result, found); - if (!ok) - return false; - if (!found) - return true; - - foundString = decodeString(result.begin(), result.end()); - return true; -} - -static void putString(LevelDBTransaction* transaction, const LevelDBSlice& key, const String& value) -{ - transaction->put(key, encodeString(value)); -} - -static void putIDBKeyPath(LevelDBTransaction* transaction, const LevelDBSlice& key, const IDBKeyPath& value) -{ - transaction->put(key, encodeIDBKeyPath(value)); -} - -static int compareKeys(const LevelDBSlice& a, const LevelDBSlice& b) -{ - return compare(a, b); -} - -int IDBBackingStoreLevelDB::compareIndexKeys(const LevelDBSlice& a, const LevelDBSlice& b) -{ - return compare(a, b, true); -} - -class Comparator : public LevelDBComparator { -public: - virtual int compare(const LevelDBSlice& a, const LevelDBSlice& b) const { return IDBLevelDBCoding::compare(a, b); } - virtual const char* name() const { return "idb_cmp1"; } -}; - -// 0 - Initial version. -// 1 - Adds UserIntVersion to DatabaseMetaData. -// 2 - Adds DataVersion to to global metadata. -const int64_t latestKnownSchemaVersion = 2; -WARN_UNUSED_RETURN static bool isSchemaKnown(LevelDBDatabase* db, bool& known) -{ - int64_t dbSchemaVersion = 0; - bool found = false; - bool ok = getInt(db, SchemaVersionKey::encode(), dbSchemaVersion, found); - if (!ok) - return false; - if (!found) { - known = true; - return true; - } - if (dbSchemaVersion > latestKnownSchemaVersion) { - known = false; - return true; - } - - const uint32_t latestKnownDataVersion = SerializedScriptValue::wireFormatVersion(); - int64_t dbDataVersion = 0; - ok = getInt(db, DataVersionKey::encode(), dbDataVersion, found); - if (!ok) - return false; - if (!found) { - known = true; - return true; - } - - if (dbDataVersion > latestKnownDataVersion) { - known = false; - return true; - } - - known = true; - return true; -} - -WARN_UNUSED_RETURN static bool setUpMetadata(LevelDBDatabase* db, const String& origin) -{ - const uint32_t latestKnownDataVersion = SerializedScriptValue::wireFormatVersion(); - const Vector schemaVersionKey = SchemaVersionKey::encode(); - const Vector dataVersionKey = DataVersionKey::encode(); - - RefPtr transaction = LevelDBTransaction::create(db); - - int64_t dbSchemaVersion = 0; - int64_t dbDataVersion = 0; - bool found = false; - bool ok = getInt(transaction.get(), schemaVersionKey, dbSchemaVersion, found); - if (!ok) { - INTERNAL_READ_ERROR(SetupMetadata); - return false; - } - if (!found) { - // Initialize new backing store. - dbSchemaVersion = latestKnownSchemaVersion; - putInt(transaction.get(), schemaVersionKey, dbSchemaVersion); - dbDataVersion = latestKnownDataVersion; - putInt(transaction.get(), dataVersionKey, dbDataVersion); - } else { - // Upgrade old backing store. - ASSERT(dbSchemaVersion <= latestKnownSchemaVersion); - if (dbSchemaVersion < 1) { - dbSchemaVersion = 1; - putInt(transaction.get(), schemaVersionKey, dbSchemaVersion); - const Vector startKey = DatabaseNameKey::encodeMinKeyForOrigin(origin); - const Vector stopKey = DatabaseNameKey::encodeStopKeyForOrigin(origin); - OwnPtr it = db->createIterator(); - for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) { - int64_t databaseId = 0; - found = false; - bool ok = getInt(transaction.get(), it->key(), databaseId, found); - if (!ok) { - INTERNAL_READ_ERROR(SetupMetadata); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(SetupMetadata); - return false; - } - Vector intVersionKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::UserIntVersion); - putVarInt(transaction.get(), intVersionKey, IDBDatabaseMetadata::DefaultIntVersion); - } - } - if (dbSchemaVersion < 2) { - dbSchemaVersion = 2; - putInt(transaction.get(), schemaVersionKey, dbSchemaVersion); - dbDataVersion = SerializedScriptValue::wireFormatVersion(); - putInt(transaction.get(), dataVersionKey, dbDataVersion); - } - } - - // All new values will be written using this serialization version. - found = false; - ok = getInt(transaction.get(), dataVersionKey, dbDataVersion, found); - if (!ok) { - INTERNAL_READ_ERROR(SetupMetadata); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(SetupMetadata); - return false; - } - if (dbDataVersion < latestKnownDataVersion) { - dbDataVersion = latestKnownDataVersion; - putInt(transaction.get(), dataVersionKey, dbDataVersion); - } - - ASSERT(dbSchemaVersion == latestKnownSchemaVersion); - ASSERT(dbDataVersion == latestKnownDataVersion); - - if (!transaction->commit()) { - INTERNAL_WRITE_ERROR(SetupMetadata); - return false; - } - return true; -} - -template -WARN_UNUSED_RETURN static bool getMaxObjectStoreId(DBOrTransaction* db, int64_t databaseId, int64_t& maxObjectStoreId) -{ - const Vector maxObjectStoreIdKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::MaxObjectStoreId); - bool ok = getMaxObjectStoreId(db, maxObjectStoreIdKey, maxObjectStoreId); - return ok; -} - -template -WARN_UNUSED_RETURN static bool getMaxObjectStoreId(DBOrTransaction* db, const Vector& maxObjectStoreIdKey, int64_t& maxObjectStoreId) -{ - maxObjectStoreId = -1; - bool found = false; - bool ok = getInt(db, maxObjectStoreIdKey, maxObjectStoreId, found); - if (!ok) - return false; - if (!found) - maxObjectStoreId = 0; - - ASSERT(maxObjectStoreId >= 0); - return true; -} - -class DefaultLevelDBFactory : public LevelDBFactory { -public: - virtual PassOwnPtr openLevelDB(const String& fileName, const LevelDBComparator* comparator) - { - return LevelDBDatabase::open(fileName, comparator); - } - virtual bool destroyLevelDB(const String& fileName) - { - return LevelDBDatabase::destroy(fileName); - } -}; - -IDBBackingStoreLevelDB::IDBBackingStoreLevelDB(const String& identifier, PassOwnPtr db, PassOwnPtr comparator) - : m_identifier(identifier) - , m_db(db) - , m_comparator(comparator) - , m_weakFactory(this) -{ -} - -IDBBackingStoreLevelDB::~IDBBackingStoreLevelDB() -{ - // m_db's destructor uses m_comparator. The order of destruction is important. - m_db.clear(); - m_comparator.clear(); -} - -enum IDBLevelDBBackingStoreOpenResult { - IDBLevelDBBackingStoreOpenMemorySuccess, - IDBLevelDBBackingStoreOpenSuccess, - IDBLevelDBBackingStoreOpenFailedDirectory, - IDBLevelDBBackingStoreOpenFailedUnknownSchema, - IDBLevelDBBackingStoreOpenCleanupDestroyFailed, - IDBLevelDBBackingStoreOpenCleanupReopenFailed, - IDBLevelDBBackingStoreOpenCleanupReopenSuccess, - IDBLevelDBBackingStoreOpenFailedIOErrCheckingSchema, - IDBLevelDBBackingStoreOpenFailedUnknownErr, - IDBLevelDBBackingStoreOpenMemoryFailed, - IDBLevelDBBackingStoreOpenAttemptNonASCII, - IDBLevelDBBackingStoreOpenMax, -}; - -PassRefPtr IDBBackingStoreLevelDB::open(const SecurityOrigin& securityOrigin, const String& pathBaseArg, const String& fileIdentifier) -{ - DefaultLevelDBFactory levelDBFactory; - return IDBBackingStoreLevelDB::open(securityOrigin, pathBaseArg, fileIdentifier, &levelDBFactory); -} - -PassRefPtr IDBBackingStoreLevelDB::open(const SecurityOrigin& securityOrigin, const String& pathBaseArg, const String& fileIdentifier, LevelDBFactory* levelDBFactory) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::open"); - ASSERT(!pathBaseArg.isEmpty()); - String pathBase = pathBaseArg; - - OwnPtr comparator = adoptPtr(new Comparator()); - OwnPtr db; - - if (!pathBase.containsOnlyASCII()) - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenAttemptNonASCII, IDBLevelDBBackingStoreOpenMax); - if (!makeAllDirectories(pathBase)) { - LOG_ERROR("Unable to create IndexedDB database path %s", pathBase.utf8().data()); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedDirectory, IDBLevelDBBackingStoreOpenMax); - return PassRefPtr(); - } - - String path = pathByAppendingComponent(pathBase, securityOrigin.databaseIdentifier() + ".indexeddb.leveldb"); - - db = levelDBFactory->openLevelDB(path, comparator.get()); - if (db) { - bool known = false; - bool ok = isSchemaKnown(db.get(), known); - if (!ok) { - LOG_ERROR("IndexedDB had IO error checking schema, treating it as failure to open"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedIOErrCheckingSchema, IDBLevelDBBackingStoreOpenMax); - db.clear(); - } else if (!known) { - LOG_ERROR("IndexedDB backing store had unknown schema, treating it as failure to open"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedUnknownSchema, IDBLevelDBBackingStoreOpenMax); - db.clear(); - } - } - - if (db) - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenSuccess, IDBLevelDBBackingStoreOpenMax); - else { - LOG_ERROR("IndexedDB backing store open failed, attempting cleanup"); - bool success = levelDBFactory->destroyLevelDB(path); - if (!success) { - LOG_ERROR("IndexedDB backing store cleanup failed"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenCleanupDestroyFailed, IDBLevelDBBackingStoreOpenMax); - return PassRefPtr(); - } - - LOG_ERROR("IndexedDB backing store cleanup succeeded, reopening"); - db = levelDBFactory->openLevelDB(path, comparator.get()); - if (!db) { - LOG_ERROR("IndexedDB backing store reopen after recovery failed"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenCleanupReopenFailed, IDBLevelDBBackingStoreOpenMax); - return PassRefPtr(); - } - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenCleanupReopenSuccess, IDBLevelDBBackingStoreOpenMax); - } - - if (!db) { - ASSERT_NOT_REACHED(); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedUnknownErr, IDBLevelDBBackingStoreOpenMax); - return PassRefPtr(); - } - - return create(fileIdentifier, db.release(), comparator.release()); -} - -PassRefPtr IDBBackingStoreLevelDB::openInMemory(const String& identifier) -{ - DefaultLevelDBFactory levelDBFactory; - return IDBBackingStoreLevelDB::openInMemory(identifier, &levelDBFactory); -} - -PassRefPtr IDBBackingStoreLevelDB::openInMemory(const String& identifier, LevelDBFactory*) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::openInMemory"); - - OwnPtr comparator = adoptPtr(new Comparator()); - OwnPtr db = LevelDBDatabase::openInMemory(comparator.get()); - if (!db) { - LOG_ERROR("LevelDBDatabase::openInMemory failed."); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenMemoryFailed, IDBLevelDBBackingStoreOpenMax); - return PassRefPtr(); - } - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenMemorySuccess, IDBLevelDBBackingStoreOpenMax); - - return create(identifier, db.release(), comparator.release()); -} - -PassRefPtr IDBBackingStoreLevelDB::create(const String& identifier, PassOwnPtr db, PassOwnPtr comparator) -{ - // FIXME: Handle comparator name changes. - RefPtr backingStore(adoptRef(new IDBBackingStoreLevelDB(identifier, db, comparator))); - - if (!setUpMetadata(backingStore->m_db.get(), identifier)) - return PassRefPtr(); - - return backingStore.release(); -} - -Vector IDBBackingStoreLevelDB::getDatabaseNames() -{ - Vector foundNames; - const Vector startKey = DatabaseNameKey::encodeMinKeyForOrigin(m_identifier); - const Vector stopKey = DatabaseNameKey::encodeStopKeyForOrigin(m_identifier); - - ASSERT(foundNames.isEmpty()); - - OwnPtr it = m_db->createIterator(); - for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) { - const char* p = it->key().begin(); - const char* limit = it->key().end(); - - DatabaseNameKey databaseNameKey; - p = DatabaseNameKey::decode(p, limit, &databaseNameKey); - ASSERT(p); - - foundNames.append(databaseNameKey.databaseName()); - } - return foundNames; -} - -bool IDBBackingStoreLevelDB::getIDBDatabaseMetaData(const String& name, IDBDatabaseMetadata* metadata, bool& found) -{ - const Vector key = DatabaseNameKey::encode(m_identifier, name); - found = false; - - bool ok = getInt(m_db.get(), key, metadata->id, found); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - return false; - } - if (!found) - return true; - - // FIXME: The string version is no longer supported, so the levelDB ports should consider refactoring off of it. - String stringVersion; - ok = getString(m_db.get(), DatabaseMetaDataKey::encode(metadata->id, DatabaseMetaDataKey::UserVersion), stringVersion, found); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(GetIDBDatabaseMetaData); - return false; - } - - int64_t version; - ok = getVarInt(m_db.get(), DatabaseMetaDataKey::encode(metadata->id, DatabaseMetaDataKey::UserIntVersion), version, found); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(GetIDBDatabaseMetaData); - return false; - } - - // FIXME: The versioning semantics have changed since this original code was written, and what was once a negative number - // stored in the database is no longer a valid version. - if (version < 0) - version = 0; - metadata->version = version; - - ok = getMaxObjectStoreId(m_db.get(), metadata->id, metadata->maxObjectStoreId); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - return false; - } - - return true; -} - -void IDBBackingStoreLevelDB::getOrEstablishIDBDatabaseMetadata(const String& name, std::function metadataFunction) -{ - const Vector key = DatabaseNameKey::encode(m_identifier, name); - bool found = false; - - IDBDatabaseMetadata resultMetadata; - - bool ok = getInt(m_db.get(), key, resultMetadata.id, found); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - metadataFunction(resultMetadata, false); - return; - } - - if (!found) { - resultMetadata.name = name; - resultMetadata.version = IDBDatabaseMetadata::DefaultIntVersion; - - metadataFunction(resultMetadata, createIDBDatabaseMetaData(resultMetadata)); - return; - } - - int64_t version; - ok = getVarInt(m_db.get(), DatabaseMetaDataKey::encode(resultMetadata.id, DatabaseMetaDataKey::UserIntVersion), version, found); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - metadataFunction(resultMetadata, false); - return; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(GetIDBDatabaseMetaData); - metadataFunction(resultMetadata, false); - return; - } - - // FIXME: The versioning semantics have changed since this original code was written, and what was once a negative number - // stored in the database is no longer a valid version. - if (version < 0) - version = 0; - resultMetadata.version = version; - - ok = getMaxObjectStoreId(m_db.get(), resultMetadata.id, resultMetadata.maxObjectStoreId); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - metadataFunction(resultMetadata, false); - return; - } - - ok = getObjectStores(resultMetadata.id, &resultMetadata.objectStores); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - metadataFunction(resultMetadata, false); - return; - } - - metadataFunction(resultMetadata, true); -} - -WARN_UNUSED_RETURN static bool getNewDatabaseId(LevelDBDatabase* db, int64_t& newId) -{ - RefPtr transaction = LevelDBTransaction::create(db); - - newId = -1; - int64_t maxDatabaseId = -1; - bool found = false; - bool ok = getInt(transaction.get(), MaxDatabaseIdKey::encode(), maxDatabaseId, found); - if (!ok) { - INTERNAL_READ_ERROR(GetNewDatabaseId); - return false; - } - if (!found) - maxDatabaseId = 0; - - ASSERT(maxDatabaseId >= 0); - - int64_t databaseId = maxDatabaseId + 1; - putInt(transaction.get(), MaxDatabaseIdKey::encode(), databaseId); - if (!transaction->commit()) { - INTERNAL_WRITE_ERROR(GetNewDatabaseId); - return false; - } - newId = databaseId; - return true; -} - -// FIXME: LevelDB needs to support uint64_t as the version type. -bool IDBBackingStoreLevelDB::createIDBDatabaseMetaData(IDBDatabaseMetadata& metadata) -{ - bool ok = getNewDatabaseId(m_db.get(), metadata.id); - if (!ok) - return false; - ASSERT(metadata.id >= 0); - - RefPtr transaction = LevelDBTransaction::create(m_db.get()); - putInt(transaction.get(), DatabaseNameKey::encode(m_identifier, metadata.name), metadata.id); - putVarInt(transaction.get(), DatabaseMetaDataKey::encode(metadata.id, DatabaseMetaDataKey::UserIntVersion), metadata.version); - if (!transaction->commit()) { - INTERNAL_WRITE_ERROR(CreateIDBDatabaseMetaData); - return false; - } - return true; -} - -bool IDBBackingStoreLevelDB::updateIDBDatabaseVersion(IDBBackingStoreTransactionLevelDB& transaction, int64_t rowId, uint64_t version) -{ - if (version == IDBDatabaseMetadata::NoIntVersion) - version = IDBDatabaseMetadata::DefaultIntVersion; - putVarInt(IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction), DatabaseMetaDataKey::encode(rowId, DatabaseMetaDataKey::UserIntVersion), version); - return true; -} - -static void deleteRange(LevelDBTransaction* transaction, const Vector& begin, const Vector& end) -{ - OwnPtr it = transaction->createIterator(); - for (it->seek(begin); it->isValid() && compareKeys(it->key(), end) < 0; it->next()) - transaction->remove(it->key()); -} - -void IDBBackingStoreLevelDB::deleteDatabase(const String& name, std::function boolCallbackFunction) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteDatabase"); - OwnPtr transaction = LevelDBWriteOnlyTransaction::create(m_db.get()); - - IDBDatabaseMetadata metadata; - bool success = false; - bool ok = getIDBDatabaseMetaData(name, &metadata, success); - if (!ok) { - boolCallbackFunction(false); - return; - } - - if (!success) { - boolCallbackFunction(true); - return; - } - - const Vector startKey = DatabaseMetaDataKey::encode(metadata.id, DatabaseMetaDataKey::OriginName); - const Vector stopKey = DatabaseMetaDataKey::encode(metadata.id + 1, DatabaseMetaDataKey::OriginName); - OwnPtr it = m_db->createIterator(); - for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) - transaction->remove(it->key()); - - const Vector key = DatabaseNameKey::encode(m_identifier, name); - transaction->remove(key); - - if (!transaction->commit()) { - INTERNAL_WRITE_ERROR(DeleteDatabase); - boolCallbackFunction(false); - return; - } - boolCallbackFunction(true); -} - -static bool checkObjectStoreAndMetaDataType(const LevelDBIterator* it, const Vector& stopKey, int64_t objectStoreId, int64_t metaDataType) -{ - if (!it->isValid() || compareKeys(it->key(), stopKey) >= 0) - return false; - - ObjectStoreMetaDataKey metaDataKey; - const char* p = ObjectStoreMetaDataKey::decode(it->key().begin(), it->key().end(), &metaDataKey); - ASSERT_UNUSED(p, p); - if (metaDataKey.objectStoreId() != objectStoreId) - return false; - if (metaDataKey.metaDataType() != metaDataType) - return false; - return true; -} - -// FIXME: This should do some error handling rather than plowing ahead when bad data is encountered. -bool IDBBackingStoreLevelDB::getObjectStores(int64_t databaseId, IDBDatabaseMetadata::ObjectStoreMap* objectStores) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::getObjectStores"); - if (!KeyPrefix::isValidDatabaseId(databaseId)) - return false; - const Vector startKey = ObjectStoreMetaDataKey::encode(databaseId, 1, 0); - const Vector stopKey = ObjectStoreMetaDataKey::encodeMaxKey(databaseId); - - ASSERT(objectStores->isEmpty()); - - OwnPtr it = m_db->createIterator(); - it->seek(startKey); - while (it->isValid() && compareKeys(it->key(), stopKey) < 0) { - const char* p = it->key().begin(); - const char* limit = it->key().end(); - - ObjectStoreMetaDataKey metaDataKey; - p = ObjectStoreMetaDataKey::decode(p, limit, &metaDataKey); - ASSERT(p); - if (metaDataKey.metaDataType() != ObjectStoreMetaDataKey::Name) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - // Possible stale metadata, but don't fail the load. - it->next(); - continue; - } - - int64_t objectStoreId = metaDataKey.objectStoreId(); - - // FIXME: Do this by direct key lookup rather than iteration, to simplify. - String objectStoreName = decodeString(it->value().begin(), it->value().end()); - - it->next(); - if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::KeyPath)) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - IDBKeyPath keyPath = decodeIDBKeyPath(it->value().begin(), it->value().end()); - - it->next(); - if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::AutoIncrement)) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - bool autoIncrement = decodeBool(it->value().begin(), it->value().end()); - - it->next(); // Is evicatble. - if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::Evictable)) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - - it->next(); // Last version. - if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::LastVersion)) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - - it->next(); // Maximum index id allocated. - if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::MaxIndexId)) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - int64_t maxIndexId = decodeInt(it->value().begin(), it->value().end()); - - it->next(); // [optional] has key path (is not null) - if (checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::HasKeyPath)) { - bool hasKeyPath = decodeBool(it->value().begin(), it->value().end()); - // This check accounts for two layers of legacy coding: - // (1) Initially, hasKeyPath was added to distinguish null vs. string. - // (2) Later, null vs. string vs. array was stored in the keyPath itself. - // So this check is only relevant for string-type keyPaths. - if (!hasKeyPath && (keyPath.type() == IDBKeyPath::StringType && !keyPath.string().isEmpty())) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - if (!hasKeyPath) - keyPath = IDBKeyPath(); - it->next(); - } - - int64_t keyGeneratorCurrentNumber = -1; - if (checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber)) { - keyGeneratorCurrentNumber = decodeInt(it->value().begin(), it->value().end()); - // FIXME: Return keyGeneratorCurrentNumber, cache in object store, and write lazily to backing store. - // For now, just assert that if it was written it was valid. - ASSERT_UNUSED(keyGeneratorCurrentNumber, keyGeneratorCurrentNumber >= KeyGeneratorInitialNumber); - it->next(); - } - - IDBObjectStoreMetadata metadata(objectStoreName, objectStoreId, keyPath, autoIncrement, maxIndexId); - if (!getIndexes(databaseId, objectStoreId, &metadata.indexes)) - return false; - objectStores->set(objectStoreId, metadata); - } - return true; -} - -WARN_UNUSED_RETURN static bool setMaxObjectStoreId(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId) -{ - const Vector maxObjectStoreIdKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::MaxObjectStoreId); - int64_t maxObjectStoreId = -1; - bool ok = getMaxObjectStoreId(transaction, maxObjectStoreIdKey, maxObjectStoreId); - if (!ok) { - INTERNAL_READ_ERROR(SetMaxObjectStoreId); - return false; - } - - if (objectStoreId <= maxObjectStoreId) { - INTERNAL_CONSISTENCY_ERROR(SetMaxObjectStoreId); - return false; - } - putInt(transaction, maxObjectStoreIdKey, objectStoreId); - return true; -} - -bool IDBBackingStoreLevelDB::createObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const String& name, const IDBKeyPath& keyPath, bool autoIncrement) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::createObjectStore"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - if (!setMaxObjectStoreId(levelDBTransaction, databaseId, objectStoreId)) - return false; - - const Vector nameKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::Name); - const Vector keyPathKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyPath); - const Vector autoIncrementKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::AutoIncrement); - const Vector evictableKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::Evictable); - const Vector lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::LastVersion); - const Vector maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::MaxIndexId); - const Vector hasKeyPathKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::HasKeyPath); - const Vector keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber); - const Vector namesKey = ObjectStoreNamesKey::encode(databaseId, name); - - putString(levelDBTransaction, nameKey, name); - putIDBKeyPath(levelDBTransaction, keyPathKey, keyPath); - putInt(levelDBTransaction, autoIncrementKey, autoIncrement); - putInt(levelDBTransaction, evictableKey, false); - putInt(levelDBTransaction, lastVersionKey, 1); - putInt(levelDBTransaction, maxIndexIdKey, MinimumIndexId); - putBool(levelDBTransaction, hasKeyPathKey, !keyPath.isNull()); - putInt(levelDBTransaction, keyGeneratorCurrentNumberKey, KeyGeneratorInitialNumber); - putInt(levelDBTransaction, namesKey, objectStoreId); - return true; -} - -bool IDBBackingStoreLevelDB::deleteObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteObjectStore"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - String objectStoreName; - bool found = false; - bool ok = getString(levelDBTransaction, ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::Name), objectStoreName, found); - if (!ok) { - INTERNAL_READ_ERROR(DeleteObjectStore); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(DeleteObjectStore); - return false; - } - - deleteRange(levelDBTransaction, ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0), ObjectStoreMetaDataKey::encodeMaxKey(databaseId, objectStoreId)); - - levelDBTransaction->remove(ObjectStoreNamesKey::encode(databaseId, objectStoreName)); - - deleteRange(levelDBTransaction, IndexFreeListKey::encode(databaseId, objectStoreId, 0), IndexFreeListKey::encodeMaxKey(databaseId, objectStoreId)); - deleteRange(levelDBTransaction, IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0), IndexMetaDataKey::encodeMaxKey(databaseId, objectStoreId)); - - return clearObjectStore(transaction, databaseId, objectStoreId); -} - -bool IDBBackingStoreLevelDB::getRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, Vector& record) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::getRecord"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - const Vector leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key); - Vector data; - - record.clear(); - - bool found = false; - bool ok = levelDBTransaction->safeGet(leveldbKey, data, found); - if (!ok) { - INTERNAL_READ_ERROR(GetRecord); - return false; - } - if (!found) - return true; - - int64_t version; - const char* p = decodeVarInt(data.begin(), data.end(), version); - if (!p) { - INTERNAL_READ_ERROR(GetRecord); - return false; - } - - record.appendRange(p, static_cast(data.end())); - return true; -} - -WARN_UNUSED_RETURN static bool getNewVersionNumber(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t& newVersionNumber) -{ - const Vector lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::LastVersion); - - newVersionNumber = -1; - int64_t lastVersion = -1; - bool found = false; - bool ok = getInt(transaction, lastVersionKey, lastVersion, found); - if (!ok) { - INTERNAL_READ_ERROR(GetNewVersionNumber); - return false; - } - if (!found) - lastVersion = 0; - - ASSERT(lastVersion >= 0); - - int64_t version = lastVersion + 1; - putInt(transaction, lastVersionKey, version); - - ASSERT(version > lastVersion); // FIXME: Think about how we want to handle the overflow scenario. - - newVersionNumber = version; - return true; -} - -bool IDBBackingStoreLevelDB::putRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, PassRefPtr prpValue, IDBRecordIdentifier* recordIdentifier) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::putRecord"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - ASSERT(key.isValid()); - - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - int64_t version = -1; - bool ok = getNewVersionNumber(levelDBTransaction, databaseId, objectStoreId, version); - if (!ok) - return false; - ASSERT(version >= 0); - const Vector objectStoredataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key); - - Vector v; - v.appendVector(encodeVarInt(version)); - RefPtr value = prpValue; - ASSERT(value); - v.append(value->data(), value->size()); - - levelDBTransaction->put(objectStoredataKey, v); - - const Vector existsEntryKey = ExistsEntryKey::encode(databaseId, objectStoreId, key); - levelDBTransaction->put(existsEntryKey, encodeInt(version)); - - recordIdentifier->reset(encodeIDBKey(key), version); - return true; -} - -bool IDBBackingStoreLevelDB::clearObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::clearObjectStore"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - const Vector startKey = KeyPrefix(databaseId, objectStoreId).encode(); - const Vector stopKey = KeyPrefix(databaseId, objectStoreId + 1).encode(); - - deleteRange(levelDBTransaction, startKey, stopKey); - return true; -} - -bool IDBBackingStoreLevelDB::deleteRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBRecordIdentifier& recordIdentifier) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteRecord"); - - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - const Vector objectStoreDataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, recordIdentifier.encodedPrimaryKey()); - levelDBTransaction->remove(objectStoreDataKey); - - const Vector existsEntryKey = ExistsEntryKey::encode(databaseId, objectStoreId, recordIdentifier.encodedPrimaryKey()); - levelDBTransaction->remove(existsEntryKey); - return true; -} - - -bool IDBBackingStoreLevelDB::getKeyGeneratorCurrentNumber(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t& keyGeneratorCurrentNumber) -{ - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - const Vector keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber); - - keyGeneratorCurrentNumber = -1; - Vector data; - - bool found = false; - bool ok = levelDBTransaction->safeGet(keyGeneratorCurrentNumberKey, data, found); - if (!ok) { - INTERNAL_READ_ERROR(GetKeyGeneratorCurrentNumber); - return false; - } - if (found) - keyGeneratorCurrentNumber = decodeInt(data.begin(), data.end()); - else { - // Previously, the key generator state was not stored explicitly but derived from the - // maximum numeric key present in existing data. This violates the spec as the data may - // be cleared but the key generator state must be preserved. - const Vector startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey()); - const Vector stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey()); - - OwnPtr it = levelDBTransaction->createIterator(); - int64_t maxNumericKey = 0; - - for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) { - const char* p = it->key().begin(); - const char* limit = it->key().end(); - - ObjectStoreDataKey dataKey; - p = ObjectStoreDataKey::decode(p, limit, &dataKey); - ASSERT(p); - - if (dataKey.userKey()->type() == IDBKey::NumberType) { - int64_t n = static_cast(dataKey.userKey()->number()); - if (n > maxNumericKey) - maxNumericKey = n; - } - } - - keyGeneratorCurrentNumber = maxNumericKey + 1; - } - - return keyGeneratorCurrentNumber; -} - -bool IDBBackingStoreLevelDB::maybeUpdateKeyGeneratorCurrentNumber(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t newNumber, bool checkCurrent) -{ - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - if (checkCurrent) { - int64_t currentNumber; - bool ok = getKeyGeneratorCurrentNumber(transaction, databaseId, objectStoreId, currentNumber); - if (!ok) - return false; - if (newNumber <= currentNumber) - return true; - } - - const Vector keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber); - putInt(levelDBTransaction, keyGeneratorCurrentNumberKey, newNumber); - return true; -} - -bool IDBBackingStoreLevelDB::keyExistsInObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, RefPtr& foundIDBRecordIdentifier) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::keyExistsInObjectStore"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - bool found = false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - const Vector leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key); - Vector data; - - bool ok = levelDBTransaction->safeGet(leveldbKey, data, found); - if (!ok) { - INTERNAL_READ_ERROR(KeyExistsInObjectStore); - return false; - } - if (!found) - return true; - - int64_t version; - if (!decodeVarInt(data.begin(), data.end(), version)) - return false; - - foundIDBRecordIdentifier = IDBRecordIdentifier::create(encodeIDBKey(key), version); - return true; -} - -static bool checkIndexAndMetaDataKey(const LevelDBIterator* it, const Vector& stopKey, int64_t indexId, unsigned char metaDataType) -{ - if (!it->isValid() || compareKeys(it->key(), stopKey) >= 0) - return false; - - IndexMetaDataKey metaDataKey; - const char* p = IndexMetaDataKey::decode(it->key().begin(), it->key().end(), &metaDataKey); - ASSERT_UNUSED(p, p); - if (metaDataKey.indexId() != indexId) - return false; - if (metaDataKey.metaDataType() != metaDataType) - return false; - return true; -} - - -// FIXME: This should do some error handling rather than plowing ahead when bad data is encountered. -bool IDBBackingStoreLevelDB::getIndexes(int64_t databaseId, int64_t objectStoreId, IDBObjectStoreMetadata::IndexMap* indexes) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::getIndexes"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - const Vector startKey = IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0); - const Vector stopKey = IndexMetaDataKey::encode(databaseId, objectStoreId + 1, 0, 0); - - ASSERT(indexes->isEmpty()); - - OwnPtr it = m_db->createIterator(); - it->seek(startKey); - while (it->isValid() && compareKeys(it->key(), stopKey) < 0) { - const char* p = it->key().begin(); - const char* limit = it->key().end(); - - IndexMetaDataKey metaDataKey; - p = IndexMetaDataKey::decode(p, limit, &metaDataKey); - ASSERT(p); - if (metaDataKey.metaDataType() != IndexMetaDataKey::Name) { - INTERNAL_CONSISTENCY_ERROR(GetIndexes); - // Possible stale metadata due to http://webkit.org/b/85557 but don't fail the load. - it->next(); - continue; - } - - // FIXME: Do this by direct key lookup rather than iteration, to simplify. - int64_t indexId = metaDataKey.indexId(); - String indexName = decodeString(it->value().begin(), it->value().end()); - - it->next(); // unique flag - if (!checkIndexAndMetaDataKey(it.get(), stopKey, indexId, IndexMetaDataKey::Unique)) { - INTERNAL_CONSISTENCY_ERROR(GetIndexes); - break; - } - bool indexUnique = decodeBool(it->value().begin(), it->value().end()); - - it->next(); // keyPath - if (!checkIndexAndMetaDataKey(it.get(), stopKey, indexId, IndexMetaDataKey::KeyPath)) { - INTERNAL_CONSISTENCY_ERROR(GetIndexes); - break; - } - IDBKeyPath keyPath = decodeIDBKeyPath(it->value().begin(), it->value().end()); - - it->next(); // [optional] multiEntry flag - bool indexMultiEntry = false; - if (checkIndexAndMetaDataKey(it.get(), stopKey, indexId, IndexMetaDataKey::MultiEntry)) { - indexMultiEntry = decodeBool(it->value().begin(), it->value().end()); - it->next(); - } - - indexes->set(indexId, IDBIndexMetadata(indexName, indexId, keyPath, indexUnique, indexMultiEntry)); - } - return true; -} - -WARN_UNUSED_RETURN static bool setMaxIndexId(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId) -{ - int64_t maxIndexId = -1; - const Vector maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::MaxIndexId); - bool found = false; - bool ok = getInt(transaction, maxIndexIdKey, maxIndexId, found); - if (!ok) { - INTERNAL_READ_ERROR(SetMaxIndexId); - return false; - } - if (!found) - maxIndexId = MinimumIndexId; - - if (indexId <= maxIndexId) { - INTERNAL_CONSISTENCY_ERROR(SetMaxIndexId); - return false; - } - - putInt(transaction, maxIndexIdKey, indexId); - return true; -} - -bool IDBBackingStoreLevelDB::createIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath& keyPath, bool isUnique, bool isMultiEntry) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::createIndex"); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - if (!setMaxIndexId(levelDBTransaction, databaseId, objectStoreId, indexId)) - return false; - - const Vector nameKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::Name); - const Vector uniqueKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::Unique); - const Vector keyPathKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::KeyPath); - const Vector multiEntryKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::MultiEntry); - - putString(levelDBTransaction, nameKey, name); - putBool(levelDBTransaction, uniqueKey, isUnique); - putIDBKeyPath(levelDBTransaction, keyPathKey, keyPath); - putBool(levelDBTransaction, multiEntryKey, isMultiEntry); - return true; -} - -bool IDBBackingStoreLevelDB::deleteIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteIndex"); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - const Vector indexMetaDataStart = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 0); - const Vector indexMetaDataEnd = IndexMetaDataKey::encodeMaxKey(databaseId, objectStoreId, indexId); - deleteRange(levelDBTransaction, indexMetaDataStart, indexMetaDataEnd); - - const Vector indexDataStart = IndexDataKey::encodeMinKey(databaseId, objectStoreId, indexId); - const Vector indexDataEnd = IndexDataKey::encodeMaxKey(databaseId, objectStoreId, indexId); - deleteRange(levelDBTransaction, indexDataStart, indexDataEnd); - return true; -} - -bool IDBBackingStoreLevelDB::putIndexDataForRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, const IDBRecordIdentifier* recordIdentifier) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::putIndexDataForRecord"); - ASSERT(key.isValid()); - ASSERT(recordIdentifier); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - const Vector indexDataKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, encodeIDBKey(key), recordIdentifier->encodedPrimaryKey()); - - Vector data; - data.appendVector(encodeVarInt(recordIdentifier->version())); - data.appendVector(recordIdentifier->encodedPrimaryKey()); - - levelDBTransaction->put(indexDataKey, data); - return true; -} - -static bool findGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction, const Vector& target, Vector& foundKey) -{ - OwnPtr it = transaction->createIterator(); - it->seek(target); - - if (!it->isValid()) { - it->seekToLast(); - if (!it->isValid()) - return false; - } - - while (IDBBackingStoreLevelDB::compareIndexKeys(it->key(), target) > 0) { - it->prev(); - if (!it->isValid()) - return false; - } - - do { - foundKey.clear(); - foundKey.append(it->key().begin(), it->key().end() - it->key().begin()); - - // There can be several index keys that compare equal. We want the last one. - it->next(); - } while (it->isValid() && !IDBBackingStoreLevelDB::compareIndexKeys(it->key(), target)); - - return true; -} - -static bool versionExists(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t version, const Vector& encodedPrimaryKey, bool& exists) -{ - const Vector key = ExistsEntryKey::encode(databaseId, objectStoreId, encodedPrimaryKey); - Vector data; - - bool ok = transaction->safeGet(key, data, exists); - if (!ok) { - INTERNAL_READ_ERROR(VersionExists); - return false; - } - if (!exists) - return true; - - exists = (decodeInt(data.begin(), data.end()) == version); - return true; -} - -bool IDBBackingStoreLevelDB::findKeyInIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, Vector& foundEncodedPrimaryKey, bool& found) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::findKeyInIndex"); - ASSERT(KeyPrefix::validIds(databaseId, objectStoreId, indexId)); - - ASSERT(foundEncodedPrimaryKey.isEmpty()); - found = false; - - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - const Vector leveldbKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key); - OwnPtr it = levelDBTransaction->createIterator(); - it->seek(leveldbKey); - - for (;;) { - if (!it->isValid()) - return true; - if (compareIndexKeys(it->key(), leveldbKey) > 0) - return true; - - int64_t version; - const char* p = decodeVarInt(it->value().begin(), it->value().end(), version); - if (!p) { - INTERNAL_READ_ERROR(FindKeyInIndex); - return false; - } - foundEncodedPrimaryKey.append(p, it->value().end() - p); - - bool exists = false; - bool ok = versionExists(levelDBTransaction, databaseId, objectStoreId, version, foundEncodedPrimaryKey, exists); - if (!ok) - return false; - if (!exists) { - // Delete stale index data entry and continue. - levelDBTransaction->remove(it->key()); - it->next(); - continue; - } - found = true; - return true; - } -} - -bool IDBBackingStoreLevelDB::getPrimaryKeyViaIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, RefPtr& primaryKey) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::getPrimaryKeyViaIndex"); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - - bool found = false; - Vector foundEncodedPrimaryKey; - bool ok = findKeyInIndex(transaction, databaseId, objectStoreId, indexId, key, foundEncodedPrimaryKey, found); - if (!ok) { - INTERNAL_READ_ERROR(GetPrimaryKeyViaIndex); - return false; - } - if (found) { - decodeIDBKey(foundEncodedPrimaryKey.begin(), foundEncodedPrimaryKey.end(), primaryKey); - return true; - } - - return true; -} - -bool IDBBackingStoreLevelDB::keyExistsInIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& indexKey, RefPtr& foundPrimaryKey, bool& exists) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::keyExistsInIndex"); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - - exists = false; - Vector foundEncodedPrimaryKey; - bool ok = findKeyInIndex(transaction, databaseId, objectStoreId, indexId, indexKey, foundEncodedPrimaryKey, exists); - if (!ok) { - INTERNAL_READ_ERROR(KeyExistsInIndex); - return false; - } - if (!exists) - return true; - - decodeIDBKey(foundEncodedPrimaryKey.begin(), foundEncodedPrimaryKey.end(), foundPrimaryKey); - return true; -} - - -bool IDBBackingStoreLevelDB::makeIndexWriters(int64_t transactionID, int64_t databaseID, const IDBObjectStoreMetadata& objectStore, IDBKey& primaryKey, bool keyWasGenerated, const Vector& indexIDs, const Vector>>& indexKeys, Vector>& indexWriters, String* errorMessage, bool& completed) -{ - ASSERT(indexIDs.size() == indexKeys.size()); - completed = false; - - HashMap indexKeyMap; - for (size_t i = 0; i < indexIDs.size(); ++i) - indexKeyMap.add(indexIDs[i], indexKeys[i]); - - for (IDBObjectStoreMetadata::IndexMap::const_iterator it = objectStore.indexes.begin(); it != objectStore.indexes.end(); ++it) { - const IDBIndexMetadata& index = it->value; - - IndexKeys keys = indexKeyMap.get(it->key); - // If the objectStore is using autoIncrement, then any indexes with an identical keyPath need to also use the primary (generated) key as a key. - if (keyWasGenerated && (index.keyPath == objectStore.keyPath)) - keys.append(&primaryKey); - - RefPtr indexWriter = IDBIndexWriterLevelDB::create(index, keys); - bool canAddKeys = false; - ASSERT(m_backingStoreTransactions.contains(transactionID)); - bool backingStoreSuccess = indexWriter->verifyIndexKeys(*this, *m_backingStoreTransactions.get(transactionID), databaseID, objectStore.id, index.id, canAddKeys, &primaryKey, errorMessage); - if (!backingStoreSuccess) - return false; - if (!canAddKeys) - return true; - - indexWriters.append(indexWriter.release()); - } - - completed = true; - return true; -} - -PassRefPtr IDBBackingStoreLevelDB::generateKey(IDBTransactionBackend& transaction, int64_t databaseId, int64_t objectStoreId) -{ - const int64_t maxGeneratorValue = 9007199254740992LL; // Maximum integer storable as ECMAScript number. - int64_t currentNumber; - ASSERT(m_backingStoreTransactions.contains(transaction.id())); - bool ok = getKeyGeneratorCurrentNumber(*m_backingStoreTransactions.get(transaction.id()), databaseId, objectStoreId, currentNumber); - if (!ok) { - LOG_ERROR("Failed to getKeyGeneratorCurrentNumber"); - return IDBKey::createInvalid(); - } - if (currentNumber < 0 || currentNumber > maxGeneratorValue) - return IDBKey::createInvalid(); - - return IDBKey::createNumber(currentNumber); -} - - -bool IDBBackingStoreLevelDB::updateKeyGenerator(IDBTransactionBackend& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, bool checkCurrent) -{ - ASSERT(key.type() == IDBKey::NumberType); - ASSERT(m_backingStoreTransactions.contains(transaction.id())); - - return maybeUpdateKeyGeneratorCurrentNumber(*m_backingStoreTransactions.get(transaction.id()), databaseId, objectStoreId, static_cast(floor(key.number())) + 1, checkCurrent); -} - -class ObjectStoreKeyCursorImpl : public IDBBackingStoreCursorLevelDB { -public: - static PassRefPtr create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - { - return adoptRef(new ObjectStoreKeyCursorImpl(cursorID, transaction, cursorOptions)); - } - - virtual PassRefPtr clone() - { - return adoptRef(new ObjectStoreKeyCursorImpl(this)); - } - - // IDBBackingStoreCursorLevelDB - virtual PassRefPtr value() const override { ASSERT_NOT_REACHED(); return 0; } - virtual bool loadCurrentRow() override; - -protected: - virtual Vector encodeKey(const IDBKey &key) - { - return ObjectStoreDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, key); - } - -private: - ObjectStoreKeyCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions) - { - } - - ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other) - : IDBBackingStoreCursorLevelDB(other) - { - } -}; - -bool ObjectStoreKeyCursorImpl::loadCurrentRow() -{ - const char* keyPosition = m_iterator->key().begin(); - const char* keyLimit = m_iterator->key().end(); - - ObjectStoreDataKey objectStoreDataKey; - keyPosition = ObjectStoreDataKey::decode(keyPosition, keyLimit, &objectStoreDataKey); - if (!keyPosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - m_currentKey = objectStoreDataKey.userKey(); - - int64_t version; - const char* valuePosition = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), version); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - // FIXME: This re-encodes what was just decoded; try and optimize. - m_recordIdentifier->reset(encodeIDBKey(*m_currentKey), version); - - return true; -} - -class ObjectStoreCursorImpl : public IDBBackingStoreCursorLevelDB { -public: - static PassRefPtr create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - { - return adoptRef(new ObjectStoreCursorImpl(cursorID, transaction, cursorOptions)); - } - - virtual PassRefPtr clone() - { - return adoptRef(new ObjectStoreCursorImpl(this)); - } - - // IDBBackingStoreCursorLevelDB - virtual PassRefPtr value() const override { return m_currentValue; } - virtual bool loadCurrentRow() override; - -protected: - virtual Vector encodeKey(const IDBKey &key) - { - return ObjectStoreDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, key); - } - -private: - ObjectStoreCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions) - { - } - - ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other) - : IDBBackingStoreCursorLevelDB(other) - , m_currentValue(other->m_currentValue) - { - } - - RefPtr m_currentValue; -}; - -bool ObjectStoreCursorImpl::loadCurrentRow() -{ - const char* keyPosition = m_iterator->key().begin(); - const char* keyLimit = m_iterator->key().end(); - - ObjectStoreDataKey objectStoreDataKey; - keyPosition = ObjectStoreDataKey::decode(keyPosition, keyLimit, &objectStoreDataKey); - if (!keyPosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - m_currentKey = objectStoreDataKey.userKey(); - - int64_t version; - const char* valuePosition = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), version); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - // FIXME: This re-encodes what was just decoded; try and optimize. - m_recordIdentifier->reset(encodeIDBKey(*m_currentKey), version); - - Vector value; - value.append(valuePosition, m_iterator->value().end() - valuePosition); - m_currentValue = SharedBuffer::adoptVector(value); - return true; -} - -class IndexKeyCursorImpl final : public IDBBackingStoreCursorLevelDB { -public: - static PassRefPtr create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - { - return adoptRef(new IndexKeyCursorImpl(cursorID, transaction, cursorOptions)); - } - - virtual PassRefPtr clone() - { - return adoptRef(new IndexKeyCursorImpl(this)); - } - - // IDBBackingStoreCursorLevelDB - virtual PassRefPtr value() const override { ASSERT_NOT_REACHED(); return 0; } - virtual PassRefPtr primaryKey() const override { return m_primaryKey; } - virtual const IDBRecordIdentifier& recordIdentifier() const override { ASSERT_NOT_REACHED(); return *m_recordIdentifier; } - virtual bool loadCurrentRow() override; - -protected: - virtual Vector encodeKey(const IDBKey &key) - { - return IndexDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, m_cursorOptions.indexId, key); - } - -private: - IndexKeyCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions) - { - } - - IndexKeyCursorImpl(const IndexKeyCursorImpl* other) - : IDBBackingStoreCursorLevelDB(other) - , m_primaryKey(other->m_primaryKey) - { - } - - RefPtr m_primaryKey; -}; - -bool IndexKeyCursorImpl::loadCurrentRow() -{ - const char* keyPosition = m_iterator->key().begin(); - const char* keyLimit = m_iterator->key().end(); - - IndexDataKey indexDataKey; - keyPosition = IndexDataKey::decode(keyPosition, keyLimit, &indexDataKey); - - m_currentKey = indexDataKey.userKey(); - - int64_t indexDataVersion; - const char* valuePosition = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), indexDataVersion); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - valuePosition = decodeIDBKey(valuePosition, m_iterator->value().end(), m_primaryKey); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - Vector primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey); - - Vector result; - bool found = false; - bool ok = m_transaction->safeGet(primaryLevelDBKey, result, found); - if (!ok) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - if (!found) { - m_transaction->remove(m_iterator->key()); - return false; - } - - int64_t objectStoreDataVersion; - const char* t = decodeVarInt(result.begin(), result.end(), objectStoreDataVersion); - if (!t) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - if (objectStoreDataVersion != indexDataVersion) { - m_transaction->remove(m_iterator->key()); - return false; - } - - return true; -} - -class IndexCursorImpl final : public IDBBackingStoreCursorLevelDB { -public: - static PassRefPtr create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - { - return adoptRef(new IndexCursorImpl(cursorID, transaction, cursorOptions)); - } - - virtual PassRefPtr clone() - { - return adoptRef(new IndexCursorImpl(this)); - } - - // IDBBackingStoreCursorLevelDB - virtual PassRefPtr value() const override { return m_currentValue; } - virtual PassRefPtr primaryKey() const override { return m_primaryKey; } - virtual const IDBRecordIdentifier& recordIdentifier() const override { ASSERT_NOT_REACHED(); return *m_recordIdentifier; } - virtual bool loadCurrentRow() override; - -protected: - virtual Vector encodeKey(const IDBKey &key) - { - return IndexDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, m_cursorOptions.indexId, key); - } - -private: - IndexCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions) - { - } - - IndexCursorImpl(const IndexCursorImpl* other) - : IDBBackingStoreCursorLevelDB(other) - , m_primaryKey(other->m_primaryKey) - , m_currentValue(other->m_currentValue) - , m_primaryLevelDBKey(other->m_primaryLevelDBKey) - { - } - - RefPtr m_primaryKey; - RefPtr m_currentValue; - Vector m_primaryLevelDBKey; -}; - -bool IndexCursorImpl::loadCurrentRow() -{ - const char* keyPosition = m_iterator->key().begin(); - const char* keyLimit = m_iterator->key().end(); - - IndexDataKey indexDataKey; - keyPosition = IndexDataKey::decode(keyPosition, keyLimit, &indexDataKey); - - m_currentKey = indexDataKey.userKey(); - - const char* valuePosition = m_iterator->value().begin(); - const char* valueLimit = m_iterator->value().end(); - - int64_t indexDataVersion; - valuePosition = decodeVarInt(valuePosition, valueLimit, indexDataVersion); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - valuePosition = decodeIDBKey(valuePosition, valueLimit, m_primaryKey); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - m_primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey); - - Vector result; - bool found = false; - bool ok = m_transaction->safeGet(m_primaryLevelDBKey, result, found); - if (!ok) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - if (!found) { - m_transaction->remove(m_iterator->key()); - return false; - } - - int64_t objectStoreDataVersion; - valuePosition = decodeVarInt(result.begin(), result.end(), objectStoreDataVersion); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - if (objectStoreDataVersion != indexDataVersion) { - m_transaction->remove(m_iterator->key()); - return false; - } - - Vector value; - value.append(valuePosition, result.end() - valuePosition); - m_currentValue = SharedBuffer::adoptVector(value); - return true; -} - -static bool objectStoreCursorOptions(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, const IDBKeyRange* range, IndexedDB::CursorDirection direction, IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) -{ - cursorOptions.databaseId = databaseId; - cursorOptions.objectStoreId = objectStoreId; - - bool lowerBound = range && range->lower(); - bool upperBound = range && range->upper(); - cursorOptions.forward = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::Next); - cursorOptions.unique = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::PrevNoDuplicate); - - if (!lowerBound) { - cursorOptions.lowKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey()); - cursorOptions.lowOpen = true; // Not included. - } else { - cursorOptions.lowKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, *range->lower()); - cursorOptions.lowOpen = range->lowerOpen(); - } - - if (!upperBound) { - cursorOptions.highKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey()); - - if (cursorOptions.forward) - cursorOptions.highOpen = true; // Not included. - else { - // We need a key that exists. - if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, cursorOptions.highKey)) - return false; - cursorOptions.highOpen = false; - } - } else { - cursorOptions.highKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, *range->upper()); - cursorOptions.highOpen = range->upperOpen(); - - if (!cursorOptions.forward) { - // For reverse cursors, we need a key that exists. - Vector foundHighKey; - if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, foundHighKey)) - return false; - - // If the target key should not be included, but we end up with a smaller key, we should include that. - if (cursorOptions.highOpen && IDBBackingStoreLevelDB::compareIndexKeys(foundHighKey, cursorOptions.highKey) < 0) - cursorOptions.highOpen = false; - - cursorOptions.highKey = foundHighKey; - } - } - - return true; -} - -static bool indexCursorOptions(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IndexedDB::CursorDirection direction, IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) -{ - ASSERT(transaction); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - - cursorOptions.databaseId = databaseId; - cursorOptions.objectStoreId = objectStoreId; - cursorOptions.indexId = indexId; - - bool lowerBound = range && range->lower(); - bool upperBound = range && range->upper(); - cursorOptions.forward = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::Next); - cursorOptions.unique = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::PrevNoDuplicate); - - if (!lowerBound) { - cursorOptions.lowKey = IndexDataKey::encodeMinKey(databaseId, objectStoreId, indexId); - cursorOptions.lowOpen = false; // Included. - } else { - cursorOptions.lowKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->lower()); - cursorOptions.lowOpen = range->lowerOpen(); - } - - if (!upperBound) { - cursorOptions.highKey = IndexDataKey::encodeMaxKey(databaseId, objectStoreId, indexId); - cursorOptions.highOpen = false; // Included. - - if (!cursorOptions.forward) { // We need a key that exists. - if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, cursorOptions.highKey)) - return false; - cursorOptions.highOpen = false; - } - } else { - cursorOptions.highKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->upper()); - cursorOptions.highOpen = range->upperOpen(); - - Vector foundHighKey; - if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, foundHighKey)) // Seek to the *last* key in the set of non-unique keys. - return false; - - // If the target key should not be included, but we end up with a smaller key, we should include that. - if (cursorOptions.highOpen && IDBBackingStoreLevelDB::compareIndexKeys(foundHighKey, cursorOptions.highKey) < 0) - cursorOptions.highOpen = false; - - cursorOptions.highKey = foundHighKey; - } - - return true; -} -PassRefPtr IDBBackingStoreLevelDB::openObjectStoreCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange* range, IndexedDB::CursorDirection direction) - -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::openObjectStoreCursor"); - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions; - if (!objectStoreCursorOptions(levelDBTransaction, databaseID, objectStoreId, range, direction, cursorOptions)) - return 0; - RefPtr cursor = ObjectStoreCursorImpl::create(cursorID, levelDBTransaction, cursorOptions); - if (!cursor->firstSeek()) - return 0; - - return cursor.release(); -} - -PassRefPtr IDBBackingStoreLevelDB::openObjectStoreKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange* range, IndexedDB::CursorDirection direction) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::openObjectStoreKeyCursor"); - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions; - if (!objectStoreCursorOptions(levelDBTransaction, databaseID, objectStoreId, range, direction, cursorOptions)) - return 0; - RefPtr cursor = ObjectStoreKeyCursorImpl::create(cursorID, levelDBTransaction, cursorOptions); - if (!cursor->firstSeek()) - return 0; - - return cursor.release(); -} - -PassRefPtr IDBBackingStoreLevelDB::openIndexKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IndexedDB::CursorDirection direction) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::openIndexKeyCursor"); - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions; - if (!indexCursorOptions(levelDBTransaction, databaseID, objectStoreId, indexId, range, direction, cursorOptions)) - return 0; - RefPtr cursor = IndexKeyCursorImpl::create(cursorID, levelDBTransaction, cursorOptions); - if (!cursor->firstSeek()) - return 0; - - return cursor.release(); -} - -PassRefPtr IDBBackingStoreLevelDB::openIndexCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IndexedDB::CursorDirection direction) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::openIndexCursor"); - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions; - if (!indexCursorOptions(levelDBTransaction, databaseID, objectStoreId, indexId, range, direction, cursorOptions)) - return 0; - RefPtr cursor = IndexCursorImpl::create(cursorID, levelDBTransaction, cursorOptions); - if (!cursor->firstSeek()) - return 0; - - return cursor.release(); -} - -void IDBBackingStoreLevelDB::establishBackingStoreTransaction(int64_t transactionID) -{ - ASSERT(!m_backingStoreTransactions.contains(transactionID)); - m_backingStoreTransactions.set(transactionID, IDBBackingStoreTransactionLevelDB::create(transactionID, this)); -} - -void IDBBackingStoreLevelDB::removeBackingStoreTransaction(IDBBackingStoreTransactionLevelDB* transaction) -{ - ASSERT(m_backingStoreTransactions.contains(transaction->transactionID())); - m_backingStoreTransactions.remove(transaction->transactionID()); -} - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB) diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h deleted file mode 100644 index 9ef75e45c..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBBackingStoreLevelDB_h -#define IDBBackingStoreLevelDB_h - -#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -#include "IDBBackingStoreCursorLevelDB.h" -#include "IDBBackingStoreTransactionLevelDB.h" -#include "IDBDatabaseMetadata.h" -#include "IDBKey.h" -#include "IDBRecordIdentifier.h" -#include "IndexedDB.h" -#include "LevelDBIterator.h" -#include "LevelDBTransaction.h" -#include -#include -#include -#include - -namespace WebCore { - -class LevelDBComparator; -class LevelDBDatabase; -class LevelDBTransaction; -class IDBIndexWriterLevelDB; -class IDBKey; -class IDBKeyRange; -class IDBTransactionBackend; -class SecurityOrigin; -class SharedBuffer; - -class LevelDBFactory { -public: - virtual ~LevelDBFactory() { } - virtual PassOwnPtr openLevelDB(const String& fileName, const LevelDBComparator*) = 0; - virtual bool destroyLevelDB(const String& fileName) = 0; -}; - -class IDBBackingStoreLevelDB : public RefCounted { -public: - class Transaction; - - virtual ~IDBBackingStoreLevelDB(); - static PassRefPtr open(const SecurityOrigin&, const String& pathBase, const String& fileIdentifier); - static PassRefPtr open(const SecurityOrigin&, const String& pathBase, const String& fileIdentifier, LevelDBFactory*); - static PassRefPtr openInMemory(const String& identifier); - static PassRefPtr openInMemory(const String& identifier, LevelDBFactory*); - WeakPtr createWeakPtr() { return m_weakFactory.createWeakPtr(); } - - void establishBackingStoreTransaction(int64_t transactionID); - - Vector getDatabaseNames(); - - // New-style asynchronous callbacks - void getOrEstablishIDBDatabaseMetadata(const String& name, std::function callbackFunction); - void deleteDatabase(const String& name, std::function successCallback); - - // Old-style synchronous callbacks - bool updateIDBDatabaseVersion(IDBBackingStoreTransactionLevelDB&, int64_t rowId, uint64_t version); - - bool createObjectStore(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, const String& name, const IDBKeyPath&, bool autoIncrement); - bool deleteObjectStore(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId) WARN_UNUSED_RETURN; - - bool getRecord(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, const IDBKey&, Vector& record) WARN_UNUSED_RETURN; - bool putRecord(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, const IDBKey&, PassRefPtr value, IDBRecordIdentifier*) WARN_UNUSED_RETURN; - bool clearObjectStore(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId) WARN_UNUSED_RETURN; - bool deleteRecord(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, const IDBRecordIdentifier&) WARN_UNUSED_RETURN; - bool getKeyGeneratorCurrentNumber(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t& currentNumber) WARN_UNUSED_RETURN; - bool maybeUpdateKeyGeneratorCurrentNumber(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t newState, bool checkCurrent) WARN_UNUSED_RETURN; - bool keyExistsInObjectStore(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, const IDBKey&, RefPtr& foundIDBRecordIdentifier) WARN_UNUSED_RETURN; - - bool createIndex(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath&, bool isUnique, bool isMultiEntry) WARN_UNUSED_RETURN; - bool deleteIndex(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId) WARN_UNUSED_RETURN; - bool putIndexDataForRecord(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const IDBRecordIdentifier*) WARN_UNUSED_RETURN; - bool getPrimaryKeyViaIndex(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, RefPtr& primaryKey) WARN_UNUSED_RETURN; - bool keyExistsInIndex(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& indexKey, RefPtr& foundPrimaryKey, bool& exists) WARN_UNUSED_RETURN; - - virtual PassRefPtr openObjectStoreKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB&, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange*, IndexedDB::CursorDirection); - virtual PassRefPtr openObjectStoreCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB&, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange*, IndexedDB::CursorDirection); - virtual PassRefPtr openIndexKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB&, int64_t databaseID, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IndexedDB::CursorDirection); - virtual PassRefPtr openIndexCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB&, int64_t databaseID, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IndexedDB::CursorDirection); - - bool makeIndexWriters(int64_t transactionID, int64_t databaseId, const IDBObjectStoreMetadata&, IDBKey& primaryKey, bool keyWasGenerated, const Vector& indexIds, const Vector>>&, Vector>& indexWriters, String* errorMessage, bool& completed) WARN_UNUSED_RETURN; - - virtual PassRefPtr generateKey(IDBTransactionBackend&, int64_t databaseId, int64_t objectStoreId); - bool updateKeyGenerator(IDBTransactionBackend&, int64_t databaseId, int64_t objectStoreId, const IDBKey&, bool checkCurrent); - - LevelDBDatabase* levelDBDatabase() { return m_db.get(); } - - static int compareIndexKeys(const LevelDBSlice&, const LevelDBSlice&); - - void removeBackingStoreTransaction(IDBBackingStoreTransactionLevelDB*); - -private: - IDBBackingStoreLevelDB(const String& identifier, PassOwnPtr, PassOwnPtr); - static PassRefPtr create(const String& identifier, PassOwnPtr, PassOwnPtr); - - // FIXME: LevelDB needs to support uint64_t as the version type. - bool createIDBDatabaseMetaData(IDBDatabaseMetadata&); - bool getIDBDatabaseMetaData(const String& name, IDBDatabaseMetadata*, bool& success) WARN_UNUSED_RETURN; - bool getObjectStores(int64_t databaseId, IDBDatabaseMetadata::ObjectStoreMap* objectStores) WARN_UNUSED_RETURN; - - bool findKeyInIndex(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, Vector& foundEncodedPrimaryKey, bool& found); - bool getIndexes(int64_t databaseId, int64_t objectStoreId, IDBObjectStoreMetadata::IndexMap*) WARN_UNUSED_RETURN; - - String m_identifier; - - OwnPtr m_db; - OwnPtr m_comparator; - WeakPtrFactory m_weakFactory; - - HashMap> m_backingStoreTransactions; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -#endif // IDBBackingStoreLevelDB_h diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.cpp deleted file mode 100644 index a7a5f66fa..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBBackingStoreTransactionLevelDB.h" - -#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -#include "HistogramSupport.h" -#include "IDBBackingStoreLevelDB.h" -#include "Logging.h" -#include - -namespace WebCore { - -IDBBackingStoreTransactionLevelDB::IDBBackingStoreTransactionLevelDB(int64_t transactionID, IDBBackingStoreLevelDB* backingStore) - : m_transactionID(transactionID) - , m_backingStore(backingStore) -{ -} - -IDBBackingStoreTransactionLevelDB::~IDBBackingStoreTransactionLevelDB() -{ - if (m_backingStore) - m_backingStore->removeBackingStoreTransaction(this); -} - -void IDBBackingStoreTransactionLevelDB::begin() -{ - LOG(StorageAPI, "IDBBackingStoreTransactionLevelDB::begin"); - ASSERT(!m_transaction); - m_transaction = LevelDBTransaction::create(reinterpret_cast(m_backingStore)->levelDBDatabase()); -} - -bool IDBBackingStoreTransactionLevelDB::commit() -{ - LOG(StorageAPI, "IDBBackingStoreTransactionLevelDB::commit"); - ASSERT(m_transaction); - bool result = m_transaction->commit(); - m_transaction.clear(); - if (!result) - LOG_ERROR("IndexedDB TransactionCommit Error"); - return result; -} - -void IDBBackingStoreTransactionLevelDB::rollback() -{ - LOG(StorageAPI, "IDBBackingStoreTransactionLevelDB::rollback"); - ASSERT(m_transaction); - m_transaction->rollback(); - m_transaction.clear(); -} - -void IDBBackingStoreTransactionLevelDB::resetTransaction() -{ - ASSERT(m_backingStore); - m_backingStore->removeBackingStoreTransaction(this); - m_backingStore = 0; - m_transaction = 0; -} - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB) diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.h deleted file mode 100644 index c7f7bc494..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBBackingStoreTransactionLevelDB_h -#define IDBBackingStoreTransactionLevelDB_h - -#include "LevelDBTransaction.h" - -#if ENABLE(INDEXED_DATABASE) -#if USE(LEVELDB) - -namespace WebCore { - -class IDBBackingStoreLevelDB; - -class IDBBackingStoreTransactionLevelDB : public RefCounted { -public: - static PassRefPtr create(int64_t transactionID, IDBBackingStoreLevelDB* backingStore) - { - return adoptRef(new IDBBackingStoreTransactionLevelDB(transactionID, backingStore)); - } - - ~IDBBackingStoreTransactionLevelDB(); - - void begin(); - bool commit(); - void rollback(); - void resetTransaction(); - - static LevelDBTransaction* levelDBTransactionFrom(IDBBackingStoreTransactionLevelDB& transaction) - { - return static_cast(transaction).m_transaction.get(); - } - - int64_t transactionID() { return m_transactionID; } - -private: - IDBBackingStoreTransactionLevelDB(int64_t transactionID, IDBBackingStoreLevelDB*); - - int64_t m_transactionID; - IDBBackingStoreLevelDB* m_backingStore; - RefPtr m_transaction; -}; - -} // namespace WebCore - -#endif // USE(LEVELDB) -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBBackingStoreTransactionLevelDB_h diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.cpp deleted file mode 100644 index 3541fa88c..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBFactoryBackendLevelDB.h" - -#include "DOMStringList.h" -#include "IDBBackingStoreLevelDB.h" -#include "IDBCursorBackend.h" -#include "IDBDatabaseBackend.h" -#include "IDBDatabaseException.h" -#include "IDBServerConnectionLevelDB.h" -#include "IDBTransactionBackend.h" -#include "IDBTransactionCoordinator.h" -#include "Logging.h" -#include "SecurityOrigin.h" - -#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -namespace WebCore { - -template -static void cleanWeakMap(HashMap >& map) -{ - HashMap > other; - other.swap(map); - - typename HashMap >::const_iterator iter = other.begin(); - while (iter != other.end()) { - if (iter->value.get()) - map.set(iter->key, iter->value); - ++iter; - } -} - -static String computeFileIdentifier(const SecurityOrigin& securityOrigin) -{ - static const char levelDBFileSuffix[] = "@1"; - return securityOrigin.databaseIdentifier() + levelDBFileSuffix; -} - -static String computeUniqueIdentifier(const String& name, const SecurityOrigin& securityOrigin) -{ - return computeFileIdentifier(securityOrigin) + name; -} - -IDBFactoryBackendLevelDB::IDBFactoryBackendLevelDB(const String& databaseDirectory) - : m_databaseDirectory(databaseDirectory) -{ -} - -IDBFactoryBackendLevelDB::~IDBFactoryBackendLevelDB() -{ -} - -void IDBFactoryBackendLevelDB::removeIDBDatabaseBackend(const String& uniqueIdentifier) -{ - ASSERT(m_databaseBackendMap.contains(uniqueIdentifier)); - m_databaseBackendMap.remove(uniqueIdentifier); -} - -void IDBFactoryBackendLevelDB::getDatabaseNames(PassRefPtr callbacks, PassRefPtr securityOrigin, ScriptExecutionContext*, const String& dataDirectory) -{ - ASSERT(securityOrigin); - LOG(StorageAPI, "IDBFactoryBackendLevelDB::getDatabaseNames"); - RefPtr backingStore = openBackingStore(*securityOrigin, dataDirectory); - if (!backingStore) { - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error opening backing store for indexedDB.webkitGetDatabaseNames.")); - return; - } - - RefPtr databaseNames = DOMStringList::create(); - - Vector foundNames = backingStore->getDatabaseNames(); - for (Vector::const_iterator it = foundNames.begin(); it != foundNames.end(); ++it) - databaseNames->append(*it); - - callbacks->onSuccess(databaseNames.release()); -} - -void IDBFactoryBackendLevelDB::deleteDatabase(const String& name, PassRefPtr callbacks, PassRefPtr securityOrigin, ScriptExecutionContext*, const String& dataDirectory) -{ - LOG(StorageAPI, "IDBFactoryBackendLevelDB::deleteDatabase"); - const String uniqueIdentifier = computeUniqueIdentifier(name, *securityOrigin); - - IDBDatabaseBackendMap::iterator it = m_databaseBackendMap.find(uniqueIdentifier); - if (it != m_databaseBackendMap.end()) { - // If there are any connections to the database, directly delete the - // database. - it->value->deleteDatabase(callbacks); - return; - } - - // FIXME: Everything from now on should be done on another thread. - RefPtr backingStore = openBackingStore(*securityOrigin, dataDirectory); - if (!backingStore) { - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error opening backing store for indexedDB.deleteDatabase.")); - return; - } - - RefPtr serverConnection = IDBServerConnectionLevelDB::create(name, backingStore.get()); - RefPtr databaseBackend = IDBDatabaseBackend::create(name, uniqueIdentifier, this, *serverConnection); - if (databaseBackend) { - m_databaseBackendMap.set(uniqueIdentifier, databaseBackend.get()); - databaseBackend->deleteDatabase(callbacks); - m_databaseBackendMap.remove(uniqueIdentifier); - } else - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error creating database backend for indexedDB.deleteDatabase.")); -} - -PassRefPtr IDBFactoryBackendLevelDB::openBackingStore(const SecurityOrigin& securityOrigin, const String& dataDirectory) -{ - const String fileIdentifier = computeFileIdentifier(securityOrigin); - const bool openInMemory = dataDirectory.isEmpty(); - - IDBBackingStoreLevelDBMap::iterator it2 = m_backingStoreMap.find(fileIdentifier); - if (it2 != m_backingStoreMap.end() && it2->value.get()) - return it2->value.get(); - - RefPtr backingStore; - if (openInMemory) - backingStore = IDBBackingStoreLevelDB::openInMemory(fileIdentifier); - else - backingStore = IDBBackingStoreLevelDB::open(securityOrigin, dataDirectory, fileIdentifier); - - if (backingStore) { - cleanWeakMap(m_backingStoreMap); - m_backingStoreMap.set(fileIdentifier, backingStore->createWeakPtr()); - // If an in-memory database, bind lifetime to this factory instance. - if (openInMemory) - m_sessionOnlyBackingStores.add(backingStore); - - // All backing stores associated with this factory should be of the same type. - ASSERT(m_sessionOnlyBackingStores.isEmpty() || openInMemory); - - return backingStore.release(); - } - - return 0; -} - -void IDBFactoryBackendLevelDB::open(const String& name, uint64_t version, int64_t transactionId, PassRefPtr callbacks, PassRefPtr databaseCallbacks, const SecurityOrigin& openingOrigin, const SecurityOrigin&) -{ - LOG(StorageAPI, "IDBFactoryBackendLevelDB::open"); - const String uniqueIdentifier = computeUniqueIdentifier(name, openingOrigin); - - RefPtr databaseBackend; - IDBDatabaseBackendMap::iterator it = m_databaseBackendMap.find(uniqueIdentifier); - if (it == m_databaseBackendMap.end()) { - RefPtr backingStore = openBackingStore(openingOrigin, m_databaseDirectory); - if (!backingStore) { - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error opening backing store for indexedDB.open.")); - return; - } - - RefPtr serverConnection = IDBServerConnectionLevelDB::create(name, backingStore.get()); - databaseBackend = IDBDatabaseBackend::create(name, uniqueIdentifier, this, *serverConnection); - if (databaseBackend) - m_databaseBackendMap.set(uniqueIdentifier, databaseBackend.get()); - else { - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error creating database backend for indexeddb.open.")); - return; - } - } else - databaseBackend = it->value; - - databaseBackend->openConnection(callbacks, databaseCallbacks, transactionId, version); -} - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB) diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.h deleted file mode 100644 index 004046062..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef IDBFactoryBackendLevelDB_h -#define IDBFactoryBackendLevelDB_h - -#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -#include "IDBCallbacks.h" -#include "IDBDatabaseCallbacks.h" -#include "IDBFactoryBackendInterface.h" -#include "SecurityOrigin.h" -#include -#include -#include -#include -#include - -namespace WebCore { - -class DOMStringList; - -class IDBBackingStoreLevelDB; -class IDBDatabaseBackend; - -class IDBFactoryBackendLevelDB : public IDBFactoryBackendInterface { -public: - static PassRefPtr create(const String& databaseDirectory) - { - return adoptRef(new IDBFactoryBackendLevelDB(databaseDirectory)); - } - virtual ~IDBFactoryBackendLevelDB(); - - // Notifications from weak pointers. - virtual void removeIDBDatabaseBackend(const String& uniqueIdentifier) override final; - - virtual void getDatabaseNames(PassRefPtr, PassRefPtr, ScriptExecutionContext*, const String& dataDir) override final; - virtual void open(const String& name, uint64_t version, int64_t transactionId, PassRefPtr, PassRefPtr, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin) override final; - - virtual void deleteDatabase(const String& name, PassRefPtr, PassRefPtr, ScriptExecutionContext*, const String& dataDir) override final; - -protected: - virtual PassRefPtr openBackingStore(const SecurityOrigin&, const String& dataDir); - -private: - explicit IDBFactoryBackendLevelDB(const String& databaseDirectory); - - typedef HashMap > IDBDatabaseBackendMap; - IDBDatabaseBackendMap m_databaseBackendMap; - - typedef HashMap > IDBBackingStoreLevelDBMap; - IDBBackingStoreLevelDBMap m_backingStoreMap; - - HashSet > m_sessionOnlyBackingStores; - - String m_databaseDirectory; -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -#endif // IDBFactoryBackendLevelDB_h diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.cpp deleted file mode 100644 index 75b824501..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBIndexWriterLevelDB.h" - -#include "IDBKey.h" -#include - -#if ENABLE(INDEXED_DATABASE) -#if USE(LEVELDB) - -namespace WebCore { - -IDBIndexWriterLevelDB::IDBIndexWriterLevelDB(const IDBIndexMetadata& metadata, const IndexKeys& keys) - : m_indexMetadata(metadata) - , m_indexKeys(keys) -{ -} - -void IDBIndexWriterLevelDB::writeIndexKeys(const IDBRecordIdentifier* recordIdentifier, IDBBackingStoreLevelDB& backingStore, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId) const -{ - ASSERT(recordIdentifier); - int64_t indexId = m_indexMetadata.id; - for (size_t i = 0; i < m_indexKeys.size(); ++i) { - bool ok = backingStore.putIndexDataForRecord(transaction, databaseId, objectStoreId, indexId, *(m_indexKeys)[i].get(), recordIdentifier); - // This should have already been verified as a valid write during verifyIndexKeys. - ASSERT_UNUSED(ok, ok); - } -} - -bool IDBIndexWriterLevelDB::verifyIndexKeys(IDBBackingStoreLevelDB& backingStore, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, bool& canAddKeys, const IDBKey* primaryKey, String* errorMessage) const -{ - canAddKeys = false; - for (size_t i = 0; i < m_indexKeys.size(); ++i) { - bool ok = addingKeyAllowed(backingStore, transaction, databaseId, objectStoreId, indexId, (m_indexKeys)[i].get(), primaryKey, canAddKeys); - if (!ok) - return false; - if (!canAddKeys) { - if (errorMessage) - *errorMessage = String::format("Unable to add key to index '%s': at least one key does not satisfy the uniqueness requirements.", m_indexMetadata.name.utf8().data()); - return true; - } - } - canAddKeys = true; - return true; -} - -bool IDBIndexWriterLevelDB::addingKeyAllowed(IDBBackingStoreLevelDB& backingStore, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey* indexKey, const IDBKey* primaryKey, bool& allowed) const -{ - allowed = false; - if (!m_indexMetadata.unique) { - allowed = true; - return true; - } - - RefPtr foundPrimaryKey; - bool found = false; - bool ok = backingStore.keyExistsInIndex(transaction, databaseId, objectStoreId, indexId, *indexKey, foundPrimaryKey, found); - if (!ok) - return false; - if (!found || (primaryKey && foundPrimaryKey->isEqual(primaryKey))) - allowed = true; - return true; -} - - -} // namespace WebCore - -#endif // USE(LEVELDB) -#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.h deleted file mode 100644 index 07e55538e..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBIndexWriterLevelDB_h -#define IDBIndexWriterLevelDB_h - -#include "IDBBackingStoreLevelDB.h" -#include "IDBDatabaseBackend.h" -#include "IDBDatabaseMetadata.h" -#include - -#if ENABLE(INDEXED_DATABASE) -#if USE(LEVELDB) - -namespace WebCore { - -typedef Vector> IndexKeys; - -class IDBIndexWriterLevelDB : public RefCounted { -public: - static PassRefPtr create(const IDBIndexMetadata& indexMetadata, const IndexKeys& indexKeys) - { - return adoptRef(new IDBIndexWriterLevelDB(indexMetadata, indexKeys)); - } - - bool verifyIndexKeys(IDBBackingStoreLevelDB&, IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, bool& canAddKeys, const IDBKey* primaryKey = 0, String* errorMessage = 0) const WARN_UNUSED_RETURN; - - void writeIndexKeys(const IDBRecordIdentifier*, IDBBackingStoreLevelDB&, IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId) const; - -private: - IDBIndexWriterLevelDB(const IDBIndexMetadata&, const IndexKeys&); - - bool addingKeyAllowed(IDBBackingStoreLevelDB&, IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey* indexKey, const IDBKey* primaryKey, bool& allowed) const WARN_UNUSED_RETURN; - - const IDBIndexMetadata m_indexMetadata; - IndexKeys m_indexKeys; -}; - -} // namespace WebCore - -#endif // #if USE(LEVELDB) -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBIndexWriterLevelDB_h diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.cpp deleted file mode 100644 index a4dcb1ede..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.cpp +++ /dev/null @@ -1,1807 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBLevelDBCoding.h" - -#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -#include "IDBKey.h" -#include "IDBKeyPath.h" -#include "LevelDBSlice.h" -#include -#include - -// LevelDB stores key/value pairs. Keys and values are strings of bytes, normally of type Vector. -// -// The keys in the backing store are variable-length tuples with different types -// of fields. Each key in the backing store starts with a ternary prefix: (database id, object store id, index id). For each, 0 is reserved for meta-data. -// The prefix makes sure that data for a specific database, object store, and index are grouped together. The locality is important for performance: common -// operations should only need a minimal number of seek operations. For example, all the meta-data for a database is grouped together so that reading that -// meta-data only requires one seek. -// -// Each key type has a class (in square brackets below) which knows how to encode, decode, and compare that key type. -// -// Global meta-data have keys with prefix (0,0,0), followed by a type byte: -// -// <0, 0, 0, 0> => IndexedDB/LevelDB schema version [SchemaVersionKey] -// <0, 0, 0, 1> => The maximum database id ever allocated [MaxDatabaseIdKey] -// <0, 0, 0, 2> => SerializedScriptValue version [DataVersionKey] -// <0, 0, 0, 100, database id> => Existence implies the database id is in the free list [DatabaseFreeListKey] -// <0, 0, 0, 201, utf16 origin name, utf16 database name> => Database id [DatabaseNameKey] -// -// -// Database meta-data: -// -// Again, the prefix is followed by a type byte. -// -// => utf16 origin name [DatabaseMetaDataKey] -// => utf16 database name [DatabaseMetaDataKey] -// => utf16 user version data [DatabaseMetaDataKey] -// => maximum object store id ever allocated [DatabaseMetaDataKey] -// => user integer version (var int) [DatabaseMetaDataKey] -// -// -// Object store meta-data: -// -// The prefix is followed by a type byte, then a variable-length integer, and then another type byte. -// -// => utf16 object store name [ObjectStoreMetaDataKey] -// => utf16 key path [ObjectStoreMetaDataKey] -// => has auto increment [ObjectStoreMetaDataKey] -// => is evictable [ObjectStoreMetaDataKey] -// => last "version" number [ObjectStoreMetaDataKey] -// => maximum index id ever allocated [ObjectStoreMetaDataKey] -// => has key path (vs. null) [ObjectStoreMetaDataKey] -// => key generator current number [ObjectStoreMetaDataKey] -// -// -// Index meta-data: -// -// The prefix is followed by a type byte, then two variable-length integers, and then another type byte. -// -// => utf16 index name [IndexMetaDataKey] -// => are index keys unique [IndexMetaDataKey] -// => utf16 key path [IndexMetaDataKey] -// => is index multi-entry [IndexMetaDataKey] -// -// -// Other object store and index meta-data: -// -// The prefix is followed by a type byte. The object store and index id are variable length integers, the utf16 strings are variable length strings. -// -// => existence implies the object store id is in the free list [ObjectStoreFreeListKey] -// => existence implies the index id is in the free list [IndexFreeListKey] -// => object store id [ObjectStoreNamesKey] -// => index id [IndexNamesKey] -// -// -// Object store data: -// -// The prefix is followed by a type byte. The user key is an encoded IDBKey. -// -// => "version", serialized script value [ObjectStoreDataKey] -// -// -// "Exists" entry: -// -// The prefix is followed by a type byte. The user key is an encoded IDBKey. -// -// => "version" [ExistsEntryKey] -// -// -// Index data: -// -// The prefix is followed by a type byte. The index key is an encoded IDBKey. The sequence number is a variable length integer. -// The primary key is an encoded IDBKey. -// -// => "version", primary key [IndexDataKey] -// -// (The sequence number is obsolete; it was used to allow two entries with -// the same user (index) key in non-unique indexes prior to the inclusion of -// the primary key in the data. The "version" field is used to weed out stale -// index data. Whenever new object store data is inserted, it gets a new -// "version" number, and new index data is written with this number. When -// the index is used for look-ups, entries are validated against the -// "exists" entries, and records with old "version" numbers are deleted -// when they are encountered in getPrimaryKeyViaIndex, -// IndexCursorImpl::loadCurrentRow, and IndexKeyCursorImpl::loadCurrentRow). - -namespace WebCore { -namespace IDBLevelDBCoding { - -#ifndef INT64_MAX -#define INT64_MAX 0x7fffffffffffffffLL -#endif -#ifndef INT32_MAX -#define INT32_MAX 0x7fffffffL -#endif - -static const unsigned char IDBKeyNullTypeByte = 0; -static const unsigned char IDBKeyStringTypeByte = 1; -static const unsigned char IDBKeyDateTypeByte = 2; -static const unsigned char IDBKeyNumberTypeByte = 3; -static const unsigned char IDBKeyArrayTypeByte = 4; -static const unsigned char IDBKeyMinKeyTypeByte = 5; - -static const unsigned char IDBKeyPathTypeCodedByte1 = 0; -static const unsigned char IDBKeyPathTypeCodedByte2 = 0; - -static const unsigned char ObjectStoreDataIndexId = 1; -static const unsigned char ExistsEntryIndexId = 2; - -static const unsigned char SchemaVersionTypeByte = 0; -static const unsigned char MaxDatabaseIdTypeByte = 1; -static const unsigned char DataVersionTypeByte = 2; -static const unsigned char MaxSimpleGlobalMetaDataTypeByte = 3; // Insert before this and increment. -static const unsigned char DatabaseFreeListTypeByte = 100; -static const unsigned char DatabaseNameTypeByte = 201; - -static const unsigned char ObjectStoreMetaDataTypeByte = 50; -static const unsigned char IndexMetaDataTypeByte = 100; -static const unsigned char ObjectStoreFreeListTypeByte = 150; -static const unsigned char IndexFreeListTypeByte = 151; -static const unsigned char ObjectStoreNamesTypeByte = 200; -static const unsigned char IndexNamesKeyTypeByte = 201; - -static const unsigned char ObjectMetaDataTypeMaximum = 255; -static const unsigned char IndexMetaDataTypeMaximum = 255; - -Vector encodeByte(unsigned char c) -{ - Vector v; - v.append(c); - - ASSERT(v.size() <= DefaultInlineBufferSize); - return v; -} - -const char* decodeByte(const char* p, const char* limit, unsigned char& foundChar) -{ - if (p >= limit) - return 0; - - foundChar = *p++; - return p; -} - -Vector maxIDBKey() -{ - return encodeByte(IDBKeyNullTypeByte); -} - -Vector minIDBKey() -{ - return encodeByte(IDBKeyMinKeyTypeByte); -} - -Vector encodeBool(bool b) -{ - Vector ret; - ret.append(b ? 1 : 0); - - ASSERT(ret.size() <= DefaultInlineBufferSize); - return ret; -} - -bool decodeBool(const char* begin, const char* end) -{ - ASSERT_UNUSED(end, begin < end); - return *begin; -} - -Vector encodeInt(int64_t nParam) -{ - ASSERT(nParam >= 0); - uint64_t n = static_cast(nParam); - Vector ret; - - do { - unsigned char c = n; - ret.append(c); - n >>= 8; - } while (n); - - ASSERT(ret.size() <= DefaultInlineBufferSize); - return ret; -} - -int64_t decodeInt(const char* begin, const char* end) -{ - ASSERT(begin <= end); - int64_t ret = 0; - - int shift = 0; - while (begin < end) { - unsigned char c = *begin++; - ret |= static_cast(c) << shift; - shift += 8; - } - - return ret; -} - -static int compareInts(int64_t a, int64_t b) -{ - ASSERT(a >= 0); - ASSERT(b >= 0); - - int64_t diff = a - b; - if (diff < 0) - return -1; - if (diff > 0) - return 1; - return 0; -} - -Vector encodeVarInt(int64_t nParam) -{ - ASSERT(nParam >= 0); - uint64_t n = static_cast(nParam); - Vector ret; - - do { - unsigned char c = n & 0x7f; - n >>= 7; - if (n) - c |= 0x80; - ret.append(c); - } while (n); - - ASSERT(ret.size() <= DefaultInlineBufferSize); - return ret; -} - -const char* decodeVarInt(const char* p, const char* limit, int64_t& foundInt) -{ - ASSERT(limit >= p); - foundInt = 0; - int shift = 0; - - do { - if (p >= limit) - return 0; - - unsigned char c = *p; - foundInt |= static_cast(c & 0x7f) << shift; - shift += 7; - } while (*p++ & 0x80); - return p; -} - -Vector encodeString(const String& s) -{ - // Backing store is UTF-16BE, convert from host endianness. - size_t length = s.length(); - Vector ret(length * sizeof(UChar)); - - const UChar* src = s.characters(); - UChar* dst = reinterpret_cast(ret.data()); - for (unsigned i = 0; i < length; ++i) - *dst++ = htons(*src++); - - return ret; -} - -String decodeString(const char* start, const char* end) -{ - // Backing store is UTF-16BE, convert to host endianness. - ASSERT(end >= start); - ASSERT(!((end - start) % sizeof(UChar))); - - size_t length = (end - start) / sizeof(UChar); - Vector buffer(length); - - const UChar* src = reinterpret_cast(start); - UChar* dst = buffer.data(); - for (unsigned i = 0; i < length; ++i) - *dst++ = ntohs(*src++); - - return String::adopt(buffer); -} - -Vector encodeStringWithLength(const String& s) -{ - Vector ret = encodeVarInt(s.length()); - ret.appendVector(encodeString(s)); - return ret; -} - -const char* decodeStringWithLength(const char* p, const char* limit, String& foundString) -{ - ASSERT(limit >= p); - int64_t len; - p = decodeVarInt(p, limit, len); - if (!p || len < 0 || p + len * 2 > limit) - return 0; - - foundString = decodeString(p, p + len * 2); - p += len * 2; - return p; -} - -int compareEncodedStringsWithLength(const char*& p, const char* limitP, const char*& q, const char* limitQ, bool& ok) -{ - ASSERT(&p != &q); - ASSERT(limitP >= p); - ASSERT(limitQ >= q); - int64_t lenP, lenQ; - p = decodeVarInt(p, limitP, lenP); - q = decodeVarInt(q, limitQ, lenQ); - if (!p || !q || lenP < 0 || lenQ < 0) { - ok = false; - return 0; - } - ASSERT(p && q); - ASSERT(lenP >= 0); - ASSERT(lenQ >= 0); - ASSERT(p + lenP * 2 <= limitP); - ASSERT(q + lenQ * 2 <= limitQ); - - const char* startP = p; - const char* startQ = q; - p += lenP * 2; - q += lenQ * 2; - - if (p > limitP || q > limitQ) { - ok = false; - return 0; - } - - ok = true; - const size_t lmin = static_cast(lenP < lenQ ? lenP : lenQ); - if (int x = memcmp(startP, startQ, lmin * 2)) - return x; - - if (lenP == lenQ) - return 0; - - return (lenP > lenQ) ? 1 : -1; -} - -Vector encodeDouble(double x) -{ - // FIXME: It would be nice if we could be byte order independent. - const char* p = reinterpret_cast(&x); - Vector v; - v.append(p, sizeof(x)); - - ASSERT(v.size() <= DefaultInlineBufferSize); - return v; -} - -const char* decodeDouble(const char* p, const char* limit, double* d) -{ - if (p + sizeof(*d) > limit) - return 0; - - char* x = reinterpret_cast(d); - for (size_t i = 0; i < sizeof(*d); ++i) - *x++ = *p++; - return p; -} - -Vector encodeIDBKey(const IDBKey& key) -{ - Vector ret; - encodeIDBKey(key, ret); - return ret; -} - -void encodeIDBKey(const IDBKey& key, Vector& into) -{ - size_t previousSize = into.size(); - ASSERT(key.isValid()); - switch (key.type()) { - case IDBKey::InvalidType: - case IDBKey::MinType: - ASSERT_NOT_REACHED(); - into.appendVector(encodeByte(IDBKeyNullTypeByte)); - return; - case IDBKey::ArrayType: { - into.appendVector(encodeByte(IDBKeyArrayTypeByte)); - size_t length = key.array().size(); - into.appendVector(encodeVarInt(length)); - for (size_t i = 0; i < length; ++i) - encodeIDBKey(*key.array()[i], into); - ASSERT_UNUSED(previousSize, into.size() > previousSize); - return; - } - case IDBKey::StringType: - into.appendVector(encodeByte(IDBKeyStringTypeByte)); - into.appendVector(encodeStringWithLength(key.string())); - ASSERT_UNUSED(previousSize, into.size() > previousSize); - return; - case IDBKey::DateType: - into.appendVector(encodeByte(IDBKeyDateTypeByte)); - into.appendVector(encodeDouble(key.date())); - ASSERT_UNUSED(previousSize, into.size() - previousSize == 9); - return; - case IDBKey::NumberType: - into.appendVector(encodeByte(IDBKeyNumberTypeByte)); - into.appendVector(encodeDouble(key.number())); - ASSERT_UNUSED(previousSize, into.size() - previousSize == 9); - return; - } - - ASSERT_NOT_REACHED(); -} - - -const char* decodeIDBKey(const char* p, const char* limit, RefPtr& foundKey) -{ - ASSERT(limit >= p); - if (p >= limit) - return 0; - - unsigned char type = *p++; - - switch (type) { - case IDBKeyNullTypeByte: - foundKey = IDBKey::createInvalid(); - return p; - - case IDBKeyArrayTypeByte: { - int64_t length; - p = decodeVarInt(p, limit, length); - if (!p || length < 0) - return 0; - IDBKey::KeyArray array; - while (length--) { - RefPtr key; - p = decodeIDBKey(p, limit, key); - if (!p) - return 0; - array.append(key); - } - foundKey = IDBKey::createArray(array); - return p; - } - case IDBKeyStringTypeByte: { - String s; - p = decodeStringWithLength(p, limit, s); - if (!p) - return 0; - foundKey = IDBKey::createString(s); - return p; - } - case IDBKeyDateTypeByte: { - double d; - p = decodeDouble(p, limit, &d); - if (!p) - return 0; - foundKey = IDBKey::createDate(d); - return p; - } - case IDBKeyNumberTypeByte: { - double d; - p = decodeDouble(p, limit, &d); - if (!p) - return 0; - foundKey = IDBKey::createNumber(d); - return p; - } - } - - ASSERT_NOT_REACHED(); - return 0; -} - -const char* extractEncodedIDBKey(const char* start, const char* limit, Vector* result = 0) -{ - const char* p = start; - if (p >= limit) - return 0; - - unsigned char type = *p++; - - switch (type) { - case IDBKeyNullTypeByte: - case IDBKeyMinKeyTypeByte: - break; - case IDBKeyArrayTypeByte: { - int64_t length; - p = decodeVarInt(p, limit, length); - if (!p || length < 0) - return 0; - while (length--) { - p = extractEncodedIDBKey(p, limit); - if (!p) - return 0; - } - break; - } - case IDBKeyStringTypeByte: { - int64_t length; - p = decodeVarInt(p, limit, length); - if (!p || length < 0 || p + length * 2 > limit) - return 0; - p += length * 2; - break; - } - case IDBKeyDateTypeByte: - case IDBKeyNumberTypeByte: - if (p + sizeof(double) > limit) - return 0; - p += sizeof(double); - break; - } - - if (result) { - ASSERT(p); - ASSERT(p <= limit); - result->clear(); - result->append(start, p - start); - } - - return p; -} - -static IDBKey::Type keyTypeByteToKeyType(unsigned char type) -{ - switch (type) { - case IDBKeyNullTypeByte: - return IDBKey::InvalidType; - case IDBKeyArrayTypeByte: - return IDBKey::ArrayType; - case IDBKeyStringTypeByte: - return IDBKey::StringType; - case IDBKeyDateTypeByte: - return IDBKey::DateType; - case IDBKeyNumberTypeByte: - return IDBKey::NumberType; - case IDBKeyMinKeyTypeByte: - return IDBKey::MinType; - } - - ASSERT_NOT_REACHED(); - return IDBKey::InvalidType; -} - -static int compareEncodedIDBKeys(const char*& ptrA, const char* limitA, const char*& ptrB, const char* limitB, bool& ok) -{ - ok = true; - ASSERT(&ptrA != &ptrB); - ASSERT_WITH_SECURITY_IMPLICATION(ptrA < limitA); - ASSERT_WITH_SECURITY_IMPLICATION(ptrB < limitB); - unsigned char typeA = *ptrA++; - unsigned char typeB = *ptrB++; - - if (int x = IDBKey::compareTypes(keyTypeByteToKeyType(typeA), keyTypeByteToKeyType(typeB))) - return x; - - switch (typeA) { - case IDBKeyNullTypeByte: - case IDBKeyMinKeyTypeByte: - // Null type or max type; no payload to compare. - return 0; - case IDBKeyArrayTypeByte: { - int64_t lengthA, lengthB; - ptrA = decodeVarInt(ptrA, limitA, lengthA); - ptrB = decodeVarInt(ptrB, limitB, lengthB); - if (!ptrA || !ptrB || lengthA < 0 || lengthB < 0) { - ok = false; - return 0; - } - for (int64_t i = 0; i < lengthA && i < lengthB; ++i) { - int result = compareEncodedIDBKeys(ptrA, limitA, ptrB, limitB, ok); - if (!ok || result) - return result; - } - if (lengthA < lengthB) - return -1; - if (lengthA > lengthB) - return 1; - return 0; - } - case IDBKeyStringTypeByte: - return compareEncodedStringsWithLength(ptrA, limitA, ptrB, limitB, ok); - case IDBKeyDateTypeByte: - case IDBKeyNumberTypeByte: { - double d, e; - ptrA = decodeDouble(ptrA, limitA, &d); - ptrB = decodeDouble(ptrB, limitB, &e); - ASSERT(ptrA); - ASSERT(ptrB); - if (!ptrA || !ptrB) { - ok = false; - return 0; - } - if (d < e) - return -1; - if (d > e) - return 1; - return 0; - } - } - - ASSERT_NOT_REACHED(); - return 0; -} - -int compareEncodedIDBKeys(const Vector& keyA, const Vector& keyB, bool& ok) -{ - ASSERT(keyA.size() >= 1); - ASSERT(keyB.size() >= 1); - - const char* ptrA = keyA.data(); - const char* limitA = ptrA + keyA.size(); - const char* ptrB = keyB.data(); - const char* limitB = ptrB + keyB.size(); - - return compareEncodedIDBKeys(ptrA, limitA, ptrB, limitB, ok); -} - -Vector encodeIDBKeyPath(const IDBKeyPath& keyPath) -{ - // May be typed, or may be a raw string. An invalid leading - // byte is used to identify typed coding. New records are - // always written as typed. - Vector ret; - ret.append(IDBKeyPathTypeCodedByte1); - ret.append(IDBKeyPathTypeCodedByte2); - ret.append(static_cast(keyPath.type())); - switch (keyPath.type()) { - case IDBKeyPath::NullType: - break; - case IDBKeyPath::StringType: - ret.appendVector(encodeStringWithLength(keyPath.string())); - break; - case IDBKeyPath::ArrayType: { - const Vector& array = keyPath.array(); - size_t count = array.size(); - ret.appendVector(encodeVarInt(count)); - for (size_t i = 0; i < count; ++i) - ret.appendVector(encodeStringWithLength(array[i])); - break; - } - } - return ret; -} - -IDBKeyPath decodeIDBKeyPath(const char* p, const char* limit) -{ - // May be typed, or may be a raw string. An invalid leading - // byte sequence is used to identify typed coding. New records are - // always written as typed. - if (p == limit || (limit - p >= 2 && (*p != IDBKeyPathTypeCodedByte1 || *(p + 1) != IDBKeyPathTypeCodedByte2))) - return IDBKeyPath(decodeString(p, limit)); - p += 2; - - ASSERT(p != limit); - IDBKeyPath::Type type = static_cast(*p++); - switch (type) { - case IDBKeyPath::NullType: - ASSERT(p == limit); - return IDBKeyPath(); - case IDBKeyPath::StringType: { - String string; - p = decodeStringWithLength(p, limit, string); - ASSERT(p == limit); - return IDBKeyPath(string); - } - case IDBKeyPath::ArrayType: { - Vector array; - int64_t count; - p = decodeVarInt(p, limit, count); - ASSERT(p); - ASSERT(count >= 0); - while (count--) { - String string; - p = decodeStringWithLength(p, limit, string); - ASSERT(p); - array.append(string); - } - ASSERT(p == limit); - return IDBKeyPath(array); - } - } - ASSERT_NOT_REACHED(); - return IDBKeyPath(); -} - -namespace { - -template -int compare(const LevelDBSlice& a, const LevelDBSlice& b, bool, bool& ok) -{ - KeyType keyA; - KeyType keyB; - - const char* ptrA = KeyType::decode(a.begin(), a.end(), &keyA); - ASSERT(ptrA); - if (!ptrA) { - ok = false; - return 0; - } - const char* ptrB = KeyType::decode(b.begin(), b.end(), &keyB); - ASSERT(ptrB); - if (!ptrB) { - ok = false; - return 0; - } - - ok = true; - return keyA.compare(keyB); -} - -template<> -int compare(const LevelDBSlice& a, const LevelDBSlice& b, bool, bool& ok) -{ - KeyPrefix prefixA; - KeyPrefix prefixB; - const char* ptrA = KeyPrefix::decode(a.begin(), a.end(), &prefixA); - const char* ptrB = KeyPrefix::decode(b.begin(), b.end(), &prefixB); - ASSERT(ptrA); - ASSERT(ptrB); - ASSERT(prefixA.m_databaseId); - ASSERT(prefixA.m_objectStoreId); - ASSERT(prefixA.m_indexId == ExistsEntryKey::SpecialIndexNumber); - ASSERT(prefixB.m_databaseId); - ASSERT(prefixB.m_objectStoreId); - ASSERT(prefixB.m_indexId == ExistsEntryKey::SpecialIndexNumber); - ASSERT(ptrA != a.end()); - ASSERT(ptrB != b.end()); - // Prefixes are not compared - it is assumed this was already done. - ASSERT(!prefixA.compare(prefixB)); - - return compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end(), ok); -} - -template<> -int compare(const LevelDBSlice& a, const LevelDBSlice& b, bool, bool& ok) -{ - KeyPrefix prefixA; - KeyPrefix prefixB; - const char* ptrA = KeyPrefix::decode(a.begin(), a.end(), &prefixA); - const char* ptrB = KeyPrefix::decode(b.begin(), b.end(), &prefixB); - ASSERT(ptrA); - ASSERT(ptrB); - ASSERT(prefixA.m_databaseId); - ASSERT(prefixA.m_objectStoreId); - ASSERT(prefixA.m_indexId == ObjectStoreDataKey::SpecialIndexNumber); - ASSERT(prefixB.m_databaseId); - ASSERT(prefixB.m_objectStoreId); - ASSERT(prefixB.m_indexId == ObjectStoreDataKey::SpecialIndexNumber); - ASSERT(ptrA != a.end()); - ASSERT(ptrB != b.end()); - // Prefixes are not compared - it is assumed this was already done. - ASSERT(!prefixA.compare(prefixB)); - - return compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end(), ok); -} - -template<> -int compare(const LevelDBSlice& a, const LevelDBSlice& b, bool ignoreDuplicates, bool& ok) -{ - KeyPrefix prefixA; - KeyPrefix prefixB; - const char* ptrA = KeyPrefix::decode(a.begin(), a.end(), &prefixA); - const char* ptrB = KeyPrefix::decode(b.begin(), b.end(), &prefixB); - ASSERT(ptrA); - ASSERT(ptrB); - ASSERT(prefixA.m_databaseId); - ASSERT(prefixA.m_objectStoreId); - ASSERT(prefixA.m_indexId >= MinimumIndexId); - ASSERT(prefixB.m_databaseId); - ASSERT(prefixB.m_objectStoreId); - ASSERT(prefixB.m_indexId >= MinimumIndexId); - ASSERT(ptrA != a.end()); - ASSERT(ptrB != b.end()); - // Prefixes are not compared - it is assumed this was already done. - ASSERT(!prefixA.compare(prefixB)); - - // index key - int result = compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end(), ok); - if (!ok || result) - return result; - if (ignoreDuplicates) - return 0; - - // sequence number [optional] - int64_t sequenceNumberA = -1; - int64_t sequenceNumberB = -1; - if (ptrA != a.end()) - ptrA = decodeVarInt(ptrA, a.end(), sequenceNumberA); - if (ptrB != b.end()) - ptrB = decodeVarInt(ptrB, b.end(), sequenceNumberB); - - // primary key [optional] - if (!ptrA || !ptrB) - return 0; - if (ptrA == a.end() && ptrB == b.end()) - return 0; - if (ptrA == a.end()) - return -1; - if (ptrB == b.end()) - return 1; - - result = compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end(), ok); - if (!ok || result) - return result; - - return compareInts(sequenceNumberA, sequenceNumberB); -} - -int compare(const LevelDBSlice& a, const LevelDBSlice& b, bool indexKeys, bool& ok) -{ - const char* ptrA = a.begin(); - const char* ptrB = b.begin(); - const char* endA = a.end(); - const char* endB = b.end(); - - KeyPrefix prefixA; - KeyPrefix prefixB; - - ptrA = KeyPrefix::decode(ptrA, endA, &prefixA); - ptrB = KeyPrefix::decode(ptrB, endB, &prefixB); - ASSERT(ptrA); - ASSERT(ptrB); - if (!ptrA || !ptrB) { - ok = false; - return 0; - } - - ok = true; - if (int x = prefixA.compare(prefixB)) - return x; - - if (prefixA.type() == KeyPrefix::GlobalMetaData) { - ASSERT(ptrA != endA); - ASSERT(ptrB != endB); - - unsigned char typeByteA = *ptrA++; - unsigned char typeByteB = *ptrB++; - - if (int x = typeByteA - typeByteB) - return x; - if (typeByteA < MaxSimpleGlobalMetaDataTypeByte) - return 0; - - const bool ignoreDuplicates = false; - if (typeByteA == DatabaseFreeListTypeByte) - return compare(a, b, ignoreDuplicates, ok); - if (typeByteA == DatabaseNameTypeByte) - return compare(a, b, ignoreDuplicates, ok); - } - - if (prefixA.type() == KeyPrefix::DatabaseMetaData) { - ASSERT(ptrA != endA); - ASSERT(ptrB != endB); - - unsigned char typeByteA = *ptrA++; - unsigned char typeByteB = *ptrB++; - - if (int x = typeByteA - typeByteB) - return x; - if (typeByteA < DatabaseMetaDataKey::MaxSimpleMetaDataType) - return 0; - - const bool ignoreDuplicates = false; - if (typeByteA == ObjectStoreMetaDataTypeByte) - return compare(a, b, ignoreDuplicates, ok); - if (typeByteA == IndexMetaDataTypeByte) - return compare(a, b, ignoreDuplicates, ok); - if (typeByteA == ObjectStoreFreeListTypeByte) - return compare(a, b, ignoreDuplicates, ok); - if (typeByteA == IndexFreeListTypeByte) - return compare(a, b, ignoreDuplicates, ok); - if (typeByteA == ObjectStoreNamesTypeByte) - return compare(a, b, ignoreDuplicates, ok); - if (typeByteA == IndexNamesKeyTypeByte) - return compare(a, b, ignoreDuplicates, ok); - } - - if (prefixA.type() == KeyPrefix::ObjectStoreData) { - if (ptrA == endA && ptrB == endB) - return 0; - if (ptrA == endA) - return -1; - if (ptrB == endB) - return 1; // FIXME: This case of non-existing user keys should not have to be handled this way. - - const bool ignoreDuplicates = false; - return compare(a, b, ignoreDuplicates, ok); - } - if (prefixA.type() == KeyPrefix::ExistsEntry) { - if (ptrA == endA && ptrB == endB) - return 0; - if (ptrA == endA) - return -1; - if (ptrB == endB) - return 1; // FIXME: This case of non-existing user keys should not have to be handled this way. - - const bool ignoreDuplicates = false; - return compare(a, b, ignoreDuplicates, ok); - } - if (prefixA.type() == KeyPrefix::IndexData) { - if (ptrA == endA && ptrB == endB) - return 0; - if (ptrA == endA) - return -1; - if (ptrB == endB) - return 1; // FIXME: This case of non-existing user keys should not have to be handled this way. - - bool ignoreDuplicates = indexKeys; - return compare(a, b, ignoreDuplicates, ok); - } - - ASSERT_NOT_REACHED(); - ok = false; - return 0; -} - -} - -int compare(const LevelDBSlice& a, const LevelDBSlice& b, bool indexKeys) -{ - bool ok; - int result = compare(a, b, indexKeys, ok); - ASSERT(ok); - if (!ok) - return 0; - return result; -} - -KeyPrefix::KeyPrefix() - : m_databaseId(InvalidType) - , m_objectStoreId(InvalidType) - , m_indexId(InvalidType) -{ -} - -KeyPrefix::KeyPrefix(int64_t databaseId) - : m_databaseId(databaseId) - , m_objectStoreId(0) - , m_indexId(0) -{ - ASSERT(KeyPrefix::isValidDatabaseId(databaseId)); -} - -KeyPrefix::KeyPrefix(int64_t databaseId, int64_t objectStoreId) - : m_databaseId(databaseId) - , m_objectStoreId(objectStoreId) - , m_indexId(0) -{ - ASSERT(KeyPrefix::isValidDatabaseId(databaseId)); - ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId)); -} - -KeyPrefix::KeyPrefix(int64_t databaseId, int64_t objectStoreId, int64_t indexId) - : m_databaseId(databaseId) - , m_objectStoreId(objectStoreId) - , m_indexId(indexId) -{ - ASSERT(KeyPrefix::isValidDatabaseId(databaseId)); - ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId)); - ASSERT(KeyPrefix::isValidIndexId(indexId)); -} - -KeyPrefix::KeyPrefix(Type type, int64_t databaseId, int64_t objectStoreId, int64_t indexId) - : m_databaseId(databaseId) - , m_objectStoreId(objectStoreId) - , m_indexId(indexId) -{ - ASSERT_UNUSED(type, type == InvalidType); - ASSERT(KeyPrefix::isValidDatabaseId(databaseId)); - ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId)); -} - - -KeyPrefix KeyPrefix::createWithSpecialIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId) -{ - ASSERT(KeyPrefix::isValidDatabaseId(databaseId)); - ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId)); - ASSERT(indexId); - return KeyPrefix(InvalidType, databaseId, objectStoreId, indexId); -} - - -bool KeyPrefix::isValidDatabaseId(int64_t databaseId) -{ - return (databaseId > 0) && (databaseId < KeyPrefix::kMaxDatabaseId); -} - -bool KeyPrefix::isValidObjectStoreId(int64_t objectStoreId) -{ - return (objectStoreId > 0) && (objectStoreId < KeyPrefix::kMaxObjectStoreId); -} - -bool KeyPrefix::isValidIndexId(int64_t indexId) -{ - return (indexId >= MinimumIndexId) && (indexId < KeyPrefix::kMaxIndexId); -} - -const char* KeyPrefix::decode(const char* start, const char* limit, KeyPrefix* result) -{ - if (start == limit) - return 0; - - unsigned char firstByte = *start++; - - int databaseIdBytes = ((firstByte >> 5) & 0x7) + 1; - int objectStoreIdBytes = ((firstByte >> 2) & 0x7) + 1; - int indexIdBytes = (firstByte & 0x3) + 1; - - if (start + databaseIdBytes + objectStoreIdBytes + indexIdBytes > limit) - return 0; - - result->m_databaseId = decodeInt(start, start + databaseIdBytes); - start += databaseIdBytes; - result->m_objectStoreId = decodeInt(start, start + objectStoreIdBytes); - start += objectStoreIdBytes; - result->m_indexId = decodeInt(start, start + indexIdBytes); - start += indexIdBytes; - - return start; -} - -Vector KeyPrefix::encodeEmpty() -{ - const Vector result(4, 0); - ASSERT(encodeInternal(0, 0, 0) == Vector(4, 0)); - return result; -} - -Vector KeyPrefix::encode() const -{ - ASSERT(m_databaseId != InvalidId); - ASSERT(m_objectStoreId != InvalidId); - ASSERT(m_indexId != InvalidId); - return encodeInternal(m_databaseId, m_objectStoreId, m_indexId); -} - -Vector KeyPrefix::encodeInternal(int64_t databaseId, int64_t objectStoreId, int64_t indexId) -{ - Vector databaseIdString = encodeIntSafely(databaseId, kMaxDatabaseId); - Vector objectStoreIdString = encodeIntSafely(objectStoreId, kMaxObjectStoreId); - Vector indexIdString = encodeIntSafely(indexId, kMaxIndexId); - - ASSERT(databaseIdString.size() <= kMaxDatabaseIdSizeBytes); - ASSERT(objectStoreIdString.size() <= kMaxObjectStoreIdSizeBytes); - ASSERT(indexIdString.size() <= kMaxIndexIdSizeBytes); - - unsigned char firstByte = (databaseIdString.size() - 1) << (kMaxObjectStoreIdSizeBits + kMaxIndexIdSizeBits) | (objectStoreIdString.size() - 1) << kMaxIndexIdSizeBits | (indexIdString.size() - 1); - COMPILE_ASSERT(kMaxDatabaseIdSizeBits + kMaxObjectStoreIdSizeBits + kMaxIndexIdSizeBits == sizeof(firstByte) * 8, CANT_ENCODE_IDS); - Vector ret; - ret.append(firstByte); - ret.appendVector(databaseIdString); - ret.appendVector(objectStoreIdString); - ret.appendVector(indexIdString); - - ASSERT(ret.size() <= DefaultInlineBufferSize); - return ret; -} - -int KeyPrefix::compare(const KeyPrefix& other) const -{ - ASSERT(m_databaseId != InvalidId); - ASSERT(m_objectStoreId != InvalidId); - ASSERT(m_indexId != InvalidId); - - if (m_databaseId != other.m_databaseId) - return compareInts(m_databaseId, other.m_databaseId); - if (m_objectStoreId != other.m_objectStoreId) - return compareInts(m_objectStoreId, other.m_objectStoreId); - if (m_indexId != other.m_indexId) - return compareInts(m_indexId, other.m_indexId); - return 0; -} - -KeyPrefix::Type KeyPrefix::type() const -{ - ASSERT(m_databaseId != InvalidId); - ASSERT(m_objectStoreId != InvalidId); - ASSERT(m_indexId != InvalidId); - - if (!m_databaseId) - return GlobalMetaData; - if (!m_objectStoreId) - return DatabaseMetaData; - if (m_indexId == ObjectStoreDataIndexId) - return ObjectStoreData; - if (m_indexId == ExistsEntryIndexId) - return ExistsEntry; - if (m_indexId >= MinimumIndexId) - return IndexData; - - ASSERT_NOT_REACHED(); - return InvalidType; -} - -Vector SchemaVersionKey::encode() -{ - Vector ret = KeyPrefix::encodeEmpty(); - ret.appendVector(encodeByte(SchemaVersionTypeByte)); - return ret; -} - -Vector MaxDatabaseIdKey::encode() -{ - Vector ret = KeyPrefix::encodeEmpty(); - ret.appendVector(encodeByte(MaxDatabaseIdTypeByte)); - return ret; -} - -Vector DataVersionKey::encode() -{ - Vector ret = KeyPrefix::encodeEmpty(); - ret.appendVector(encodeByte(DataVersionTypeByte)); - return ret; -} - -DatabaseFreeListKey::DatabaseFreeListKey() - : m_databaseId(-1) -{ -} - -const char* DatabaseFreeListKey::decode(const char* start, const char* limit, DatabaseFreeListKey* result) -{ - KeyPrefix prefix; - const char* p = KeyPrefix::decode(start, limit, &prefix); - if (!p) - return 0; - ASSERT(!prefix.m_databaseId); - ASSERT(!prefix.m_objectStoreId); - ASSERT(!prefix.m_indexId); - if (p == limit) - return 0; - unsigned char typeByte = 0; - p = decodeByte(p, limit, typeByte); - ASSERT_UNUSED(typeByte, typeByte == DatabaseFreeListTypeByte); - if (p == limit) - return 0; - return decodeVarInt(p, limit, result->m_databaseId); -} - -Vector DatabaseFreeListKey::encode(int64_t databaseId) -{ - Vector ret = KeyPrefix::encodeEmpty(); - ret.appendVector(encodeByte(DatabaseFreeListTypeByte)); - ret.appendVector(encodeVarInt(databaseId)); - return ret; -} - -Vector DatabaseFreeListKey::encodeMaxKey() -{ - return encode(INT64_MAX); -} - -int64_t DatabaseFreeListKey::databaseId() const -{ - ASSERT(m_databaseId >= 0); - return m_databaseId; -} - -int DatabaseFreeListKey::compare(const DatabaseFreeListKey& other) const -{ - ASSERT(m_databaseId >= 0); - return compareInts(m_databaseId, other.m_databaseId); -} - -const char* DatabaseNameKey::decode(const char* start, const char* limit, DatabaseNameKey* result) -{ - KeyPrefix prefix; - const char* p = KeyPrefix::decode(start, limit, &prefix); - if (!p) - return p; - ASSERT(!prefix.m_databaseId); - ASSERT(!prefix.m_objectStoreId); - ASSERT(!prefix.m_indexId); - if (p == limit) - return 0; - unsigned char typeByte = 0; - p = decodeByte(p, limit, typeByte); - ASSERT_UNUSED(typeByte, typeByte == DatabaseNameTypeByte); - if (p == limit) - return 0; - p = decodeStringWithLength(p, limit, result->m_origin); - if (!p) - return 0; - return decodeStringWithLength(p, limit, result->m_databaseName); -} - -Vector DatabaseNameKey::encode(const String& origin, const String& databaseName) -{ - Vector ret = KeyPrefix::encodeEmpty(); - ret.appendVector(encodeByte(DatabaseNameTypeByte)); - ret.appendVector(encodeStringWithLength(origin)); - ret.appendVector(encodeStringWithLength(databaseName)); - return ret; -} - -Vector DatabaseNameKey::encodeMinKeyForOrigin(const String& origin) -{ - return encode(origin, ""); -} - -Vector DatabaseNameKey::encodeStopKeyForOrigin(const String& origin) -{ - // just after origin in collation order - return encodeMinKeyForOrigin(origin + "\x01"); -} - -int DatabaseNameKey::compare(const DatabaseNameKey& other) -{ - if (int x = codePointCompare(m_origin, other.m_origin)) - return x; - return codePointCompare(m_databaseName, other.m_databaseName); -} - -Vector DatabaseMetaDataKey::encode(int64_t databaseId, MetaDataType metaDataType) -{ - KeyPrefix prefix(databaseId); - Vector ret = prefix.encode(); - ret.appendVector(encodeByte(metaDataType)); - return ret; -} - -ObjectStoreMetaDataKey::ObjectStoreMetaDataKey() - : m_objectStoreId(-1) - , m_metaDataType(-1) -{ -} - -const char* ObjectStoreMetaDataKey::decode(const char* start, const char* limit, ObjectStoreMetaDataKey* result) -{ - KeyPrefix prefix; - const char* p = KeyPrefix::decode(start, limit, &prefix); - if (!p) - return 0; - ASSERT(prefix.m_databaseId); - ASSERT(!prefix.m_objectStoreId); - ASSERT(!prefix.m_indexId); - if (p == limit) - return 0; - unsigned char typeByte = 0; - p = decodeByte(p, limit, typeByte); - ASSERT_UNUSED(typeByte, typeByte == ObjectStoreMetaDataTypeByte); - if (p == limit) - return 0; - p = decodeVarInt(p, limit, result->m_objectStoreId); - if (!p) - return 0; - ASSERT(result->m_objectStoreId); - if (p == limit) - return 0; - return decodeByte(p, limit, result->m_metaDataType); -} - -Vector ObjectStoreMetaDataKey::encode(int64_t databaseId, int64_t objectStoreId, unsigned char metaDataType) -{ - KeyPrefix prefix(databaseId); - Vector ret = prefix.encode(); - ret.appendVector(encodeByte(ObjectStoreMetaDataTypeByte)); - ret.appendVector(encodeVarInt(objectStoreId)); - ret.appendVector(encodeByte(metaDataType)); - return ret; -} - -Vector ObjectStoreMetaDataKey::encodeMaxKey(int64_t databaseId) -{ - return encode(databaseId, INT64_MAX, ObjectMetaDataTypeMaximum); -} - -Vector ObjectStoreMetaDataKey::encodeMaxKey(int64_t databaseId, int64_t objectStoreId) -{ - return encode(databaseId, objectStoreId, ObjectMetaDataTypeMaximum); -} - -int64_t ObjectStoreMetaDataKey::objectStoreId() const -{ - ASSERT(m_objectStoreId >= 0); - return m_objectStoreId; -} -unsigned char ObjectStoreMetaDataKey::metaDataType() const -{ - return m_metaDataType; -} - -int ObjectStoreMetaDataKey::compare(const ObjectStoreMetaDataKey& other) -{ - ASSERT(m_objectStoreId >= 0); - if (int x = compareInts(m_objectStoreId, other.m_objectStoreId)) - return x; - int64_t result = m_metaDataType - other.m_metaDataType; - if (result < 0) - return -1; - return (result > 0) ? 1 : result; -} - -IndexMetaDataKey::IndexMetaDataKey() - : m_objectStoreId(-1) - , m_indexId(-1) - , m_metaDataType(0) -{ -} - -const char* IndexMetaDataKey::decode(const char* start, const char* limit, IndexMetaDataKey* result) -{ - KeyPrefix prefix; - const char* p = KeyPrefix::decode(start, limit, &prefix); - if (!p) - return 0; - ASSERT(prefix.m_databaseId); - ASSERT(!prefix.m_objectStoreId); - ASSERT(!prefix.m_indexId); - if (p == limit) - return 0; - unsigned char typeByte = 0; - p = decodeByte(p, limit, typeByte); - ASSERT_UNUSED(typeByte, typeByte == IndexMetaDataTypeByte); - if (p == limit) - return 0; - p = decodeVarInt(p, limit, result->m_objectStoreId); - if (!p) - return 0; - p = decodeVarInt(p, limit, result->m_indexId); - if (!p) - return 0; - if (p == limit) - return 0; - return decodeByte(p, limit, result->m_metaDataType); -} - -Vector IndexMetaDataKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, unsigned char metaDataType) -{ - KeyPrefix prefix(databaseId); - Vector ret = prefix.encode(); - ret.appendVector(encodeByte(IndexMetaDataTypeByte)); - ret.appendVector(encodeVarInt(objectStoreId)); - ret.appendVector(encodeVarInt(indexId)); - ret.appendVector(encodeByte(metaDataType)); - return ret; -} - -Vector IndexMetaDataKey::encodeMaxKey(int64_t databaseId, int64_t objectStoreId) -{ - return encode(databaseId, objectStoreId, INT64_MAX, IndexMetaDataTypeMaximum); -} - -Vector IndexMetaDataKey::encodeMaxKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId) -{ - return encode(databaseId, objectStoreId, indexId, IndexMetaDataTypeMaximum); -} - -int IndexMetaDataKey::compare(const IndexMetaDataKey& other) -{ - ASSERT(m_objectStoreId >= 0); - ASSERT(m_indexId >= 0); - - if (int x = compareInts(m_objectStoreId, other.m_objectStoreId)) - return x; - if (int x = compareInts(m_indexId, other.m_indexId)) - return x; - return m_metaDataType - other.m_metaDataType; -} - -int64_t IndexMetaDataKey::indexId() const -{ - ASSERT(m_indexId >= 0); - return m_indexId; -} - -ObjectStoreFreeListKey::ObjectStoreFreeListKey() - : m_objectStoreId(-1) -{ -} - -const char* ObjectStoreFreeListKey::decode(const char* start, const char* limit, ObjectStoreFreeListKey* result) -{ - KeyPrefix prefix; - const char* p = KeyPrefix::decode(start, limit, &prefix); - if (!p) - return 0; - ASSERT(prefix.m_databaseId); - ASSERT(!prefix.m_objectStoreId); - ASSERT(!prefix.m_indexId); - if (p == limit) - return 0; - unsigned char typeByte = 0; - p = decodeByte(p, limit, typeByte); - ASSERT_UNUSED(typeByte, typeByte == ObjectStoreFreeListTypeByte); - if (p == limit) - return 0; - return decodeVarInt(p, limit, result->m_objectStoreId); -} - -Vector ObjectStoreFreeListKey::encode(int64_t databaseId, int64_t objectStoreId) -{ - KeyPrefix prefix(databaseId); - Vector ret = prefix.encode(); - ret.appendVector(encodeByte(ObjectStoreFreeListTypeByte)); - ret.appendVector(encodeVarInt(objectStoreId)); - return ret; -} - -Vector ObjectStoreFreeListKey::encodeMaxKey(int64_t databaseId) -{ - return encode(databaseId, INT64_MAX); -} - -int64_t ObjectStoreFreeListKey::objectStoreId() const -{ - ASSERT(m_objectStoreId >= 0); - return m_objectStoreId; -} - -int ObjectStoreFreeListKey::compare(const ObjectStoreFreeListKey& other) -{ - // FIXME: It may seem strange that we're not comparing database id's, - // but that comparison will have been made earlier. - // We should probably make this more clear, though... - ASSERT(m_objectStoreId >= 0); - return compareInts(m_objectStoreId, other.m_objectStoreId); -} - -IndexFreeListKey::IndexFreeListKey() - : m_objectStoreId(-1) - , m_indexId(-1) -{ -} - -const char* IndexFreeListKey::decode(const char* start, const char* limit, IndexFreeListKey* result) -{ - KeyPrefix prefix; - const char* p = KeyPrefix::decode(start, limit, &prefix); - if (!p) - return 0; - ASSERT(prefix.m_databaseId); - ASSERT(!prefix.m_objectStoreId); - ASSERT(!prefix.m_indexId); - if (p == limit) - return 0; - unsigned char typeByte = 0; - p = decodeByte(p, limit, typeByte); - ASSERT_UNUSED(typeByte, typeByte == IndexFreeListTypeByte); - if (p == limit) - return 0; - p = decodeVarInt(p, limit, result->m_objectStoreId); - if (!p) - return 0; - return decodeVarInt(p, limit, result->m_indexId); -} - -Vector IndexFreeListKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId) -{ - KeyPrefix prefix(databaseId); - Vector ret = prefix.encode(); - ret.appendVector(encodeByte(IndexFreeListTypeByte)); - ret.appendVector(encodeVarInt(objectStoreId)); - ret.appendVector(encodeVarInt(indexId)); - return ret; -} - -Vector IndexFreeListKey::encodeMaxKey(int64_t databaseId, int64_t objectStoreId) -{ - return encode(databaseId, objectStoreId, INT64_MAX); -} - -int IndexFreeListKey::compare(const IndexFreeListKey& other) -{ - ASSERT(m_objectStoreId >= 0); - ASSERT(m_indexId >= 0); - if (int x = compareInts(m_objectStoreId, other.m_objectStoreId)) - return x; - return compareInts(m_indexId, other.m_indexId); -} - -int64_t IndexFreeListKey::objectStoreId() const -{ - ASSERT(m_objectStoreId >= 0); - return m_objectStoreId; -} - -int64_t IndexFreeListKey::indexId() const -{ - ASSERT(m_indexId >= 0); - return m_indexId; -} - -// FIXME: We never use this to look up object store ids, because a mapping -// is kept in the IDBDatabaseBackend. Can the mapping become unreliable? -// Can we remove this? -const char* ObjectStoreNamesKey::decode(const char* start, const char* limit, ObjectStoreNamesKey* result) -{ - KeyPrefix prefix; - const char* p = KeyPrefix::decode(start, limit, &prefix); - if (!p) - return 0; - ASSERT(prefix.m_databaseId); - ASSERT(!prefix.m_objectStoreId); - ASSERT(!prefix.m_indexId); - if (p == limit) - return 0; - unsigned char typeByte = 0; - p = decodeByte(p, limit, typeByte); - ASSERT_UNUSED(typeByte, typeByte == ObjectStoreNamesTypeByte); - return decodeStringWithLength(p, limit, result->m_objectStoreName); -} - -Vector ObjectStoreNamesKey::encode(int64_t databaseId, const String& objectStoreName) -{ - KeyPrefix prefix(databaseId); - Vector ret = prefix.encode(); - ret.appendVector(encodeByte(ObjectStoreNamesTypeByte)); - ret.appendVector(encodeStringWithLength(objectStoreName)); - return ret; -} - -int ObjectStoreNamesKey::compare(const ObjectStoreNamesKey& other) -{ - return codePointCompare(m_objectStoreName, other.m_objectStoreName); -} - -IndexNamesKey::IndexNamesKey() - : m_objectStoreId(-1) -{ -} - -// FIXME: We never use this to look up index ids, because a mapping -// is kept at a higher level. -const char* IndexNamesKey::decode(const char* start, const char* limit, IndexNamesKey* result) -{ - KeyPrefix prefix; - const char* p = KeyPrefix::decode(start, limit, &prefix); - if (!p) - return 0; - ASSERT(prefix.m_databaseId); - ASSERT(!prefix.m_objectStoreId); - ASSERT(!prefix.m_indexId); - if (p == limit) - return 0; - unsigned char typeByte = 0; - p = decodeByte(p, limit, typeByte); - ASSERT_UNUSED(typeByte, typeByte == IndexNamesKeyTypeByte); - if (p == limit) - return 0; - p = decodeVarInt(p, limit, result->m_objectStoreId); - if (!p) - return 0; - return decodeStringWithLength(p, limit, result->m_indexName); -} - -Vector IndexNamesKey::encode(int64_t databaseId, int64_t objectStoreId, const String& indexName) -{ - KeyPrefix prefix(databaseId); - Vector ret = prefix.encode(); - ret.appendVector(encodeByte(IndexNamesKeyTypeByte)); - ret.appendVector(encodeVarInt(objectStoreId)); - ret.appendVector(encodeStringWithLength(indexName)); - return ret; -} - -int IndexNamesKey::compare(const IndexNamesKey& other) -{ - ASSERT(m_objectStoreId >= 0); - if (int x = compareInts(m_objectStoreId, other.m_objectStoreId)) - return x; - return codePointCompare(m_indexName, other.m_indexName); -} - -const char* ObjectStoreDataKey::decode(const char* start, const char* end, ObjectStoreDataKey* result) -{ - KeyPrefix prefix; - const char* p = KeyPrefix::decode(start, end, &prefix); - if (!p) - return 0; - ASSERT(prefix.m_databaseId); - ASSERT(prefix.m_objectStoreId); - ASSERT(prefix.m_indexId == SpecialIndexNumber); - if (p == end) - return 0; - return extractEncodedIDBKey(p, end, &result->m_encodedUserKey); -} - -Vector ObjectStoreDataKey::encode(int64_t databaseId, int64_t objectStoreId, const Vector encodedUserKey) -{ - KeyPrefix prefix(KeyPrefix::createWithSpecialIndex(databaseId, objectStoreId, SpecialIndexNumber)); - Vector ret = prefix.encode(); - ret.appendVector(encodedUserKey); - - return ret; -} - -Vector ObjectStoreDataKey::encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey) -{ - return encode(databaseId, objectStoreId, encodeIDBKey(userKey)); -} - -int ObjectStoreDataKey::compare(const ObjectStoreDataKey& other, bool& ok) -{ - return compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey, ok); -} - -PassRefPtr ObjectStoreDataKey::userKey() const -{ - RefPtr key; - decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key); - return key; -} - -const int64_t ObjectStoreDataKey::SpecialIndexNumber = ObjectStoreDataIndexId; - -const char* ExistsEntryKey::decode(const char* start, const char* end, ExistsEntryKey* result) -{ - KeyPrefix prefix; - const char* p = KeyPrefix::decode(start, end, &prefix); - if (!p) - return 0; - ASSERT(prefix.m_databaseId); - ASSERT(prefix.m_objectStoreId); - ASSERT(prefix.m_indexId == SpecialIndexNumber); - if (p == end) - return 0; - return extractEncodedIDBKey(p, end, &result->m_encodedUserKey); -} - -Vector ExistsEntryKey::encode(int64_t databaseId, int64_t objectStoreId, const Vector& encodedKey) -{ - KeyPrefix prefix(KeyPrefix::createWithSpecialIndex(databaseId, objectStoreId, SpecialIndexNumber)); - Vector ret = prefix.encode(); - ret.appendVector(encodedKey); - return ret; -} - -Vector ExistsEntryKey::encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey) -{ - return encode(databaseId, objectStoreId, encodeIDBKey(userKey)); -} - -int ExistsEntryKey::compare(const ExistsEntryKey& other, bool& ok) -{ - return compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey, ok); -} - -PassRefPtr ExistsEntryKey::userKey() const -{ - RefPtr key; - decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key); - return key; -} - -const int64_t ExistsEntryKey::SpecialIndexNumber = ExistsEntryIndexId; - -IndexDataKey::IndexDataKey() - : m_databaseId(-1) - , m_objectStoreId(-1) - , m_indexId(-1) - , m_sequenceNumber(-1) -{ -} - -const char* IndexDataKey::decode(const char* start, const char* limit, IndexDataKey* result) -{ - KeyPrefix prefix; - const char* p = KeyPrefix::decode(start, limit, &prefix); - if (!p) - return 0; - ASSERT(prefix.m_databaseId); - ASSERT(prefix.m_objectStoreId); - ASSERT(prefix.m_indexId >= MinimumIndexId); - result->m_databaseId = prefix.m_databaseId; - result->m_objectStoreId = prefix.m_objectStoreId; - result->m_indexId = prefix.m_indexId; - result->m_sequenceNumber = -1; - result->m_encodedPrimaryKey = minIDBKey(); - - p = extractEncodedIDBKey(p, limit, &result->m_encodedUserKey); - if (!p) - return 0; - - // [optional] sequence number - if (p == limit) - return p; - p = decodeVarInt(p, limit, result->m_sequenceNumber); - if (!p) - return 0; - - // [optional] primary key - if (p == limit) - return p; - p = extractEncodedIDBKey(p, limit, &result->m_encodedPrimaryKey); - if (!p) - return 0; - - return p; -} - -Vector IndexDataKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const Vector& encodedUserKey, const Vector& encodedPrimaryKey, int64_t sequenceNumber) -{ - KeyPrefix prefix(databaseId, objectStoreId, indexId); - Vector ret = prefix.encode(); - ret.appendVector(encodedUserKey); - ret.appendVector(encodeVarInt(sequenceNumber)); - ret.appendVector(encodedPrimaryKey); - return ret; -} - -Vector IndexDataKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& userKey) -{ - return encode(databaseId, objectStoreId, indexId, encodeIDBKey(userKey), minIDBKey()); -} - -Vector IndexDataKey::encodeMinKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId) -{ - return encode(databaseId, objectStoreId, indexId, minIDBKey(), minIDBKey()); -} - -Vector IndexDataKey::encodeMaxKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId) -{ - return encode(databaseId, objectStoreId, indexId, maxIDBKey(), maxIDBKey(), INT64_MAX); -} - -int IndexDataKey::compare(const IndexDataKey& other, bool ignoreDuplicates, bool& ok) -{ - ASSERT(m_databaseId >= 0); - ASSERT(m_objectStoreId >= 0); - ASSERT(m_indexId >= 0); - int result = compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey, ok); - if (!ok || result) - return result; - if (ignoreDuplicates) - return 0; - result = compareEncodedIDBKeys(m_encodedPrimaryKey, other.m_encodedPrimaryKey, ok); - if (!ok || result) - return result; - return compareInts(m_sequenceNumber, other.m_sequenceNumber); -} - -int64_t IndexDataKey::databaseId() const -{ - ASSERT(m_databaseId >= 0); - return m_databaseId; -} - -int64_t IndexDataKey::objectStoreId() const -{ - ASSERT(m_objectStoreId >= 0); - return m_objectStoreId; -} - -int64_t IndexDataKey::indexId() const -{ - ASSERT(m_indexId >= 0); - return m_indexId; -} - -PassRefPtr IndexDataKey::userKey() const -{ - RefPtr key; - decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key); - return key; -} - -PassRefPtr IndexDataKey::primaryKey() const -{ - RefPtr key; - decodeIDBKey(m_encodedPrimaryKey.begin(), m_encodedPrimaryKey.end(), key); - return key; -} - -} // namespace IDBLevelDBCoding -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB) diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.h deleted file mode 100644 index 9ac39bfd3..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.h +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBLevelDBCoding_h -#define IDBLevelDBCoding_h - -#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -#include -#include -#include - -namespace WebCore { - -class IDBKey; -class IDBKeyPath; -class LevelDBSlice; - -namespace IDBLevelDBCoding { - -const unsigned char MinimumIndexId = 30; - -// As most of the IDBKeys and encoded values are short, we initialize some Vectors with a default inline buffer size -// to reduce the memory re-allocations when the Vectors are appended. -static const size_t DefaultInlineBufferSize = 32; - -Vector encodeByte(unsigned char); -const char* decodeByte(const char* p, const char* limit, unsigned char& foundChar); -Vector maxIDBKey(); -Vector minIDBKey(); -Vector encodeBool(bool); -bool decodeBool(const char* begin, const char* end); -Vector encodeInt(int64_t); -inline Vector encodeIntSafely(int64_t nParam, int64_t max) -{ - ASSERT_UNUSED(max, nParam <= max); - return encodeInt(nParam); -} -int64_t decodeInt(const char* begin, const char* end); -Vector encodeVarInt(int64_t); -const char* decodeVarInt(const char* p, const char* limit, int64_t& foundInt); -Vector encodeString(const String&); -String decodeString(const char* p, const char* end); -Vector encodeStringWithLength(const String&); -const char* decodeStringWithLength(const char* p, const char* limit, String& foundString); -int compareEncodedStringsWithLength(const char*& p, const char* limitP, const char*& q, const char* limitQ, bool& ok); -Vector encodeDouble(double); -const char* decodeDouble(const char* p, const char* limit, double*); -void encodeIDBKey(const IDBKey&, Vector& into); -Vector encodeIDBKey(const IDBKey&); -const char* decodeIDBKey(const char* p, const char* limit, RefPtr& foundKey); -const char* extractEncodedIDBKey(const char* start, const char* limit, Vector* result); -int compareEncodedIDBKeys(const Vector&, const Vector&, bool& ok); -Vector encodeIDBKeyPath(const IDBKeyPath&); -IDBKeyPath decodeIDBKeyPath(const char*, const char*); - -int compare(const LevelDBSlice&, const LevelDBSlice&, bool indexKeys = false); - -class KeyPrefix { -public: - KeyPrefix(); - explicit KeyPrefix(int64_t databaseId); - KeyPrefix(int64_t databaseId, int64_t objectStoreId); - KeyPrefix(int64_t databaseId, int64_t objectStoreId, int64_t indexId); - static KeyPrefix createWithSpecialIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId); - - static const char* decode(const char* start, const char* limit, KeyPrefix* result); - Vector encode() const; - static Vector encodeEmpty(); - int compare(const KeyPrefix& other) const; - - enum Type { - GlobalMetaData, - DatabaseMetaData, - ObjectStoreData, - ExistsEntry, - IndexData, - InvalidType - }; - - static const size_t kMaxDatabaseIdSizeBits = 3; - static const size_t kMaxObjectStoreIdSizeBits = 3; - static const size_t kMaxIndexIdSizeBits = 2; - - static const size_t kMaxDatabaseIdSizeBytes = 1ULL << kMaxDatabaseIdSizeBits; // 8 - static const size_t kMaxObjectStoreIdSizeBytes = 1ULL << kMaxObjectStoreIdSizeBits; // 8 - static const size_t kMaxIndexIdSizeBytes = 1ULL << kMaxIndexIdSizeBits; // 4 - - static const size_t kMaxDatabaseIdBits = kMaxDatabaseIdSizeBytes * 8 - 1; // 63 - static const size_t kMaxObjectStoreIdBits = kMaxObjectStoreIdSizeBytes * 8 - 1; // 63 - static const size_t kMaxIndexIdBits = kMaxIndexIdSizeBytes * 8 - 1; // 31 - - static const int64_t kMaxDatabaseId = (1ULL << kMaxDatabaseIdBits) - 1; // max signed int64_t - static const int64_t kMaxObjectStoreId = (1ULL << kMaxObjectStoreIdBits) - 1; // max signed int64_t - static const int64_t kMaxIndexId = (1ULL << kMaxIndexIdBits) - 1; // max signed int32_t - - static bool isValidDatabaseId(int64_t databaseId); - static bool isValidObjectStoreId(int64_t indexId); - static bool isValidIndexId(int64_t indexId); - static bool validIds(int64_t databaseId, int64_t objectStoreId, int64_t indexId) - { - return isValidDatabaseId(databaseId) && isValidObjectStoreId(objectStoreId) && isValidIndexId(indexId); - } - static bool validIds(int64_t databaseId, int64_t objectStoreId) - { - return isValidDatabaseId(databaseId) && isValidObjectStoreId(objectStoreId); - } - - Type type() const; - - int64_t m_databaseId; - int64_t m_objectStoreId; - int64_t m_indexId; - - static const int64_t InvalidId = -1; - -private: - static Vector encodeInternal(int64_t databaseId, int64_t objectStoreId, int64_t indexId); - // Special constructor for createWithSpecialIndex() - KeyPrefix(Type, int64_t databaseId, int64_t objectStoreId, int64_t indexId); -}; - -class SchemaVersionKey { -public: - static Vector encode(); -}; - -class MaxDatabaseIdKey { -public: - static Vector encode(); -}; - -class DataVersionKey { -public: - static Vector encode(); -}; - -class DatabaseFreeListKey { -public: - DatabaseFreeListKey(); - static const char* decode(const char* start, const char* limit, DatabaseFreeListKey* result); - static Vector encode(int64_t databaseId); - static Vector encodeMaxKey(); - int64_t databaseId() const; - int compare(const DatabaseFreeListKey& other) const; - -private: - int64_t m_databaseId; -}; - -class DatabaseNameKey { -public: - static const char* decode(const char* start, const char* limit, DatabaseNameKey* result); - static Vector encode(const String& origin, const String& databaseName); - static Vector encodeMinKeyForOrigin(const String& origin); - static Vector encodeStopKeyForOrigin(const String& origin); - String origin() const { return m_origin; } - String databaseName() const { return m_databaseName; } - int compare(const DatabaseNameKey& other); - -private: - String m_origin; // FIXME: Store encoded strings, or just pointers. - String m_databaseName; -}; - -class DatabaseMetaDataKey { -public: - enum MetaDataType { - OriginName = 0, - DatabaseName = 1, - UserVersion = 2, - MaxObjectStoreId = 3, - UserIntVersion = 4, - MaxSimpleMetaDataType = 5 - }; - - static Vector encode(int64_t databaseId, MetaDataType); -}; - -class ObjectStoreMetaDataKey { -public: - enum MetaDataType { - Name = 0, - KeyPath = 1, - AutoIncrement = 2, - Evictable = 3, - LastVersion = 4, - MaxIndexId = 5, - HasKeyPath = 6, - KeyGeneratorCurrentNumber = 7 - }; - - ObjectStoreMetaDataKey(); - static const char* decode(const char* start, const char* limit, ObjectStoreMetaDataKey* result); - static Vector encode(int64_t databaseId, int64_t objectStoreId, unsigned char metaDataType); - static Vector encodeMaxKey(int64_t databaseId); - static Vector encodeMaxKey(int64_t databaseId, int64_t objectStoreId); - int64_t objectStoreId() const; - unsigned char metaDataType() const; - int compare(const ObjectStoreMetaDataKey& other); - -private: - int64_t m_objectStoreId; - unsigned char m_metaDataType; -}; - -class IndexMetaDataKey { -public: - enum MetaDataType { - Name = 0, - Unique = 1, - KeyPath = 2, - MultiEntry = 3 - }; - - IndexMetaDataKey(); - static const char* decode(const char* start, const char* limit, IndexMetaDataKey* result); - static Vector encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, unsigned char metaDataType); - static Vector encodeMaxKey(int64_t databaseId, int64_t objectStoreId); - static Vector encodeMaxKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId); - int compare(const IndexMetaDataKey& other); - int64_t indexId() const; - unsigned char metaDataType() const { return m_metaDataType; } - -private: - int64_t m_objectStoreId; - int64_t m_indexId; - unsigned char m_metaDataType; -}; - -class ObjectStoreFreeListKey { -public: - ObjectStoreFreeListKey(); - static const char* decode(const char* start, const char* limit, ObjectStoreFreeListKey* result); - static Vector encode(int64_t databaseId, int64_t objectStoreId); - static Vector encodeMaxKey(int64_t databaseId); - int64_t objectStoreId() const; - int compare(const ObjectStoreFreeListKey& other); - -private: - int64_t m_objectStoreId; -}; - -class IndexFreeListKey { -public: - IndexFreeListKey(); - static const char* decode(const char* start, const char* limit, IndexFreeListKey* result); - static Vector encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId); - static Vector encodeMaxKey(int64_t databaseId, int64_t objectStoreId); - int compare(const IndexFreeListKey& other); - int64_t objectStoreId() const; - int64_t indexId() const; - -private: - int64_t m_objectStoreId; - int64_t m_indexId; -}; - -class ObjectStoreNamesKey { -public: - // FIXME: We never use this to look up object store ids, because a mapping - // is kept in the IDBDatabaseBackend. Can the mapping become unreliable? - // Can we remove this? - static const char* decode(const char* start, const char* limit, ObjectStoreNamesKey* result); - static Vector encode(int64_t databaseId, const String& objectStoreName); - int compare(const ObjectStoreNamesKey& other); - String objectStoreName() const { return m_objectStoreName; } - -private: - String m_objectStoreName; // FIXME: Store the encoded string, or just pointers to it. -}; - -class IndexNamesKey { -public: - IndexNamesKey(); - // FIXME: We never use this to look up index ids, because a mapping - // is kept at a higher level. - static const char* decode(const char* start, const char* limit, IndexNamesKey* result); - static Vector encode(int64_t databaseId, int64_t objectStoreId, const String& indexName); - int compare(const IndexNamesKey& other); - String indexName() const { return m_indexName; } - -private: - int64_t m_objectStoreId; - String m_indexName; -}; - -class ObjectStoreDataKey { -public: - static const char* decode(const char* start, const char* end, ObjectStoreDataKey* result); - static Vector encode(int64_t databaseId, int64_t objectStoreId, const Vector encodedUserKey); - static Vector encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey); - int compare(const ObjectStoreDataKey& other, bool& ok); - PassRefPtr userKey() const; - static const int64_t SpecialIndexNumber; - -private: - Vector m_encodedUserKey; -}; - -class ExistsEntryKey { -public: - static const char* decode(const char* start, const char* end, ExistsEntryKey* result); - static Vector encode(int64_t databaseId, int64_t objectStoreId, const Vector& encodedKey); - static Vector encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey); - int compare(const ExistsEntryKey& other, bool& ok); - PassRefPtr userKey() const; - - static const int64_t SpecialIndexNumber; - -private: - Vector m_encodedUserKey; -}; - -class IndexDataKey { -public: - IndexDataKey(); - static const char* decode(const char* start, const char* limit, IndexDataKey* result); - static Vector encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const Vector& encodedUserKey, const Vector& encodedPrimaryKey, int64_t sequenceNumber = 0); - static Vector encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& userKey); - static Vector encodeMinKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId); - static Vector encodeMaxKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId); - int compare(const IndexDataKey& other, bool ignoreDuplicates, bool& ok); - int64_t databaseId() const; - int64_t objectStoreId() const; - int64_t indexId() const; - PassRefPtr userKey() const; - PassRefPtr primaryKey() const; - -private: - int64_t m_databaseId; - int64_t m_objectStoreId; - int64_t m_indexId; - Vector m_encodedUserKey; - Vector m_encodedPrimaryKey; - int64_t m_sequenceNumber; -}; - -} // namespace IDBLevelDBCoding - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -#endif // IDBLevelDBCoding_h diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.cpp deleted file mode 100644 index b7333f965..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.cpp +++ /dev/null @@ -1,626 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBServerConnectionLevelDB.h" - -#if ENABLE(INDEXED_DATABASE) -#if USE(LEVELDB) - -#include "IDBBackingStoreCursorLevelDB.h" -#include "IDBBackingStoreLevelDB.h" -#include "IDBBackingStoreTransactionLevelDB.h" -#include "IDBCursorBackend.h" -#include "IDBFactoryBackendLevelDB.h" -#include "IDBIndexWriterLevelDB.h" -#include - -#define ASYNC_COMPLETION_CALLBACK_WITH_ARG(callback, arg) \ - callOnMainThread([callback, arg]() { \ - callback(arg); \ - }); - -#define ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(callback, arg1, arg2) \ - callOnMainThread([callback]() { \ - callback(arg1, arg2); \ - }); - -#define ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(callback) \ - callOnMainThread([callback]() { \ - callback(0); \ - }); - -#define EMPTY_ASYNC_COMPLETION_CALLBACK(callback) \ - callOnMainThread([callback]() { \ - callback(); \ - }); - - -namespace WebCore { - -IDBServerConnectionLevelDB::IDBServerConnectionLevelDB(const String& databaseName, IDBBackingStoreLevelDB* backingStore) - : m_backingStore(backingStore) - , m_nextCursorID(1) - , m_closed(false) - , m_databaseName(databaseName) -{ -} - -IDBServerConnectionLevelDB::~IDBServerConnectionLevelDB() -{ -} - -bool IDBServerConnectionLevelDB::isClosed() -{ - return m_closed; -} - -void IDBServerConnectionLevelDB::getOrEstablishIDBDatabaseMetadata(GetIDBDatabaseMetadataFunction callback) -{ - RefPtr self(this); - m_backingStore->getOrEstablishIDBDatabaseMetadata(m_databaseName, [self, this, callback](const IDBDatabaseMetadata& metadata, bool success) { - callback(metadata, success); - }); -} - -void IDBServerConnectionLevelDB::deleteDatabase(const String& name, BoolCallbackFunction successCallback) -{ - RefPtr self(this); - m_backingStore->deleteDatabase(name, [self, this, successCallback](bool success) { - successCallback(success); - }); -} - -void IDBServerConnectionLevelDB::close() -{ - m_backingStore.clear(); - m_closed = true; -} - -void IDBServerConnectionLevelDB::openTransaction(int64_t transactionID, const HashSet&, IndexedDB::TransactionMode, BoolCallbackFunction successCallback) -{ - if (!m_backingStore) { - callOnMainThread([successCallback]() { - successCallback(false); - }); - return; - } - - m_backingStore->establishBackingStoreTransaction(transactionID); - callOnMainThread([successCallback]() { - successCallback(true); - }); -} - -void IDBServerConnectionLevelDB::beginTransaction(int64_t transactionID, std::function completionCallback) -{ - RefPtr transaction = m_backingStoreTransactions.get(transactionID); - ASSERT(transaction); - - transaction->begin(); - callOnMainThread(completionCallback); -} - -void IDBServerConnectionLevelDB::commitTransaction(int64_t transactionID, BoolCallbackFunction successCallback) -{ - RefPtr transaction = m_backingStoreTransactions.get(transactionID); - ASSERT(transaction); - - bool result = transaction->commit(); - callOnMainThread([successCallback, result]() { - successCallback(result); - }); -} - -void IDBServerConnectionLevelDB::resetTransaction(int64_t transactionID, std::function completionCallback) -{ - RefPtr transaction = m_backingStoreTransactions.get(transactionID); - ASSERT(transaction); - - transaction->resetTransaction(); - callOnMainThread(completionCallback); -} - -void IDBServerConnectionLevelDB::rollbackTransaction(int64_t transactionID, std::function completionCallback) -{ - RefPtr transaction = m_backingStoreTransactions.get(transactionID); - ASSERT(transaction); - - transaction->rollback(); - callOnMainThread(completionCallback); -} - -void IDBServerConnectionLevelDB::setIndexKeys(int64_t transactionID, int64_t databaseID, int64_t objectStoreID, const IDBObjectStoreMetadata& objectStoreMetadata, IDBKey& primaryKey, const Vector& indexIDs, const Vector>>& indexKeys, std::function)> completionCallback) -{ - RefPtr backingStoreTransaction = m_backingStoreTransactions.get(transactionID); - ASSERT(backingStoreTransaction); - - RefPtr recordIdentifier; - bool ok = m_backingStore->keyExistsInObjectStore(*backingStoreTransaction, databaseID, objectStoreID, primaryKey, recordIdentifier); - if (!ok) { - callOnMainThread([completionCallback]() { - completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: setting index keys.")); - }); - return; - } - if (!recordIdentifier) { - callOnMainThread([completionCallback]() { - completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: setting index keys for object store.")); - }); - return; - } - - Vector> indexWriters; - String errorMessage; - bool obeysConstraints = false; - - bool backingStoreSuccess = m_backingStore->makeIndexWriters(transactionID, databaseID, objectStoreMetadata, primaryKey, false, indexIDs, indexKeys, indexWriters, &errorMessage, obeysConstraints); - if (!backingStoreSuccess) { - callOnMainThread([completionCallback]() { - completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: backing store error updating index keys.")); - }); - return; - } - if (!obeysConstraints) { - callOnMainThread([completionCallback, errorMessage]() { - completionCallback(IDBDatabaseError::create(IDBDatabaseException::ConstraintError, errorMessage)); - }); - return; - } - - for (size_t i = 0; i < indexWriters.size(); ++i) { - IDBIndexWriterLevelDB* indexWriter = indexWriters[i].get(); - indexWriter->writeIndexKeys(recordIdentifier.get(), *m_backingStore, *backingStoreTransaction, databaseID, objectStoreID); - } - - ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback); -} - -void IDBServerConnectionLevelDB::createObjectStore(IDBTransactionBackend& transaction, const CreateObjectStoreOperation& operation, std::function)> completionCallback) -{ - IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id()); - ASSERT(backingStoreTransaction); - - String objectStoreName = operation.objectStoreMetadata().name; - - if (!m_backingStore->createObjectStore(*backingStoreTransaction, transaction.database().id(), operation.objectStoreMetadata().id, objectStoreName, operation.objectStoreMetadata().keyPath, operation.objectStoreMetadata().autoIncrement)) { - callOnMainThread([completionCallback, objectStoreName]() { - completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, String::format("Internal error creating object store '%s'.", objectStoreName.utf8().data()))); - }); - return; - } - ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback); -} - -void IDBServerConnectionLevelDB::createIndex(IDBTransactionBackend& transaction, const CreateIndexOperation& operation, std::function)> completionCallback) -{ - IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id()); - ASSERT(backingStoreTransaction); - - const IDBIndexMetadata& indexMetadata = operation.idbIndexMetadata(); - if (!m_backingStore->createIndex(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), indexMetadata.id, indexMetadata.name, indexMetadata.keyPath, indexMetadata.unique, indexMetadata.multiEntry)) { - callOnMainThread([completionCallback, indexMetadata]() { - completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, String::format("Internal error when trying to create index '%s'.", indexMetadata.name.utf8().data()))); - }); - return; - } - ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback); -} - -void IDBServerConnectionLevelDB::deleteIndex(IDBTransactionBackend& transaction, const DeleteIndexOperation& operation, std::function)> completionCallback) -{ - IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id()); - ASSERT(backingStoreTransaction); - - const IDBIndexMetadata& indexMetadata = operation.idbIndexMetadata(); - if (!m_backingStore->deleteIndex(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), indexMetadata.id)) { - callOnMainThread([completionCallback, indexMetadata]() { - completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, String::format("Internal error deleting index '%s'.", indexMetadata.name.utf8().data()))); - }); - return; - } - ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback); -} - -void IDBServerConnectionLevelDB::get(IDBTransactionBackend& transaction, const GetOperation& operation, std::function)> completionCallback) -{ - IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id()); - ASSERT(backingStoreTransaction); - - RefPtr key; - - if (operation.keyRange()->isOnlyKey()) - key = operation.keyRange()->lower(); - else { - RefPtr backingStoreCursor; - int64_t cursorID = m_nextCursorID++; - - if (operation.indexID() == IDBIndexMetadata::InvalidId) { - ASSERT(operation.cursorType() != IndexedDB::CursorType::KeyOnly); - // ObjectStore Retrieval Operation - backingStoreCursor = m_backingStore->openObjectStoreCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.keyRange(), IndexedDB::CursorDirection::Next); - } else { - if (operation.cursorType() == IndexedDB::CursorType::KeyOnly) { - // Index Value Retrieval Operation - backingStoreCursor = m_backingStore->openIndexKeyCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), operation.keyRange(), IndexedDB::CursorDirection::Next); - } else { - // Index Referenced Value Retrieval Operation - backingStoreCursor = m_backingStore->openIndexCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), operation.keyRange(), IndexedDB::CursorDirection::Next); - } - } - - if (!backingStoreCursor) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), nullptr); - return; - } - - key = backingStoreCursor->key(); - } - - RefPtr primaryKey; - bool ok; - if (operation.indexID() == IDBIndexMetadata::InvalidId) { - // Object Store Retrieval Operation - Vector value; - ok = m_backingStore->getRecord(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), *key, value); - if (!ok) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error in getRecord.")); - return; - } - - if (value.isEmpty()) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), nullptr); - return; - } - - IDBGetResult result; - - if (operation.autoIncrement() && !operation.keyPath().isNull()) - result = IDBGetResult(SharedBuffer::adoptVector(value), key, operation.keyPath()); - else - result = IDBGetResult(SharedBuffer::adoptVector(value)); - - callOnMainThread([completionCallback, result]() { - completionCallback(result, nullptr); - }); - return; - } - - // From here we are dealing only with indexes. - ok = m_backingStore->getPrimaryKeyViaIndex(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), *key, primaryKey); - if (!ok) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error in getPrimaryKeyViaIndex.")); - return; - } - if (!primaryKey) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), nullptr); - return; - } - if (operation.cursorType() == IndexedDB::CursorType::KeyOnly) { - // Index Value Retrieval Operation - IDBGetResult result(primaryKey.release()); - callOnMainThread([completionCallback, result]() { - completionCallback(result, nullptr); - }); - return; - } - - // Index Referenced Value Retrieval Operation - Vector value; - ok = m_backingStore->getRecord(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), *primaryKey, value); - if (!ok) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error in getRecord.")); - return; - } - - if (value.isEmpty()) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), nullptr); - return; - } - if (operation.autoIncrement() && !operation.keyPath().isNull()) { - IDBGetResult result(SharedBuffer::adoptVector(value), key, operation.keyPath()); - callOnMainThread([completionCallback, result]() { - completionCallback(result, nullptr); - }); - - return; - } - - IDBGetResult result(SharedBuffer::adoptVector(value)); - callOnMainThread([completionCallback, result]() { - completionCallback(result, nullptr); - }); -} - -void IDBServerConnectionLevelDB::put(IDBTransactionBackend& transaction, const PutOperation& operation, std::function, PassRefPtr)> completionCallback) -{ - IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id()); - ASSERT(backingStoreTransaction); - - bool keyWasGenerated = false; - - RefPtr key; - if (operation.putMode() != IDBDatabaseBackend::CursorUpdate && operation.objectStore().autoIncrement && !operation.key()) { - RefPtr autoIncKey = m_backingStore->generateKey(transaction, transaction.database().id(), operation.objectStore().id); - keyWasGenerated = true; - if (!autoIncKey->isValid()) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::ConstraintError, "Maximum key generator value reached.")); - return; - } - key = autoIncKey; - } else - key = operation.key(); - - ASSERT(key); - ASSERT(key->isValid()); - - RefPtr recordIdentifier; - if (operation.putMode() == IDBDatabaseBackend::AddOnly) { - bool ok = m_backingStore->keyExistsInObjectStore(*backingStoreTransaction, transaction.database().id(), operation.objectStore().id, *key, recordIdentifier); - if (!ok) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error checking key existence.")); - return; - } - if (recordIdentifier) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::ConstraintError, "Key already exists in the object store.")); - return; - } - } - - Vector> indexWriters; - String errorMessage; - bool obeysConstraints = false; - bool backingStoreSuccess = m_backingStore->makeIndexWriters(transaction.id(), transaction.database().id(), operation.objectStore(), *key, keyWasGenerated, operation.indexIDs(), operation.indexKeys(), indexWriters, &errorMessage, obeysConstraints); - if (!backingStoreSuccess) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: backing store error updating index keys.")); - return; - } - if (!obeysConstraints) { - callOnMainThread([completionCallback, errorMessage]() { \ - completionCallback(nullptr, IDBDatabaseError::create(IDBDatabaseException::ConstraintError, errorMessage)); \ - }); - return; - } - - // Before this point, don't do any mutation. After this point, rollback the transaction in case of error. - backingStoreSuccess = m_backingStore->putRecord(*backingStoreTransaction, transaction.database().id(), operation.objectStore().id, *key, operation.value(), recordIdentifier.get()); - if (!backingStoreSuccess) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: backing store error performing put/add.")); - return; - } - - for (size_t i = 0; i < indexWriters.size(); ++i) { - IDBIndexWriterLevelDB* indexWriter = indexWriters[i].get(); - indexWriter->writeIndexKeys(recordIdentifier.get(), *m_backingStore, *backingStoreTransaction, transaction.database().id(), operation.objectStore().id); - } - - if (operation.objectStore().autoIncrement && operation.putMode() != IDBDatabaseBackend::CursorUpdate && key->type() == IDBKey::NumberType) { - bool ok = m_backingStore->updateKeyGenerator(transaction, transaction.database().id(), operation.objectStore().id, *key, !keyWasGenerated); - if (!ok) { - ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error updating key generator.")); - return; - } - } - - callOnMainThread([completionCallback, key]() { \ - completionCallback(key.get(), nullptr); \ - }); -} - -void IDBServerConnectionLevelDB::openCursor(IDBTransactionBackend& transaction, const OpenCursorOperation& operation, std::function)> completionCallback) -{ - IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id()); - ASSERT(backingStoreTransaction); - - // The frontend has begun indexing, so this pauses the transaction - // until the indexing is complete. This can't happen any earlier - // because we don't want to switch to early mode in case multiple - // indexes are being created in a row, with put()'s in between. - if (operation.taskType() == IDBDatabaseBackend::PreemptiveTask) - transaction.addPreemptiveEvent(); - - int64_t cursorID = m_nextCursorID++; - - RefPtr backingStoreCursor; - if (operation.indexID() == IDBIndexMetadata::InvalidId) { - ASSERT(operation.cursorType() != IndexedDB::CursorType::KeyOnly); - backingStoreCursor = m_backingStore->openObjectStoreCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.keyRange(), operation.direction()); - } else { - ASSERT(operation.taskType() == IDBDatabaseBackend::NormalTask); - if (operation.cursorType() == IndexedDB::CursorType::KeyOnly) - backingStoreCursor = m_backingStore->openIndexKeyCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), operation.keyRange(), operation.direction()); - else - backingStoreCursor = m_backingStore->openIndexCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), operation.keyRange(), operation.direction()); - } - - if (!backingStoreCursor) { - // FIXME: Should it be an error to not have a backing store cursor? - cursorID = 0; - } - - callOnMainThread([completionCallback, cursorID]() { - completionCallback(cursorID, nullptr); - }); -} - -void IDBServerConnectionLevelDB::count(IDBTransactionBackend& transaction, const CountOperation& operation, std::function)> completionCallback) -{ - IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id()); - ASSERT(backingStoreTransaction); - - uint32_t count = 0; - RefPtr backingStoreCursor; - - int64_t cursorID = m_nextCursorID++; - - if (operation.indexID() == IDBIndexMetadata::InvalidId) - backingStoreCursor = m_backingStore->openObjectStoreKeyCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.keyRange(), IndexedDB::CursorDirection::Next); - else - backingStoreCursor = m_backingStore->openIndexKeyCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), operation.keyRange(), IndexedDB::CursorDirection::Next); - if (!backingStoreCursor) { - // FIXME: Is this an error case? - callOnMainThread([completionCallback, count]() { - completionCallback(count, nullptr); - }); - return; - } - - do { - ++count; - } while (backingStoreCursor->continueFunction(0)); - - callOnMainThread([completionCallback, count]() { - completionCallback(count, nullptr); - }); -} - -void IDBServerConnectionLevelDB::deleteRange(IDBTransactionBackend& transaction, const DeleteRangeOperation& operation, std::function)> completionCallback) -{ - IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id()); - ASSERT(backingStoreTransaction); - - int64_t cursorID = m_nextCursorID++; - - RefPtr backingStoreCursor = m_backingStore->openObjectStoreCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.keyRange(), IndexedDB::CursorDirection::Next); - if (backingStoreCursor) { - do { - if (!m_backingStore->deleteRecord(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), backingStoreCursor->recordIdentifier())) { - callOnMainThread([completionCallback]() { - completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Error deleting data in range")); - }); - return; - } - } while (backingStoreCursor->continueFunction(0)); - } - - callOnMainThread([completionCallback]() { - completionCallback(nullptr); - }); -} - -void IDBServerConnectionLevelDB::clearObjectStore(IDBTransactionBackend& transaction, const ClearObjectStoreOperation& operation, std::function)> completionCallback) -{ - IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id()); - ASSERT(backingStoreTransaction); - - if (!m_backingStore->clearObjectStore(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID())) { - callOnMainThread([completionCallback]() { - completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Error clearing object store")); - }); - return; - } - - ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback); -} - -void IDBServerConnectionLevelDB::deleteObjectStore(IDBTransactionBackend& transaction, const DeleteObjectStoreOperation& operation, std::function)> completionCallback) -{ - IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id()); - ASSERT(backingStoreTransaction); - - const IDBObjectStoreMetadata& objectStoreMetadata = operation.objectStoreMetadata(); - - if (!m_backingStore->deleteObjectStore(*backingStoreTransaction, transaction.database().id(), objectStoreMetadata.id)) { - callOnMainThread([completionCallback, objectStoreMetadata]() { - completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, String::format("Internal error deleting object store '%s'.", objectStoreMetadata.name.utf8().data()))); - }); - return; - } - ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback); -} - -void IDBServerConnectionLevelDB::changeDatabaseVersion(IDBTransactionBackend& transaction, const IDBDatabaseBackend::VersionChangeOperation&, std::function)> completionCallback) -{ - IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id()); - ASSERT(backingStoreTransaction); - - IDBDatabaseBackend& database = transaction.database(); - int64_t databaseId = database.id(); - - if (!m_backingStore->updateIDBDatabaseVersion(*backingStoreTransaction, databaseId, database.metadata().version)) { - RefPtr error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Error writing data to stable storage when updating version."); - ASYNC_COMPLETION_CALLBACK_WITH_ARG(completionCallback, error); - return; - } - - ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback); -} - -void IDBServerConnectionLevelDB::cursorAdvance(IDBCursorBackend& cursor, const CursorAdvanceOperation& operation, std::function, PassRefPtr, PassRefPtr, PassRefPtr)> completionCallback) -{ - IDBBackingStoreCursorLevelDB* backingStoreCursor = cursor.id() ? m_backingStoreCursors.get(cursor.id()) : 0; -#ifndef NDEBUG - if (cursor.id()) - ASSERT(backingStoreCursor); -#endif - - if (!backingStoreCursor || !backingStoreCursor->advance(operation.count())) { - m_backingStoreCursors.remove(cursor.id()); - - callOnMainThread([completionCallback]() { - completionCallback(nullptr, nullptr, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Unknown error advancing cursor")); - }); - - return; - } - - RefPtr key = backingStoreCursor->key(), primaryKey = backingStoreCursor->primaryKey(); - RefPtr value = backingStoreCursor->value(); - - callOnMainThread([completionCallback, key, primaryKey, value]() { - completionCallback(key, primaryKey, value, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Unknown error advancing cursor")); - }); -} - -void IDBServerConnectionLevelDB::cursorIterate(IDBCursorBackend& cursor, const CursorIterationOperation& operation, std::function, PassRefPtr, PassRefPtr, PassRefPtr)> completionCallback) -{ - IDBBackingStoreCursorLevelDB* backingStoreCursor = cursor.id() ? m_backingStoreCursors.get(cursor.id()) : 0; -#ifndef NDEBUG - if (cursor.id()) - ASSERT(backingStoreCursor); -#endif - - if (!backingStoreCursor || !backingStoreCursor->continueFunction(operation.key())) { - m_backingStoreCursors.remove(cursor.id()); - - callOnMainThread([completionCallback]() { - completionCallback(nullptr, nullptr, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Unknown error advancing cursor")); - }); - - return; - } - - RefPtr key = backingStoreCursor->key(), primaryKey = backingStoreCursor->primaryKey(); - RefPtr value = backingStoreCursor->value(); - - callOnMainThread([completionCallback, key, primaryKey, value]() { - completionCallback(key, primaryKey, value, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Unknown error advancing cursor")); - }); -} - -} // namespace WebCore - -#endif // USE(LEVELDB) -#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.h deleted file mode 100644 index e574f389f..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBServerConnectionLevelDB_h -#define IDBServerConnectionLevelDB_h - -#include "IDBServerConnection.h" - -#if ENABLE(INDEXED_DATABASE) -#if USE(LEVELDB) - -namespace WebCore { - -class IDBBackingStoreCursorLevelDB; -class IDBBackingStoreLevelDB; -class IDBBackingStoreTransactionLevelDB; - -class IDBServerConnectionLevelDB final : public IDBServerConnection { -public: - static PassRefPtr create(const String& databaseName, IDBBackingStoreLevelDB* backingStore) - { - return adoptRef(new IDBServerConnectionLevelDB(databaseName, backingStore)); - } - - virtual ~IDBServerConnectionLevelDB(); - - virtual bool isClosed() override; - - // Factory-level operations - virtual void deleteDatabase(const String& name, BoolCallbackFunction successCallback) override; - - // Database-level operations - virtual void getOrEstablishIDBDatabaseMetadata(GetIDBDatabaseMetadataFunction) override; - virtual void close() override; - - // Transaction-level operations - virtual void openTransaction(int64_t transactionID, const HashSet& objectStoreIds, IndexedDB::TransactionMode, BoolCallbackFunction successCallback) override; - virtual void beginTransaction(int64_t transactionID, std::function completionCallback) override; - virtual void commitTransaction(int64_t transactionID, BoolCallbackFunction successCallback) override; - virtual void resetTransaction(int64_t transactionID, std::function completionCallback) override; - virtual void rollbackTransaction(int64_t transactionID, std::function completionCallback) override; - virtual void setIndexKeys(int64_t transactionID, int64_t databaseID, int64_t objectStoreID, const IDBObjectStoreMetadata&, IDBKey& primaryKey, const Vector& indexIDs, const Vector>>& indexKeys, std::function)> completionCallback); - - virtual void createObjectStore(IDBTransactionBackend&, const CreateObjectStoreOperation&, std::function)> completionCallback) override; - virtual void createIndex(IDBTransactionBackend&, const CreateIndexOperation&, std::function)> completionCallback) override; - virtual void deleteIndex(IDBTransactionBackend&, const DeleteIndexOperation&, std::function)> completionCallback) override; - virtual void get(IDBTransactionBackend&, const GetOperation&, std::function)> completionCallback) override; - virtual void put(IDBTransactionBackend&, const PutOperation&, std::function, PassRefPtr)> completionCallback) override; - virtual void openCursor(IDBTransactionBackend&, const OpenCursorOperation&, std::function)> completionCallback) override; - virtual void count(IDBTransactionBackend&, const CountOperation&, std::function)> completionCallback) override; - virtual void deleteRange(IDBTransactionBackend&, const DeleteRangeOperation&, std::function)> completionCallback) override; - virtual void clearObjectStore(IDBTransactionBackend&, const ClearObjectStoreOperation&, std::function)> completionCallback) override; - virtual void deleteObjectStore(IDBTransactionBackend&, const DeleteObjectStoreOperation&, std::function)> completionCallback) override; - virtual void changeDatabaseVersion(IDBTransactionBackend&, const IDBDatabaseBackend::VersionChangeOperation&, std::function)> completionCallback) override; - - // Cursor-level operations - virtual void cursorAdvance(IDBCursorBackend&, const CursorAdvanceOperation&, std::function, PassRefPtr, PassRefPtr, PassRefPtr)> completionCallback) override; - virtual void cursorIterate(IDBCursorBackend&, const CursorIterationOperation&, std::function, PassRefPtr, PassRefPtr, PassRefPtr)> completionCallback) override; - -private: - IDBServerConnectionLevelDB(const String& databaseName, IDBBackingStoreLevelDB*); - - RefPtr m_backingStore; - HashMap> m_backingStoreTransactions; - HashMap> m_backingStoreCursors; - - int64_t m_nextCursorID; - - bool m_closed; - - String m_databaseName; -}; - -} // namespace WebCore - -#endif // USE(LEVELDB) -#endif // ENABLE(INDEXED_DATABASE) -#endif // IDBServerConnectionLevelDB_h diff --git a/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h b/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h new file mode 100644 index 000000000..9dde74c83 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBDatabaseInfo.h" +#include "IDBError.h" + +namespace WebCore { + +class IDBCursorInfo; +class IDBGetAllResult; +class IDBGetResult; +class IDBIndexInfo; +class IDBKeyData; +class IDBObjectStoreInfo; +class IDBResourceIdentifier; +class IDBTransactionInfo; +class IDBValue; +class ThreadSafeDataBuffer; + +enum class IDBGetRecordDataType; + +struct IDBGetAllRecordsData; +struct IDBIterateCursorData; +struct IDBKeyRangeData; + +namespace IndexedDB { +enum class IndexRecordType; +} + +namespace IDBServer { + +class IDBBackingStoreTemporaryFileHandler { +public: + virtual ~IDBBackingStoreTemporaryFileHandler() { } + virtual void prepareForAccessToTemporaryFile(const String& path) = 0; + virtual void accessToTemporaryFileComplete(const String& path) = 0; +}; + +class IDBBackingStore { +public: + virtual ~IDBBackingStore() { } + + virtual IDBError getOrEstablishDatabaseInfo(IDBDatabaseInfo&) = 0; + + virtual IDBError beginTransaction(const IDBTransactionInfo&) = 0; + virtual IDBError abortTransaction(const IDBResourceIdentifier& transactionIdentifier) = 0; + virtual IDBError commitTransaction(const IDBResourceIdentifier& transactionIdentifier) = 0; + + virtual IDBError createObjectStore(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&) = 0; + virtual IDBError deleteObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) = 0; + virtual IDBError renameObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName) = 0; + virtual IDBError clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) = 0; + virtual IDBError createIndex(const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo&) = 0; + virtual IDBError deleteIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier) = 0; + virtual IDBError renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) = 0; + virtual IDBError keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData&, bool& keyExists) = 0; + virtual IDBError deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&) = 0; + virtual IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IDBValue&) = 0; + virtual IDBError getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IDBGetRecordDataType, IDBGetResult& outValue) = 0; + virtual IDBError getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue) = 0; + virtual IDBError getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&, IDBGetResult& outValue) = 0; + virtual IDBError getCount(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, uint64_t& outCount) = 0; + virtual IDBError generateKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t& keyNumber) = 0; + virtual IDBError revertGeneratedKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t keyNumber) = 0; + virtual IDBError maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, double newKeyNumber) = 0; + virtual IDBError openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&, IDBGetResult& outResult) = 0; + virtual IDBError iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&, IDBGetResult& outResult) = 0; + virtual bool prefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier) = 0; + + virtual IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) = 0; + virtual void deleteBackingStore() = 0; + + virtual bool supportsSimultaneousTransactions() = 0; + virtual bool isEphemeral() = 0; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.cpp b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.cpp new file mode 100644 index 000000000..55bea5424 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBConnectionToClient.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "UniqueIDBDatabaseConnection.h" + +namespace WebCore { +namespace IDBServer { + +Ref IDBConnectionToClient::create(IDBConnectionToClientDelegate& delegate) +{ + return adoptRef(*new IDBConnectionToClient(delegate)); +} + +IDBConnectionToClient::IDBConnectionToClient(IDBConnectionToClientDelegate& delegate) + : m_delegate(delegate) +{ +} + +uint64_t IDBConnectionToClient::identifier() const +{ + return m_delegate->identifier(); +} + +void IDBConnectionToClient::didDeleteDatabase(const IDBResultData& result) +{ + m_delegate->didDeleteDatabase(result); +} + +void IDBConnectionToClient::didOpenDatabase(const IDBResultData& result) +{ + m_delegate->didOpenDatabase(result); +} + +void IDBConnectionToClient::didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + m_delegate->didAbortTransaction(transactionIdentifier, error); +} + +void IDBConnectionToClient::didCreateObjectStore(const IDBResultData& result) +{ + m_delegate->didCreateObjectStore(result); +} + +void IDBConnectionToClient::didDeleteObjectStore(const IDBResultData& result) +{ + m_delegate->didDeleteObjectStore(result); +} + +void IDBConnectionToClient::didRenameObjectStore(const IDBResultData& result) +{ + m_delegate->didRenameObjectStore(result); +} + +void IDBConnectionToClient::didClearObjectStore(const IDBResultData& result) +{ + m_delegate->didClearObjectStore(result); +} + +void IDBConnectionToClient::didCreateIndex(const IDBResultData& result) +{ + m_delegate->didCreateIndex(result); +} + +void IDBConnectionToClient::didDeleteIndex(const IDBResultData& result) +{ + m_delegate->didDeleteIndex(result); +} + +void IDBConnectionToClient::didRenameIndex(const IDBResultData& result) +{ + m_delegate->didRenameIndex(result); +} + +void IDBConnectionToClient::didPutOrAdd(const IDBResultData& result) +{ + m_delegate->didPutOrAdd(result); +} + +void IDBConnectionToClient::didGetRecord(const IDBResultData& result) +{ + m_delegate->didGetRecord(result); +} + +void IDBConnectionToClient::didGetAllRecords(const IDBResultData& result) +{ + m_delegate->didGetAllRecords(result); +} + +void IDBConnectionToClient::didGetCount(const IDBResultData& result) +{ + m_delegate->didGetCount(result); +} + +void IDBConnectionToClient::didDeleteRecord(const IDBResultData& result) +{ + m_delegate->didDeleteRecord(result); +} + +void IDBConnectionToClient::didOpenCursor(const IDBResultData& result) +{ + m_delegate->didOpenCursor(result); +} + +void IDBConnectionToClient::didIterateCursor(const IDBResultData& result) +{ + m_delegate->didIterateCursor(result); +} + +void IDBConnectionToClient::didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + m_delegate->didCommitTransaction(transactionIdentifier, error); +} + +void IDBConnectionToClient::fireVersionChangeEvent(UniqueIDBDatabaseConnection& connection, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) +{ + m_delegate->fireVersionChangeEvent(connection, requestIdentifier, requestedVersion); +} + +void IDBConnectionToClient::didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + m_delegate->didStartTransaction(transactionIdentifier, error); +} + +void IDBConnectionToClient::didCloseFromServer(UniqueIDBDatabaseConnection& connection, const IDBError& error) +{ + m_delegate->didCloseFromServer(connection, error); +} + +void IDBConnectionToClient::notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion) +{ + m_delegate->notifyOpenDBRequestBlocked(requestIdentifier, oldVersion, newVersion); +} + +void IDBConnectionToClient::didGetAllDatabaseNames(uint64_t callbackID, const Vector& databaseNames) +{ + m_delegate->didGetAllDatabaseNames(callbackID, databaseNames); +} + +void IDBConnectionToClient::registerDatabaseConnection(UniqueIDBDatabaseConnection& connection) +{ + ASSERT(!m_databaseConnections.contains(&connection)); + m_databaseConnections.add(&connection); +} + +void IDBConnectionToClient::unregisterDatabaseConnection(UniqueIDBDatabaseConnection& connection) +{ + m_databaseConnections.remove(&connection); +} + +void IDBConnectionToClient::connectionToClientClosed() +{ + auto databaseConnections = m_databaseConnections; + + for (auto connection : databaseConnections) { + if (m_databaseConnections.contains(connection)) + connection->connectionClosedFromClient(); + } + + m_databaseConnections.clear(); +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.h b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.h new file mode 100644 index 000000000..642813dd0 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBConnectionToClientDelegate.h" +#include +#include +#include + +namespace WebCore { + +class IDBError; +class IDBResourceIdentifier; +class IDBResultData; + +namespace IDBServer { + +class UniqueIDBDatabaseConnection; + +class IDBConnectionToClient : public RefCounted { +public: + WEBCORE_EXPORT static Ref create(IDBConnectionToClientDelegate&); + + uint64_t identifier() const; + + void didDeleteDatabase(const IDBResultData&); + void didOpenDatabase(const IDBResultData&); + void didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&); + void didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&); + void didCreateObjectStore(const IDBResultData&); + void didDeleteObjectStore(const IDBResultData&); + void didRenameObjectStore(const IDBResultData&); + void didClearObjectStore(const IDBResultData&); + void didCreateIndex(const IDBResultData&); + void didDeleteIndex(const IDBResultData&); + void didRenameIndex(const IDBResultData&); + void didPutOrAdd(const IDBResultData&); + void didGetRecord(const IDBResultData&); + void didGetAllRecords(const IDBResultData&); + void didGetCount(const IDBResultData&); + void didDeleteRecord(const IDBResultData&); + void didOpenCursor(const IDBResultData&); + void didIterateCursor(const IDBResultData&); + + void fireVersionChangeEvent(UniqueIDBDatabaseConnection&, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion); + void didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&); + void didCloseFromServer(UniqueIDBDatabaseConnection&, const IDBError&); + + void notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion); + + void didGetAllDatabaseNames(uint64_t callbackID, const Vector& databaseNames); + + void registerDatabaseConnection(UniqueIDBDatabaseConnection&); + void unregisterDatabaseConnection(UniqueIDBDatabaseConnection&); + void connectionToClientClosed(); + +private: + IDBConnectionToClient(IDBConnectionToClientDelegate&); + + Ref m_delegate; + HashSet m_databaseConnections; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClientDelegate.h b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClientDelegate.h new file mode 100644 index 000000000..cc9465366 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClientDelegate.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include +#include + +namespace WebCore { + +class IDBError; +class IDBResourceIdentifier; +class IDBResultData; + +namespace IDBServer { + +class UniqueIDBDatabaseConnection; + +class IDBConnectionToClientDelegate { +public: + virtual ~IDBConnectionToClientDelegate() { } + + virtual uint64_t identifier() const = 0; + + virtual void didDeleteDatabase(const IDBResultData&) = 0; + virtual void didOpenDatabase(const IDBResultData&) = 0; + virtual void didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) = 0; + virtual void didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) = 0; + virtual void didCreateObjectStore(const IDBResultData&) = 0; + virtual void didDeleteObjectStore(const IDBResultData&) = 0; + virtual void didRenameObjectStore(const IDBResultData&) = 0; + virtual void didClearObjectStore(const IDBResultData&) = 0; + virtual void didCreateIndex(const IDBResultData&) = 0; + virtual void didDeleteIndex(const IDBResultData&) = 0; + virtual void didRenameIndex(const IDBResultData&) = 0; + virtual void didPutOrAdd(const IDBResultData&) = 0; + virtual void didGetRecord(const IDBResultData&) = 0; + virtual void didGetAllRecords(const IDBResultData&) = 0; + virtual void didGetCount(const IDBResultData&) = 0; + virtual void didDeleteRecord(const IDBResultData&) = 0; + virtual void didOpenCursor(const IDBResultData&) = 0; + virtual void didIterateCursor(const IDBResultData&) = 0; + + virtual void fireVersionChangeEvent(UniqueIDBDatabaseConnection&, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) = 0; + virtual void didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) = 0; + virtual void didCloseFromServer(UniqueIDBDatabaseConnection&, const IDBError&) = 0; + virtual void notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion) = 0; + + virtual void didGetAllDatabaseNames(uint64_t callbackID, const Vector& databaseNames) = 0; + + virtual void ref() = 0; + virtual void deref() = 0; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp b/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp new file mode 100644 index 000000000..4806d5cc5 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2014, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include "IDBSerialization.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyData.h" +#include "IDBKeyPath.h" +#include "KeyedCoding.h" + +#if USE(GLIB) +#include +#include +#endif + +namespace WebCore { + +enum class KeyPathType { Null, String, Array }; + +RefPtr serializeIDBKeyPath(const std::optional& keyPath) +{ + auto encoder = KeyedEncoder::encoder(); + + if (keyPath) { + auto visitor = WTF::makeVisitor([&](const String& string) { + encoder->encodeEnum("type", KeyPathType::String); + encoder->encodeString("string", string); + }, [&](const Vector& vector) { + encoder->encodeEnum("type", KeyPathType::Array); + encoder->encodeObjects("array", vector.begin(), vector.end(), [](WebCore::KeyedEncoder& encoder, const String& string) { + encoder.encodeString("string", string); + }); + }); + WTF::visit(visitor, keyPath.value()); + } else + encoder->encodeEnum("type", KeyPathType::Null); + + return encoder->finishEncoding(); +} + +bool deserializeIDBKeyPath(const uint8_t* data, size_t size, std::optional& result) +{ + if (!data || !size) + return false; + + auto decoder = KeyedDecoder::decoder(data, size); + + KeyPathType type; + bool succeeded = decoder->decodeEnum("type", type, [](KeyPathType value) { + return value == KeyPathType::Null || value == KeyPathType::String || value == KeyPathType::Array; + }); + if (!succeeded) + return false; + + switch (type) { + case KeyPathType::Null: + break; + case KeyPathType::String: { + String string; + if (!decoder->decodeString("string", string)) + return false; + result = IDBKeyPath(WTFMove(string)); + break; + } + case KeyPathType::Array: { + Vector vector; + succeeded = decoder->decodeObjects("array", vector, [](KeyedDecoder& decoder, String& result) { + return decoder.decodeString("string", result); + }); + if (!succeeded) + return false; + result = IDBKeyPath(WTFMove(vector)); + break; + } + } + return true; +} + +static bool isLegacySerializedIDBKeyData(const uint8_t* data, size_t size) +{ +#if USE(CF) + UNUSED_PARAM(size); + + // This is the magic character that begins serialized PropertyLists, and tells us whether + // the key we're looking at is an old-style key. + static const uint8_t legacySerializedKeyVersion = 'b'; + if (data[0] == legacySerializedKeyVersion) + return true; +#elif USE(GLIB) + // KeyedEncoderGLib uses a GVariant dictionary, so check if the given data is a valid GVariant dictionary. + GRefPtr bytes = adoptGRef(g_bytes_new(data, size)); + GRefPtr variant = g_variant_new_from_bytes(G_VARIANT_TYPE("a{sv}"), bytes.get(), FALSE); + return g_variant_is_normal_form(variant.get()); +#endif + return false; +} + + +/* +The IDBKeyData serialization format is as follows: +[1 byte version header][Key Buffer] + +The Key Buffer serialization format is as follows: +[1 byte key type][Type specific data] + +Type specific serialization formats are as follows for each of the types: +Min: +[0 bytes] + +Number: +[8 bytes representing a double encoded in little endian] + +Date: +[8 bytes representing a double encoded in little endian] + +String: +[4 bytes representing string "length" in little endian]["length" number of 2-byte pairs representing ECMAScript 16-bit code units] + +Binary: +[8 bytes representing the "size" of the binary blob]["size" bytes] + +Array: +[8 bytes representing the "length" of the key array]["length" individual Key Buffer entries] + +Max: +[0 bytes] +*/ + +static const uint8_t SIDBKeyVersion = 0x00; +enum class SIDBKeyType : uint8_t { + Min = 0x00, + Number = 0x20, + Date = 0x40, + String = 0x60, + Binary = 0x80, + Array = 0xA0, + Max = 0xFF, +}; + +static SIDBKeyType serializedTypeForKeyType(IndexedDB::KeyType type) +{ + switch (type) { + case IndexedDB::KeyType::Min: + return SIDBKeyType::Min; + case IndexedDB::KeyType::Number: + return SIDBKeyType::Number; + case IndexedDB::KeyType::Date: + return SIDBKeyType::Date; + case IndexedDB::KeyType::String: + return SIDBKeyType::String; + case IndexedDB::KeyType::Binary: + return SIDBKeyType::Binary; + case IndexedDB::KeyType::Array: + return SIDBKeyType::Array; + case IndexedDB::KeyType::Max: + return SIDBKeyType::Max; + case IndexedDB::KeyType::Invalid: + RELEASE_ASSERT_NOT_REACHED(); + }; + + RELEASE_ASSERT_NOT_REACHED(); +} + +#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) || CPU(NEEDS_ALIGNED_ACCESS) +template static void writeLittleEndian(Vector& buffer, T value) +{ + for (unsigned i = 0; i < sizeof(T); i++) { + buffer.append(value & 0xFF); + value >>= 8; + } +} + +template static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value) +{ + if (ptr > end - sizeof(value)) + return false; + + value = 0; + for (size_t i = 0; i < sizeof(T); i++) + value += ((T)*ptr++) << (i * 8); + return true; +} +#else +template static void writeLittleEndian(Vector& buffer, T value) +{ + buffer.append(reinterpret_cast(&value), sizeof(value)); +} + +template static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value) +{ + if (ptr > end - sizeof(value)) + return false; + + value = *reinterpret_cast(ptr); + ptr += sizeof(T); + + return true; +} +#endif + +static void writeDouble(Vector& data, double d) +{ + writeLittleEndian(data, *reinterpret_cast(&d)); +} + +static bool readDouble(const uint8_t*& data, const uint8_t* end, double& d) +{ + return readLittleEndian(data, end, *reinterpret_cast(&d)); +} + +static void encodeKey(Vector& data, const IDBKeyData& key) +{ + SIDBKeyType type = serializedTypeForKeyType(key.type()); + data.append(static_cast(type)); + + switch (type) { + case SIDBKeyType::Number: + writeDouble(data, key.number()); + break; + case SIDBKeyType::Date: + writeDouble(data, key.date()); + break; + case SIDBKeyType::String: { + auto string = key.string(); + uint32_t length = string.length(); + writeLittleEndian(data, length); + + for (size_t i = 0; i < length; ++i) + writeLittleEndian(data, string[i]); + + break; + } + case SIDBKeyType::Binary: { + auto& buffer = key.binary(); + uint64_t size = buffer.size(); + writeLittleEndian(data, size); + + auto* bufferData = buffer.data(); + ASSERT(bufferData || !size); + if (bufferData) + data.append(bufferData->data(), bufferData->size()); + + break; + } + case SIDBKeyType::Array: { + auto& array = key.array(); + uint64_t size = array.size(); + writeLittleEndian(data, size); + for (auto& key : array) + encodeKey(data, key); + + break; + } + case SIDBKeyType::Min: + case SIDBKeyType::Max: + break; + } +} + +RefPtr serializeIDBKeyData(const IDBKeyData& key) +{ + Vector data; + data.append(SIDBKeyVersion); + + encodeKey(data, key); + return SharedBuffer::adoptVector(data); +} + +static bool decodeKey(const uint8_t*& data, const uint8_t* end, IDBKeyData& result) +{ + if (!data || data >= end) + return false; + + SIDBKeyType type = static_cast(data++[0]); + switch (type) { + case SIDBKeyType::Min: + result = IDBKeyData::minimum(); + return true; + case SIDBKeyType::Max: + result = IDBKeyData::maximum(); + return true; + case SIDBKeyType::Number: { + double d; + if (!readDouble(data, end, d)) + return false; + + result.setNumberValue(d); + return true; + } + case SIDBKeyType::Date: { + double d; + if (!readDouble(data, end, d)) + return false; + + result.setDateValue(d); + return true; + } + case SIDBKeyType::String: { + uint32_t length; + if (!readLittleEndian(data, end, length)) + return false; + + if (static_cast(end - data) < length * 2) + return false; + + Vector buffer; + buffer.reserveInitialCapacity(length); + for (size_t i = 0; i < length; i++) { + uint16_t ch; + if (!readLittleEndian(data, end, ch)) + return false; + buffer.uncheckedAppend(ch); + } + + result.setStringValue(String::adopt(WTFMove(buffer))); + + return true; + } + case SIDBKeyType::Binary: { + uint64_t size64; + if (!readLittleEndian(data, end, size64)) + return false; + + if (static_cast(end - data) < size64) + return false; + + if (size64 > std::numeric_limits::max()) + return false; + + size_t size = static_cast(size64); + Vector dataVector; + + dataVector.append(data, size); + data += size; + + result.setBinaryValue(ThreadSafeDataBuffer::adoptVector(dataVector)); + return true; + } + case SIDBKeyType::Array: { + uint64_t size64; + if (!readLittleEndian(data, end, size64)) + return false; + + if (size64 > std::numeric_limits::max()) + return false; + + size_t size = static_cast(size64); + Vector array; + array.reserveInitialCapacity(size); + + for (size_t i = 0; i < size; ++i) { + IDBKeyData keyData; + if (!decodeKey(data, end, keyData)) + return false; + + ASSERT(keyData.isValid()); + array.uncheckedAppend(WTFMove(keyData)); + } + + result.setArrayValue(array); + + return true; + } + default: + LOG_ERROR("decodeKey encountered unexpected type: %i", (int)type); + return false; + } +} + +bool deserializeIDBKeyData(const uint8_t* data, size_t size, IDBKeyData& result) +{ + if (!data || !size) + return false; + + if (isLegacySerializedIDBKeyData(data, size)) { + auto decoder = KeyedDecoder::decoder(data, size); + return IDBKeyData::decode(*decoder, result); + } + + // Verify this is a SerializedIDBKey version we understand. + const uint8_t* current = data; + const uint8_t* end = data + size; + if (current++[0] != SIDBKeyVersion) + return false; + + if (decodeKey(current, end, result)) { + // Even if we successfully decoded a key, the deserialize is only successful + // if we actually consumed all input data. + return current == end; + } + + return false; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/IDBSerialization.h b/Source/WebCore/Modules/indexeddb/server/IDBSerialization.h new file mode 100644 index 000000000..094fbc6ec --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IDBSerialization.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyPath.h" +#include "SharedBuffer.h" + +namespace WebCore { + +class IDBKeyData; + +RefPtr serializeIDBKeyPath(const std::optional&); +bool deserializeIDBKeyPath(const uint8_t* buffer, size_t bufferSize, std::optional&); + +RefPtr serializeIDBKeyData(const IDBKeyData&); +bool deserializeIDBKeyData(const uint8_t* buffer, size_t bufferSize, IDBKeyData&); + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp b/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp new file mode 100644 index 000000000..87476438c --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp @@ -0,0 +1,682 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBServer.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBRequestData.h" +#include "IDBResultData.h" +#include "Logging.h" +#include "MemoryIDBBackingStore.h" +#include "SQLiteFileSystem.h" +#include "SQLiteIDBBackingStore.h" +#include "SecurityOrigin.h" +#include +#include +#include + +namespace WebCore { +namespace IDBServer { + +Ref IDBServer::create(IDBBackingStoreTemporaryFileHandler& fileHandler) +{ + return adoptRef(*new IDBServer(fileHandler)); +} + +Ref IDBServer::create(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler) +{ + return adoptRef(*new IDBServer(databaseDirectoryPath, fileHandler)); +} + +IDBServer::IDBServer(IDBBackingStoreTemporaryFileHandler& fileHandler) + : m_backingStoreTemporaryFileHandler(fileHandler) +{ + Locker locker(m_databaseThreadCreationLock); + m_threadID = createThread(IDBServer::databaseThreadEntry, this, "IndexedDatabase Server"); +} + +IDBServer::IDBServer(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler) + : m_databaseDirectoryPath(databaseDirectoryPath) + , m_backingStoreTemporaryFileHandler(fileHandler) +{ + LOG(IndexedDB, "IDBServer created at path %s", databaseDirectoryPath.utf8().data()); + + Locker locker(m_databaseThreadCreationLock); + m_threadID = createThread(IDBServer::databaseThreadEntry, this, "IndexedDatabase Server"); +} + +void IDBServer::registerConnection(IDBConnectionToClient& connection) +{ + ASSERT(!m_connectionMap.contains(connection.identifier())); + m_connectionMap.set(connection.identifier(), &connection); +} + +void IDBServer::unregisterConnection(IDBConnectionToClient& connection) +{ + ASSERT(m_connectionMap.contains(connection.identifier())); + ASSERT(m_connectionMap.get(connection.identifier()) == &connection); + + connection.connectionToClientClosed(); + + m_connectionMap.remove(connection.identifier()); +} + +void IDBServer::registerTransaction(UniqueIDBDatabaseTransaction& transaction) +{ + ASSERT(!m_transactions.contains(transaction.info().identifier())); + m_transactions.set(transaction.info().identifier(), &transaction); +} + +void IDBServer::unregisterTransaction(UniqueIDBDatabaseTransaction& transaction) +{ + ASSERT(m_transactions.contains(transaction.info().identifier())); + ASSERT(m_transactions.get(transaction.info().identifier()) == &transaction); + + m_transactions.remove(transaction.info().identifier()); +} + +void IDBServer::registerDatabaseConnection(UniqueIDBDatabaseConnection& connection) +{ + ASSERT(!m_databaseConnections.contains(connection.identifier())); + m_databaseConnections.set(connection.identifier(), &connection); +} + +void IDBServer::unregisterDatabaseConnection(UniqueIDBDatabaseConnection& connection) +{ + ASSERT(m_databaseConnections.contains(connection.identifier())); + m_databaseConnections.remove(connection.identifier()); +} + +UniqueIDBDatabase& IDBServer::getOrCreateUniqueIDBDatabase(const IDBDatabaseIdentifier& identifier) +{ + ASSERT(isMainThread()); + + auto uniqueIDBDatabase = m_uniqueIDBDatabaseMap.add(identifier, nullptr); + if (uniqueIDBDatabase.isNewEntry) + uniqueIDBDatabase.iterator->value = UniqueIDBDatabase::create(*this, identifier); + + return *uniqueIDBDatabase.iterator->value; +} + +std::unique_ptr IDBServer::createBackingStore(const IDBDatabaseIdentifier& identifier) +{ + ASSERT(!isMainThread()); + + if (m_databaseDirectoryPath.isEmpty()) + return MemoryIDBBackingStore::create(identifier); + + return std::make_unique(identifier, m_databaseDirectoryPath, m_backingStoreTemporaryFileHandler); +} + +void IDBServer::openDatabase(const IDBRequestData& requestData) +{ + LOG(IndexedDB, "IDBServer::openDatabase"); + + auto& uniqueIDBDatabase = getOrCreateUniqueIDBDatabase(requestData.databaseIdentifier()); + + auto connection = m_connectionMap.get(requestData.requestIdentifier().connectionIdentifier()); + if (!connection) { + // If the connection back to the client is gone, there's no way to open the database as + // well as no way to message back failure. + return; + } + + uniqueIDBDatabase.openDatabaseConnection(*connection, requestData); +} + +void IDBServer::deleteDatabase(const IDBRequestData& requestData) +{ + LOG(IndexedDB, "IDBServer::deleteDatabase - %s", requestData.databaseIdentifier().debugString().utf8().data()); + ASSERT(isMainThread()); + + auto connection = m_connectionMap.get(requestData.requestIdentifier().connectionIdentifier()); + if (!connection) { + // If the connection back to the client is gone, there's no way to delete the database as + // well as no way to message back failure. + return; + } + + auto* database = m_uniqueIDBDatabaseMap.get(requestData.databaseIdentifier()); + if (!database) + database = &getOrCreateUniqueIDBDatabase(requestData.databaseIdentifier()); + + database->handleDelete(*connection, requestData); +} + +void IDBServer::closeUniqueIDBDatabase(UniqueIDBDatabase& database) +{ + LOG(IndexedDB, "IDBServer::closeUniqueIDBDatabase"); + ASSERT(isMainThread()); + + m_uniqueIDBDatabaseMap.remove(database.identifier()); +} + +void IDBServer::abortTransaction(const IDBResourceIdentifier& transactionIdentifier) +{ + LOG(IndexedDB, "IDBServer::abortTransaction"); + + auto transaction = m_transactions.get(transactionIdentifier); + if (!transaction) { + // If there is no transaction there is nothing to abort. + // We also have no access to a connection over which to message failure-to-abort. + return; + } + + transaction->abort(); +} + +void IDBServer::createObjectStore(const IDBRequestData& requestData, const IDBObjectStoreInfo& info) +{ + LOG(IndexedDB, "IDBServer::createObjectStore"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + ASSERT(transaction->isVersionChange()); + transaction->createObjectStore(requestData, info); +} + +void IDBServer::deleteObjectStore(const IDBRequestData& requestData, const String& objectStoreName) +{ + LOG(IndexedDB, "IDBServer::deleteObjectStore"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + ASSERT(transaction->isVersionChange()); + transaction->deleteObjectStore(requestData, objectStoreName); +} + +void IDBServer::renameObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& newName) +{ + LOG(IndexedDB, "IDBServer::renameObjectStore"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + ASSERT(transaction->isVersionChange()); + transaction->renameObjectStore(requestData, objectStoreIdentifier, newName); +} + +void IDBServer::clearObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier) +{ + LOG(IndexedDB, "IDBServer::clearObjectStore"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + transaction->clearObjectStore(requestData, objectStoreIdentifier); +} + +void IDBServer::createIndex(const IDBRequestData& requestData, const IDBIndexInfo& info) +{ + LOG(IndexedDB, "IDBServer::createIndex"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + ASSERT(transaction->isVersionChange()); + transaction->createIndex(requestData, info); +} + +void IDBServer::deleteIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName) +{ + LOG(IndexedDB, "IDBServer::deleteIndex"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + ASSERT(transaction->isVersionChange()); + transaction->deleteIndex(requestData, objectStoreIdentifier, indexName); +} + +void IDBServer::renameIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) +{ + LOG(IndexedDB, "IDBServer::renameIndex"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + ASSERT(transaction->isVersionChange()); + transaction->renameIndex(requestData, objectStoreIdentifier, indexIdentifier, newName); +} + +void IDBServer::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode) +{ + LOG(IndexedDB, "IDBServer::putOrAdd"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + transaction->putOrAdd(requestData, keyData, value, overwriteMode); +} + +void IDBServer::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData) +{ + LOG(IndexedDB, "IDBServer::getRecord"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + transaction->getRecord(requestData, getRecordData); +} + +void IDBServer::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData) +{ + LOG(IndexedDB, "IDBServer::getAllRecords"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + transaction->getAllRecords(requestData, getAllRecordsData); +} + +void IDBServer::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData) +{ + LOG(IndexedDB, "IDBServer::getCount"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + transaction->getCount(requestData, keyRangeData); +} + +void IDBServer::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData) +{ + LOG(IndexedDB, "IDBServer::deleteRecord"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + transaction->deleteRecord(requestData, keyRangeData); +} + +void IDBServer::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info) +{ + LOG(IndexedDB, "IDBServer::openCursor"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + transaction->openCursor(requestData, info); +} + +void IDBServer::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data) +{ + LOG(IndexedDB, "IDBServer::iterateCursor"); + + auto transaction = m_transactions.get(requestData.transactionIdentifier()); + if (!transaction) + return; + + transaction->iterateCursor(requestData, data); +} + +void IDBServer::establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo& info) +{ + LOG(IndexedDB, "IDBServer::establishTransaction"); + + auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier); + if (!databaseConnection) + return; + + databaseConnection->establishTransaction(info); +} + +void IDBServer::commitTransaction(const IDBResourceIdentifier& transactionIdentifier) +{ + LOG(IndexedDB, "IDBServer::commitTransaction"); + + auto transaction = m_transactions.get(transactionIdentifier); + if (!transaction) { + // If there is no transaction there is nothing to commit. + // We also have no access to a connection over which to message failure-to-commit. + return; + } + + transaction->commit(); +} + +void IDBServer::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) +{ + LOG(IndexedDB, "IDBServer::didFinishHandlingVersionChangeTransaction - %s", transactionIdentifier.loggingString().utf8().data()); + + auto* connection = m_databaseConnections.get(databaseConnectionIdentifier); + if (!connection) + return; + + connection->didFinishHandlingVersionChange(transactionIdentifier); +} + +void IDBServer::databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier) +{ + LOG(IndexedDB, "IDBServer::databaseConnectionPendingClose - %" PRIu64, databaseConnectionIdentifier); + + auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier); + if (!databaseConnection) + return; + + databaseConnection->connectionPendingCloseFromClient(); +} + +void IDBServer::databaseConnectionClosed(uint64_t databaseConnectionIdentifier) +{ + LOG(IndexedDB, "IDBServer::databaseConnectionClosed - %" PRIu64, databaseConnectionIdentifier); + + auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier); + if (!databaseConnection) + return; + + databaseConnection->connectionClosedFromClient(); +} + +void IDBServer::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) +{ + LOG(IndexedDB, "IDBServer::abortOpenAndUpgradeNeeded"); + + auto transaction = m_transactions.get(transactionIdentifier); + if (transaction) + transaction->abortWithoutCallback(); + + auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier); + if (!databaseConnection) + return; + + databaseConnection->connectionClosedFromClient(); +} + +void IDBServer::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier) +{ + LOG(IndexedDB, "IDBServer::didFireVersionChangeEvent"); + + if (auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier)) + databaseConnection->didFireVersionChangeEvent(requestIdentifier); +} + +void IDBServer::openDBRequestCancelled(const IDBRequestData& requestData) +{ + LOG(IndexedDB, "IDBServer::openDBRequestCancelled"); + ASSERT(isMainThread()); + + auto* uniqueIDBDatabase = m_uniqueIDBDatabaseMap.get(requestData.databaseIdentifier()); + if (!uniqueIDBDatabase) + return; + + uniqueIDBDatabase->openDBRequestCancelled(requestData.requestIdentifier()); +} + +void IDBServer::confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier) +{ + LOG(IndexedDB, "IDBServer::confirmDidCloseFromServer"); + + if (auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier)) + databaseConnection->confirmDidCloseFromServer(); +} + +void IDBServer::getAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID) +{ + postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performGetAllDatabaseNames, serverConnectionIdentifier, mainFrameOrigin, openingOrigin, callbackID)); +} + +void IDBServer::performGetAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID) +{ + String directory = IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(mainFrameOrigin, openingOrigin, m_databaseDirectoryPath); + + Vector entries = listDirectory(directory, ASCIILiteral("*")); + Vector databases; + databases.reserveInitialCapacity(entries.size()); + for (auto& entry : entries) { + String encodedName = lastComponentOfPathIgnoringTrailingSlash(entry); + databases.uncheckedAppend(SQLiteIDBBackingStore::databaseNameFromEncodedFilename(encodedName)); + } + + postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didGetAllDatabaseNames, serverConnectionIdentifier, callbackID, databases)); +} + +void IDBServer::didGetAllDatabaseNames(uint64_t serverConnectionIdentifier, uint64_t callbackID, const Vector& databaseNames) +{ + auto connection = m_connectionMap.get(serverConnectionIdentifier); + if (!connection) + return; + + connection->didGetAllDatabaseNames(callbackID, databaseNames); +} + +void IDBServer::postDatabaseTask(CrossThreadTask&& task) +{ + m_databaseQueue.append(WTFMove(task)); +} + +void IDBServer::postDatabaseTaskReply(CrossThreadTask&& task) +{ + ASSERT(!isMainThread()); + m_databaseReplyQueue.append(WTFMove(task)); + + + Locker locker(m_mainThreadReplyLock); + if (m_mainThreadReplyScheduled) + return; + + m_mainThreadReplyScheduled = true; + callOnMainThread([this] { + handleTaskRepliesOnMainThread(); + }); +} + +void IDBServer::databaseThreadEntry(void* threadData) +{ + ASSERT(threadData); + IDBServer* server = reinterpret_cast(threadData); + server->databaseRunLoop(); +} + +void IDBServer::databaseRunLoop() +{ + ASSERT(!isMainThread()); + { + Locker locker(m_databaseThreadCreationLock); + } + + while (!m_databaseQueue.isKilled()) + m_databaseQueue.waitForMessage().performTask(); +} + +void IDBServer::handleTaskRepliesOnMainThread() +{ + { + Locker locker(m_mainThreadReplyLock); + m_mainThreadReplyScheduled = false; + } + + while (auto task = m_databaseReplyQueue.tryGetMessage()) + task->performTask(); +} + +static uint64_t generateDeleteCallbackID() +{ + ASSERT(isMainThread()); + static uint64_t currentID = 0; + return ++currentID; +} + +void IDBServer::closeAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point modificationTime, std::function completionHandler) +{ + uint64_t callbackID = generateDeleteCallbackID(); + auto addResult = m_deleteDatabaseCompletionHandlers.add(callbackID, WTFMove(completionHandler)); + ASSERT_UNUSED(addResult, addResult.isNewEntry); + + // If the modification time is in the future, don't both doing anything. + if (modificationTime > std::chrono::system_clock::now()) { + postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID)); + return; + } + + HashSet> openDatabases; + for (auto* connection : m_databaseConnections.values()) + openDatabases.add(&connection->database()); + + for (auto& database : openDatabases) + database->immediateCloseForUserDelete(); + + postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performCloseAndDeleteDatabasesModifiedSince, modificationTime, callbackID)); +} + +void IDBServer::closeAndDeleteDatabasesForOrigins(const Vector& origins, std::function completionHandler) +{ + uint64_t callbackID = generateDeleteCallbackID(); + auto addResult = m_deleteDatabaseCompletionHandlers.add(callbackID, WTFMove(completionHandler)); + ASSERT_UNUSED(addResult, addResult.isNewEntry); + + HashSet> openDatabases; + for (auto* connection : m_databaseConnections.values()) { + const auto& identifier = connection->database().identifier(); + for (auto& origin : origins) { + if (identifier.isRelatedToOrigin(origin)) { + openDatabases.add(&connection->database()); + break; + } + } + } + + for (auto& database : openDatabases) + database->immediateCloseForUserDelete(); + + postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performCloseAndDeleteDatabasesForOrigins, origins, callbackID)); +} + +static void removeAllDatabasesForOriginPath(const String& originPath, std::chrono::system_clock::time_point modifiedSince) +{ + Vector databasePaths = listDirectory(originPath, "*"); + + for (auto& databasePath : databasePaths) { + String databaseFile = pathByAppendingComponent(databasePath, "IndexedDB.sqlite3"); + + if (modifiedSince > std::chrono::system_clock::time_point::min() && fileExists(databaseFile)) { + time_t modificationTime; + if (!getFileModificationTime(databaseFile, modificationTime)) + continue; + + if (std::chrono::system_clock::from_time_t(modificationTime) < modifiedSince) + continue; + } + + // Deleting this database means we need to delete all files that represent it. + // This includes: + // - The directory itself, which is named after the database. + // - IndexedDB.sqlite3 and related SQLite files. + // - Blob files that we stored in the directory. + // + // To be conservative, we should *not* try to delete files that are unexpected; + // We should only delete files we think we put there. + // + // IndexedDB blob files are named "N.blob" where N is a decimal integer, + // so those are the only blob files we should be trying to delete. + for (auto& blobPath : listDirectory(databasePath, "[0-9]*.blob")) { + // Globbing can't give us only filenames starting with 1-or-more digits. + // The above globbing gives us files that start with a digit and ends with ".blob", but there might be non-digits in between. + // We need to validate that each filename contains only digits before deleting it, as any other files are not ones we put there. + String filename = pathGetFileName(blobPath); + auto filenameLength = filename.length(); + + ASSERT(filenameLength >= 6); + ASSERT(filename.endsWith(".blob")); + + if (filename.length() < 6) + continue; + if (!filename.endsWith(".blob")) + continue; + + bool validFilename = true; + for (unsigned i = 0; i < filenameLength - 5; ++i) { + if (!isASCIIDigit(filename[i])) { + validFilename = false; + break; + } + } + + if (validFilename) + deleteFile(blobPath); + } + + // Now delete IndexedDB.sqlite3 and related SQLite files. + SQLiteFileSystem::deleteDatabaseFile(databaseFile); + + // And finally, if we can, delete the empty directory. + deleteEmptyDirectory(databasePath); + } + + // If no databases remain for this origin, we can delete the origin directory as well. + deleteEmptyDirectory(originPath); +} + +void IDBServer::performCloseAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point modifiedSince, uint64_t callbackID) +{ + if (!m_databaseDirectoryPath.isEmpty()) { + Vector originPaths = listDirectory(m_databaseDirectoryPath, "*"); + for (auto& originPath : originPaths) + removeAllDatabasesForOriginPath(originPath, modifiedSince); + } + + postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID)); +} + +void IDBServer::performCloseAndDeleteDatabasesForOrigins(const Vector& origins, uint64_t callbackID) +{ + if (!m_databaseDirectoryPath.isEmpty()) { + for (const auto& origin : origins) { + String originPath = pathByAppendingComponent(m_databaseDirectoryPath, origin.databaseIdentifier()); + removeAllDatabasesForOriginPath(originPath, std::chrono::system_clock::time_point::min()); + } + } + + postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID)); +} + +void IDBServer::didPerformCloseAndDeleteDatabases(uint64_t callbackID) +{ + auto callback = m_deleteDatabaseCompletionHandlers.take(callbackID); + ASSERT(callback); + callback(); +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/IDBServer.h b/Source/WebCore/Modules/indexeddb/server/IDBServer.h new file mode 100644 index 000000000..256cd507e --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IDBServer.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBConnectionToClient.h" +#include "IDBDatabaseIdentifier.h" +#include "UniqueIDBDatabase.h" +#include "UniqueIDBDatabaseConnection.h" +#include +#include +#include +#include +#include +#include +#include + +namespace WebCore { + +class IDBCursorInfo; +class IDBRequestData; +class IDBValue; + +struct IDBGetRecordData; + +namespace IDBServer { + +class IDBBackingStoreTemporaryFileHandler; + +class IDBServer : public RefCounted { +public: + static Ref create(IDBBackingStoreTemporaryFileHandler&); + WEBCORE_EXPORT static Ref create(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler&); + + WEBCORE_EXPORT void registerConnection(IDBConnectionToClient&); + WEBCORE_EXPORT void unregisterConnection(IDBConnectionToClient&); + + // Operations requested by the client. + WEBCORE_EXPORT void openDatabase(const IDBRequestData&); + WEBCORE_EXPORT void deleteDatabase(const IDBRequestData&); + WEBCORE_EXPORT void abortTransaction(const IDBResourceIdentifier&); + WEBCORE_EXPORT void commitTransaction(const IDBResourceIdentifier&); + WEBCORE_EXPORT void didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier&); + WEBCORE_EXPORT void createObjectStore(const IDBRequestData&, const IDBObjectStoreInfo&); + WEBCORE_EXPORT void renameObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& newName); + WEBCORE_EXPORT void deleteObjectStore(const IDBRequestData&, const String& objectStoreName); + WEBCORE_EXPORT void clearObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier); + WEBCORE_EXPORT void createIndex(const IDBRequestData&, const IDBIndexInfo&); + WEBCORE_EXPORT void deleteIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& indexName); + WEBCORE_EXPORT void renameIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName); + WEBCORE_EXPORT void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, IndexedDB::ObjectStoreOverwriteMode); + WEBCORE_EXPORT void getRecord(const IDBRequestData&, const IDBGetRecordData&); + WEBCORE_EXPORT void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&); + WEBCORE_EXPORT void getCount(const IDBRequestData&, const IDBKeyRangeData&); + WEBCORE_EXPORT void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&); + WEBCORE_EXPORT void openCursor(const IDBRequestData&, const IDBCursorInfo&); + WEBCORE_EXPORT void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&); + + WEBCORE_EXPORT void establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo&); + WEBCORE_EXPORT void databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier); + WEBCORE_EXPORT void databaseConnectionClosed(uint64_t databaseConnectionIdentifier); + WEBCORE_EXPORT void abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier); + WEBCORE_EXPORT void didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier); + WEBCORE_EXPORT void openDBRequestCancelled(const IDBRequestData&); + WEBCORE_EXPORT void confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier); + + WEBCORE_EXPORT void getAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID); + + void postDatabaseTask(CrossThreadTask&&); + void postDatabaseTaskReply(CrossThreadTask&&); + + void registerDatabaseConnection(UniqueIDBDatabaseConnection&); + void unregisterDatabaseConnection(UniqueIDBDatabaseConnection&); + void registerTransaction(UniqueIDBDatabaseTransaction&); + void unregisterTransaction(UniqueIDBDatabaseTransaction&); + + void closeUniqueIDBDatabase(UniqueIDBDatabase&); + + std::unique_ptr createBackingStore(const IDBDatabaseIdentifier&); + + WEBCORE_EXPORT void closeAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point, std::function completionHandler); + WEBCORE_EXPORT void closeAndDeleteDatabasesForOrigins(const Vector&, std::function completionHandler); + +private: + IDBServer(IDBBackingStoreTemporaryFileHandler&); + IDBServer(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler&); + + UniqueIDBDatabase& getOrCreateUniqueIDBDatabase(const IDBDatabaseIdentifier&); + + void performGetAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID); + void didGetAllDatabaseNames(uint64_t serverConnectionIdentifier, uint64_t callbackID, const Vector& databaseNames); + + void performCloseAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point, uint64_t callbackID); + void performCloseAndDeleteDatabasesForOrigins(const Vector&, uint64_t callbackID); + void didPerformCloseAndDeleteDatabases(uint64_t callbackID); + + static void databaseThreadEntry(void*); + void databaseRunLoop(); + void handleTaskRepliesOnMainThread(); + + HashMap> m_connectionMap; + HashMap> m_uniqueIDBDatabaseMap; + + ThreadIdentifier m_threadID { 0 }; + Lock m_databaseThreadCreationLock; + Lock m_mainThreadReplyLock; + bool m_mainThreadReplyScheduled { false }; + + CrossThreadQueue m_databaseQueue; + CrossThreadQueue m_databaseReplyQueue; + + HashMap m_databaseConnections; + HashMap m_transactions; + + HashMap> m_deleteDatabaseCompletionHandlers; + + String m_databaseDirectoryPath; + IDBBackingStoreTemporaryFileHandler& m_backingStoreTemporaryFileHandler; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.cpp b/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.cpp new file mode 100644 index 000000000..38c860677 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IndexValueEntry.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorInfo.h" + +namespace WebCore { +namespace IDBServer { + +IndexValueEntry::IndexValueEntry(bool unique) + : m_unique(unique) +{ + if (m_unique) + m_key = nullptr; + else + m_orderedKeys = new std::set; +} + +IndexValueEntry::~IndexValueEntry() +{ + if (m_unique) + delete m_key; + else + delete m_orderedKeys; +} + +void IndexValueEntry::addKey(const IDBKeyData& key) +{ + if (m_unique) { + delete m_key; + m_key = new IDBKeyData(key); + return; + } + + m_orderedKeys->insert(key); +} + +bool IndexValueEntry::removeKey(const IDBKeyData& key) +{ + if (m_unique) { + if (m_key && *m_key == key) { + delete m_key; + m_key = nullptr; + return true; + } + + return false; + } + + return m_orderedKeys->erase(key); +} + +const IDBKeyData* IndexValueEntry::getLowest() const +{ + if (m_unique) + return m_key; + + if (m_orderedKeys->empty()) + return nullptr; + + return &(*m_orderedKeys->begin()); +} + +uint64_t IndexValueEntry::getCount() const +{ + if (m_unique) + return m_key ? 1 : 0; + + return m_orderedKeys->size(); +} + +IndexValueEntry::Iterator::Iterator(IndexValueEntry& entry) + : m_entry(&entry) +{ + ASSERT(m_entry->m_key); +} + +IndexValueEntry::Iterator::Iterator(IndexValueEntry& entry, std::set::iterator iterator) + : m_entry(&entry) + , m_forwardIterator(iterator) +{ +} + +IndexValueEntry::Iterator::Iterator(IndexValueEntry& entry, std::set::reverse_iterator iterator) + : m_entry(&entry) + , m_forward(false) + , m_reverseIterator(iterator) +{ +} + +const IDBKeyData& IndexValueEntry::Iterator::key() const +{ + ASSERT(isValid()); + if (m_entry->unique()) { + ASSERT(m_entry->m_key); + return *m_entry->m_key; + } + + return m_forward ? *m_forwardIterator : *m_reverseIterator; +} + +bool IndexValueEntry::Iterator::isValid() const +{ +#if !LOG_DISABLED + if (m_entry) { + if (m_entry->m_unique) + ASSERT(m_entry->m_key); + else + ASSERT(m_entry->m_orderedKeys); + } +#endif + + return m_entry; +} + +void IndexValueEntry::Iterator::invalidate() +{ + m_entry = nullptr; +} + +IndexValueEntry::Iterator& IndexValueEntry::Iterator::operator++() +{ + if (!isValid()) + return *this; + + if (m_entry->m_unique) { + invalidate(); + return *this; + } + + if (m_forward) { + ++m_forwardIterator; + if (m_forwardIterator == m_entry->m_orderedKeys->end()) + invalidate(); + } else { + ++m_reverseIterator; + if (m_reverseIterator == m_entry->m_orderedKeys->rend()) + invalidate(); + } + + return *this; +} + +IndexValueEntry::Iterator IndexValueEntry::begin() +{ + if (m_unique) { + ASSERT(m_key); + return { *this }; + } + + ASSERT(m_orderedKeys); + return { *this, m_orderedKeys->begin() }; +} + +IndexValueEntry::Iterator IndexValueEntry::reverseBegin(CursorDuplicity duplicity) +{ + if (m_unique) { + ASSERT(m_key); + return { *this }; + } + + ASSERT(m_orderedKeys); + + if (duplicity == CursorDuplicity::Duplicates) + return { *this, m_orderedKeys->rbegin() }; + + auto iterator = m_orderedKeys->rend(); + --iterator; + return { *this, iterator }; +} + +IndexValueEntry::Iterator IndexValueEntry::find(const IDBKeyData& key) +{ + if (m_unique) { + ASSERT(m_key); + return *m_key == key ? IndexValueEntry::Iterator(*this) : IndexValueEntry::Iterator(); + } + + ASSERT(m_orderedKeys); + auto iterator = m_orderedKeys->lower_bound(key); + if (iterator == m_orderedKeys->end()) + return { }; + + return { *this, iterator }; +} + +IndexValueEntry::Iterator IndexValueEntry::reverseFind(const IDBKeyData& key, CursorDuplicity) +{ + if (m_unique) { + ASSERT(m_key); + return *m_key == key ? IndexValueEntry::Iterator(*this) : IndexValueEntry::Iterator(); + } + + ASSERT(m_orderedKeys); + auto iterator = std::set::reverse_iterator(m_orderedKeys->upper_bound(key)); + if (iterator == m_orderedKeys->rend()) + return { }; + + return { *this, iterator }; +} + + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.h b/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.h new file mode 100644 index 000000000..df0543fcf --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyData.h" +#include + +namespace WebCore { + +class ThreadSafeDataBuffer; + +enum class CursorDuplicity; + +namespace IDBServer { + +class IndexValueEntry { +public: + IndexValueEntry(bool unique); + ~IndexValueEntry(); + + void addKey(const IDBKeyData&); + + // Returns true if a key was actually removed. + bool removeKey(const IDBKeyData&); + + const IDBKeyData* getLowest() const; + + uint64_t getCount() const; + + class Iterator { + public: + Iterator() + { + } + + Iterator(IndexValueEntry&); + Iterator(IndexValueEntry&, std::set::iterator); + Iterator(IndexValueEntry&, std::set::reverse_iterator); + + bool isValid() const; + void invalidate(); + + const IDBKeyData& key() const; + const ThreadSafeDataBuffer& value() const; + + Iterator& operator++(); + + private: + IndexValueEntry* m_entry { nullptr }; + bool m_forward { true }; + std::set::iterator m_forwardIterator; + std::set::reverse_iterator m_reverseIterator; + }; + + Iterator begin(); + Iterator reverseBegin(CursorDuplicity); + + // Finds the key, or the next higher record after the key. + Iterator find(const IDBKeyData&); + // Finds the key, or the next lowest record before the key. + Iterator reverseFind(const IDBKeyData&, CursorDuplicity); + + bool unique() const { return m_unique; } + +private: + union { + std::set* m_orderedKeys; + IDBKeyData* m_key; + }; + + bool m_unique; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/IndexValueStore.cpp b/Source/WebCore/Modules/indexeddb/server/IndexValueStore.cpp new file mode 100644 index 000000000..339888462 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IndexValueStore.cpp @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IndexValueStore.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBError.h" +#include "IDBKeyRangeData.h" +#include "Logging.h" +#include "MemoryIndex.h" +#include + +namespace WebCore { +namespace IDBServer { + +IndexValueStore::IndexValueStore(bool unique) + : m_unique(unique) +{ +} + +const IDBKeyData* IndexValueStore::lowestValueForKey(const IDBKeyData& key) const +{ + const auto& entry = m_records.get(key); + if (!entry) + return nullptr; + + return entry->getLowest(); +} + +Vector IndexValueStore::allValuesForKey(const IDBKeyData& key, uint32_t limit) const +{ + const auto& entry = m_records.get(key); + if (!entry) + return { }; + + Vector results; + for (auto iterator = entry->begin(); results.size() < limit && iterator.isValid(); ++iterator) + results.append(iterator.key()); + + return results; +} + +uint64_t IndexValueStore::countForKey(const IDBKeyData& key) const +{ + const auto& entry = m_records.get(key); + if (!entry) + return 0; + + return entry->getCount(); +} + +bool IndexValueStore::contains(const IDBKeyData& key) const +{ + const auto& entry = m_records.get(key); + if (!entry) + return false; + + ASSERT(entry->getCount()); + + return true; +} + +IDBError IndexValueStore::addRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey) +{ + auto result = m_records.add(indexKey, nullptr); + + if (!result.isNewEntry && m_unique) + return IDBError(IDBDatabaseException::ConstraintError); + + if (result.isNewEntry) + result.iterator->value = std::make_unique(m_unique); + + result.iterator->value->addKey(valueKey); + m_orderedKeys.insert(indexKey); + + return { }; +} + +void IndexValueStore::removeRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey) +{ + auto iterator = m_records.find(indexKey); + if (!iterator->value) + return; + + if (iterator->value->removeKey(valueKey)) + m_records.remove(iterator); +} + +void IndexValueStore::removeEntriesWithValueKey(MemoryIndex& index, const IDBKeyData& valueKey) +{ + Vector entryKeysToRemove; + entryKeysToRemove.reserveInitialCapacity(m_records.size()); + + for (auto& entry : m_records) { + if (entry.value->removeKey(valueKey)) + index.notifyCursorsOfValueChange(entry.key, valueKey); + if (!entry.value->getCount()) + entryKeysToRemove.uncheckedAppend(entry.key); + } + + for (auto& entry : entryKeysToRemove) { + m_orderedKeys.erase(entry); + m_records.remove(entry); + } +} + +IDBKeyData IndexValueStore::lowestKeyWithRecordInRange(const IDBKeyRangeData& range) const +{ + LOG(IndexedDB, "IndexValueStore::lowestKeyWithRecordInRange - %s", range.loggingString().utf8().data()); + + if (range.isExactlyOneKey()) + return m_records.contains(range.lowerKey) ? range.lowerKey : IDBKeyData(); + + auto iterator = lowestIteratorInRange(range); + if (iterator == m_orderedKeys.end()) + return { }; + + return *iterator; +} + +std::set::iterator IndexValueStore::lowestIteratorInRange(const IDBKeyRangeData& range) const +{ + auto lowestInRange = m_orderedKeys.lower_bound(range.lowerKey); + + if (lowestInRange == m_orderedKeys.end()) + return lowestInRange; + + if (range.lowerOpen && *lowestInRange == range.lowerKey) { + ++lowestInRange; + + if (lowestInRange == m_orderedKeys.end()) + return lowestInRange; + } + + if (!range.upperKey.isNull()) { + if (lowestInRange->compare(range.upperKey) > 0) + return m_orderedKeys.end(); + if (range.upperOpen && *lowestInRange == range.upperKey) + return m_orderedKeys.end(); + } + + return lowestInRange; +} + +std::set::reverse_iterator IndexValueStore::highestReverseIteratorInRange(const IDBKeyRangeData& range) const +{ + auto highestInRange = std::set::reverse_iterator(m_orderedKeys.upper_bound(range.upperKey)); + + if (highestInRange == m_orderedKeys.rend()) + return highestInRange; + + if (range.upperOpen && *highestInRange == range.upperKey) { + ++highestInRange; + + if (highestInRange == m_orderedKeys.rend()) + return highestInRange; + } + + if (!range.lowerKey.isNull()) { + if (highestInRange->compare(range.lowerKey) < 0) + return m_orderedKeys.rend(); + if (range.lowerOpen && *highestInRange == range.lowerKey) + return m_orderedKeys.rend(); + } + + return highestInRange; +} + +IndexValueStore::Iterator IndexValueStore::find(const IDBKeyData& key, bool open) +{ + IDBKeyRangeData range; + if (!key.isNull()) + range.lowerKey = key; + else + range.lowerKey = IDBKeyData::minimum(); + range.lowerOpen = open; + + auto iterator = lowestIteratorInRange(range); + if (iterator == m_orderedKeys.end()) + return { }; + + auto record = m_records.get(*iterator); + ASSERT(record); + + auto primaryIterator = record->begin(); + ASSERT(primaryIterator.isValid()); + return { *this, iterator, primaryIterator }; +} + +IndexValueStore::Iterator IndexValueStore::find(const IDBKeyData& key, const IDBKeyData& primaryKey) +{ + ASSERT(!key.isNull()); + ASSERT(!primaryKey.isNull()); + + IDBKeyRangeData range; + range.lowerKey = key; + range.lowerOpen = false; + + auto iterator = lowestIteratorInRange(range); + if (iterator == m_orderedKeys.end()) + return { }; + + auto record = m_records.get(*iterator); + ASSERT(record); + + // If the main record iterator is not equal to the key we were looking for, + // we know the primary key record should be the first. + if (*iterator != key) { + auto primaryIterator = record->begin(); + ASSERT(primaryIterator.isValid()); + + return { *this, iterator, primaryIterator }; + } + + auto primaryIterator = record->find(primaryKey); + if (primaryIterator.isValid()) + return { *this, iterator, primaryIterator }; + + // If we didn't find a primary key iterator in this entry, + // we need to move on to start of the next record. + iterator++; + if (iterator == m_orderedKeys.end()) + return { }; + + record = m_records.get(*iterator); + ASSERT(record); + + primaryIterator = record->begin(); + ASSERT(primaryIterator.isValid()); + + return { *this, iterator, primaryIterator }; +} + +IndexValueStore::Iterator IndexValueStore::reverseFind(const IDBKeyData& key, CursorDuplicity duplicity, bool open) +{ + IDBKeyRangeData range; + if (!key.isNull()) + range.upperKey = key; + else + range.upperKey = IDBKeyData::maximum(); + range.upperOpen = open; + + auto iterator = highestReverseIteratorInRange(range); + if (iterator == m_orderedKeys.rend()) + return { }; + + auto record = m_records.get(*iterator); + ASSERT(record); + + auto primaryIterator = record->reverseBegin(duplicity); + ASSERT(primaryIterator.isValid()); + return { *this, duplicity, iterator, primaryIterator }; +} + +IndexValueStore::Iterator IndexValueStore::reverseFind(const IDBKeyData& key, const IDBKeyData& primaryKey, CursorDuplicity duplicity) +{ + ASSERT(!key.isNull()); + ASSERT(!primaryKey.isNull()); + + IDBKeyRangeData range; + range.upperKey = key; + range.upperOpen = false; + + auto iterator = highestReverseIteratorInRange(range); + if (iterator == m_orderedKeys.rend()) + return { }; + + auto record = m_records.get(*iterator); + ASSERT(record); + + auto primaryIterator = record->reverseFind(primaryKey, duplicity); + if (primaryIterator.isValid()) + return { *this, duplicity, iterator, primaryIterator }; + + // If we didn't find a primary key iterator in this entry, + // we need to move on to start of the next record. + iterator++; + if (iterator == m_orderedKeys.rend()) + return { }; + + record = m_records.get(*iterator); + ASSERT(record); + + primaryIterator = record->reverseBegin(duplicity); + ASSERT(primaryIterator.isValid()); + + return { *this, duplicity, iterator, primaryIterator }; +} + + +IndexValueStore::Iterator::Iterator(IndexValueStore& store, std::set::iterator iterator, IndexValueEntry::Iterator primaryIterator) + : m_store(&store) + , m_forwardIterator(iterator) + , m_primaryKeyIterator(primaryIterator) +{ +} + +IndexValueStore::Iterator::Iterator(IndexValueStore& store, CursorDuplicity duplicity, std::set::reverse_iterator iterator, IndexValueEntry::Iterator primaryIterator) + : m_store(&store) + , m_forward(false) + , m_duplicity(duplicity) + , m_reverseIterator(iterator) + , m_primaryKeyIterator(primaryIterator) +{ +} + +IndexValueStore::Iterator& IndexValueStore::Iterator::nextIndexEntry() +{ + if (!m_store) + return *this; + + if (m_forward) { + ++m_forwardIterator; + if (m_forwardIterator == m_store->m_orderedKeys.end()) { + invalidate(); + return *this; + } + + auto* entry = m_store->m_records.get(*m_forwardIterator); + ASSERT(entry); + + m_primaryKeyIterator = entry->begin(); + ASSERT(m_primaryKeyIterator.isValid()); + } else { + ++m_reverseIterator; + if (m_reverseIterator == m_store->m_orderedKeys.rend()) { + invalidate(); + return *this; + } + + auto* entry = m_store->m_records.get(*m_reverseIterator); + ASSERT(entry); + + m_primaryKeyIterator = entry->reverseBegin(m_duplicity); + ASSERT(m_primaryKeyIterator.isValid()); + } + + return *this; +} + +IndexValueStore::Iterator& IndexValueStore::Iterator::operator++() +{ + if (!isValid()) + return *this; + + ++m_primaryKeyIterator; + if (m_primaryKeyIterator.isValid()) + return *this; + + // Ran out of primary key records, so move the main index iterator. + return nextIndexEntry(); +} + +void IndexValueStore::Iterator::invalidate() +{ + m_store = nullptr; + m_primaryKeyIterator.invalidate(); +} + +bool IndexValueStore::Iterator::isValid() +{ + return m_store && m_primaryKeyIterator.isValid(); +} + +const IDBKeyData& IndexValueStore::Iterator::key() +{ + ASSERT(isValid()); + return m_forward ? *m_forwardIterator : *m_reverseIterator; +} + +const IDBKeyData& IndexValueStore::Iterator::primaryKey() +{ + ASSERT(isValid()); + return m_primaryKeyIterator.key(); +} + +#if !LOG_DISABLED +String IndexValueStore::loggingString() const +{ + StringBuilder builder; + for (auto& key : m_orderedKeys) { + builder.appendLiteral("Key: "); + builder.append(key.loggingString()); + builder.appendLiteral(" Entry has "); + builder.appendNumber(m_records.get(key)->getCount()); + builder.appendLiteral(" entries"); + } + return builder.toString(); +} +#endif + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/IndexValueStore.h b/Source/WebCore/Modules/indexeddb/server/IndexValueStore.h new file mode 100644 index 000000000..3af7c1fc0 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IndexValueStore.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorInfo.h" +#include "IDBKeyData.h" +#include "IndexValueEntry.h" +#include +#include + +namespace WebCore { + +class IDBError; + +struct IDBKeyRangeData; + +namespace IDBServer { + +class MemoryIndex; + +typedef HashMap, IDBKeyDataHash, IDBKeyDataHashTraits> IndexKeyValueMap; + +class IndexValueStore { +public: + IndexValueStore(bool unique); + + const IDBKeyData* lowestValueForKey(const IDBKeyData&) const; + Vector allValuesForKey(const IDBKeyData&, uint32_t limit) const; + uint64_t countForKey(const IDBKeyData&) const; + IDBKeyData lowestKeyWithRecordInRange(const IDBKeyRangeData&) const; + bool contains(const IDBKeyData&) const; + + IDBError addRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey); + void removeRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey); + + void removeEntriesWithValueKey(MemoryIndex&, const IDBKeyData& valueKey); + + class Iterator { + friend class IndexValueStore; + public: + Iterator() + { + } + + Iterator(IndexValueStore&, std::set::iterator, IndexValueEntry::Iterator); + Iterator(IndexValueStore&, CursorDuplicity, std::set::reverse_iterator, IndexValueEntry::Iterator); + + void invalidate(); + bool isValid(); + + const IDBKeyData& key(); + const IDBKeyData& primaryKey(); + const ThreadSafeDataBuffer& value(); + + Iterator& operator++(); + Iterator& nextIndexEntry(); + + private: + IndexValueStore* m_store { nullptr }; + bool m_forward { true }; + CursorDuplicity m_duplicity { CursorDuplicity::Duplicates }; + std::set::iterator m_forwardIterator; + std::set::reverse_iterator m_reverseIterator; + + IndexValueEntry::Iterator m_primaryKeyIterator; + }; + + // Returns an iterator pointing to the first primaryKey record in the requested key, or the next key if it doesn't exist. + Iterator find(const IDBKeyData&, bool open = false); + Iterator reverseFind(const IDBKeyData&, CursorDuplicity, bool open = false); + + // Returns an iterator pointing to the key/primaryKey record, or the next one after it if it doesn't exist. + Iterator find(const IDBKeyData&, const IDBKeyData& primaryKey); + Iterator reverseFind(const IDBKeyData&, const IDBKeyData& primaryKey, CursorDuplicity); + +#if !LOG_DISABLED + String loggingString() const; +#endif + +private: + std::set::iterator lowestIteratorInRange(const IDBKeyRangeData&) const; + std::set::reverse_iterator highestReverseIteratorInRange(const IDBKeyRangeData&) const; + + IndexKeyValueMap m_records; + std::set m_orderedKeys; + + bool m_unique; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp new file mode 100644 index 000000000..0e7bb6bda --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MemoryBackingStoreTransaction.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyRangeData.h" +#include "IDBValue.h" +#include "IndexedDB.h" +#include "Logging.h" +#include "MemoryIDBBackingStore.h" +#include "MemoryObjectStore.h" +#include + +namespace WebCore { +namespace IDBServer { + +std::unique_ptr MemoryBackingStoreTransaction::create(MemoryIDBBackingStore& backingStore, const IDBTransactionInfo& info) +{ + return std::make_unique(backingStore, info); +} + +MemoryBackingStoreTransaction::MemoryBackingStoreTransaction(MemoryIDBBackingStore& backingStore, const IDBTransactionInfo& info) + : m_backingStore(backingStore) + , m_info(info) +{ + if (m_info.mode() == IDBTransactionMode::Versionchange) { + IDBDatabaseInfo info; + auto error = m_backingStore.getOrEstablishDatabaseInfo(info); + if (error.isNull()) + m_originalDatabaseInfo = std::make_unique(info); + } +} + +MemoryBackingStoreTransaction::~MemoryBackingStoreTransaction() +{ + ASSERT(!m_inProgress); +} + +void MemoryBackingStoreTransaction::addNewObjectStore(MemoryObjectStore& objectStore) +{ + LOG(IndexedDB, "MemoryBackingStoreTransaction::addNewObjectStore()"); + + ASSERT(isVersionChange()); + m_versionChangeAddedObjectStores.add(&objectStore); + + addExistingObjectStore(objectStore); +} + +void MemoryBackingStoreTransaction::addNewIndex(MemoryIndex& index) +{ + LOG(IndexedDB, "MemoryBackingStoreTransaction::addNewIndex()"); + + ASSERT(isVersionChange()); + m_versionChangeAddedIndexes.add(&index); + + addExistingIndex(index); +} + +void MemoryBackingStoreTransaction::addExistingIndex(MemoryIndex& index) +{ + LOG(IndexedDB, "MemoryBackingStoreTransaction::addExistingIndex"); + + ASSERT(isWriting()); + + ASSERT(!m_indexes.contains(&index)); + m_indexes.add(&index); +} + +void MemoryBackingStoreTransaction::indexDeleted(Ref&& index) +{ + m_indexes.remove(&index.get()); + + // If this MemoryIndex belongs to an object store that will not get restored if this transaction aborts, + // then we can forget about it altogether. + auto& objectStore = index->objectStore(); + if (auto deletedObjectStore = m_deletedObjectStores.get(objectStore.info().name())) { + if (deletedObjectStore != &objectStore) + return; + } + + auto addResult = m_deletedIndexes.add(index->info().name(), nullptr); + if (addResult.isNewEntry) + addResult.iterator->value = WTFMove(index); +} + +void MemoryBackingStoreTransaction::addExistingObjectStore(MemoryObjectStore& objectStore) +{ + LOG(IndexedDB, "MemoryBackingStoreTransaction::addExistingObjectStore"); + + ASSERT(isWriting()); + + ASSERT(!m_objectStores.contains(&objectStore)); + m_objectStores.add(&objectStore); + + objectStore.writeTransactionStarted(*this); + + m_originalKeyGenerators.add(&objectStore, objectStore.currentKeyGeneratorValue()); +} + +void MemoryBackingStoreTransaction::objectStoreDeleted(Ref&& objectStore) +{ + ASSERT(m_objectStores.contains(&objectStore.get())); + m_objectStores.remove(&objectStore.get()); + + objectStore->deleteAllIndexes(*this); + + auto addResult = m_deletedObjectStores.add(objectStore->info().name(), nullptr); + if (addResult.isNewEntry) + addResult.iterator->value = WTFMove(objectStore); +} + +void MemoryBackingStoreTransaction::objectStoreCleared(MemoryObjectStore& objectStore, std::unique_ptr&& keyValueMap, std::unique_ptr>&& orderedKeys) +{ + ASSERT(m_objectStores.contains(&objectStore)); + + auto addResult = m_clearedKeyValueMaps.add(&objectStore, nullptr); + + // If this object store has already been cleared during this transaction, we shouldn't remember this clearing. + if (!addResult.isNewEntry) + return; + + addResult.iterator->value = WTFMove(keyValueMap); + + ASSERT(!m_clearedOrderedKeys.contains(&objectStore)); + m_clearedOrderedKeys.add(&objectStore, WTFMove(orderedKeys)); +} + +void MemoryBackingStoreTransaction::objectStoreRenamed(MemoryObjectStore& objectStore, const String& oldName) +{ + ASSERT(m_objectStores.contains(&objectStore)); + ASSERT(m_info.mode() == IDBTransactionMode::Versionchange); + + // We only care about the first rename in a given transaction, because if the transaction is aborted we want + // to go back to the first 'oldName' + m_originalObjectStoreNames.add(&objectStore, oldName); +} + +void MemoryBackingStoreTransaction::indexRenamed(MemoryIndex& index, const String& oldName) +{ + ASSERT(m_objectStores.contains(&index.objectStore())); + ASSERT(m_info.mode() == IDBTransactionMode::Versionchange); + + // We only care about the first rename in a given transaction, because if the transaction is aborted we want + // to go back to the first 'oldName' + m_originalIndexNames.add(&index, oldName); +} + +void MemoryBackingStoreTransaction::indexCleared(MemoryIndex& index, std::unique_ptr&& valueStore) +{ + auto addResult = m_clearedIndexValueStores.add(&index, nullptr); + + // If this index has already been cleared during this transaction, we shouldn't remember this clearing. + if (!addResult.isNewEntry) + return; + + addResult.iterator->value = WTFMove(valueStore); +} + +void MemoryBackingStoreTransaction::recordValueChanged(MemoryObjectStore& objectStore, const IDBKeyData& key, ThreadSafeDataBuffer* value) +{ + ASSERT(m_objectStores.contains(&objectStore)); + + if (m_isAborting) + return; + + // If this object store had been cleared during the transaction, no point in recording this + // individual key/value change as its entire key/value map will be restored upon abort. + if (m_clearedKeyValueMaps.contains(&objectStore)) + return; + + auto originalAddResult = m_originalValues.add(&objectStore, nullptr); + if (originalAddResult.isNewEntry) + originalAddResult.iterator->value = std::make_unique(); + + auto* map = originalAddResult.iterator->value.get(); + + auto addResult = map->add(key, ThreadSafeDataBuffer()); + if (!addResult.isNewEntry) + return; + + if (value) + addResult.iterator->value = *value; +} + +void MemoryBackingStoreTransaction::abort() +{ + LOG(IndexedDB, "MemoryBackingStoreTransaction::abort()"); + + SetForScope change(m_isAborting, true); + + for (auto iterator : m_originalIndexNames) + iterator.key->rename(iterator.value); + m_originalIndexNames.clear(); + + for (auto iterator : m_originalObjectStoreNames) + iterator.key->rename(iterator.value); + m_originalObjectStoreNames.clear(); + + for (auto objectStore : m_versionChangeAddedObjectStores) + m_backingStore.removeObjectStoreForVersionChangeAbort(*objectStore); + m_versionChangeAddedObjectStores.clear(); + + for (auto& objectStore : m_deletedObjectStores.values()) { + m_backingStore.restoreObjectStoreForVersionChangeAbort(*objectStore); + ASSERT(!m_objectStores.contains(objectStore.get())); + m_objectStores.add(objectStore); + } + m_deletedObjectStores.clear(); + + if (m_originalDatabaseInfo) { + ASSERT(m_info.mode() == IDBTransactionMode::Versionchange); + m_backingStore.setDatabaseInfo(*m_originalDatabaseInfo); + } + + // Restore cleared index value stores before we re-insert values into object stores + // because inserting those values will regenerate the appropriate index values. + for (auto& iterator : m_clearedIndexValueStores) + iterator.key->replaceIndexValueStore(WTFMove(iterator.value)); + m_clearedIndexValueStores.clear(); + + for (auto& objectStore : m_objectStores) { + ASSERT(m_originalKeyGenerators.contains(objectStore.get())); + objectStore->setKeyGeneratorValue(m_originalKeyGenerators.get(objectStore.get())); + + auto clearedKeyValueMap = m_clearedKeyValueMaps.take(objectStore.get()); + if (clearedKeyValueMap) { + ASSERT(m_clearedOrderedKeys.contains(objectStore.get())); + objectStore->replaceKeyValueStore(WTFMove(clearedKeyValueMap), m_clearedOrderedKeys.take(objectStore.get())); + } + + auto keyValueMap = m_originalValues.take(objectStore.get()); + if (!keyValueMap) + continue; + + for (auto entry : *keyValueMap) { + objectStore->deleteRecord(entry.key); + objectStore->addRecord(*this, entry.key, { entry.value }); + } + } + + for (auto& index : m_deletedIndexes.values()) + index->objectStore().maybeRestoreDeletedIndex(*index); + m_deletedIndexes.clear(); + + finish(); +} + +void MemoryBackingStoreTransaction::commit() +{ + LOG(IndexedDB, "MemoryBackingStoreTransaction::commit()"); + + finish(); +} + +void MemoryBackingStoreTransaction::finish() +{ + m_inProgress = false; + + if (!isWriting()) + return; + + for (auto& objectStore : m_objectStores) + objectStore->writeTransactionFinished(*this); + for (auto& objectStore : m_deletedObjectStores.values()) + objectStore->writeTransactionFinished(*this); +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.h b/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.h new file mode 100644 index 000000000..5ac9302cc --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBDatabaseInfo.h" +#include "IDBKeyData.h" +#include "IDBTransactionInfo.h" +#include "IndexValueStore.h" +#include "ThreadSafeDataBuffer.h" +#include +#include + +namespace WebCore { +namespace IDBServer { + +class MemoryIDBBackingStore; +class MemoryIndex; +class MemoryObjectStore; + +typedef HashMap KeyValueMap; + +class MemoryBackingStoreTransaction { +public: + static std::unique_ptr create(MemoryIDBBackingStore&, const IDBTransactionInfo&); + + MemoryBackingStoreTransaction(MemoryIDBBackingStore&, const IDBTransactionInfo&); + ~MemoryBackingStoreTransaction(); + + bool isVersionChange() const { return m_info.mode() == IDBTransactionMode::Versionchange; } + bool isWriting() const { return m_info.mode() != IDBTransactionMode::Readonly; } + bool isAborting() const { return m_isAborting; } + + const IDBDatabaseInfo& originalDatabaseInfo() const; + + void addNewObjectStore(MemoryObjectStore&); + void addExistingObjectStore(MemoryObjectStore&); + + void recordValueChanged(MemoryObjectStore&, const IDBKeyData&, ThreadSafeDataBuffer*); + void objectStoreDeleted(Ref&&); + void objectStoreCleared(MemoryObjectStore&, std::unique_ptr&&, std::unique_ptr>&&); + void objectStoreRenamed(MemoryObjectStore&, const String& oldName); + void indexRenamed(MemoryIndex&, const String& oldName); + void indexCleared(MemoryIndex&, std::unique_ptr&&); + + void addNewIndex(MemoryIndex&); + void addExistingIndex(MemoryIndex&); + void indexDeleted(Ref&&); + + void abort(); + void commit(); + +private: + void finish(); + + MemoryIDBBackingStore& m_backingStore; + IDBTransactionInfo m_info; + + std::unique_ptr m_originalDatabaseInfo; + + bool m_inProgress { true }; + bool m_isAborting { false }; + + HashSet> m_objectStores; + HashSet> m_versionChangeAddedObjectStores; + HashSet> m_indexes; + HashSet> m_versionChangeAddedIndexes; + + HashMap m_originalKeyGenerators; + HashMap> m_deletedObjectStores; + HashMap> m_deletedIndexes; + HashMap> m_originalValues; + HashMap> m_clearedKeyValueMaps; + HashMap>> m_clearedOrderedKeys; + HashMap m_originalObjectStoreNames; + HashMap m_originalIndexNames; + HashMap> m_clearedIndexValueStores; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryCursor.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryCursor.cpp new file mode 100644 index 000000000..67059f313 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryCursor.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MemoryCursor.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBResourceIdentifier.h" +#include "MemoryIDBBackingStore.h" +#include +#include + +namespace WebCore { +namespace IDBServer { + +static HashMap& cursorMap() +{ + static NeverDestroyed> map; + return map; +} + +MemoryCursor::MemoryCursor(const IDBCursorInfo& info) + : m_info(info) +{ + ASSERT(!cursorMap().contains(m_info.identifier())); + cursorMap().set(m_info.identifier(), this); +} + +MemoryCursor::~MemoryCursor() +{ + ASSERT(cursorMap().contains(m_info.identifier())); + cursorMap().remove(m_info.identifier()); +} + +MemoryCursor* MemoryCursor::cursorForIdentifier(const IDBResourceIdentifier& identifier) +{ + return cursorMap().get(identifier); +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryCursor.h b/Source/WebCore/Modules/indexeddb/server/MemoryCursor.h new file mode 100644 index 000000000..64ac3e82d --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryCursor.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorInfo.h" + +namespace WebCore { + +class IDBGetResult; +class IDBKeyData; +class IDBResourceIdentifier; + +namespace IDBServer { + +class MemoryCursor { +public: + virtual ~MemoryCursor(); + + virtual void currentData(IDBGetResult&) = 0; + virtual void iterate(const IDBKeyData&, const IDBKeyData& primaryKey, uint32_t count, IDBGetResult&) = 0; + + static MemoryCursor* cursorForIdentifier(const IDBResourceIdentifier&); + +protected: + MemoryCursor(const IDBCursorInfo&); + + IDBCursorInfo m_info; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp new file mode 100644 index 000000000..e6330891f --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp @@ -0,0 +1,599 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MemoryIDBBackingStore.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorInfo.h" +#include "IDBGetAllRecordsData.h" +#include "IDBGetRecordData.h" +#include "IDBGetResult.h" +#include "IDBIndexInfo.h" +#include "IDBIterateCursorData.h" +#include "IDBKeyRangeData.h" +#include "Logging.h" +#include "MemoryIndexCursor.h" +#include "MemoryObjectStore.h" +#include "MemoryObjectStoreCursor.h" + +namespace WebCore { +namespace IDBServer { + +// The IndexedDB spec states the value you can get from the key generator is 2^53 +static uint64_t maxGeneratedKeyValue = 0x20000000000000; + +std::unique_ptr MemoryIDBBackingStore::create(const IDBDatabaseIdentifier& identifier) +{ + return std::make_unique(identifier); +} + +MemoryIDBBackingStore::MemoryIDBBackingStore(const IDBDatabaseIdentifier& identifier) + : m_identifier(identifier) +{ +} + +MemoryIDBBackingStore::~MemoryIDBBackingStore() +{ +} + +IDBError MemoryIDBBackingStore::getOrEstablishDatabaseInfo(IDBDatabaseInfo& info) +{ + if (!m_databaseInfo) + m_databaseInfo = std::make_unique(m_identifier.databaseName(), 0); + + info = *m_databaseInfo; + return { }; +} + +void MemoryIDBBackingStore::setDatabaseInfo(const IDBDatabaseInfo& info) +{ + // It is not valid to directly set database info on a backing store that hasn't already set its own database info. + ASSERT(m_databaseInfo); + + m_databaseInfo = std::make_unique(info); +} + +IDBError MemoryIDBBackingStore::beginTransaction(const IDBTransactionInfo& info) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::beginTransaction"); + + if (m_transactions.contains(info.identifier())) + return { IDBDatabaseException::InvalidStateError, "Backing store asked to create transaction it already has a record of" }; + + auto transaction = MemoryBackingStoreTransaction::create(*this, info); + + // VersionChange transactions are scoped to "every object store". + if (transaction->isVersionChange()) { + for (auto& objectStore : m_objectStoresByIdentifier.values()) + transaction->addExistingObjectStore(*objectStore); + } else if (transaction->isWriting()) { + for (auto& iterator : m_objectStoresByName) { + if (info.objectStores().contains(iterator.key)) + transaction->addExistingObjectStore(*iterator.value); + } + } + + m_transactions.set(info.identifier(), WTFMove(transaction)); + + return { }; +} + +IDBError MemoryIDBBackingStore::abortTransaction(const IDBResourceIdentifier& transactionIdentifier) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::abortTransaction - %s", transactionIdentifier.loggingString().utf8().data()); + + auto transaction = m_transactions.take(transactionIdentifier); + if (!transaction) + return { IDBDatabaseException::InvalidStateError, "Backing store asked to abort transaction it didn't have record of" }; + + transaction->abort(); + + return { }; +} + +IDBError MemoryIDBBackingStore::commitTransaction(const IDBResourceIdentifier& transactionIdentifier) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::commitTransaction - %s", transactionIdentifier.loggingString().utf8().data()); + + auto transaction = m_transactions.take(transactionIdentifier); + if (!transaction) + return { IDBDatabaseException::InvalidStateError, "Backing store asked to commit transaction it didn't have record of" }; + + transaction->commit(); + + return { }; +} + +IDBError MemoryIDBBackingStore::createObjectStore(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& info) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::createObjectStore - adding OS %s with ID %" PRIu64, info.name().utf8().data(), info.identifier()); + + ASSERT(m_databaseInfo); + if (m_databaseInfo->hasObjectStore(info.name())) + return { IDBDatabaseException::ConstraintError }; + + ASSERT(!m_objectStoresByIdentifier.contains(info.identifier())); + auto objectStore = MemoryObjectStore::create(info); + + m_databaseInfo->addExistingObjectStore(info); + + auto rawTransaction = m_transactions.get(transactionIdentifier); + ASSERT(rawTransaction); + ASSERT(rawTransaction->isVersionChange()); + + rawTransaction->addNewObjectStore(objectStore.get()); + registerObjectStore(WTFMove(objectStore)); + + return { }; +} + +IDBError MemoryIDBBackingStore::deleteObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::deleteObjectStore"); + + ASSERT(m_databaseInfo); + if (!m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier)) + return { IDBDatabaseException::ConstraintError }; + + auto transaction = m_transactions.get(transactionIdentifier); + ASSERT(transaction); + ASSERT(transaction->isVersionChange()); + + auto objectStore = takeObjectStoreByIdentifier(objectStoreIdentifier); + ASSERT(objectStore); + if (!objectStore) + return { IDBDatabaseException::ConstraintError }; + + m_databaseInfo->deleteObjectStore(objectStore->info().name()); + transaction->objectStoreDeleted(*objectStore); + + return { }; +} + +IDBError MemoryIDBBackingStore::renameObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::renameObjectStore"); + + ASSERT(m_databaseInfo); + if (!m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier)) + return { IDBDatabaseException::ConstraintError }; + + auto transaction = m_transactions.get(transactionIdentifier); + ASSERT(transaction); + ASSERT(transaction->isVersionChange()); + + auto objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + ASSERT(objectStore); + if (!objectStore) + return { IDBDatabaseException::ConstraintError }; + + String oldName = objectStore->info().name(); + objectStore->rename(newName); + transaction->objectStoreRenamed(*objectStore, oldName); + + m_databaseInfo->renameObjectStore(objectStoreIdentifier, newName); + + return { }; +} + +IDBError MemoryIDBBackingStore::clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::clearObjectStore"); + ASSERT(objectStoreIdentifier); + + ASSERT_UNUSED(transactionIdentifier, m_transactions.contains(transactionIdentifier)); + +#if !LOG_DISABLED + auto transaction = m_transactions.get(transactionIdentifier); + ASSERT(transaction->isWriting()); +#endif + + auto objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + if (!objectStore) + return { IDBDatabaseException::ConstraintError }; + + objectStore->clear(); + + return { }; +} + +IDBError MemoryIDBBackingStore::createIndex(const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo& info) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::createIndex"); + + auto rawTransaction = m_transactions.get(transactionIdentifier); + ASSERT(rawTransaction); + ASSERT(rawTransaction->isVersionChange()); + + auto* objectStore = m_objectStoresByIdentifier.get(info.objectStoreIdentifier()); + if (!objectStore) + return { IDBDatabaseException::ConstraintError }; + + return objectStore->createIndex(*rawTransaction, info); +} + +IDBError MemoryIDBBackingStore::deleteIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::deleteIndex"); + + auto rawTransaction = m_transactions.get(transactionIdentifier); + ASSERT(rawTransaction); + ASSERT(rawTransaction->isVersionChange()); + + auto* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + if (!objectStore) + return { IDBDatabaseException::ConstraintError }; + + return objectStore->deleteIndex(*rawTransaction, indexIdentifier); +} + +IDBError MemoryIDBBackingStore::renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::renameIndex"); + + ASSERT(m_databaseInfo); + auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); + if (!objectStoreInfo) + return { IDBDatabaseException::ConstraintError }; + + auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier); + if (!indexInfo) + return { IDBDatabaseException::ConstraintError }; + + auto transaction = m_transactions.get(transactionIdentifier); + ASSERT(transaction); + ASSERT(transaction->isVersionChange()); + + auto objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + ASSERT(objectStore); + if (!objectStore) + return { IDBDatabaseException::ConstraintError }; + + auto* index = objectStore->indexForIdentifier(indexIdentifier); + ASSERT(index); + if (!index) + return { IDBDatabaseException::ConstraintError }; + + String oldName = index->info().name(); + objectStore->renameIndex(*index, newName); + transaction->indexRenamed(*index, oldName); + + indexInfo->rename(newName); + + return { }; +} + +void MemoryIDBBackingStore::removeObjectStoreForVersionChangeAbort(MemoryObjectStore& objectStore) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::removeObjectStoreForVersionChangeAbort"); + + if (!m_objectStoresByIdentifier.contains(objectStore.info().identifier())) + return; + + ASSERT(m_objectStoresByIdentifier.get(objectStore.info().identifier()) == &objectStore); + + unregisterObjectStore(objectStore); +} + +void MemoryIDBBackingStore::restoreObjectStoreForVersionChangeAbort(Ref&& objectStore) +{ + registerObjectStore(WTFMove(objectStore)); +} + +IDBError MemoryIDBBackingStore::keyExistsInObjectStore(const IDBResourceIdentifier&, uint64_t objectStoreIdentifier, const IDBKeyData& keyData, bool& keyExists) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::keyExistsInObjectStore"); + + ASSERT(objectStoreIdentifier); + + MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + RELEASE_ASSERT(objectStore); + + keyExists = objectStore->containsRecord(keyData); + return { }; +} + +IDBError MemoryIDBBackingStore::deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::deleteRange"); + + ASSERT(objectStoreIdentifier); + + if (!m_transactions.contains(transactionIdentifier)) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to delete from") }; + + MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + if (!objectStore) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") }; + + objectStore->deleteRange(range); + return { }; +} + +IDBError MemoryIDBBackingStore::addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& objectStoreInfo, const IDBKeyData& keyData, const IDBValue& value) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::addRecord"); + + ASSERT(objectStoreInfo.identifier()); + + auto transaction = m_transactions.get(transactionIdentifier); + if (!transaction) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to put record") }; + + MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreInfo.identifier()); + if (!objectStore) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found to put record") }; + + return objectStore->addRecord(*transaction, keyData, value); +} + +IDBError MemoryIDBBackingStore::getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range, IDBGetRecordDataType type, IDBGetResult& outValue) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::getRecord"); + + ASSERT(objectStoreIdentifier); + + if (!m_transactions.contains(transactionIdentifier)) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to get record") }; + + MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + if (!objectStore) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") }; + + switch (type) { + case IDBGetRecordDataType::KeyAndValue: + outValue = objectStore->valueForKeyRange(range); + break; + case IDBGetRecordDataType::KeyOnly: + outValue = objectStore->lowestKeyWithRecordInRange(range); + break; + } + + return { }; +} + +IDBError MemoryIDBBackingStore::getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData, IDBGetAllResult& result) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::getAllRecords"); + + ASSERT(getAllRecordsData.objectStoreIdentifier); + + if (!m_transactions.contains(transactionIdentifier)) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to get all records") }; + + auto* objectStore = m_objectStoresByIdentifier.get(getAllRecordsData.objectStoreIdentifier); + if (!objectStore) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") }; + + if (getAllRecordsData.indexIdentifier) { + auto* index = objectStore->indexForIdentifier(getAllRecordsData.indexIdentifier); + if (!index) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store index found") }; + + index->getAllRecords(getAllRecordsData.keyRangeData, getAllRecordsData.count, getAllRecordsData.getAllType, result); + } else + objectStore->getAllRecords(getAllRecordsData.keyRangeData, getAllRecordsData.count, getAllRecordsData.getAllType, result); + + return { }; +} + +IDBError MemoryIDBBackingStore::getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType recordType, const IDBKeyRangeData& range, IDBGetResult& outValue) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::getIndexRecord"); + + ASSERT(objectStoreIdentifier); + + if (!m_transactions.contains(transactionIdentifier)) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to get record") }; + + MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + if (!objectStore) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") }; + + outValue = objectStore->indexValueForKeyRange(indexIdentifier, recordType, range); + return { }; +} + +IDBError MemoryIDBBackingStore::getCount(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& range, uint64_t& outCount) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::getCount"); + + ASSERT(objectStoreIdentifier); + + if (!m_transactions.contains(transactionIdentifier)) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to get count") }; + + MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + if (!objectStore) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") }; + + outCount = objectStore->countForKeyRange(indexIdentifier, range); + + return { }; +} + +IDBError MemoryIDBBackingStore::generateKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t& keyNumber) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::generateKeyNumber"); + ASSERT(objectStoreIdentifier); + ASSERT_UNUSED(transactionIdentifier, m_transactions.contains(transactionIdentifier)); + ASSERT_UNUSED(transactionIdentifier, m_transactions.get(transactionIdentifier)->isWriting()); + + MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + RELEASE_ASSERT(objectStore); + + keyNumber = objectStore->currentKeyGeneratorValue(); + if (keyNumber > maxGeneratedKeyValue) + return { IDBDatabaseException::ConstraintError, "Cannot generate new key value over 2^53 for object store operation" }; + + objectStore->setKeyGeneratorValue(keyNumber + 1); + + return { }; +} + +IDBError MemoryIDBBackingStore::revertGeneratedKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t keyNumber) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::revertGeneratedKeyNumber"); + ASSERT(objectStoreIdentifier); + ASSERT_UNUSED(transactionIdentifier, m_transactions.contains(transactionIdentifier)); + ASSERT_UNUSED(transactionIdentifier, m_transactions.get(transactionIdentifier)->isWriting()); + + MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + RELEASE_ASSERT(objectStore); + + objectStore->setKeyGeneratorValue(keyNumber); + + return { }; +} + +IDBError MemoryIDBBackingStore::maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, double newKeyNumber) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::maybeUpdateKeyGeneratorNumber"); + ASSERT(objectStoreIdentifier); + ASSERT_UNUSED(transactionIdentifier, m_transactions.contains(transactionIdentifier)); + ASSERT_UNUSED(transactionIdentifier, m_transactions.get(transactionIdentifier)->isWriting()); + + MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); + RELEASE_ASSERT(objectStore); + + if (newKeyNumber < objectStore->currentKeyGeneratorValue()) + return { }; + + uint64_t newKeyInteger(newKeyNumber); + if (newKeyInteger <= uint64_t(newKeyNumber)) + ++newKeyInteger; + + ASSERT(newKeyInteger > uint64_t(newKeyNumber)); + + objectStore->setKeyGeneratorValue(newKeyInteger); + + return { }; +} + +IDBError MemoryIDBBackingStore::openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo& info, IDBGetResult& outData) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::openCursor"); + + ASSERT(!MemoryCursor::cursorForIdentifier(info.identifier())); + + if (!m_transactions.contains(transactionIdentifier)) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found in which to open a cursor") }; + + switch (info.cursorSource()) { + case IndexedDB::CursorSource::ObjectStore: { + auto* objectStore = m_objectStoresByIdentifier.get(info.sourceIdentifier()); + if (!objectStore) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") }; + + MemoryCursor* cursor = objectStore->maybeOpenCursor(info); + if (!cursor) + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not create object store cursor in backing store") }; + + cursor->currentData(outData); + break; + } + case IndexedDB::CursorSource::Index: + auto* objectStore = m_objectStoresByIdentifier.get(info.objectStoreIdentifier()); + if (!objectStore) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") }; + + auto* index = objectStore->indexForIdentifier(info.sourceIdentifier()); + if (!index) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store index found") }; + + MemoryCursor* cursor = index->maybeOpenCursor(info); + if (!cursor) + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not create index cursor in backing store") }; + + cursor->currentData(outData); + break; + } + + return { }; +} + +IDBError MemoryIDBBackingStore::iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData& data, IDBGetResult& outData) +{ + LOG(IndexedDB, "MemoryIDBBackingStore::iterateCursor"); + + if (!m_transactions.contains(transactionIdentifier)) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found in which to iterate cursor") }; + + auto* cursor = MemoryCursor::cursorForIdentifier(cursorIdentifier); + if (!cursor) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store cursor found in which to iterate cursor") }; + + cursor->iterate(data.keyData, data.primaryKeyData, data.count, outData); + + return { }; +} + +void MemoryIDBBackingStore::registerObjectStore(Ref&& objectStore) +{ + ASSERT(!m_objectStoresByIdentifier.contains(objectStore->info().identifier())); + ASSERT(!m_objectStoresByName.contains(objectStore->info().name())); + + m_objectStoresByName.set(objectStore->info().name(), &objectStore.get()); + m_objectStoresByIdentifier.set(objectStore->info().identifier(), WTFMove(objectStore)); +} + +void MemoryIDBBackingStore::unregisterObjectStore(MemoryObjectStore& objectStore) +{ + ASSERT(m_objectStoresByIdentifier.contains(objectStore.info().identifier())); + ASSERT(m_objectStoresByName.contains(objectStore.info().name())); + + m_objectStoresByName.remove(objectStore.info().name()); + m_objectStoresByIdentifier.remove(objectStore.info().identifier()); +} + +RefPtr MemoryIDBBackingStore::takeObjectStoreByIdentifier(uint64_t identifier) +{ + auto objectStoreByIdentifier = m_objectStoresByIdentifier.take(identifier); + if (!objectStoreByIdentifier) + return nullptr; + + auto objectStore = m_objectStoresByName.take(objectStoreByIdentifier->info().name()); + ASSERT_UNUSED(objectStore, objectStore); + + return objectStoreByIdentifier; +} + +IDBObjectStoreInfo* MemoryIDBBackingStore::infoForObjectStore(uint64_t objectStoreIdentifier) +{ + ASSERT(m_databaseInfo); + return m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); +} + +void MemoryIDBBackingStore::deleteBackingStore() +{ + // The in-memory IDB backing store doesn't need to do any cleanup when it is deleted. +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h new file mode 100644 index 000000000..e93a17df3 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBBackingStore.h" +#include "IDBDatabaseIdentifier.h" +#include "IDBResourceIdentifier.h" +#include "MemoryBackingStoreTransaction.h" +#include + +namespace WebCore { +namespace IDBServer { + +class MemoryObjectStore; + +class MemoryIDBBackingStore : public IDBBackingStore { +public: + static std::unique_ptr create(const IDBDatabaseIdentifier&); + + MemoryIDBBackingStore(const IDBDatabaseIdentifier&); + ~MemoryIDBBackingStore() final; + + IDBError getOrEstablishDatabaseInfo(IDBDatabaseInfo&) final; + void setDatabaseInfo(const IDBDatabaseInfo&); + + IDBError beginTransaction(const IDBTransactionInfo&) final; + IDBError abortTransaction(const IDBResourceIdentifier& transactionIdentifier) final; + IDBError commitTransaction(const IDBResourceIdentifier& transactionIdentifier) final; + IDBError createObjectStore(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&) final; + IDBError deleteObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) final; + IDBError renameObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName) final; + IDBError clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) final; + IDBError createIndex(const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo&) final; + IDBError deleteIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier) final; + IDBError renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) final; + IDBError keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData&, bool& keyExists) final; + IDBError deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&) final; + IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IDBValue&) final; + IDBError getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IDBGetRecordDataType, IDBGetResult& outValue) final; + IDBError getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue) final; + IDBError getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&, IDBGetResult& outValue) final; + IDBError getCount(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, uint64_t& outCount) final; + IDBError generateKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t& keyNumber) final; + IDBError revertGeneratedKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t keyNumber) final; + IDBError maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, double newKeyNumber) final; + IDBError openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&, IDBGetResult& outResult) final; + IDBError iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&, IDBGetResult& outResult) final; + bool prefetchCursor(const IDBResourceIdentifier&, const IDBResourceIdentifier&) final { return false; } + + IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) final; + void deleteBackingStore() final; + + bool supportsSimultaneousTransactions() final { return true; } + bool isEphemeral() final { return true; } + + void removeObjectStoreForVersionChangeAbort(MemoryObjectStore&); + void restoreObjectStoreForVersionChangeAbort(Ref&&); + +private: + RefPtr takeObjectStoreByIdentifier(uint64_t identifier); + + IDBDatabaseIdentifier m_identifier; + std::unique_ptr m_databaseInfo; + + HashMap> m_transactions; + + void registerObjectStore(Ref&&); + void unregisterObjectStore(MemoryObjectStore&); + HashMap> m_objectStoresByIdentifier; + HashMap m_objectStoresByName; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIndex.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryIndex.cpp new file mode 100644 index 000000000..e92d26801 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryIndex.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MemoryIndex.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBError.h" +#include "IDBGetAllResult.h" +#include "IDBGetResult.h" +#include "IDBKeyRangeData.h" +#include "IndexKey.h" +#include "Logging.h" +#include "MemoryBackingStoreTransaction.h" +#include "MemoryIndexCursor.h" +#include "MemoryObjectStore.h" +#include "ThreadSafeDataBuffer.h" + +namespace WebCore { +namespace IDBServer { + +Ref MemoryIndex::create(const IDBIndexInfo& info, MemoryObjectStore& objectStore) +{ + return adoptRef(*new MemoryIndex(info, objectStore)); +} + +MemoryIndex::MemoryIndex(const IDBIndexInfo& info, MemoryObjectStore& objectStore) + : m_info(info) + , m_objectStore(objectStore) +{ +} + +MemoryIndex::~MemoryIndex() +{ +} + +void MemoryIndex::cursorDidBecomeClean(MemoryIndexCursor& cursor) +{ + m_cleanCursors.add(&cursor); +} + +void MemoryIndex::cursorDidBecomeDirty(MemoryIndexCursor& cursor) +{ + m_cleanCursors.remove(&cursor); +} + +void MemoryIndex::objectStoreCleared() +{ + auto transaction = m_objectStore.writeTransaction(); + ASSERT(transaction); + + transaction->indexCleared(*this, WTFMove(m_records)); + + notifyCursorsOfAllRecordsChanged(); +} + +void MemoryIndex::notifyCursorsOfValueChange(const IDBKeyData& indexKey, const IDBKeyData& primaryKey) +{ + Vector cursors; + copyToVector(m_cleanCursors, cursors); + for (auto* cursor : cursors) + cursor->indexValueChanged(indexKey, primaryKey); +} + +void MemoryIndex::notifyCursorsOfAllRecordsChanged() +{ + Vector cursors; + copyToVector(m_cleanCursors, cursors); + for (auto* cursor : cursors) + cursor->indexRecordsAllChanged(); + + ASSERT(m_cleanCursors.isEmpty()); +} + +void MemoryIndex::clearIndexValueStore() +{ + ASSERT(m_objectStore.writeTransaction()); + ASSERT(m_objectStore.writeTransaction()->isAborting()); + + m_records = nullptr; +} + +void MemoryIndex::replaceIndexValueStore(std::unique_ptr&& valueStore) +{ + ASSERT(m_objectStore.writeTransaction()); + ASSERT(m_objectStore.writeTransaction()->isAborting()); + + m_records = WTFMove(valueStore); +} + +IDBGetResult MemoryIndex::getResultForKeyRange(IndexedDB::IndexRecordType type, const IDBKeyRangeData& range) const +{ + LOG(IndexedDB, "MemoryIndex::getResultForKeyRange - %s", range.loggingString().utf8().data()); + + if (!m_records) + return { }; + + IDBKeyData keyToLookFor; + if (range.isExactlyOneKey()) + keyToLookFor = range.lowerKey; + else + keyToLookFor = m_records->lowestKeyWithRecordInRange(range); + + if (keyToLookFor.isNull()) + return { }; + + const IDBKeyData* keyValue = m_records->lowestValueForKey(keyToLookFor); + + if (!keyValue) + return { }; + + return type == IndexedDB::IndexRecordType::Key ? IDBGetResult(*keyValue) : IDBGetResult(m_objectStore.valueForKeyRange(*keyValue)); +} + +uint64_t MemoryIndex::countForKeyRange(const IDBKeyRangeData& inRange) +{ + LOG(IndexedDB, "MemoryIndex::countForKeyRange"); + + if (!m_records) + return 0; + + uint64_t count = 0; + IDBKeyRangeData range = inRange; + while (true) { + auto key = m_records->lowestKeyWithRecordInRange(range); + if (key.isNull()) + break; + + count += m_records->countForKey(key); + + range.lowerKey = key; + range.lowerOpen = true; + } + + return count; +} + +void MemoryIndex::getAllRecords(const IDBKeyRangeData& keyRangeData, std::optional count, IndexedDB::GetAllType type, IDBGetAllResult& result) const +{ + LOG(IndexedDB, "MemoryIndex::getAllRecords"); + + result = { type }; + + if (!m_records) + return; + + uint32_t targetCount; + if (count && count.value()) + targetCount = count.value(); + else + targetCount = std::numeric_limits::max(); + + IDBKeyRangeData range = keyRangeData; + uint32_t currentCount = 0; + while (currentCount < targetCount) { + auto key = m_records->lowestKeyWithRecordInRange(range); + if (key.isNull()) + return; + + range.lowerKey = key; + range.lowerOpen = true; + + auto allValues = m_records->allValuesForKey(key, targetCount - currentCount); + for (auto& keyValue : allValues) { + if (type == IndexedDB::GetAllType::Keys) { + IDBKeyData keyCopy { keyValue }; + result.addKey(WTFMove(keyCopy)); + } else + result.addValue(m_objectStore.valueForKeyRange(keyValue)); + } + + currentCount += allValues.size(); + } +} + + +IDBError MemoryIndex::putIndexKey(const IDBKeyData& valueKey, const IndexKey& indexKey) +{ + LOG(IndexedDB, "MemoryIndex::provisionalPutIndexKey"); + + if (!m_records) { + m_records = std::make_unique(m_info.unique()); + notifyCursorsOfAllRecordsChanged(); + } + + if (!m_info.multiEntry()) { + IDBKeyData key = indexKey.asOneKey(); + IDBError result = m_records->addRecord(key, valueKey); + notifyCursorsOfValueChange(key, valueKey); + return result; + } + + Vector keys = indexKey.multiEntry(); + + if (m_info.unique()) { + for (auto& key : keys) { + if (m_records->contains(key)) + return IDBError(IDBDatabaseException::ConstraintError); + } + } + + for (auto& key : keys) { + auto error = m_records->addRecord(key, valueKey); + ASSERT_UNUSED(error, error.isNull()); + notifyCursorsOfValueChange(key, valueKey); + } + + return { }; +} + +void MemoryIndex::removeRecord(const IDBKeyData& valueKey, const IndexKey& indexKey) +{ + LOG(IndexedDB, "MemoryIndex::removeRecord"); + + ASSERT(m_records); + + if (!m_info.multiEntry()) { + IDBKeyData key = indexKey.asOneKey(); + m_records->removeRecord(key, valueKey); + notifyCursorsOfValueChange(key, valueKey); + return; + } + + Vector keys = indexKey.multiEntry(); + for (auto& key : keys) { + m_records->removeRecord(key, valueKey); + notifyCursorsOfValueChange(key, valueKey); + } +} + +void MemoryIndex::removeEntriesWithValueKey(const IDBKeyData& valueKey) +{ + LOG(IndexedDB, "MemoryIndex::removeEntriesWithValueKey"); + + if (!m_records) + return; + + m_records->removeEntriesWithValueKey(*this, valueKey); +} + +MemoryIndexCursor* MemoryIndex::maybeOpenCursor(const IDBCursorInfo& info) +{ + auto result = m_cursors.add(info.identifier(), nullptr); + if (!result.isNewEntry) + return nullptr; + + result.iterator->value = std::make_unique(*this, info); + return result.iterator->value.get(); +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIndex.h b/Source/WebCore/Modules/indexeddb/server/MemoryIndex.h new file mode 100644 index 000000000..5bf9cba89 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryIndex.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBIndexInfo.h" +#include "IDBResourceIdentifier.h" +#include +#include + +namespace WebCore { + +class IDBCursorInfo; +class IDBError; +class IDBGetAllResult; +class IDBGetResult; +class IDBKeyData; +class IndexKey; +class ThreadSafeDataBuffer; + +struct IDBKeyRangeData; + +namespace IndexedDB { +enum class GetAllType; +enum class IndexRecordType; +} + +namespace IDBServer { + +class IndexValueStore; +class MemoryBackingStoreTransaction; +class MemoryIndexCursor; +class MemoryObjectStore; + +class MemoryIndex : public RefCounted { +public: + static Ref create(const IDBIndexInfo&, MemoryObjectStore&); + + ~MemoryIndex(); + + const IDBIndexInfo& info() const { return m_info; } + + void rename(const String& newName) { m_info.rename(newName); } + + IDBGetResult getResultForKeyRange(IndexedDB::IndexRecordType, const IDBKeyRangeData&) const; + uint64_t countForKeyRange(const IDBKeyRangeData&); + void getAllRecords(const IDBKeyRangeData&, std::optional count, IndexedDB::GetAllType, IDBGetAllResult&) const; + + IDBError putIndexKey(const IDBKeyData&, const IndexKey&); + + void removeEntriesWithValueKey(const IDBKeyData&); + void removeRecord(const IDBKeyData&, const IndexKey&); + + void objectStoreCleared(); + void clearIndexValueStore(); + void replaceIndexValueStore(std::unique_ptr&&); + + MemoryIndexCursor* maybeOpenCursor(const IDBCursorInfo&); + + IndexValueStore* valueStore() { return m_records.get(); } + + MemoryObjectStore& objectStore() { return m_objectStore; } + + void cursorDidBecomeClean(MemoryIndexCursor&); + void cursorDidBecomeDirty(MemoryIndexCursor&); + + void notifyCursorsOfValueChange(const IDBKeyData& indexKey, const IDBKeyData& primaryKey); + +private: + MemoryIndex(const IDBIndexInfo&, MemoryObjectStore&); + + uint64_t recordCountForKey(const IDBKeyData&) const; + + void notifyCursorsOfAllRecordsChanged(); + + IDBIndexInfo m_info; + MemoryObjectStore& m_objectStore; + + std::unique_ptr m_records; + + HashMap> m_cursors; + HashSet m_cleanCursors; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp new file mode 100644 index 000000000..72b6eab56 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MemoryIndexCursor.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorInfo.h" +#include "IDBGetResult.h" +#include "IndexValueStore.h" +#include "Logging.h" +#include "MemoryCursor.h" +#include "MemoryIndex.h" +#include "MemoryObjectStore.h" + +namespace WebCore { +namespace IDBServer { + +MemoryIndexCursor::MemoryIndexCursor(MemoryIndex& index, const IDBCursorInfo& info) + : MemoryCursor(info) + , m_index(index) +{ + LOG(IndexedDB, "MemoryIndexCursor::MemoryIndexCursor %s", info.range().loggingString().utf8().data()); + + auto* valueStore = m_index.valueStore(); + if (!valueStore) + return; + + if (m_info.isDirectionForward()) + m_currentIterator = valueStore->find(m_info.range().lowerKey, m_info.range().lowerOpen); + else + m_currentIterator = valueStore->reverseFind(m_info.range().upperKey, m_info.duplicity(), m_info.range().upperOpen); + + if (m_currentIterator.isValid() && m_info.range().containsKey(m_currentIterator.key())) { + m_currentKey = m_currentIterator.key(); + m_currentPrimaryKey = m_currentIterator.primaryKey(); + m_index.cursorDidBecomeClean(*this); + } else + m_currentIterator.invalidate(); +} + +MemoryIndexCursor::~MemoryIndexCursor() +{ +} + +void MemoryIndexCursor::currentData(IDBGetResult& getResult) +{ + if (!m_currentIterator.isValid()) { + getResult = { }; + return; + } + + if (m_info.cursorType() == IndexedDB::CursorType::KeyOnly) + getResult = { m_currentKey, m_currentPrimaryKey }; + else { + IDBValue value = { m_index.objectStore().valueForKey(m_currentPrimaryKey), { }, { } }; + getResult = { m_currentKey, m_currentPrimaryKey, WTFMove(value) }; + } +} + +void MemoryIndexCursor::iterate(const IDBKeyData& key, const IDBKeyData& primaryKey, uint32_t count, IDBGetResult& getResult) +{ + LOG(IndexedDB, "MemoryIndexCursor::iterate to key %s, %u count", key.loggingString().utf8().data(), count); + +#ifndef NDEBUG + if (primaryKey.isValid()) + ASSERT(key.isValid()); +#endif + + if (key.isValid()) { + // Cannot iterate by both a count and to a key + ASSERT(!count); + + auto* valueStore = m_index.valueStore(); + if (!valueStore) { + m_currentKey = { }; + m_currentPrimaryKey = { }; + getResult = { }; + return; + } + + if (primaryKey.isValid()) { + if (m_info.isDirectionForward()) + m_currentIterator = valueStore->find(key, primaryKey); + else + m_currentIterator = valueStore->reverseFind(key, primaryKey, m_info.duplicity()); + } else { + if (m_info.isDirectionForward()) + m_currentIterator = valueStore->find(key); + else + m_currentIterator = valueStore->reverseFind(key, m_info.duplicity()); + } + + if (m_currentIterator.isValid() && !m_info.range().containsKey(m_currentIterator.key())) + m_currentIterator.invalidate(); + + if (!m_currentIterator.isValid()) { + m_currentKey = { }; + m_currentPrimaryKey = { }; + getResult = { }; + return; + } + + m_index.cursorDidBecomeClean(*this); + + m_currentKey = m_currentIterator.key(); + m_currentPrimaryKey = m_currentIterator.primaryKey(); + currentData(getResult); + + return; + } + + // If there was not a valid key argument and no positive count argument + // that means the default iteration count of "1" + if (!count) + count = 1; + + if (!m_currentIterator.isValid()) { + auto* valueStore = m_index.valueStore(); + if (!valueStore) { + m_currentKey = { }; + m_currentPrimaryKey = { }; + getResult = { }; + return; + } + + switch (m_info.cursorDirection()) { + case IndexedDB::CursorDirection::Next: + m_currentIterator = valueStore->find(m_currentKey, m_currentPrimaryKey); + break; + case IndexedDB::CursorDirection::Nextunique: + m_currentIterator = valueStore->find(m_currentKey, true); + break; + case IndexedDB::CursorDirection::Prev: + m_currentIterator = valueStore->reverseFind(m_currentKey, m_currentPrimaryKey, m_info.duplicity()); + break; + case IndexedDB::CursorDirection::Prevunique: + m_currentIterator = valueStore->reverseFind(m_currentKey, m_info.duplicity(), true); + break; + } + + if (!m_currentIterator.isValid()) { + m_currentKey = { }; + m_currentPrimaryKey = { }; + getResult = { }; + return; + } + + m_index.cursorDidBecomeClean(*this); + + // If we restored the current iterator and it does *not* match the current key/primaryKey, + // then it is the next record in line and we should consider that an iteration. + if (m_currentKey != m_currentIterator.key() || m_currentPrimaryKey != m_currentIterator.primaryKey()) + --count; + } + + ASSERT(m_currentIterator.isValid()); + + while (count) { + if (m_info.duplicity() == CursorDuplicity::NoDuplicates) + m_currentIterator.nextIndexEntry(); + else + ++m_currentIterator; + + if (!m_currentIterator.isValid()) + break; + + --count; + } + + if (m_currentIterator.isValid() && !m_info.range().containsKey(m_currentIterator.key())) + m_currentIterator.invalidate(); + + // Not having a valid iterator after finishing any iteration means we've reached the end of the cursor. + if (!m_currentIterator.isValid()) { + m_currentKey = { }; + m_currentPrimaryKey = { }; + getResult = { }; + return; + } + + m_currentKey = m_currentIterator.key(); + m_currentPrimaryKey = m_currentIterator.primaryKey(); + currentData(getResult); +} + +void MemoryIndexCursor::indexRecordsAllChanged() +{ + m_currentIterator.invalidate(); + m_index.cursorDidBecomeDirty(*this); +} + +void MemoryIndexCursor::indexValueChanged(const IDBKeyData& key, const IDBKeyData& primaryKey) +{ + if (m_currentKey != key || m_currentPrimaryKey != primaryKey) + return; + + m_currentIterator.invalidate(); + m_index.cursorDidBecomeDirty(*this); +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h b/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h new file mode 100644 index 000000000..d55d20709 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorInfo.h" +#include "IndexValueStore.h" +#include "MemoryCursor.h" + +namespace WebCore { +namespace IDBServer { + +class MemoryIndex; + +class MemoryIndexCursor : public MemoryCursor { +public: + MemoryIndexCursor(MemoryIndex&, const IDBCursorInfo&); + virtual ~MemoryIndexCursor(); + + void indexRecordsAllChanged(); + void indexValueChanged(const IDBKeyData& indexKey, const IDBKeyData& primaryKey); + +private: + void currentData(IDBGetResult&) final; + void iterate(const IDBKeyData&, const IDBKeyData& primaryKey, uint32_t count, IDBGetResult&) final; + + MemoryIndex& m_index; + + IndexValueStore::Iterator m_currentIterator; + IDBKeyData m_currentKey; + IDBKeyData m_currentPrimaryKey; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp new file mode 100644 index 000000000..34b4e129a --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp @@ -0,0 +1,522 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MemoryObjectStore.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBBindingUtilities.h" +#include "IDBDatabaseException.h" +#include "IDBError.h" +#include "IDBGetAllResult.h" +#include "IDBKeyRangeData.h" +#include "IDBValue.h" +#include "IndexKey.h" +#include "Logging.h" +#include "MemoryBackingStoreTransaction.h" +#include "UniqueIDBDatabase.h" +#include +#include +#include +#include + +using namespace JSC; + +namespace WebCore { +namespace IDBServer { + +Ref MemoryObjectStore::create(const IDBObjectStoreInfo& info) +{ + return adoptRef(*new MemoryObjectStore(info)); +} + +MemoryObjectStore::MemoryObjectStore(const IDBObjectStoreInfo& info) + : m_info(info) +{ +} + +MemoryObjectStore::~MemoryObjectStore() +{ + m_writeTransaction = nullptr; +} + +MemoryIndex* MemoryObjectStore::indexForIdentifier(uint64_t identifier) +{ + ASSERT(identifier); + return m_indexesByIdentifier.get(identifier); +} + +void MemoryObjectStore::writeTransactionStarted(MemoryBackingStoreTransaction& transaction) +{ + LOG(IndexedDB, "MemoryObjectStore::writeTransactionStarted"); + + ASSERT(!m_writeTransaction); + m_writeTransaction = &transaction; +} + +void MemoryObjectStore::writeTransactionFinished(MemoryBackingStoreTransaction& transaction) +{ + LOG(IndexedDB, "MemoryObjectStore::writeTransactionFinished"); + + ASSERT_UNUSED(transaction, m_writeTransaction == &transaction); + m_writeTransaction = nullptr; +} + +IDBError MemoryObjectStore::createIndex(MemoryBackingStoreTransaction& transaction, const IDBIndexInfo& info) +{ + LOG(IndexedDB, "MemoryObjectStore::createIndex"); + + if (!m_writeTransaction || !m_writeTransaction->isVersionChange() || m_writeTransaction != &transaction) + return IDBError(IDBDatabaseException::ConstraintError); + + ASSERT(!m_indexesByIdentifier.contains(info.identifier())); + auto index = MemoryIndex::create(info, *this); + + // If there was an error populating the new index, then the current records in the object store violate its contraints + auto error = populateIndexWithExistingRecords(index.get()); + if (!error.isNull()) + return error; + + m_info.addExistingIndex(info); + transaction.addNewIndex(index.get()); + registerIndex(WTFMove(index)); + + return { }; +} + +void MemoryObjectStore::maybeRestoreDeletedIndex(Ref&& index) +{ + LOG(IndexedDB, "MemoryObjectStore::maybeRestoreDeletedIndex"); + + if (m_info.hasIndex(index->info().name())) + return; + + m_info.addExistingIndex(index->info()); + + ASSERT(!m_indexesByIdentifier.contains(index->info().identifier())); + index->clearIndexValueStore(); + auto error = populateIndexWithExistingRecords(index.get()); + + // Since this index was installed in the object store before this transaction started, + // assuming things were in a valid state then, we should definitely be able to successfully + // repopulate the index with the object store's pre-transaction records. + ASSERT_UNUSED(error, error.isNull()); + + registerIndex(WTFMove(index)); +} + +RefPtr MemoryObjectStore::takeIndexByIdentifier(uint64_t indexIdentifier) +{ + auto indexByIdentifier = m_indexesByIdentifier.take(indexIdentifier); + if (!indexByIdentifier) + return nullptr; + + auto index = m_indexesByName.take(indexByIdentifier->info().name()); + ASSERT(index); + + return index; +} + +IDBError MemoryObjectStore::deleteIndex(MemoryBackingStoreTransaction& transaction, uint64_t indexIdentifier) +{ + LOG(IndexedDB, "MemoryObjectStore::deleteIndex"); + + if (!m_writeTransaction || !m_writeTransaction->isVersionChange() || m_writeTransaction != &transaction) + return IDBError(IDBDatabaseException::ConstraintError); + + auto index = takeIndexByIdentifier(indexIdentifier); + ASSERT(index); + if (!index) + return IDBError(IDBDatabaseException::ConstraintError); + + m_info.deleteIndex(indexIdentifier); + transaction.indexDeleted(*index); + + return { }; +} + +void MemoryObjectStore::deleteAllIndexes(MemoryBackingStoreTransaction& transaction) +{ + Vector indexIdentifiers; + indexIdentifiers.reserveInitialCapacity(m_indexesByName.size()); + + for (auto& index : m_indexesByName.values()) + indexIdentifiers.uncheckedAppend(index->info().identifier()); + + for (auto identifier : indexIdentifiers) + deleteIndex(transaction, identifier); +} + +bool MemoryObjectStore::containsRecord(const IDBKeyData& key) +{ + if (!m_keyValueStore) + return false; + + return m_keyValueStore->contains(key); +} + +void MemoryObjectStore::clear() +{ + LOG(IndexedDB, "MemoryObjectStore::clear"); + ASSERT(m_writeTransaction); + + m_writeTransaction->objectStoreCleared(*this, WTFMove(m_keyValueStore), WTFMove(m_orderedKeys)); + for (auto& index : m_indexesByIdentifier.values()) + index->objectStoreCleared(); + + for (auto& cursor : m_cursors.values()) + cursor->objectStoreCleared(); +} + +void MemoryObjectStore::replaceKeyValueStore(std::unique_ptr&& store, std::unique_ptr>&& orderedKeys) +{ + ASSERT(m_writeTransaction); + ASSERT(m_writeTransaction->isAborting()); + + m_keyValueStore = WTFMove(store); + m_orderedKeys = WTFMove(orderedKeys); +} + +void MemoryObjectStore::deleteRecord(const IDBKeyData& key) +{ + LOG(IndexedDB, "MemoryObjectStore::deleteRecord"); + + ASSERT(m_writeTransaction); + + if (!m_keyValueStore) { + m_writeTransaction->recordValueChanged(*this, key, nullptr); + return; + } + + ASSERT(m_orderedKeys); + + auto iterator = m_keyValueStore->find(key); + if (iterator == m_keyValueStore->end()) { + m_writeTransaction->recordValueChanged(*this, key, nullptr); + return; + } + + m_writeTransaction->recordValueChanged(*this, key, &iterator->value); + m_keyValueStore->remove(iterator); + m_orderedKeys->erase(key); + + updateIndexesForDeleteRecord(key); + updateCursorsForDeleteRecord(key); +} + +void MemoryObjectStore::deleteRange(const IDBKeyRangeData& inputRange) +{ + LOG(IndexedDB, "MemoryObjectStore::deleteRange"); + + ASSERT(m_writeTransaction); + + if (inputRange.isExactlyOneKey()) { + deleteRecord(inputRange.lowerKey); + return; + } + + IDBKeyRangeData range = inputRange; + while (true) { + auto key = lowestKeyWithRecordInRange(range); + if (key.isNull()) + break; + + deleteRecord(key); + + range.lowerKey = key; + range.lowerOpen = true; + } +} + +IDBError MemoryObjectStore::addRecord(MemoryBackingStoreTransaction& transaction, const IDBKeyData& keyData, const IDBValue& value) +{ + LOG(IndexedDB, "MemoryObjectStore::addRecord"); + + ASSERT(m_writeTransaction); + ASSERT_UNUSED(transaction, m_writeTransaction == &transaction); + ASSERT(!m_keyValueStore || !m_keyValueStore->contains(keyData)); + ASSERT(!m_orderedKeys || m_orderedKeys->find(keyData) == m_orderedKeys->end()); + + if (!m_keyValueStore) { + ASSERT(!m_orderedKeys); + m_keyValueStore = std::make_unique(); + m_orderedKeys = std::make_unique>(); + } + + auto mapResult = m_keyValueStore->set(keyData, value.data()); + ASSERT(mapResult.isNewEntry); + auto listResult = m_orderedKeys->insert(keyData); + ASSERT(listResult.second); + + // If there was an error indexing this addition, then revert it. + auto error = updateIndexesForPutRecord(keyData, value.data()); + if (!error.isNull()) { + m_keyValueStore->remove(mapResult.iterator); + m_orderedKeys->erase(listResult.first); + } else + updateCursorsForPutRecord(listResult.first); + + return error; +} + +void MemoryObjectStore::updateCursorsForPutRecord(std::set::iterator iterator) +{ + for (auto& cursor : m_cursors.values()) + cursor->keyAdded(iterator); +} + +void MemoryObjectStore::updateCursorsForDeleteRecord(const IDBKeyData& key) +{ + for (auto& cursor : m_cursors.values()) + cursor->keyDeleted(key); +} + +void MemoryObjectStore::updateIndexesForDeleteRecord(const IDBKeyData& value) +{ + for (auto& index : m_indexesByName.values()) + index->removeEntriesWithValueKey(value); +} + +IDBError MemoryObjectStore::updateIndexesForPutRecord(const IDBKeyData& key, const ThreadSafeDataBuffer& value) +{ + JSLockHolder locker(UniqueIDBDatabase::databaseThreadVM()); + + auto jsValue = deserializeIDBValueToJSValue(UniqueIDBDatabase::databaseThreadExecState(), value); + if (jsValue.isUndefinedOrNull()) + return { }; + + IDBError error; + Vector> changedIndexRecords; + + for (auto& index : m_indexesByName.values()) { + IndexKey indexKey; + generateIndexKeyForValue(UniqueIDBDatabase::databaseThreadExecState(), index->info(), jsValue, indexKey); + + if (indexKey.isNull()) + continue; + + error = index->putIndexKey(key, indexKey); + if (!error.isNull()) + break; + + changedIndexRecords.append(std::make_pair(index.get(), indexKey)); + } + + // If any of the index puts failed, revert all of the ones that went through. + if (!error.isNull()) { + for (auto& record : changedIndexRecords) + record.first->removeRecord(key, record.second); + } + + return error; +} + +IDBError MemoryObjectStore::populateIndexWithExistingRecords(MemoryIndex& index) +{ + if (!m_keyValueStore) + return { }; + + JSLockHolder locker(UniqueIDBDatabase::databaseThreadVM()); + + for (auto iterator : *m_keyValueStore) { + auto jsValue = deserializeIDBValueToJSValue(UniqueIDBDatabase::databaseThreadExecState(), iterator.value); + if (jsValue.isUndefinedOrNull()) + return { }; + + IndexKey indexKey; + generateIndexKeyForValue(UniqueIDBDatabase::databaseThreadExecState(), index.info(), jsValue, indexKey); + + if (indexKey.isNull()) + continue; + + IDBError error = index.putIndexKey(iterator.key, indexKey); + if (!error.isNull()) + return error; + } + + return { }; +} + +uint64_t MemoryObjectStore::countForKeyRange(uint64_t indexIdentifier, const IDBKeyRangeData& inRange) const +{ + LOG(IndexedDB, "MemoryObjectStore::countForKeyRange"); + + if (indexIdentifier) { + auto* index = m_indexesByIdentifier.get(indexIdentifier); + ASSERT(index); + return index->countForKeyRange(inRange); + } + + if (!m_keyValueStore) + return 0; + + uint64_t count = 0; + IDBKeyRangeData range = inRange; + while (true) { + auto key = lowestKeyWithRecordInRange(range); + if (key.isNull()) + break; + + ++count; + range.lowerKey = key; + range.lowerOpen = true; + } + + return count; +} + +ThreadSafeDataBuffer MemoryObjectStore::valueForKey(const IDBKeyData& key) const +{ + if (!m_keyValueStore) + return { }; + + return m_keyValueStore->get(key); +} + +ThreadSafeDataBuffer MemoryObjectStore::valueForKeyRange(const IDBKeyRangeData& keyRangeData) const +{ + LOG(IndexedDB, "MemoryObjectStore::valueForKey"); + + IDBKeyData key = lowestKeyWithRecordInRange(keyRangeData); + if (key.isNull()) + return ThreadSafeDataBuffer(); + + ASSERT(m_keyValueStore); + return m_keyValueStore->get(key); +} + +void MemoryObjectStore::getAllRecords(const IDBKeyRangeData& keyRangeData, std::optional count, IndexedDB::GetAllType type, IDBGetAllResult& result) const +{ + result = { type }; + + uint32_t targetCount; + if (count && count.value()) + targetCount = count.value(); + else + targetCount = std::numeric_limits::max(); + + IDBKeyRangeData range = keyRangeData; + uint32_t currentCount = 0; + while (currentCount < targetCount) { + IDBKeyData key = lowestKeyWithRecordInRange(range); + if (key.isNull()) + return; + + range.lowerKey = key; + range.lowerOpen = true; + + if (type == IndexedDB::GetAllType::Keys) + result.addKey(WTFMove(key)); + else + result.addValue(valueForKey(key)); + + ++currentCount; + } +} + +IDBGetResult MemoryObjectStore::indexValueForKeyRange(uint64_t indexIdentifier, IndexedDB::IndexRecordType recordType, const IDBKeyRangeData& range) const +{ + LOG(IndexedDB, "MemoryObjectStore::indexValueForKeyRange"); + + auto* index = m_indexesByIdentifier.get(indexIdentifier); + ASSERT(index); + return index->getResultForKeyRange(recordType, range); +} + +IDBKeyData MemoryObjectStore::lowestKeyWithRecordInRange(const IDBKeyRangeData& keyRangeData) const +{ + if (!m_keyValueStore) + return { }; + + if (keyRangeData.isExactlyOneKey() && m_keyValueStore->contains(keyRangeData.lowerKey)) + return keyRangeData.lowerKey; + + ASSERT(m_orderedKeys); + + auto lowestInRange = m_orderedKeys->lower_bound(keyRangeData.lowerKey); + + if (lowestInRange == m_orderedKeys->end()) + return { }; + + if (keyRangeData.lowerOpen && *lowestInRange == keyRangeData.lowerKey) + ++lowestInRange; + + if (lowestInRange == m_orderedKeys->end()) + return { }; + + if (!keyRangeData.upperKey.isNull()) { + if (lowestInRange->compare(keyRangeData.upperKey) > 0) + return { }; + if (keyRangeData.upperOpen && *lowestInRange == keyRangeData.upperKey) + return { }; + } + + return *lowestInRange; +} + +void MemoryObjectStore::registerIndex(Ref&& index) +{ + ASSERT(!m_indexesByIdentifier.contains(index->info().identifier())); + ASSERT(!m_indexesByName.contains(index->info().name())); + + m_indexesByName.set(index->info().name(), &index.get()); + m_indexesByIdentifier.set(index->info().identifier(), WTFMove(index)); +} + +void MemoryObjectStore::unregisterIndex(MemoryIndex& index) +{ + ASSERT(m_indexesByIdentifier.contains(index.info().identifier())); + ASSERT(m_indexesByName.contains(index.info().name())); + + m_indexesByName.remove(index.info().name()); + m_indexesByIdentifier.remove(index.info().identifier()); +} + +MemoryObjectStoreCursor* MemoryObjectStore::maybeOpenCursor(const IDBCursorInfo& info) +{ + auto result = m_cursors.add(info.identifier(), nullptr); + if (!result.isNewEntry) + return nullptr; + + result.iterator->value = std::make_unique(*this, info); + return result.iterator->value.get(); +} + +void MemoryObjectStore::renameIndex(MemoryIndex& index, const String& newName) +{ + ASSERT(m_indexesByName.get(index.info().name()) == &index); + ASSERT(!m_indexesByName.contains(newName)); + ASSERT(m_info.infoForExistingIndex(index.info().name())); + + m_info.infoForExistingIndex(index.info().name())->rename(newName); + m_indexesByName.set(newName, m_indexesByName.take(index.info().name())); + index.rename(newName); +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h new file mode 100644 index 000000000..883959e32 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyData.h" +#include "IDBObjectStoreInfo.h" +#include "MemoryIndex.h" +#include "MemoryObjectStoreCursor.h" +#include "ThreadSafeDataBuffer.h" +#include +#include +#include + +namespace WebCore { + +class IDBCursorInfo; +class IDBError; +class IDBGetAllResult; +class IDBKeyData; +class IDBValue; + +struct IDBKeyRangeData; + +namespace IndexedDB { +enum class GetAllType; +enum class IndexRecordType; +} + +namespace IDBServer { + +class MemoryBackingStoreTransaction; + +typedef HashMap KeyValueMap; + +class MemoryObjectStore : public RefCounted { +public: + static Ref create(const IDBObjectStoreInfo&); + + ~MemoryObjectStore(); + + void writeTransactionStarted(MemoryBackingStoreTransaction&); + void writeTransactionFinished(MemoryBackingStoreTransaction&); + MemoryBackingStoreTransaction* writeTransaction() { return m_writeTransaction; } + + IDBError createIndex(MemoryBackingStoreTransaction&, const IDBIndexInfo&); + IDBError deleteIndex(MemoryBackingStoreTransaction&, uint64_t indexIdentifier); + void deleteAllIndexes(MemoryBackingStoreTransaction&); + void registerIndex(Ref&&); + + bool containsRecord(const IDBKeyData&); + void deleteRecord(const IDBKeyData&); + void deleteRange(const IDBKeyRangeData&); + IDBError addRecord(MemoryBackingStoreTransaction&, const IDBKeyData&, const IDBValue&); + + uint64_t currentKeyGeneratorValue() const { return m_keyGeneratorValue; } + void setKeyGeneratorValue(uint64_t value) { m_keyGeneratorValue = value; } + + void clear(); + void replaceKeyValueStore(std::unique_ptr&&, std::unique_ptr>&&); + + ThreadSafeDataBuffer valueForKey(const IDBKeyData&) const; + ThreadSafeDataBuffer valueForKeyRange(const IDBKeyRangeData&) const; + IDBKeyData lowestKeyWithRecordInRange(const IDBKeyRangeData&) const; + IDBGetResult indexValueForKeyRange(uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&) const; + uint64_t countForKeyRange(uint64_t indexIdentifier, const IDBKeyRangeData&) const; + + void getAllRecords(const IDBKeyRangeData&, std::optional count, IndexedDB::GetAllType, IDBGetAllResult&) const; + + const IDBObjectStoreInfo& info() const { return m_info; } + + MemoryObjectStoreCursor* maybeOpenCursor(const IDBCursorInfo&); + + std::set* orderedKeys() { return m_orderedKeys.get(); } + + MemoryIndex* indexForIdentifier(uint64_t); + + void maybeRestoreDeletedIndex(Ref&&); + + void rename(const String& newName) { m_info.rename(newName); } + void renameIndex(MemoryIndex&, const String& newName); + +private: + MemoryObjectStore(const IDBObjectStoreInfo&); + + std::set::iterator lowestIteratorInRange(const IDBKeyRangeData&, bool reverse) const; + + IDBError populateIndexWithExistingRecords(MemoryIndex&); + IDBError updateIndexesForPutRecord(const IDBKeyData&, const ThreadSafeDataBuffer& value); + void updateIndexesForDeleteRecord(const IDBKeyData& value); + void updateCursorsForPutRecord(std::set::iterator); + void updateCursorsForDeleteRecord(const IDBKeyData&); + + RefPtr takeIndexByIdentifier(uint64_t indexIdentifier); + + IDBObjectStoreInfo m_info; + + MemoryBackingStoreTransaction* m_writeTransaction { nullptr }; + uint64_t m_keyGeneratorValue { 1 }; + + std::unique_ptr m_keyValueStore; + std::unique_ptr> m_orderedKeys; + + void unregisterIndex(MemoryIndex&); + HashMap> m_indexesByIdentifier; + HashMap> m_indexesByName; + HashMap> m_cursors; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.cpp new file mode 100644 index 000000000..d95efe6a9 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.cpp @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MemoryObjectStoreCursor.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBGetResult.h" +#include "Logging.h" +#include "MemoryObjectStore.h" + +namespace WebCore { +namespace IDBServer { + +MemoryObjectStoreCursor::MemoryObjectStoreCursor(MemoryObjectStore& objectStore, const IDBCursorInfo& info) + : MemoryCursor(info) + , m_objectStore(objectStore) + , m_remainingRange(info.range()) +{ + LOG(IndexedDB, "MemoryObjectStoreCursor::MemoryObjectStoreCursor %s", info.range().loggingString().utf8().data()); + + auto* orderedKeys = objectStore.orderedKeys(); + if (!orderedKeys) + return; + + setFirstInRemainingRange(*orderedKeys); +} + +void MemoryObjectStoreCursor::objectStoreCleared() +{ + m_iterator = std::nullopt; +} + +void MemoryObjectStoreCursor::keyDeleted(const IDBKeyData& key) +{ + if (m_currentPositionKey != key) + return; + + m_iterator = std::nullopt; +} + +void MemoryObjectStoreCursor::keyAdded(std::set::iterator iterator) +{ + if (m_iterator) + return; + + if (*iterator == m_currentPositionKey) + m_iterator = iterator; +} + +void MemoryObjectStoreCursor::setFirstInRemainingRange(std::set& set) +{ + m_iterator = std::nullopt; + + if (m_info.isDirectionForward()) { + setForwardIteratorFromRemainingRange(set); + if (m_iterator) { + m_remainingRange.lowerKey = **m_iterator; + m_remainingRange.lowerOpen = true; + } + } else { + setReverseIteratorFromRemainingRange(set); + if (m_iterator) { + m_remainingRange.upperKey = **m_iterator; + m_remainingRange.upperOpen = true; + } + } + + ASSERT(!m_iterator || *m_iterator != set.end()); +} + +void MemoryObjectStoreCursor::setForwardIteratorFromRemainingRange(std::set& set) +{ + if (!set.size()) { + m_iterator = std::nullopt; + return; + } + + if (m_remainingRange.isExactlyOneKey()) { + m_iterator = set.find(m_remainingRange.lowerKey); + if (*m_iterator == set.end()) + m_iterator = std::nullopt; + + return; + } + + m_iterator = std::nullopt; + + auto lowest = set.lower_bound(m_remainingRange.lowerKey); + if (lowest == set.end()) + return; + + if (m_remainingRange.lowerOpen && *lowest == m_remainingRange.lowerKey) { + ++lowest; + if (lowest == set.end()) + return; + } + + if (!m_remainingRange.upperKey.isNull()) { + if (lowest->compare(m_remainingRange.upperKey) > 0) + return; + + if (m_remainingRange.upperOpen && *lowest == m_remainingRange.upperKey) + return; + } + + m_iterator = lowest; +} + +void MemoryObjectStoreCursor::setReverseIteratorFromRemainingRange(std::set& set) +{ + if (!set.size()) { + m_iterator = std::nullopt; + return; + } + + if (m_remainingRange.isExactlyOneKey()) { + m_iterator = set.find(m_remainingRange.lowerKey); + if (*m_iterator == set.end()) + m_iterator = std::nullopt; + + return; + } + + if (!m_remainingRange.upperKey.isValid()) { + m_iterator = --set.end(); + if (!m_remainingRange.containsKey(**m_iterator)) + m_iterator = std::nullopt; + + return; + } + + m_iterator = std::nullopt; + + // This is one record past the actual key we're looking for. + auto highest = set.upper_bound(m_remainingRange.upperKey); + + if (highest == set.begin()) + return; + + // This is one record before that, which *is* the key we're looking for. + --highest; + + if (m_remainingRange.upperOpen && *highest == m_remainingRange.upperKey) { + if (highest == set.begin()) + return; + --highest; + } + + if (!m_remainingRange.lowerKey.isNull()) { + if (highest->compare(m_remainingRange.lowerKey) < 0) + return; + + if (m_remainingRange.lowerOpen && *highest == m_remainingRange.lowerKey) + return; + } + + m_iterator = highest; +} + +void MemoryObjectStoreCursor::currentData(IDBGetResult& data) +{ + if (!m_iterator) { + m_currentPositionKey = { }; + data = { }; + return; + } + + m_currentPositionKey = **m_iterator; + if (m_info.cursorType() == IndexedDB::CursorType::KeyOnly) + data = { m_currentPositionKey, m_currentPositionKey }; + else { + IDBValue value = { m_objectStore.valueForKeyRange(m_currentPositionKey), { }, { } }; + data = { m_currentPositionKey, m_currentPositionKey, WTFMove(value) }; + } +} + +void MemoryObjectStoreCursor::incrementForwardIterator(std::set& set, const IDBKeyData& key, uint32_t count) +{ + // We might need to re-grab the current iterator. + // e.g. If the record it was pointed to had been deleted. + bool didResetIterator = false; + if (!m_iterator) { + if (!m_currentPositionKey.isValid()) + return; + + m_remainingRange.lowerKey = m_currentPositionKey; + m_remainingRange.lowerOpen = false; + setFirstInRemainingRange(set); + + didResetIterator = true; + } + + if (!m_iterator) + return; + + ASSERT(*m_iterator != set.end()); + + if (key.isValid()) { + // If iterating to a key, the count passed in must be zero, as there is no way to iterate by both a count and to a key. + ASSERT(!count); + + if (!m_info.range().containsKey(key)) + return; + + if ((*m_iterator)->compare(key) < 0) { + m_remainingRange.lowerKey = key; + m_remainingRange.lowerOpen = false; + setFirstInRemainingRange(set); + } + + return; + } + + if (!count) + count = 1; + + // If the forwardIterator was reset because it's previous record had been deleted, + // we might have already advanced past the current position, eating one one of the iteration counts. + if (didResetIterator && (*m_iterator)->compare(m_currentPositionKey) > 0) + --count; + + while (count) { + --count; + ++*m_iterator; + + if (*m_iterator == set.end() || !m_info.range().containsKey(**m_iterator)) { + m_iterator = std::nullopt; + return; + } + } +} + +void MemoryObjectStoreCursor::incrementReverseIterator(std::set& set, const IDBKeyData& key, uint32_t count) +{ + // We might need to re-grab the current iterator. + // e.g. If the record it was pointed to had been deleted. + bool didResetIterator = false; + if (!m_iterator) { + if (!m_currentPositionKey.isValid()) + return; + + m_remainingRange.upperKey = m_currentPositionKey; + m_remainingRange.upperOpen = false; + setFirstInRemainingRange(set); + + didResetIterator = true; + } + + if (*m_iterator == set.end()) + return; + + if (key.isValid()) { + // If iterating to a key, the count passed in must be zero, as there is no way to iterate by both a count and to a key. + ASSERT(!count); + + if (!m_info.range().containsKey(key)) + return; + + if ((*m_iterator)->compare(key) > 0) { + m_remainingRange.upperKey = key; + m_remainingRange.upperOpen = false; + + setFirstInRemainingRange(set); + } + + return; + } + + if (!count) + count = 1; + + // If the reverseIterator was reset because it's previous record had been deleted, + // we might have already advanced past the current position, eating one one of the iteration counts. + if (didResetIterator && (*m_iterator)->compare(m_currentPositionKey) < 0) + --count; + + while (count) { + if (*m_iterator == set.begin()) { + m_iterator = std::nullopt; + return; + } + + --count; + --*m_iterator; + + if (!m_info.range().containsKey(**m_iterator)) { + m_iterator = std::nullopt; + return; + } + } +} + +void MemoryObjectStoreCursor::iterate(const IDBKeyData& key, const IDBKeyData& primaryKeyData, uint32_t count, IDBGetResult& outData) +{ + LOG(IndexedDB, "MemoryObjectStoreCursor::iterate to key %s", key.loggingString().utf8().data()); + + ASSERT_UNUSED(primaryKeyData, primaryKeyData.isNull()); + + if (!m_objectStore.orderedKeys()) { + m_currentPositionKey = { }; + outData = { }; + return; + } + + if (key.isValid() && !m_info.range().containsKey(key)) { + m_currentPositionKey = { }; + outData = { }; + return; + } + + auto* set = m_objectStore.orderedKeys(); + if (set) { + if (m_info.isDirectionForward()) + incrementForwardIterator(*set, key, count); + else + incrementReverseIterator(*set, key, count); + } + + m_currentPositionKey = { }; + + if (!m_iterator) { + outData = { }; + return; + } + + currentData(outData); +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.h b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.h new file mode 100644 index 000000000..633f1e66c --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorInfo.h" +#include "IDBKeyData.h" +#include "MemoryCursor.h" +#include +#include + +namespace WebCore { +namespace IDBServer { + +class MemoryObjectStore; + +class MemoryObjectStoreCursor : public MemoryCursor { +public: + MemoryObjectStoreCursor(MemoryObjectStore&, const IDBCursorInfo&); + + void objectStoreCleared(); + void keyDeleted(const IDBKeyData&); + void keyAdded(std::set::iterator); + +private: + void currentData(IDBGetResult&) final; + void iterate(const IDBKeyData&, const IDBKeyData& primaryKey, uint32_t count, IDBGetResult&) final; + + void setFirstInRemainingRange(std::set&); + void setForwardIteratorFromRemainingRange(std::set&); + void setReverseIteratorFromRemainingRange(std::set&); + + void incrementForwardIterator(std::set&, const IDBKeyData&, uint32_t count); + void incrementReverseIterator(std::set&, const IDBKeyData&, uint32_t count); + + bool hasValidPosition() const; + + MemoryObjectStore& m_objectStore; + + IDBKeyRangeData m_remainingRange; + + std::optional::iterator> m_iterator; + + IDBKeyData m_currentPositionKey; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp new file mode 100644 index 000000000..fa3755e60 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp @@ -0,0 +1,2601 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SQLiteIDBBackingStore.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "FileSystem.h" +#include "IDBBindingUtilities.h" +#include "IDBDatabaseException.h" +#include "IDBGetAllRecordsData.h" +#include "IDBGetAllResult.h" +#include "IDBGetRecordData.h" +#include "IDBGetResult.h" +#include "IDBIterateCursorData.h" +#include "IDBKeyData.h" +#include "IDBObjectStoreInfo.h" +#include "IDBSerialization.h" +#include "IDBTransactionInfo.h" +#include "IDBValue.h" +#include "IndexKey.h" +#include "Logging.h" +#include "SQLiteDatabase.h" +#include "SQLiteFileSystem.h" +#include "SQLiteIDBCursor.h" +#include "SQLiteStatement.h" +#include "SQLiteTransaction.h" +#include "ThreadSafeDataBuffer.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace JSC; + +namespace WebCore { +namespace IDBServer { + +// Current version of the metadata schema being used in the metadata database. +static const int currentMetadataVersion = 1; + +static int idbKeyCollate(int aLength, const void* aBuffer, int bLength, const void* bBuffer) +{ + IDBKeyData a, b; + if (!deserializeIDBKeyData(static_cast(aBuffer), aLength, a)) { + LOG_ERROR("Unable to deserialize key A in collation function."); + + // There's no way to indicate an error to SQLite - we have to return a sorting decision. + // We arbitrarily choose "A > B" + return 1; + } + if (!deserializeIDBKeyData(static_cast(bBuffer), bLength, b)) { + LOG_ERROR("Unable to deserialize key B in collation function."); + + // There's no way to indicate an error to SQLite - we have to return a sorting decision. + // We arbitrarily choose "A > B" + return 1; + } + + return a.compare(b); +} + +static const String v1RecordsTableSchema(const String& tableName) +{ + return makeString("CREATE TABLE ", tableName, " (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value NOT NULL ON CONFLICT FAIL)"); +} + +static const String& v1RecordsTableSchema() +{ + static NeverDestroyed v1RecordsTableSchemaString(v1RecordsTableSchema("Records")); + return v1RecordsTableSchemaString; +} + +static const String& v1RecordsTableSchemaAlternate() +{ + static NeverDestroyed v1RecordsTableSchemaString(v1RecordsTableSchema("\"Records\"")); + return v1RecordsTableSchemaString; +} + +static const String v2RecordsTableSchema(const String& tableName) +{ + return makeString("CREATE TABLE ", tableName, " (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value NOT NULL ON CONFLICT FAIL)"); +} + +static const String& v2RecordsTableSchema() +{ + static NeverDestroyed v2RecordsTableSchemaString(v2RecordsTableSchema("Records")); + return v2RecordsTableSchemaString; +} + +static const String& v2RecordsTableSchemaAlternate() +{ + static NeverDestroyed v2RecordsTableSchemaString(v2RecordsTableSchema("\"Records\"")); + return v2RecordsTableSchemaString; +} + +static const String v3RecordsTableSchema(const String& tableName) +{ + return makeString("CREATE TABLE ", tableName, " (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value NOT NULL ON CONFLICT FAIL, recordID INTEGER PRIMARY KEY)"); +} + +static const String& v3RecordsTableSchema() +{ + static NeverDestroyed v3RecordsTableSchemaString(v3RecordsTableSchema("Records")); + return v3RecordsTableSchemaString; +} + +static const String& v3RecordsTableSchemaAlternate() +{ + static NeverDestroyed v3RecordsTableSchemaString(v3RecordsTableSchema("\"Records\"")); + return v3RecordsTableSchemaString; +} + +static const String v1IndexRecordsTableSchema(const String& tableName) +{ + return makeString("CREATE TABLE ", tableName, " (indexID INTEGER NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value NOT NULL ON CONFLICT FAIL)"); +} + +static const String& v1IndexRecordsTableSchema() +{ + static NeverDestroyed v1IndexRecordsTableSchemaString(v1IndexRecordsTableSchema("IndexRecords")); + return v1IndexRecordsTableSchemaString; +} + +static const String& v1IndexRecordsTableSchemaAlternate() +{ + static NeverDestroyed v1IndexRecordsTableSchemaString(v1IndexRecordsTableSchema("\"IndexRecords\"")); + return v1IndexRecordsTableSchemaString; +} + +static const String v2IndexRecordsTableSchema(const String& tableName) +{ + return makeString("CREATE TABLE ", tableName, " (indexID INTEGER NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL)"); +} + +static const String& v2IndexRecordsTableSchema() +{ + static NeverDestroyed v2IndexRecordsTableSchemaString(v2IndexRecordsTableSchema("IndexRecords")); + return v2IndexRecordsTableSchemaString; +} + +static const String& v2IndexRecordsTableSchemaAlternate() +{ + static NeverDestroyed v2IndexRecordsTableSchemaString(v2IndexRecordsTableSchema("\"IndexRecords\"")); + return v2IndexRecordsTableSchemaString; +} + +static const String v3IndexRecordsTableSchema(const String& tableName) +{ + return makeString("CREATE TABLE ", tableName, " (indexID INTEGER NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, objectStoreRecordID INTEGER NOT NULL ON CONFLICT FAIL)"); +} + +static const String v3IndexRecordsTableSchema() +{ + static NeverDestroyed indexRecordsTableSchemaString = v3IndexRecordsTableSchema("IndexRecords"); + return indexRecordsTableSchemaString; +} + +static const String v3IndexRecordsTableSchemaAlternate() +{ + static NeverDestroyed indexRecordsTableSchemaString = v3IndexRecordsTableSchema("\"IndexRecords\""); + return indexRecordsTableSchemaString; +} + +static const String& v1IndexRecordsIndexSchema() +{ + static NeverDestroyed indexRecordsIndexSchemaString("CREATE INDEX IndexRecordsIndex ON IndexRecords (key)"); + return indexRecordsIndexSchemaString; +} + +static const String blobRecordsTableSchema(const String& tableName) +{ + return makeString("CREATE TABLE ", tableName, " (objectStoreRow INTEGER NOT NULL ON CONFLICT FAIL, blobURL TEXT NOT NULL ON CONFLICT FAIL)"); +} + +static const String& blobRecordsTableSchema() +{ + static NeverDestroyed blobRecordsTableSchemaString(blobRecordsTableSchema("BlobRecords")); + return blobRecordsTableSchemaString; +} + +static const String& blobRecordsTableSchemaAlternate() +{ + static NeverDestroyed blobRecordsTableSchemaString(blobRecordsTableSchema("\"BlobRecords\"")); + return blobRecordsTableSchemaString; +} + +static const String blobFilesTableSchema(const String& tableName) +{ + return makeString("CREATE TABLE ", tableName, " (blobURL TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, fileName TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL)"); +} + +static const String& blobFilesTableSchema() +{ + static NeverDestroyed blobFilesTableSchemaString(blobFilesTableSchema("BlobFiles")); + return blobFilesTableSchemaString; +} + +static const String& blobFilesTableSchemaAlternate() +{ + static NeverDestroyed blobFilesTableSchemaString(blobFilesTableSchema("\"BlobFiles\"")); + return blobFilesTableSchemaString; +} + +SQLiteIDBBackingStore::SQLiteIDBBackingStore(const IDBDatabaseIdentifier& identifier, const String& databaseRootDirectory, IDBBackingStoreTemporaryFileHandler& fileHandler) + : m_identifier(identifier) + , m_temporaryFileHandler(fileHandler) +{ + m_absoluteDatabaseDirectory = identifier.databaseDirectoryRelativeToRoot(databaseRootDirectory); +} + +SQLiteIDBBackingStore::~SQLiteIDBBackingStore() +{ + if (m_sqliteDB) + closeSQLiteDB(); + + if (m_vm) { + JSLockHolder locker(m_vm.get()); + m_globalObject.clear(); + m_vm = nullptr; + } +} + + +void SQLiteIDBBackingStore::initializeVM() +{ + if (!m_vm) { + ASSERT(!m_globalObject); + m_vm = VM::create(); + + JSLockHolder locker(m_vm.get()); + m_globalObject.set(*m_vm, JSGlobalObject::create(*m_vm, JSGlobalObject::createStructure(*m_vm, jsNull()))); + } +} + +VM& SQLiteIDBBackingStore::vm() +{ + initializeVM(); + return *m_vm; +} + +JSGlobalObject& SQLiteIDBBackingStore::globalObject() +{ + initializeVM(); + return **m_globalObject; +} + +static bool createOrMigrateRecordsTableIfNecessary(SQLiteDatabase& database) +{ + String currentSchema; + { + // Fetch the schema for an existing records table. + SQLiteStatement statement(database, "SELECT type, sql FROM sqlite_master WHERE tbl_name='Records'"); + if (statement.prepare() != SQLITE_OK) { + LOG_ERROR("Unable to prepare statement to fetch schema for the Records table."); + return false; + } + + int sqliteResult = statement.step(); + + // If there is no Records table at all, create it and then bail. + if (sqliteResult == SQLITE_DONE) { + if (!database.executeCommand(v3RecordsTableSchema())) { + LOG_ERROR("Could not create Records table in database (%i) - %s", database.lastError(), database.lastErrorMsg()); + return false; + } + + return true; + } + + if (sqliteResult != SQLITE_ROW) { + LOG_ERROR("Error executing statement to fetch schema for the Records table."); + return false; + } + + currentSchema = statement.getColumnText(1); + } + + ASSERT(!currentSchema.isEmpty()); + + // If the schema in the backing store is the current schema, we're done. + if (currentSchema == v3RecordsTableSchema() || currentSchema == v3RecordsTableSchemaAlternate()) + return true; + + // If the record table is not the current schema then it must be one of the previous schemas. + // If it is not then the database is in an unrecoverable state and this should be considered a fatal error. + if (currentSchema != v1RecordsTableSchema() && currentSchema != v1RecordsTableSchemaAlternate() + && currentSchema != v2RecordsTableSchema() && currentSchema != v2RecordsTableSchemaAlternate()) + RELEASE_ASSERT_NOT_REACHED(); + + SQLiteTransaction transaction(database); + transaction.begin(); + + // Create a temporary table with the correct schema and migrate all existing content over. + if (!database.executeCommand(v3RecordsTableSchema("_Temp_Records"))) { + LOG_ERROR("Could not create temporary records table in database (%i) - %s", database.lastError(), database.lastErrorMsg()); + return false; + } + + if (!database.executeCommand("INSERT INTO _Temp_Records (objectStoreID, key, value) SELECT objectStoreID, CAST(key AS TEXT), value FROM Records")) { + LOG_ERROR("Could not migrate existing Records content (%i) - %s", database.lastError(), database.lastErrorMsg()); + return false; + } + + if (!database.executeCommand("DROP TABLE Records")) { + LOG_ERROR("Could not drop existing Records table (%i) - %s", database.lastError(), database.lastErrorMsg()); + return false; + } + + if (!database.executeCommand("ALTER TABLE _Temp_Records RENAME TO Records")) { + LOG_ERROR("Could not rename temporary Records table (%i) - %s", database.lastError(), database.lastErrorMsg()); + return false; + } + + transaction.commit(); + + return true; +} + +bool SQLiteIDBBackingStore::ensureValidBlobTables() +{ + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + String currentSchema; + { + // Fetch the schema for an existing blob record table. + SQLiteStatement statement(*m_sqliteDB, "SELECT type, sql FROM sqlite_master WHERE tbl_name='BlobRecords'"); + if (statement.prepare() != SQLITE_OK) { + LOG_ERROR("Unable to prepare statement to fetch schema for the BlobRecords table."); + return false; + } + + int sqliteResult = statement.step(); + + // If there is no BlobRecords table at all, create it.. + if (sqliteResult == SQLITE_DONE) { + if (!m_sqliteDB->executeCommand(blobRecordsTableSchema())) { + LOG_ERROR("Could not create BlobRecords table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return false; + } + + currentSchema = blobRecordsTableSchema(); + } else if (sqliteResult != SQLITE_ROW) { + LOG_ERROR("Error executing statement to fetch schema for the BlobRecords table."); + return false; + } else + currentSchema = statement.getColumnText(1); + } + + if (currentSchema != blobRecordsTableSchema() && currentSchema != blobRecordsTableSchemaAlternate()) { + LOG_ERROR("Invalid BlobRecords table schema found"); + return false; + } + + { + // Fetch the schema for an existing blob file table. + SQLiteStatement statement(*m_sqliteDB, "SELECT type, sql FROM sqlite_master WHERE tbl_name='BlobFiles'"); + if (statement.prepare() != SQLITE_OK) { + LOG_ERROR("Unable to prepare statement to fetch schema for the BlobFiles table."); + return false; + } + + int sqliteResult = statement.step(); + + // If there is no BlobFiles table at all, create it and then bail. + if (sqliteResult == SQLITE_DONE) { + if (!m_sqliteDB->executeCommand(blobFilesTableSchema())) { + LOG_ERROR("Could not create BlobFiles table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return false; + } + + return true; + } + + if (sqliteResult != SQLITE_ROW) { + LOG_ERROR("Error executing statement to fetch schema for the BlobFiles table."); + return false; + } + + currentSchema = statement.getColumnText(1); + } + + if (currentSchema != blobFilesTableSchema() && currentSchema != blobFilesTableSchemaAlternate()) { + LOG_ERROR("Invalid BlobFiles table schema found"); + return false; + } + + return true; +} + +bool SQLiteIDBBackingStore::ensureValidRecordsTable() +{ + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + if (!createOrMigrateRecordsTableIfNecessary(*m_sqliteDB)) + return false; + + // Whether the updated records table already existed or if it was just created and the data migrated over, + // make sure the uniqueness index exists. + if (!m_sqliteDB->executeCommand("CREATE UNIQUE INDEX IF NOT EXISTS RecordsIndex ON Records (objectStoreID, key);")) { + LOG_ERROR("Could not create RecordsIndex on Records table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return false; + } + + return true; +} + +bool SQLiteIDBBackingStore::ensureValidIndexRecordsTable() +{ + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + String currentSchema; + { + // Fetch the schema for an existing index record table. + SQLiteStatement statement(*m_sqliteDB, "SELECT type, sql FROM sqlite_master WHERE tbl_name='IndexRecords'"); + if (statement.prepare() != SQLITE_OK) { + LOG_ERROR("Unable to prepare statement to fetch schema for the IndexRecords table."); + return false; + } + + int sqliteResult = statement.step(); + + // If there is no IndexRecords table at all, create it and then bail. + if (sqliteResult == SQLITE_DONE) { + if (!m_sqliteDB->executeCommand(v3IndexRecordsTableSchema())) { + LOG_ERROR("Could not create IndexRecords table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return false; + } + + return true; + } + + if (sqliteResult != SQLITE_ROW) { + LOG_ERROR("Error executing statement to fetch schema for the IndexRecords table."); + return false; + } + + currentSchema = statement.getColumnText(1); + } + + ASSERT(!currentSchema.isEmpty()); + + // If the schema in the backing store is the current schema, we're done. + if (currentSchema == v3IndexRecordsTableSchema() || currentSchema == v3IndexRecordsTableSchemaAlternate()) + return true; + + // If the record table is not the current schema then it must be one of the previous schemas. + // If it is not then the database is in an unrecoverable state and this should be considered a fatal error. + if (currentSchema != v1IndexRecordsTableSchema() && currentSchema != v1IndexRecordsTableSchemaAlternate() + && currentSchema != v2IndexRecordsTableSchema() && currentSchema != v2IndexRecordsTableSchemaAlternate()) + RELEASE_ASSERT_NOT_REACHED(); + + SQLiteTransaction transaction(*m_sqliteDB); + transaction.begin(); + + // Create a temporary table with the correct schema and migrate all existing content over. + if (!m_sqliteDB->executeCommand(v3IndexRecordsTableSchema("_Temp_IndexRecords"))) { + LOG_ERROR("Could not create temporary index records table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return false; + } + + if (!m_sqliteDB->executeCommand("INSERT INTO _Temp_IndexRecords SELECT IndexRecords.indexID, IndexRecords.objectStoreID, IndexRecords.key, IndexRecords.value, Records.rowid FROM IndexRecords INNER JOIN Records ON Records.key = IndexRecords.value AND Records.objectStoreID = IndexRecords.objectStoreID")) { + LOG_ERROR("Could not migrate existing IndexRecords content (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return false; + } + + if (!m_sqliteDB->executeCommand("DROP TABLE IndexRecords")) { + LOG_ERROR("Could not drop existing IndexRecords table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return false; + } + + if (!m_sqliteDB->executeCommand("ALTER TABLE _Temp_IndexRecords RENAME TO IndexRecords")) { + LOG_ERROR("Could not rename temporary IndexRecords table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return false; + } + + transaction.commit(); + + return true; +} + +bool SQLiteIDBBackingStore::ensureValidIndexRecordsIndex() +{ + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + String currentSchema; + { + // Fetch the schema for an existing index record index. + SQLiteStatement statement(*m_sqliteDB, "SELECT sql FROM sqlite_master WHERE name='IndexRecordsIndex'"); + if (statement.prepare() != SQLITE_OK) { + LOG_ERROR("Unable to prepare statement to fetch schema for the IndexRecordsIndex index."); + return false; + } + + int sqliteResult = statement.step(); + + // If there is no IndexRecordsIndex index at all, create it and then bail. + if (sqliteResult == SQLITE_DONE) { + if (!m_sqliteDB->executeCommand(v1IndexRecordsIndexSchema())) { + LOG_ERROR("Could not create IndexRecordsIndex index in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return false; + } + + return true; + } + + if (sqliteResult != SQLITE_ROW) { + LOG_ERROR("Error executing statement to fetch schema for the IndexRecordsIndex index."); + return false; + } + + currentSchema = statement.getColumnText(0); + } + + ASSERT(!currentSchema.isEmpty()); + + // If the schema in the backing store is the current schema, we're done. + if (currentSchema == v1IndexRecordsIndexSchema()) + return true; + + // There is currently no outdated schema for the IndexRecordsIndex, so any other existing schema means this database is invalid. + return false; +} + +std::unique_ptr SQLiteIDBBackingStore::createAndPopulateInitialDatabaseInfo() +{ + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + if (!m_sqliteDB->executeCommand("CREATE TABLE IDBDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value TEXT NOT NULL ON CONFLICT FAIL);")) { + LOG_ERROR("Could not create IDBDatabaseInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + closeSQLiteDB(); + return nullptr; + } + + if (!m_sqliteDB->executeCommand("CREATE TABLE ObjectStoreInfo (id INTEGER PRIMARY KEY NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, name TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, keyPath BLOB NOT NULL ON CONFLICT FAIL, autoInc INTEGER NOT NULL ON CONFLICT FAIL, maxIndexID INTEGER NOT NULL ON CONFLICT FAIL);")) { + LOG_ERROR("Could not create ObjectStoreInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + closeSQLiteDB(); + return nullptr; + } + + if (!m_sqliteDB->executeCommand("CREATE TABLE IndexInfo (id INTEGER NOT NULL ON CONFLICT FAIL, name TEXT NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, keyPath BLOB NOT NULL ON CONFLICT FAIL, isUnique INTEGER NOT NULL ON CONFLICT FAIL, multiEntry INTEGER NOT NULL ON CONFLICT FAIL);")) { + LOG_ERROR("Could not create IndexInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + closeSQLiteDB(); + return nullptr; + } + + if (!m_sqliteDB->executeCommand("CREATE TABLE KeyGenerators (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, currentKey INTEGER NOT NULL ON CONFLICT FAIL);")) { + LOG_ERROR("Could not create KeyGenerators table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + closeSQLiteDB(); + return nullptr; + } + + { + SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('MetadataVersion', ?);")); + if (sql.prepare() != SQLITE_OK + || sql.bindInt(1, currentMetadataVersion) != SQLITE_OK + || sql.step() != SQLITE_DONE) { + LOG_ERROR("Could not insert database metadata version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + closeSQLiteDB(); + return nullptr; + } + } + { + SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('DatabaseName', ?);")); + if (sql.prepare() != SQLITE_OK + || sql.bindText(1, m_identifier.databaseName()) != SQLITE_OK + || sql.step() != SQLITE_DONE) { + LOG_ERROR("Could not insert database name into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + closeSQLiteDB(); + return nullptr; + } + } + { + // Database versions are defined to be a uin64_t in the spec but sqlite3 doesn't support native binding of unsigned integers. + // Therefore we'll store the version as a String. + SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('DatabaseVersion', ?);")); + if (sql.prepare() != SQLITE_OK + || sql.bindText(1, String::number(0)) != SQLITE_OK + || sql.step() != SQLITE_DONE) { + LOG_ERROR("Could not insert default version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + closeSQLiteDB(); + return nullptr; + } + } + + if (!m_sqliteDB->executeCommand(ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('MaxObjectStoreID', 1);"))) { + LOG_ERROR("Could not insert default version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + closeSQLiteDB(); + return nullptr; + } + + // This initial database info matches the default values we just put into the metadata database. + return std::make_unique(m_identifier.databaseName(), 0); +} + +std::unique_ptr SQLiteIDBBackingStore::extractExistingDatabaseInfo() +{ + ASSERT(m_sqliteDB); + + if (!m_sqliteDB->tableExists(ASCIILiteral("IDBDatabaseInfo"))) + return nullptr; + + String databaseName; + { + SQLiteStatement sql(*m_sqliteDB, "SELECT value FROM IDBDatabaseInfo WHERE key = 'DatabaseName';"); + if (sql.isColumnNull(0)) + return nullptr; + databaseName = sql.getColumnText(0); + if (databaseName != m_identifier.databaseName()) { + LOG_ERROR("Database name in the info database ('%s') does not match the expected name ('%s')", databaseName.utf8().data(), m_identifier.databaseName().utf8().data()); + return nullptr; + } + } + uint64_t databaseVersion; + { + SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT value FROM IDBDatabaseInfo WHERE key = 'DatabaseVersion';")); + if (sql.isColumnNull(0)) + return nullptr; + String stringVersion = sql.getColumnText(0); + bool ok; + databaseVersion = stringVersion.toUInt64Strict(&ok); + if (!ok) { + LOG_ERROR("Database version on disk ('%s') does not cleanly convert to an unsigned 64-bit integer version", stringVersion.utf8().data()); + return nullptr; + } + } + + auto databaseInfo = std::make_unique(databaseName, databaseVersion); + + { + SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT id, name, keyPath, autoInc, maxIndexID FROM ObjectStoreInfo;")); + if (sql.prepare() != SQLITE_OK) + return nullptr; + + int result = sql.step(); + while (result == SQLITE_ROW) { + uint64_t objectStoreID = sql.getColumnInt64(0); + String objectStoreName = sql.getColumnText(1); + + Vector keyPathBuffer; + sql.getColumnBlobAsVector(2, keyPathBuffer); + + std::optional objectStoreKeyPath; + if (!deserializeIDBKeyPath(reinterpret_cast(keyPathBuffer.data()), keyPathBuffer.size(), objectStoreKeyPath)) { + LOG_ERROR("Unable to extract key path from database"); + return nullptr; + } + + bool autoIncrement = sql.getColumnInt(3); + + databaseInfo->addExistingObjectStore({ objectStoreID, objectStoreName, WTFMove(objectStoreKeyPath), autoIncrement }); + + result = sql.step(); + } + + if (result != SQLITE_DONE) { + LOG_ERROR("Error fetching object store info from database on disk"); + return nullptr; + } + } + + { + SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT id, name, objectStoreID, keyPath, isUnique, multiEntry FROM IndexInfo;")); + if (sql.prepare() != SQLITE_OK) + return nullptr; + + int result = sql.step(); + while (result == SQLITE_ROW) { + uint64_t indexID = sql.getColumnInt64(0); + String indexName = sql.getColumnText(1); + uint64_t objectStoreID = sql.getColumnInt64(2); + + Vector keyPathBuffer; + sql.getColumnBlobAsVector(3, keyPathBuffer); + + std::optional indexKeyPath; + if (!deserializeIDBKeyPath(reinterpret_cast(keyPathBuffer.data()), keyPathBuffer.size(), indexKeyPath)) { + LOG_ERROR("Unable to extract key path from database"); + return nullptr; + } + if (!indexKeyPath) { + LOG_ERROR("Unable to extract key path from database"); + return nullptr; + } + + bool unique = sql.getColumnInt(4); + bool multiEntry = sql.getColumnInt(5); + + auto objectStore = databaseInfo->infoForExistingObjectStore(objectStoreID); + if (!objectStore) { + LOG_ERROR("Found index referring to a non-existant object store"); + return nullptr; + } + + objectStore->addExistingIndex({ indexID, objectStoreID, indexName, WTFMove(indexKeyPath.value()), unique, multiEntry }); + + result = sql.step(); + } + + if (result != SQLITE_DONE) { + LOG_ERROR("Error fetching index info from database on disk"); + return nullptr; + } + } + + return databaseInfo; +} + +String SQLiteIDBBackingStore::databaseNameFromEncodedFilename(const String& encodedName) +{ + if (equal(encodedName, ASCIILiteral("%00"))) + return { }; + + String partiallyDecoded = encodedName; + partiallyDecoded.replace(ASCIILiteral("%2E"), ASCIILiteral(".")); + + return decodeFromFilename(partiallyDecoded); +} + +String SQLiteIDBBackingStore::filenameForDatabaseName() const +{ + ASSERT(!m_identifier.databaseName().isNull()); + + if (m_identifier.databaseName().isEmpty()) + return "%00"; + + String filename = encodeForFileName(m_identifier.databaseName()); + filename.replace('.', "%2E"); + + return filename; +} + +String SQLiteIDBBackingStore::fullDatabaseDirectory() const +{ + ASSERT(!m_identifier.databaseName().isNull()); + + return pathByAppendingComponent(m_absoluteDatabaseDirectory, filenameForDatabaseName()); +} + +String SQLiteIDBBackingStore::fullDatabasePath() const +{ + ASSERT(!m_identifier.databaseName().isNull()); + + return pathByAppendingComponent(fullDatabaseDirectory(), "IndexedDB.sqlite3"); +} + +IDBError SQLiteIDBBackingStore::getOrEstablishDatabaseInfo(IDBDatabaseInfo& info) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::getOrEstablishDatabaseInfo - database %s", m_identifier.databaseName().utf8().data()); + + if (m_databaseInfo) { + info = *m_databaseInfo; + return { }; + } + + makeAllDirectories(fullDatabaseDirectory()); + String dbFilename = fullDatabasePath(); + + m_sqliteDB = std::make_unique(); + if (!m_sqliteDB->open(dbFilename)) { + LOG_ERROR("Failed to open SQLite database at path '%s'", dbFilename.utf8().data()); + closeSQLiteDB(); + } + + if (!m_sqliteDB) + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to open database file on disk") }; + + m_sqliteDB->setCollationFunction("IDBKEY", [this](int aLength, const void* a, int bLength, const void* b) { + return idbKeyCollate(aLength, a, bLength, b); + }); + + if (!ensureValidRecordsTable()) { + LOG_ERROR("Error creating or migrating Records table in database"); + closeSQLiteDB(); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Records table in database") }; + } + + if (!ensureValidIndexRecordsTable()) { + LOG_ERROR("Error creating or migrating Index Records table in database"); + closeSQLiteDB(); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Index Records table in database") }; + } + + if (!ensureValidIndexRecordsIndex()) { + LOG_ERROR("Error creating or migrating Index Records index in database"); + closeSQLiteDB(); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Index Records index in database") }; + } + + if (!ensureValidBlobTables()) { + LOG_ERROR("Error creating or confirming Blob Records tables in database"); + closeSQLiteDB(); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or confirming Blob Records tables in database") }; + } + + auto databaseInfo = extractExistingDatabaseInfo(); + if (!databaseInfo) + databaseInfo = createAndPopulateInitialDatabaseInfo(); + + if (!databaseInfo) { + LOG_ERROR("Unable to establish IDB database at path '%s'", dbFilename.utf8().data()); + closeSQLiteDB(); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to establish IDB database file") }; + } + + m_databaseInfo = WTFMove(databaseInfo); + info = *m_databaseInfo; + return { }; +} + +IDBError SQLiteIDBBackingStore::beginTransaction(const IDBTransactionInfo& info) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::beginTransaction - %s", info.identifier().loggingString().utf8().data()); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + ASSERT(m_databaseInfo); + + auto addResult = m_transactions.add(info.identifier(), nullptr); + if (!addResult.isNewEntry) { + LOG_ERROR("Attempt to establish transaction identifier that already exists"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to establish transaction identifier that already exists") }; + } + + addResult.iterator->value = std::make_unique(*this, info); + + auto error = addResult.iterator->value->begin(*m_sqliteDB); + if (error.isNull() && info.mode() == IDBTransactionMode::Versionchange) { + m_originalDatabaseInfoBeforeVersionChange = std::make_unique(*m_databaseInfo); + + SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("UPDATE IDBDatabaseInfo SET value = ? where key = 'DatabaseVersion';")); + if (sql.prepare() != SQLITE_OK + || sql.bindText(1, String::number(info.newVersion())) != SQLITE_OK + || sql.step() != SQLITE_DONE) + error = { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to store new database version in database") }; + } + + return error; +} + +IDBError SQLiteIDBBackingStore::abortTransaction(const IDBResourceIdentifier& identifier) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::abortTransaction - %s", identifier.loggingString().utf8().data()); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto transaction = m_transactions.take(identifier); + if (!transaction) { + LOG_ERROR("Attempt to commit a transaction that hasn't been established"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to abort a transaction that hasn't been established") }; + } + + if (transaction->mode() == IDBTransactionMode::Versionchange && m_originalDatabaseInfoBeforeVersionChange) + m_databaseInfo = WTFMove(m_originalDatabaseInfoBeforeVersionChange); + + return transaction->abort(); +} + +IDBError SQLiteIDBBackingStore::commitTransaction(const IDBResourceIdentifier& identifier) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::commitTransaction - %s", identifier.loggingString().utf8().data()); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto transaction = m_transactions.take(identifier); + if (!transaction) { + LOG_ERROR("Attempt to commit a transaction that hasn't been established"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to commit a transaction that hasn't been established") }; + } + + auto error = transaction->commit(); + if (!error.isNull()) { + if (transaction->mode() == IDBTransactionMode::Versionchange) { + ASSERT(m_originalDatabaseInfoBeforeVersionChange); + m_databaseInfo = WTFMove(m_originalDatabaseInfoBeforeVersionChange); + } + } else + m_originalDatabaseInfoBeforeVersionChange = nullptr; + + return error; +} + +IDBError SQLiteIDBBackingStore::createObjectStore(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& info) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::createObjectStore - adding OS %s with ID %" PRIu64, info.name().utf8().data(), info.identifier()); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to create an object store without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an object store without an in-progress transaction") }; + } + if (transaction->mode() != IDBTransactionMode::Versionchange) { + LOG_ERROR("Attempt to create an object store in a non-version-change transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an object store in a non-version-change transaction") }; + } + + RefPtr keyPathBlob = serializeIDBKeyPath(info.keyPath()); + if (!keyPathBlob) { + LOG_ERROR("Unable to serialize IDBKeyPath to save in database for new object store"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKeyPath to save in database for new object store") }; + } + + { + auto* sql = cachedStatement(SQL::CreateObjectStoreInfo, ASCIILiteral("INSERT INTO ObjectStoreInfo VALUES (?, ?, ?, ?, ?);")); + if (!sql + || sql->bindInt64(1, info.identifier()) != SQLITE_OK + || sql->bindText(2, info.name()) != SQLITE_OK + || sql->bindBlob(3, keyPathBlob->data(), keyPathBlob->size()) != SQLITE_OK + || sql->bindInt(4, info.autoIncrement()) != SQLITE_OK + || sql->bindInt64(5, info.maxIndexID()) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not add object store '%s' to ObjectStoreInfo table (%i) - %s", info.name().utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not create object store") }; + } + } + + { + auto* sql = cachedStatement(SQL::CreateObjectStoreKeyGenerator, ASCIILiteral("INSERT INTO KeyGenerators VALUES (?, 0);")); + if (!sql + || sql->bindInt64(1, info.identifier()) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not seed initial key generator value for ObjectStoreInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not seed initial key generator value for object store") }; + } + } + + m_databaseInfo->addExistingObjectStore(info); + + return { }; +} + +IDBError SQLiteIDBBackingStore::deleteObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::deleteObjectStore - object store %" PRIu64, objectStoreIdentifier); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to delete an object store without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete an object store without an in-progress transaction") }; + } + if (transaction->mode() != IDBTransactionMode::Versionchange) { + LOG_ERROR("Attempt to delete an object store in a non-version-change transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete an object store in a non-version-change transaction") }; + } + + // Delete the ObjectStore record + { + auto* sql = cachedStatement(SQL::DeleteObjectStoreInfo, ASCIILiteral("DELETE FROM ObjectStoreInfo WHERE id = ?;")); + if (!sql + || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete object store id %" PRIi64 " from ObjectStoreInfo table (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete object store") }; + } + } + + // Delete the ObjectStore's key generator record if there is one. + { + auto* sql = cachedStatement(SQL::DeleteObjectStoreKeyGenerator, ASCIILiteral("DELETE FROM KeyGenerators WHERE objectStoreID = ?;")); + if (!sql + || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete object store from KeyGenerators table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete key generator for deleted object store") }; + } + } + + // Delete all associated records + { + auto* sql = cachedStatement(SQL::DeleteObjectStoreRecords, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ?;")); + if (!sql + || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete records for object store %" PRIi64 " (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete records for deleted object store") }; + } + } + + // Delete all associated Indexes + { + auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexInfo, ASCIILiteral("DELETE FROM IndexInfo WHERE objectStoreID = ?;")); + if (!sql + || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete index from IndexInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete IDBIndex for deleted object store") }; + } + } + + // Delete all associated Index records + { + auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecords, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ?;")); + if (!sql + || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete index records(%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete IDBIndex records for deleted object store") }; + } + } + + // Delete all unused Blob URL records. + { + auto* sql = cachedStatement(SQL::DeleteObjectStoreBlobRecords, ASCIILiteral("DELETE FROM BlobRecords WHERE objectStoreRow NOT IN (SELECT recordID FROM Records)")); + if (!sql + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete Blob URL records(%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete stored blob records for deleted object store") }; + } + } + + // Delete all unused Blob File records. + auto error = deleteUnusedBlobFileRecords(*transaction); + if (!error.isNull()) + return error; + + m_databaseInfo->deleteObjectStore(objectStoreIdentifier); + + return { }; +} + +IDBError SQLiteIDBBackingStore::renameObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::renameObjectStore - object store %" PRIu64, objectStoreIdentifier); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to rename an object store without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an object store without an in-progress transaction") }; + } + if (transaction->mode() != IDBTransactionMode::Versionchange) { + LOG_ERROR("Attempt to rename an object store in a non-version-change transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an object store in a non-version-change transaction") }; + } + + { + auto* sql = cachedStatement(SQL::RenameObjectStore, ASCIILiteral("UPDATE ObjectStoreInfo SET name = ? WHERE id = ?;")); + if (!sql + || sql->bindText(1, newName) != SQLITE_OK + || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not update name for object store id %" PRIi64 " in ObjectStoreInfo table (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename object store") }; + } + } + + m_databaseInfo->renameObjectStore(objectStoreIdentifier, newName); + + return { }; +} + +IDBError SQLiteIDBBackingStore::clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::clearObjectStore - object store %" PRIu64, objectStoreID); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to clear an object store without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to clear an object store without an in-progress transaction") }; + } + if (transaction->mode() == IDBTransactionMode::Readonly) { + LOG_ERROR("Attempt to clear an object store in a read-only transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to clear an object store in a read-only transaction") }; + } + + { + auto* sql = cachedStatement(SQL::ClearObjectStoreRecords, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ?;")); + if (!sql + || sql->bindInt64(1, objectStoreID) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not clear records from object store id %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to clear object store") }; + } + } + + { + auto* sql = cachedStatement(SQL::ClearObjectStoreIndexRecords, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ?;")); + if (!sql + || sql->bindInt64(1, objectStoreID) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete records from index record store id %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to delete index records while clearing object store") }; + } + } + + transaction->notifyCursorsOfChanges(objectStoreID); + + return { }; +} + +IDBError SQLiteIDBBackingStore::createIndex(const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo& info) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::createIndex - ObjectStore %" PRIu64 ", Index %" PRIu64, info.objectStoreIdentifier(), info.identifier()); + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to create an index without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an index without an in-progress transaction") }; + } + if (transaction->mode() != IDBTransactionMode::Versionchange) { + LOG_ERROR("Attempt to create an index in a non-version-change transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an index in a non-version-change transaction") }; + } + + RefPtr keyPathBlob = serializeIDBKeyPath(info.keyPath()); + if (!keyPathBlob) { + LOG_ERROR("Unable to serialize IDBKeyPath to save in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKeyPath to create index in database") }; + } + + auto* sql = cachedStatement(SQL::CreateIndexInfo, ASCIILiteral("INSERT INTO IndexInfo VALUES (?, ?, ?, ?, ?, ?);")); + if (!sql + || sql->bindInt64(1, info.identifier()) != SQLITE_OK + || sql->bindText(2, info.name()) != SQLITE_OK + || sql->bindInt64(3, info.objectStoreIdentifier()) != SQLITE_OK + || sql->bindBlob(4, keyPathBlob->data(), keyPathBlob->size()) != SQLITE_OK + || sql->bindInt(5, info.unique()) != SQLITE_OK + || sql->bindInt(6, info.multiEntry()) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not add index '%s' to IndexInfo table (%i) - %s", info.name().utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to create index in database") }; + } + + // Write index records for any records that already exist in this object store. + + auto cursor = transaction->maybeOpenBackingStoreCursor(info.objectStoreIdentifier(), 0, IDBKeyRangeData::allKeys()); + + if (!cursor) { + LOG_ERROR("Cannot open cursor to populate indexes in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to populate indexes in database") }; + } + + while (!cursor->currentKey().isNull()) { + auto& key = cursor->currentKey(); + auto* value = cursor->currentValue(); + ThreadSafeDataBuffer valueBuffer = value ? value->data() : ThreadSafeDataBuffer(); + + ASSERT(cursor->currentRecordRowID()); + + IDBError error = updateOneIndexForAddRecord(info, key, valueBuffer, cursor->currentRecordRowID()); + if (!error.isNull()) { + auto* sql = cachedStatement(SQL::DeleteIndexInfo, ASCIILiteral("DELETE FROM IndexInfo WHERE id = ? AND objectStoreID = ?;")); + if (!sql + || sql->bindInt64(1, info.identifier()) != SQLITE_OK + || sql->bindInt64(2, info.objectStoreIdentifier()) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Index creation failed due to uniqueness constraint failure, but there was an error deleting the Index record from the database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Index creation failed due to uniqueness constraint failure, but there was an error deleting the Index record from the database") }; + } + + return error; + } + + if (!cursor->advance(1)) { + LOG_ERROR("Error advancing cursor while indexing existing records for new index."); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error advancing cursor while indexing existing records for new index") }; + } + } + + auto* objectStore = m_databaseInfo->infoForExistingObjectStore(info.objectStoreIdentifier()); + ASSERT(objectStore); + objectStore->addExistingIndex(info); + + return { }; +} + +IDBError SQLiteIDBBackingStore::uncheckedHasIndexRecord(const IDBIndexInfo& info, const IDBKeyData& indexKey, bool& hasRecord) +{ + hasRecord = false; + + RefPtr indexKeyBuffer = serializeIDBKeyData(indexKey); + if (!indexKeyBuffer) { + LOG_ERROR("Unable to serialize index key to be stored in the database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to check for index record in database") }; + } + + auto* sql = cachedStatement(SQL::HasIndexRecord, ASCIILiteral("SELECT rowid FROM IndexRecords WHERE indexID = ? AND objectStoreID = ? AND key = CAST(? AS TEXT);")); + if (!sql + || sql->bindInt64(1, info.identifier()) != SQLITE_OK + || sql->bindInt64(2, info.objectStoreIdentifier()) != SQLITE_OK + || sql->bindBlob(3, indexKeyBuffer->data(), indexKeyBuffer->size()) != SQLITE_OK) { + LOG_ERROR("Error checking for index record in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error checking for index record in database") }; + } + + int sqlResult = sql->step(); + if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE) + return { }; + + if (sqlResult != SQLITE_ROW) { + // There was an error fetching the record from the database. + LOG_ERROR("Could not check if key exists in index (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error checking for existence of IDBKey in index") }; + } + + hasRecord = true; + return { }; +} + +IDBError SQLiteIDBBackingStore::uncheckedPutIndexKey(const IDBIndexInfo& info, const IDBKeyData& key, const IndexKey& indexKey, int64_t recordID) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::uncheckedPutIndexKey - (%" PRIu64 ") %s, %s", info.identifier(), key.loggingString().utf8().data(), indexKey.asOneKey().loggingString().utf8().data()); + + Vector indexKeys; + if (info.multiEntry()) + indexKeys = indexKey.multiEntry(); + else + indexKeys.append(indexKey.asOneKey()); + + if (info.unique()) { + bool hasRecord; + IDBError error; + for (auto& indexKey : indexKeys) { + if (!indexKey.isValid()) + continue; + error = uncheckedHasIndexRecord(info, indexKey, hasRecord); + if (!error.isNull()) + return error; + if (hasRecord) + return IDBError(IDBDatabaseException::ConstraintError); + } + } + + for (auto& indexKey : indexKeys) { + if (!indexKey.isValid()) + continue; + auto error = uncheckedPutIndexRecord(info.objectStoreIdentifier(), info.identifier(), key, indexKey, recordID); + if (!error.isNull()) { + LOG_ERROR("Unable to put index record for newly created index"); + return error; + } + } + + return { }; +} + +IDBError SQLiteIDBBackingStore::uncheckedPutIndexRecord(int64_t objectStoreID, int64_t indexID, const WebCore::IDBKeyData& keyValue, const WebCore::IDBKeyData& indexKey, int64_t recordID) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::uncheckedPutIndexRecord - %s, %s", keyValue.loggingString().utf8().data(), indexKey.loggingString().utf8().data()); + + RefPtr indexKeyBuffer = serializeIDBKeyData(indexKey); + if (!indexKeyBuffer) { + LOG_ERROR("Unable to serialize index key to be stored in the database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize index key to be stored in the database") }; + } + + RefPtr valueBuffer = serializeIDBKeyData(keyValue); + if (!valueBuffer) { + LOG_ERROR("Unable to serialize the value to be stored in the database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize value to be stored in the database") }; + } + + { + auto* sql = cachedStatement(SQL::PutIndexRecord, ASCIILiteral("INSERT INTO IndexRecords VALUES (?, ?, CAST(? AS TEXT), CAST(? AS TEXT), ?);")); + if (!sql + || sql->bindInt64(1, indexID) != SQLITE_OK + || sql->bindInt64(2, objectStoreID) != SQLITE_OK + || sql->bindBlob(3, indexKeyBuffer->data(), indexKeyBuffer->size()) != SQLITE_OK + || sql->bindBlob(4, valueBuffer->data(), valueBuffer->size()) != SQLITE_OK + || sql->bindInt64(5, recordID) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not put index record for index %" PRIi64 " in object store %" PRIi64 " in Records table (%i) - %s", indexID, objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error putting index record into database") }; + } + } + + return { }; +} + + +IDBError SQLiteIDBBackingStore::deleteIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::deleteIndex - object store %" PRIu64, objectStoreIdentifier); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to delete index without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete index without an in-progress transaction") }; + } + + if (transaction->mode() != IDBTransactionMode::Versionchange) { + LOG_ERROR("Attempt to delete index during a non-version-change transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete index during a non-version-change transaction") }; + } + + { + auto* sql = cachedStatement(SQL::DeleteIndexInfo, ASCIILiteral("DELETE FROM IndexInfo WHERE id = ? AND objectStoreID = ?;")); + if (!sql + || sql->bindInt64(1, indexIdentifier) != SQLITE_OK + || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete index id %" PRIi64 " from IndexInfo table (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting index from database") }; + } + } + + { + auto* sql = cachedStatement(SQL::DeleteIndexRecords, ASCIILiteral("DELETE FROM IndexRecords WHERE indexID = ? AND objectStoreID = ?;")); + if (!sql + || sql->bindInt64(1, indexIdentifier) != SQLITE_OK + || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete index records for index id %" PRIi64 " from IndexRecords table (%i) - %s", indexIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting index records from database") }; + } + } + + auto* objectStore = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); + ASSERT(objectStore); + objectStore->deleteIndex(indexIdentifier); + + return { }; +} + +IDBError SQLiteIDBBackingStore::renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::renameIndex - object store %" PRIu64 ", index %" PRIu64, objectStoreIdentifier, indexIdentifier); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); + if (!objectStoreInfo) + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename index") }; + + auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier); + if (!indexInfo) + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename index") }; + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to rename an index without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an index without an in-progress transaction") }; + } + + if (transaction->mode() != IDBTransactionMode::Versionchange) { + LOG_ERROR("Attempt to rename an index in a non-version-change transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an index in a non-version-change transaction") }; + } + + { + auto* sql = cachedStatement(SQL::RenameIndex, ASCIILiteral("UPDATE IndexInfo SET name = ? WHERE objectStoreID = ? AND id = ?;")); + if (!sql + || sql->bindText(1, newName) != SQLITE_OK + || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK + || sql->bindInt64(3, indexIdentifier) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not update name for index id (%" PRIi64 ", %" PRIi64 ") in IndexInfo table (%i) - %s", objectStoreIdentifier, indexIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename index") }; + } + } + + indexInfo->rename(newName); + + return { }; +} + +IDBError SQLiteIDBBackingStore::keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, const IDBKeyData& keyData, bool& keyExists) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::keyExistsInObjectStore - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreID); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + keyExists = false; + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to see if key exists in objectstore without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to see if key exists in objectstore without an in-progress transaction") }; + } + + RefPtr keyBuffer = serializeIDBKeyData(keyData); + if (!keyBuffer) { + LOG_ERROR("Unable to serialize IDBKey to check for existence in object store"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to check for existence in object store") }; + } + auto* sql = cachedStatement(SQL::KeyExistsInObjectStore, ASCIILiteral("SELECT key FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT) LIMIT 1;")); + if (!sql + || sql->bindInt64(1, objectStoreID) != SQLITE_OK + || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK) { + LOG_ERROR("Could not get record from object store %" PRIi64 " from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to check for existence of IDBKey in object store") }; + } + + int sqlResult = sql->step(); + if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE) + return { }; + + if (sqlResult != SQLITE_ROW) { + // There was an error fetching the record from the database. + LOG_ERROR("Could not check if key exists in object store (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error checking for existence of IDBKey in object store") }; + } + + keyExists = true; + return { }; +} + +IDBError SQLiteIDBBackingStore::deleteUnusedBlobFileRecords(SQLiteIDBTransaction& transaction) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::deleteUnusedBlobFileRecords"); + + // Gather the set of blob URLs and filenames that are no longer in use. + HashSet removedBlobFilenames; + { + auto* sql = cachedStatement(SQL::GetUnusedBlobFilenames, ASCIILiteral("SELECT fileName FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)")); + + if (!sql) { + LOG_ERROR("Error deleting stored blobs (%i) (Could not gather unused blobURLs) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") }; + } + + int result = sql->step(); + while (result == SQLITE_ROW) { + removedBlobFilenames.add(sql->getColumnText(0)); + result = sql->step(); + } + + if (result != SQLITE_DONE) { + LOG_ERROR("Error deleting stored blobs (%i) (Could not gather unused blobURLs) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") }; + } + } + + // Remove the blob records that are no longer in use. + if (!removedBlobFilenames.isEmpty()) { + auto* sql = cachedStatement(SQL::DeleteUnusedBlobs, ASCIILiteral("DELETE FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)")); + + if (!sql + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Error deleting stored blobs (%i) (Could not delete blobFile records) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") }; + } + } + + for (auto& file : removedBlobFilenames) + transaction.addRemovedBlobFile(file); + + return { }; +} + +IDBError SQLiteIDBBackingStore::deleteRecord(SQLiteIDBTransaction& transaction, int64_t objectStoreID, const IDBKeyData& keyData) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::deleteRecord - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreID); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + ASSERT(transaction.inProgress()); + ASSERT(transaction.mode() != IDBTransactionMode::Readonly); + UNUSED_PARAM(transaction); + + RefPtr keyBuffer = serializeIDBKeyData(keyData); + if (!keyBuffer) { + LOG_ERROR("Unable to serialize IDBKeyData to be removed from the database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKeyData to be removed from the database") }; + } + + // Get the record ID + int64_t recordID; + { + auto* sql = cachedStatement(SQL::GetObjectStoreRecordID, ASCIILiteral("SELECT recordID FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);")); + + if (!sql + || sql->bindInt64(1, objectStoreID) != SQLITE_OK + || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK) { + LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") }; + } + + int result = sql->step(); + + // If there's no record ID, there's no record to delete. + if (result == SQLITE_DONE) + return { }; + + if (result != SQLITE_ROW) { + LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (unable to fetch record ID) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") }; + } + + recordID = sql->getColumnInt64(0); + } + + if (recordID < 1) { + LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (record ID is invalid) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") }; + } + + // Delete the blob records for this object store record. + { + auto* sql = cachedStatement(SQL::DeleteBlobRecord, ASCIILiteral("DELETE FROM BlobRecords WHERE objectStoreRow = ?;")); + + if (!sql + || sql->bindInt64(1, recordID) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (Could not delete BlobRecords records) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") }; + } + } + + auto error = deleteUnusedBlobFileRecords(transaction); + if (!error.isNull()) + return error; + + // Delete record from object store + { + auto* sql = cachedStatement(SQL::DeleteObjectStoreRecord, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);")); + + if (!sql + || sql->bindInt64(1, objectStoreID) != SQLITE_OK + || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") }; + } + } + + // Delete record from indexes store + { + auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecord, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ? AND value = CAST(? AS TEXT);")); + + if (!sql + || sql->bindInt64(1, objectStoreID) != SQLITE_OK + || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not delete record from indexes for object store %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete index entries for object store record") }; + } + } + + return { }; +} + +IDBError SQLiteIDBBackingStore::deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, const IDBKeyRangeData& keyRange) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::deleteRange - range %s, object store %" PRIu64, keyRange.loggingString().utf8().data(), objectStoreID); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to delete range from database without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete range from database without an in-progress transaction") }; + } + if (transaction->mode() == IDBTransactionMode::Readonly) { + LOG_ERROR("Attempt to delete records from an object store in a read-only transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete records from an object store in a read-only transaction") }; + } + + // If the range to delete is exactly one key we can delete it right now. + if (keyRange.isExactlyOneKey()) { + auto error = deleteRecord(*transaction, objectStoreID, keyRange.lowerKey); + if (!error.isNull()) { + LOG_ERROR("Failed to delete record for key '%s'", keyRange.lowerKey.loggingString().utf8().data()); + return error; + } + + transaction->notifyCursorsOfChanges(objectStoreID); + + return { }; + } + + auto cursor = transaction->maybeOpenBackingStoreCursor(objectStoreID, 0, keyRange); + if (!cursor) { + LOG_ERROR("Cannot open cursor to delete range of records from the database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Cannot open cursor to delete range of records from the database") }; + } + + Vector keys; + while (!cursor->didComplete() && !cursor->didError()) { + keys.append(cursor->currentKey()); + cursor->advance(1); + } + + if (cursor->didError()) { + LOG_ERROR("Cursor failed while accumulating range of records from the database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while accumulating range of records from the database") }; + } + + IDBError error; + for (auto& key : keys) { + error = deleteRecord(*transaction, objectStoreID, key); + if (!error.isNull()) { + LOG_ERROR("deleteRange: Error deleting keys in range"); + break; + } + } + + transaction->notifyCursorsOfChanges(objectStoreID); + + return error; +} + +IDBError SQLiteIDBBackingStore::updateOneIndexForAddRecord(const IDBIndexInfo& info, const IDBKeyData& key, const ThreadSafeDataBuffer& value, int64_t recordID) +{ + JSLockHolder locker(vm()); + + auto jsValue = deserializeIDBValueToJSValue(*globalObject().globalExec(), value); + if (jsValue.isUndefinedOrNull()) + return { }; + + IndexKey indexKey; + generateIndexKeyForValue(*m_globalObject->globalExec(), info, jsValue, indexKey); + + if (indexKey.isNull()) + return { }; + + return uncheckedPutIndexKey(info, key, indexKey, recordID); +} + +IDBError SQLiteIDBBackingStore::updateAllIndexesForAddRecord(const IDBObjectStoreInfo& info, const IDBKeyData& key, const ThreadSafeDataBuffer& value, int64_t recordID) +{ + JSLockHolder locker(vm()); + + auto jsValue = deserializeIDBValueToJSValue(*globalObject().globalExec(), value); + if (jsValue.isUndefinedOrNull()) + return { }; + + IDBError error; + bool anyRecordsSucceeded = false; + for (auto& index : info.indexMap().values()) { + IndexKey indexKey; + generateIndexKeyForValue(*m_globalObject->globalExec(), index, jsValue, indexKey); + + if (indexKey.isNull()) + continue; + + error = uncheckedPutIndexKey(index, key, indexKey, recordID); + if (!error.isNull()) + break; + + anyRecordsSucceeded = true; + } + + if (!error.isNull() && anyRecordsSucceeded) { + RefPtr keyBuffer = serializeIDBKeyData(key); + + auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecord, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ? AND value = CAST(? AS TEXT);")); + + if (!sql + || sql->bindInt64(1, info.identifier()) != SQLITE_OK + || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Adding one Index record failed, but failed to remove all others that previously succeeded"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Adding one Index record failed, but failed to remove all others that previously succeeded") }; + } + } + + return error; +} + +IDBError SQLiteIDBBackingStore::addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& objectStoreInfo, const IDBKeyData& keyData, const IDBValue& value) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::addRecord - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreInfo.identifier()); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + ASSERT(value.data().data()); + ASSERT(value.blobURLs().size() == value.blobFilePaths().size()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to store a record in an object store without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to store a record in an object store without an in-progress transaction") }; + } + if (transaction->mode() == IDBTransactionMode::Readonly) { + LOG_ERROR("Attempt to store a record in an object store in a read-only transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to store a record in an object store in a read-only transaction") }; + } + + RefPtr keyBuffer = serializeIDBKeyData(keyData); + if (!keyBuffer) { + LOG_ERROR("Unable to serialize IDBKey to be stored in an object store"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to be stored in an object store") }; + } + + int64_t recordID = 0; + { + auto* sql = cachedStatement(SQL::AddObjectStoreRecord, ASCIILiteral("INSERT INTO Records VALUES (?, CAST(? AS TEXT), ?, NULL);")); + if (!sql + || sql->bindInt64(1, objectStoreInfo.identifier()) != SQLITE_OK + || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK + || sql->bindBlob(3, value.data().data()->data(), value.data().data()->size()) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not put record for object store %" PRIi64 " in Records table (%i) - %s", objectStoreInfo.identifier(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to store record in object store") }; + } + + recordID = m_sqliteDB->lastInsertRowID(); + } + + auto error = updateAllIndexesForAddRecord(objectStoreInfo, keyData, value.data(), recordID); + + if (!error.isNull()) { + auto* sql = cachedStatement(SQL::DeleteObjectStoreRecord, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);")); + if (!sql + || sql->bindInt64(1, objectStoreInfo.identifier()) != SQLITE_OK + || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Indexing new object store record failed, but unable to remove the object store record itself"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Indexing new object store record failed, but unable to remove the object store record itself") }; + } + + return error; + } + + const Vector& blobURLs = value.blobURLs(); + const Vector& blobFiles = value.blobFilePaths(); + for (size_t i = 0; i < blobURLs.size(); ++i) { + auto& url = blobURLs[i]; + { + auto* sql = cachedStatement(SQL::AddBlobRecord, ASCIILiteral("INSERT INTO BlobRecords VALUES (?, ?);")); + if (!sql + || sql->bindInt64(1, recordID) != SQLITE_OK + || sql->bindText(2, url) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Unable to record Blob record in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to record Blob record in database") }; + } + } + int64_t potentialFileNameInteger = m_sqliteDB->lastInsertRowID(); + + // If we already have a file for this blobURL, nothing left to do. + { + auto* sql = cachedStatement(SQL::BlobFilenameForBlobURL, ASCIILiteral("SELECT fileName FROM BlobFiles WHERE blobURL = ?;")); + if (!sql + || sql->bindText(1, url) != SQLITE_OK) { + LOG_ERROR("Unable to examine Blob filenames in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to examine Blob filenames in database") }; + } + + int result = sql->step(); + if (result != SQLITE_ROW && result != SQLITE_DONE) { + LOG_ERROR("Unable to examine Blob filenames in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to examine Blob filenames in database") }; + } + + if (result == SQLITE_ROW) + continue; + } + + // We don't already have a file for this blobURL, so commit our file as a unique filename + String storedFilename = String::format("%" PRId64 ".blob", potentialFileNameInteger); + { + auto* sql = cachedStatement(SQL::AddBlobFilename, ASCIILiteral("INSERT INTO BlobFiles VALUES (?, ?);")); + if (!sql + || sql->bindText(1, url) != SQLITE_OK + || sql->bindText(2, storedFilename) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Unable to record Blob file record in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to record Blob file record in database") }; + } + } + + transaction->addBlobFile(blobFiles[i], storedFilename); + } + + transaction->notifyCursorsOfChanges(objectStoreInfo.identifier()); + + return error; +} + +IDBError SQLiteIDBBackingStore::getBlobRecordsForObjectStoreRecord(int64_t objectStoreRecord, Vector& blobURLs, Vector& blobFilePaths) +{ + ASSERT(objectStoreRecord); + + HashSet blobURLSet; + { + auto* sql = cachedStatement(SQL::GetBlobURL, ASCIILiteral("SELECT blobURL FROM BlobRecords WHERE objectStoreRow = ?")); + if (!sql + || sql->bindInt64(1, objectStoreRecord) != SQLITE_OK) { + LOG_ERROR("Could not prepare statement to fetch blob URLs for object store record (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") }; + } + + int sqlResult = sql->step(); + if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE) { + // There are no blobURLs in the database for this object store record. + return { }; + } + + while (sqlResult == SQLITE_ROW) { + blobURLSet.add(sql->getColumnText(0)); + sqlResult = sql->step(); + } + + if (sqlResult != SQLITE_DONE) { + LOG_ERROR("Could not fetch blob URLs for object store record (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") }; + } + } + + ASSERT(!blobURLSet.isEmpty()); + String databaseDirectory = fullDatabaseDirectory(); + for (auto& blobURL : blobURLSet) { + auto* sql = cachedStatement(SQL::BlobFilenameForBlobURL, ASCIILiteral("SELECT fileName FROM BlobFiles WHERE blobURL = ?;")); + if (!sql + || sql->bindText(1, blobURL) != SQLITE_OK) { + LOG_ERROR("Could not prepare statement to fetch blob filename for object store record (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") }; + } + + if (sql->step() != SQLITE_ROW) { + LOG_ERROR("Entry for blob filename for blob url %s does not exist (%i) - %s", blobURL.utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") }; + } + + blobURLs.append(blobURL); + + String fileName = sql->getColumnText(0); + blobFilePaths.append(pathByAppendingComponent(databaseDirectory, fileName)); + } + + return { }; +} + +IDBError SQLiteIDBBackingStore::getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, const IDBKeyRangeData& keyRange, IDBGetRecordDataType type, IDBGetResult& resultValue) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::getRecord - key range %s, object store %" PRIu64, keyRange.loggingString().utf8().data(), objectStoreID); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to get a record from database without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get a record from database without an in-progress transaction") }; + } + + auto key = keyRange.lowerKey; + if (key.isNull()) + key = IDBKeyData::minimum(); + RefPtr lowerBuffer = serializeIDBKeyData(key); + if (!lowerBuffer) { + LOG_ERROR("Unable to serialize lower IDBKey in lookup range"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize lower IDBKey in lookup range") }; + } + + key = keyRange.upperKey; + if (key.isNull()) + key = IDBKeyData::maximum(); + RefPtr upperBuffer = serializeIDBKeyData(key); + if (!upperBuffer) { + LOG_ERROR("Unable to serialize upper IDBKey in lookup range"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize upper IDBKey in lookup range") }; + } + + int64_t recordID = 0; + ThreadSafeDataBuffer resultBuffer; + { + static NeverDestroyed lowerOpenUpperOpen("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerOpenUpperClosed("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerClosedUpperOpen("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerClosedUpperClosed("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"); + + static NeverDestroyed lowerOpenUpperOpenKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerOpenUpperClosedKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerClosedUpperOpenKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerClosedUpperClosedKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"); + + SQLiteStatement* sql = nullptr; + + switch (type) { + case IDBGetRecordDataType::KeyAndValue: + if (keyRange.lowerOpen) { + if (keyRange.upperOpen) + sql = cachedStatement(SQL::GetValueRecordsLowerOpenUpperOpen, lowerOpenUpperOpen.get()); + else + sql = cachedStatement(SQL::GetValueRecordsLowerOpenUpperClosed, lowerOpenUpperClosed.get()); + } else { + if (keyRange.upperOpen) + sql = cachedStatement(SQL::GetValueRecordsLowerClosedUpperOpen, lowerClosedUpperOpen.get()); + else + sql = cachedStatement(SQL::GetValueRecordsLowerClosedUpperClosed, lowerClosedUpperClosed.get()); + } + break; + case IDBGetRecordDataType::KeyOnly: + if (keyRange.lowerOpen) { + if (keyRange.upperOpen) + sql = cachedStatement(SQL::GetKeyRecordsLowerOpenUpperOpen, lowerOpenUpperOpenKeyOnly.get()); + else + sql = cachedStatement(SQL::GetKeyRecordsLowerOpenUpperClosed, lowerOpenUpperClosedKeyOnly.get()); + } else { + if (keyRange.upperOpen) + sql = cachedStatement(SQL::GetKeyRecordsLowerClosedUpperOpen, lowerClosedUpperOpenKeyOnly.get()); + else + sql = cachedStatement(SQL::GetKeyRecordsLowerClosedUpperClosed, lowerClosedUpperClosedKeyOnly.get()); + } + } + + if (!sql + || sql->bindInt64(1, objectStoreID) != SQLITE_OK + || sql->bindBlob(2, lowerBuffer->data(), lowerBuffer->size()) != SQLITE_OK + || sql->bindBlob(3, upperBuffer->data(), upperBuffer->size()) != SQLITE_OK) { + LOG_ERROR("Could not get key range record from object store %" PRIi64 " from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up record in object store by key range") }; + } + + int sqlResult = sql->step(); + + if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE) { + // There was no record for the key in the database. + return { }; + } + if (sqlResult != SQLITE_ROW) { + // There was an error fetching the record from the database. + LOG_ERROR("Could not get record from object store %" PRIi64 " from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error looking up record in object store by key range") }; + } + + Vector buffer; + sql->getColumnBlobAsVector(0, buffer); + resultBuffer = ThreadSafeDataBuffer::adoptVector(buffer); + + if (type == IDBGetRecordDataType::KeyAndValue) + recordID = sql->getColumnInt64(1); + } + + if (type == IDBGetRecordDataType::KeyOnly) { + auto* vector = resultBuffer.data(); + if (!vector) { + LOG_ERROR("Unable to deserialize key data from database for IDBObjectStore.getKey()"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error extracting key data from database executing IDBObjectStore.getKey()") }; + } + + IDBKeyData keyData; + if (!deserializeIDBKeyData(vector->data(), vector->size(), keyData)) { + LOG_ERROR("Unable to deserialize key data from database for IDBObjectStore.getKey()"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error extracting key data from database executing IDBObjectStore.getKey()") }; + } + + resultValue = { keyData }; + return { }; + } + + ASSERT(recordID); + Vector blobURLs, blobFilePaths; + auto error = getBlobRecordsForObjectStoreRecord(recordID, blobURLs, blobFilePaths); + ASSERT(blobURLs.size() == blobFilePaths.size()); + + if (!error.isNull()) + return error; + + resultValue = { { resultBuffer, WTFMove(blobURLs), WTFMove(blobFilePaths) } }; + return { }; +} + +IDBError SQLiteIDBBackingStore::getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData, IDBGetAllResult& result) +{ + return getAllRecordsData.indexIdentifier ? getAllIndexRecords(transactionIdentifier, getAllRecordsData, result) : getAllObjectStoreRecords(transactionIdentifier, getAllRecordsData, result); +} + +SQLiteStatement* SQLiteIDBBackingStore::cachedStatementForGetAllObjectStoreRecords(const IDBGetAllRecordsData& getAllRecordsData) +{ + static NeverDestroyed lowerOpenUpperOpenKey("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerOpenUpperClosedKey("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerClosedUpperOpenKey("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerClosedUpperClosedKey("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerOpenUpperOpenValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerOpenUpperClosedValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerClosedUpperOpenValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"); + static NeverDestroyed lowerClosedUpperClosedValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"); + + if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Keys) { + if (getAllRecordsData.keyRangeData.lowerOpen) { + if (getAllRecordsData.keyRangeData.upperOpen) + return cachedStatement(SQL::GetAllKeyRecordsLowerOpenUpperOpen, lowerOpenUpperOpenKey.get()); + return cachedStatement(SQL::GetAllKeyRecordsLowerOpenUpperClosed, lowerOpenUpperClosedKey.get()); + } + + if (getAllRecordsData.keyRangeData.upperOpen) + return cachedStatement(SQL::GetAllKeyRecordsLowerClosedUpperOpen, lowerClosedUpperOpenKey.get()); + return cachedStatement(SQL::GetAllKeyRecordsLowerClosedUpperClosed, lowerClosedUpperClosedKey.get()); + } + + if (getAllRecordsData.keyRangeData.lowerOpen) { + if (getAllRecordsData.keyRangeData.upperOpen) + return cachedStatement(SQL::GetValueRecordsLowerOpenUpperOpen, lowerOpenUpperOpenValue.get()); + return cachedStatement(SQL::GetValueRecordsLowerOpenUpperClosed, lowerOpenUpperClosedValue.get()); + } + + if (getAllRecordsData.keyRangeData.upperOpen) + return cachedStatement(SQL::GetValueRecordsLowerClosedUpperOpen, lowerClosedUpperOpenValue.get()); + return cachedStatement(SQL::GetValueRecordsLowerClosedUpperClosed, lowerClosedUpperClosedValue.get()); +} + +IDBError SQLiteIDBBackingStore::getAllObjectStoreRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData, IDBGetAllResult& result) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::getAllObjectStoreRecords"); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to get records from database without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get records from database without an in-progress transaction") }; + } + + auto key = getAllRecordsData.keyRangeData.lowerKey; + if (key.isNull()) + key = IDBKeyData::minimum(); + auto lowerBuffer = serializeIDBKeyData(key); + if (!lowerBuffer) { + LOG_ERROR("Unable to serialize lower IDBKey in lookup range"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize lower IDBKey in lookup range") }; + } + + key = getAllRecordsData.keyRangeData.upperKey; + if (key.isNull()) + key = IDBKeyData::maximum(); + auto upperBuffer = serializeIDBKeyData(key); + if (!upperBuffer) { + LOG_ERROR("Unable to serialize upper IDBKey in lookup range"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize upper IDBKey in lookup range") }; + } + + auto* sql = cachedStatementForGetAllObjectStoreRecords(getAllRecordsData); + if (!sql + || sql->bindInt64(1, getAllRecordsData.objectStoreIdentifier) != SQLITE_OK + || sql->bindBlob(2, lowerBuffer->data(), lowerBuffer->size()) != SQLITE_OK + || sql->bindBlob(3, upperBuffer->data(), upperBuffer->size()) != SQLITE_OK) { + LOG_ERROR("Could not get key range record from object store %" PRIi64 " from Records table (%i) - %s", getAllRecordsData.objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up record in object store by key range") }; + } + + result = { getAllRecordsData.getAllType }; + + uint32_t targetResults; + if (getAllRecordsData.count && getAllRecordsData.count.value()) + targetResults = getAllRecordsData.count.value(); + else + targetResults = std::numeric_limits::max(); + + int sqlResult = sql->step(); + uint32_t returnedResults = 0; + + while (sqlResult == SQLITE_ROW && returnedResults < targetResults) { + if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Values) { + Vector buffer; + sql->getColumnBlobAsVector(0, buffer); + ThreadSafeDataBuffer resultBuffer = ThreadSafeDataBuffer::adoptVector(buffer); + + auto recordID = sql->getColumnInt64(1); + + ASSERT(recordID); + Vector blobURLs, blobFilePaths; + auto error = getBlobRecordsForObjectStoreRecord(recordID, blobURLs, blobFilePaths); + ASSERT(blobURLs.size() == blobFilePaths.size()); + + if (!error.isNull()) + return error; + + result.addValue({ resultBuffer, WTFMove(blobURLs), WTFMove(blobFilePaths) }); + } else { + Vector keyData; + IDBKeyData key; + sql->getColumnBlobAsVector(0, keyData); + + if (!deserializeIDBKeyData(keyData.data(), keyData.size(), key)) { + LOG_ERROR("Unable to deserialize key data from database while getting all key records"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to deserialize key data while getting all key records") }; + } + + result.addKey(WTFMove(key)); + } + + ++returnedResults; + sqlResult = sql->step(); + } + + if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE || sqlResult == SQLITE_ROW) { + // Finished getting results + return { }; + } + + // There was an error fetching records from the database. + LOG_ERROR("Could not get record from object store %" PRIi64 " from Records table (%i) - %s", getAllRecordsData.objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error looking up record in object store by key range") }; +} + +IDBError SQLiteIDBBackingStore::getAllIndexRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData, IDBGetAllResult& result) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::getAllIndexRecords - %s", getAllRecordsData.keyRangeData.loggingString().utf8().data()); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to get all index records from database without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get all index records from database without an in-progress transaction") }; + } + + auto cursor = transaction->maybeOpenBackingStoreCursor(getAllRecordsData.objectStoreIdentifier, getAllRecordsData.indexIdentifier, getAllRecordsData.keyRangeData); + if (!cursor) { + LOG_ERROR("Cannot open cursor to perform index gets in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Cannot open cursor to perform index gets in database") }; + } + + if (cursor->didError()) { + LOG_ERROR("Cursor failed while looking up index records in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while looking up index records in database") }; + } + + result = { getAllRecordsData.getAllType }; + uint32_t currentCount = 0; + uint32_t targetCount = getAllRecordsData.count ? getAllRecordsData.count.value() : 0; + if (!targetCount) + targetCount = std::numeric_limits::max(); + while (!cursor->didComplete() && !cursor->didError() && currentCount < targetCount) { + if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Keys) { + IDBKeyData keyCopy = cursor->currentPrimaryKey(); + result.addKey(WTFMove(keyCopy)); + } else + result.addValue(cursor->currentValue() ? *cursor->currentValue() : IDBValue()); + + ++currentCount; + cursor->advance(1); + } + + if (cursor->didError()) { + LOG_ERROR("Cursor failed while looking up index records in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while looking up index records in database") }; + } + + return { }; +} + +IDBError SQLiteIDBBackingStore::getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, uint64_t indexID, IndexedDB::IndexRecordType type, const IDBKeyRangeData& range, IDBGetResult& getResult) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::getIndexRecord - %s", range.loggingString().utf8().data()); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to get an index record from database without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get an index record from database without an in-progress transaction") }; + } + + if (range.isExactlyOneKey()) + return uncheckedGetIndexRecordForOneKey(indexID, objectStoreID, type, range.lowerKey, getResult); + + auto cursor = transaction->maybeOpenBackingStoreCursor(objectStoreID, indexID, range); + if (!cursor) { + LOG_ERROR("Cannot open cursor to perform index get in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Cannot open cursor to perform index get in database") }; + } + + if (cursor->didError()) { + LOG_ERROR("Cursor failed while looking up index record in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while looking up index record in database") }; + } + + if (cursor->didComplete()) + getResult = { }; + else { + if (type == IndexedDB::IndexRecordType::Key) + getResult = { cursor->currentPrimaryKey() }; + else + getResult = { cursor->currentValue() ? *cursor->currentValue() : IDBValue(), cursor->currentPrimaryKey() }; + } + + return { }; +} + +IDBError SQLiteIDBBackingStore::uncheckedGetIndexRecordForOneKey(int64_t indexID, int64_t objectStoreID, IndexedDB::IndexRecordType type, const IDBKeyData& key, IDBGetResult& getResult) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::uncheckedGetIndexRecordForOneKey"); + + ASSERT(key.isValid() && key.type() != KeyType::Max && key.type() != KeyType::Min); + + RefPtr buffer = serializeIDBKeyData(key); + if (!buffer) { + LOG_ERROR("Unable to serialize IDBKey to look up one index record"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to look up one index record") }; + } + + auto* sql = cachedStatement(SQL::GetIndexRecordForOneKey, ASCIILiteral("SELECT IndexRecords.value, Records.value, Records.recordID FROM Records INNER JOIN IndexRecords ON Records.recordID = IndexRecords.objectStoreRecordID WHERE IndexRecords.indexID = ? AND IndexRecords.objectStoreID = ? AND IndexRecords.key = CAST(? AS TEXT) ORDER BY IndexRecords.key, IndexRecords.value")); + + if (!sql + || sql->bindInt64(1, indexID) != SQLITE_OK + || sql->bindInt64(2, objectStoreID) != SQLITE_OK + || sql->bindBlob(3, buffer->data(), buffer->size()) != SQLITE_OK) { + LOG_ERROR("Unable to lookup index record in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to lookup index record in database") }; + } + + int result = sql->step(); + if (result != SQLITE_ROW && result != SQLITE_DONE) { + LOG_ERROR("Unable to lookup index record in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to lookup index record in database") }; + } + + if (result == SQLITE_DONE) + return { }; + + IDBKeyData objectStoreKey; + Vector keyVector; + sql->getColumnBlobAsVector(0, keyVector); + + if (!deserializeIDBKeyData(keyVector.data(), keyVector.size(), objectStoreKey)) { + LOG_ERROR("Unable to deserialize key looking up index record in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to deserialize key looking up index record in database") }; + } + + if (type == IndexedDB::IndexRecordType::Key) { + getResult = { objectStoreKey }; + return { }; + } + + sql->getColumnBlobAsVector(1, keyVector); + + int64_t recordID = sql->getColumnInt64(2); + Vector blobURLs, blobFilePaths; + auto error = getBlobRecordsForObjectStoreRecord(recordID, blobURLs, blobFilePaths); + ASSERT(blobURLs.size() == blobFilePaths.size()); + + if (!error.isNull()) + return error; + + getResult = { { ThreadSafeDataBuffer::adoptVector(keyVector), WTFMove(blobURLs), WTFMove(blobFilePaths) }, objectStoreKey }; + return { }; +} + +IDBError SQLiteIDBBackingStore::getCount(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& range, uint64_t& outCount) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::getCount - object store %" PRIu64, objectStoreIdentifier); + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + outCount = 0; + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to get count from database without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get count from database without an in-progress transaction") }; + } + + auto cursor = transaction->maybeOpenBackingStoreCursor(objectStoreIdentifier, indexIdentifier, range); + if (!cursor) { + LOG_ERROR("Cannot open cursor to populate indexes in database"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to populate indexes in database") }; + } + + while (cursor->advance(1)) + ++outCount; + + return { }; +} + +IDBError SQLiteIDBBackingStore::uncheckedGetKeyGeneratorValue(int64_t objectStoreID, uint64_t& outValue) +{ + auto* sql = cachedStatement(SQL::GetKeyGeneratorValue, ASCIILiteral("SELECT currentKey FROM KeyGenerators WHERE objectStoreID = ?;")); + if (!sql + || sql->bindInt64(1, objectStoreID) != SQLITE_OK) { + LOG_ERROR("Could not retrieve currentKey from KeyGenerators table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error getting current key generator value from database") }; + } + int result = sql->step(); + if (result != SQLITE_ROW) { + LOG_ERROR("Could not retreive key generator value for object store, but it should be there."); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Error finding current key generator value in database") }; + } + + int64_t value = sql->getColumnInt64(0); + if (value < 0) + return { IDBDatabaseException::ConstraintError, "Current key generator value from database is invalid" }; + + outValue = value; + return { }; +} + +IDBError SQLiteIDBBackingStore::uncheckedSetKeyGeneratorValue(int64_t objectStoreID, uint64_t value) +{ + auto* sql = cachedStatement(SQL::SetKeyGeneratorValue, ASCIILiteral("INSERT INTO KeyGenerators VALUES (?, ?);")); + if (!sql + || sql->bindInt64(1, objectStoreID) != SQLITE_OK + || sql->bindInt64(2, value) != SQLITE_OK + || sql->step() != SQLITE_DONE) { + LOG_ERROR("Could not update key generator value (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg()); + return { IDBDatabaseException::ConstraintError, "Error storing new key generator value in database" }; + } + + return { }; +} + +IDBError SQLiteIDBBackingStore::generateKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, uint64_t& generatedKey) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::generateKeyNumber"); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + // The IndexedDatabase spec defines the max key generator value as 2^53; + static uint64_t maxGeneratorValue = 0x20000000000000; + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to generate key in database without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to generate key in database without an in-progress transaction") }; + } + if (transaction->mode() == IDBTransactionMode::Readonly) { + LOG_ERROR("Attempt to generate key in a read-only transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to generate key in a read-only transaction") }; + } + + uint64_t currentValue; + auto error = uncheckedGetKeyGeneratorValue(objectStoreID, currentValue); + if (!error.isNull()) + return error; + + if (currentValue + 1 > maxGeneratorValue) + return { IDBDatabaseException::ConstraintError, "Cannot generate new key value over 2^53 for object store operation" }; + + generatedKey = currentValue + 1; + return uncheckedSetKeyGeneratorValue(objectStoreID, generatedKey); +} + +IDBError SQLiteIDBBackingStore::revertGeneratedKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, uint64_t newKeyNumber) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::revertGeneratedKeyNumber - object store %" PRIu64 ", reverted number %" PRIu64, objectStoreID, newKeyNumber); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to revert key generator value in database without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to revert key generator value in database without an in-progress transaction") }; + } + if (transaction->mode() == IDBTransactionMode::Readonly) { + LOG_ERROR("Attempt to revert key generator value in a read-only transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to revert key generator value in a read-only transaction") }; + } + + ASSERT(newKeyNumber); + return uncheckedSetKeyGeneratorValue(objectStoreID, newKeyNumber - 1); +} + +IDBError SQLiteIDBBackingStore::maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, double newKeyNumber) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::maybeUpdateKeyGeneratorNumber"); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to update key generator value in database without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to update key generator value in database without an in-progress transaction") }; + } + if (transaction->mode() == IDBTransactionMode::Readonly) { + LOG_ERROR("Attempt to update key generator value in a read-only transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to update key generator value in a read-only transaction") }; + } + + uint64_t currentValue; + auto error = uncheckedGetKeyGeneratorValue(objectStoreID, currentValue); + if (!error.isNull()) + return error; + + if (newKeyNumber <= currentValue) + return { }; + + uint64_t newKeyInteger(newKeyNumber); + if (newKeyInteger <= uint64_t(newKeyNumber)) + ++newKeyInteger; + + ASSERT(newKeyInteger > uint64_t(newKeyNumber)); + + return uncheckedSetKeyGeneratorValue(objectStoreID, newKeyInteger - 1); +} + +IDBError SQLiteIDBBackingStore::openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo& info, IDBGetResult& result) +{ + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* transaction = m_transactions.get(transactionIdentifier); + if (!transaction || !transaction->inProgress()) { + LOG_ERROR("Attempt to open a cursor in database without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to open a cursor in database without an in-progress transaction") }; + } + + auto* cursor = transaction->maybeOpenCursor(info); + if (!cursor) { + LOG_ERROR("Unable to open cursor"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to open cursor") }; + } + + m_cursors.set(cursor->identifier(), cursor); + + cursor->currentData(result); + return { }; +} + +IDBError SQLiteIDBBackingStore::iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData& data, IDBGetResult& result) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::iterateCursor"); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* cursor = m_cursors.get(cursorIdentifier); + if (!cursor) { + LOG_ERROR("Attempt to iterate a cursor that doesn't exist"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to iterate a cursor that doesn't exist") }; + } + + ASSERT_UNUSED(transactionIdentifier, cursor->transaction()->transactionIdentifier() == transactionIdentifier); + + if (!cursor->transaction() || !cursor->transaction()->inProgress()) { + LOG_ERROR("Attempt to iterate a cursor without an in-progress transaction"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to iterate a cursor without an in-progress transaction") }; + } + + auto key = data.keyData; + auto primaryKey = data.primaryKeyData; + auto count = data.count; + + if (key.isValid()) { + if (!cursor->iterate(key, primaryKey)) { + LOG_ERROR("Attempt to iterate cursor failed"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to iterate cursor failed") }; + } + } else { + ASSERT(!primaryKey.isValid()); + if (!count) + count = 1; + if (!cursor->advance(count)) { + LOG_ERROR("Attempt to advance cursor failed"); + return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to advance cursor failed") }; + } + } + + cursor->currentData(result); + return { }; +} + +bool SQLiteIDBBackingStore::prefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier) +{ + LOG(IndexedDB, "SQLiteIDBBackingStore::prefetchCursor"); + + ASSERT(m_sqliteDB); + ASSERT(m_sqliteDB->isOpen()); + + auto* cursor = m_cursors.get(cursorIdentifier); + if (!cursor || !cursor->transaction() || !cursor->transaction()->inProgress()) + return false; + + ASSERT_UNUSED(transactionIdentifier, cursor->transaction()->transactionIdentifier() == transactionIdentifier); + + return cursor->prefetch(); +} + +IDBObjectStoreInfo* SQLiteIDBBackingStore::infoForObjectStore(uint64_t objectStoreIdentifier) +{ + ASSERT(m_databaseInfo); + return m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); +} + +void SQLiteIDBBackingStore::deleteBackingStore() +{ + String dbFilename = fullDatabasePath(); + + LOG(IndexedDB, "SQLiteIDBBackingStore::deleteBackingStore deleting file '%s' on disk", dbFilename.utf8().data()); + + Vector blobFiles; + { + bool errored = true; + + if (m_sqliteDB) { + SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT fileName FROM BlobFiles;")); + if (sql.prepare() == SQLITE_OK) { + int result = sql.step(); + while (result == SQLITE_ROW) { + blobFiles.append(sql.getColumnText(0)); + result = sql.step(); + } + + if (result == SQLITE_DONE) + errored = false; + } + } + + if (errored) + LOG_ERROR("Error getting all blob filenames to be deleted"); + } + + String databaseDirectory = fullDatabaseDirectory(); + for (auto& file : blobFiles) { + String fullPath = pathByAppendingComponent(databaseDirectory, file); + if (!deleteFile(fullPath)) + LOG_ERROR("Error deleting blob file %s", fullPath.utf8().data()); + } + + if (m_sqliteDB) + closeSQLiteDB(); + + SQLiteFileSystem::deleteDatabaseFile(dbFilename); + SQLiteFileSystem::deleteEmptyDatabaseDirectory(fullDatabaseDirectory()); + SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_absoluteDatabaseDirectory); +} + +void SQLiteIDBBackingStore::unregisterCursor(SQLiteIDBCursor& cursor) +{ + ASSERT(m_cursors.contains(cursor.identifier())); + m_cursors.remove(cursor.identifier()); +} + +SQLiteStatement* SQLiteIDBBackingStore::cachedStatement(SQLiteIDBBackingStore::SQL sql, const char* statement) +{ + if (sql >= SQL::Count) { + LOG_ERROR("Invalid SQL statement ID passed to cachedStatement()"); + return nullptr; + } + + if (m_cachedStatements[static_cast(sql)]) { + if (m_cachedStatements[static_cast(sql)]->reset() == SQLITE_OK) + return m_cachedStatements[static_cast(sql)].get(); + m_cachedStatements[static_cast(sql)] = nullptr; + } + + if (m_sqliteDB) { + m_cachedStatements[static_cast(sql)] = std::make_unique(*m_sqliteDB, statement); + if (m_cachedStatements[static_cast(sql)]->prepare() != SQLITE_OK) + m_cachedStatements[static_cast(sql)] = nullptr; + } + + return m_cachedStatements[static_cast(sql)].get(); +} + +void SQLiteIDBBackingStore::closeSQLiteDB() +{ + for (size_t i = 0; i < static_cast(SQL::Count); ++i) + m_cachedStatements[i] = nullptr; + + if (m_sqliteDB) + m_sqliteDB->close(); + + m_sqliteDB = nullptr; +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h new file mode 100644 index 000000000..257efe8c1 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBBackingStore.h" +#include "IDBDatabaseIdentifier.h" +#include "IDBDatabaseInfo.h" +#include "IDBResourceIdentifier.h" +#include "SQLiteIDBTransaction.h" +#include +#include + +namespace WebCore { + +class IndexKey; +class SQLiteDatabase; +class SQLiteStatement; + +namespace IDBServer { + +class SQLiteIDBCursor; + +class SQLiteIDBBackingStore : public IDBBackingStore { +public: + SQLiteIDBBackingStore(const IDBDatabaseIdentifier&, const String& databaseRootDirectory, IDBBackingStoreTemporaryFileHandler&); + + ~SQLiteIDBBackingStore() final; + + IDBError getOrEstablishDatabaseInfo(IDBDatabaseInfo&) final; + + IDBError beginTransaction(const IDBTransactionInfo&) final; + IDBError abortTransaction(const IDBResourceIdentifier& transactionIdentifier) final; + IDBError commitTransaction(const IDBResourceIdentifier& transactionIdentifier) final; + IDBError createObjectStore(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&) final; + IDBError deleteObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) final; + IDBError renameObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName) final; + IDBError clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) final; + IDBError createIndex(const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo&) final; + IDBError deleteIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier) final; + IDBError renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) final; + IDBError keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData&, bool& keyExists) final; + IDBError deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&) final; + IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IDBValue&) final; + IDBError getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IDBGetRecordDataType, IDBGetResult& outValue) final; + IDBError getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue) final; + IDBError getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&, IDBGetResult& outValue) final; + IDBError getCount(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, uint64_t& outCount) final; + IDBError generateKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t& keyNumber) final; + IDBError revertGeneratedKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t keyNumber) final; + IDBError maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, double newKeyNumber) final; + IDBError openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&, IDBGetResult& outResult) final; + IDBError iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&, IDBGetResult& outResult) final; + bool prefetchCursor(const IDBResourceIdentifier&, const IDBResourceIdentifier&) final; + + IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) final; + void deleteBackingStore() final; + + bool supportsSimultaneousTransactions() final { return false; } + bool isEphemeral() final { return false; } + + void unregisterCursor(SQLiteIDBCursor&); + + String fullDatabaseDirectory() const; + + IDBBackingStoreTemporaryFileHandler& temporaryFileHandler() const { return m_temporaryFileHandler; } + + IDBError getBlobRecordsForObjectStoreRecord(int64_t objectStoreRecord, Vector& blobURLs, Vector& blobFilePaths); + + static String databaseNameFromEncodedFilename(const String&); + +private: + String filenameForDatabaseName() const; + String fullDatabasePath() const; + + bool ensureValidRecordsTable(); + bool ensureValidIndexRecordsTable(); + bool ensureValidIndexRecordsIndex(); + bool ensureValidBlobTables(); + std::unique_ptr createAndPopulateInitialDatabaseInfo(); + std::unique_ptr extractExistingDatabaseInfo(); + + IDBError deleteRecord(SQLiteIDBTransaction&, int64_t objectStoreID, const IDBKeyData&); + IDBError uncheckedGetKeyGeneratorValue(int64_t objectStoreID, uint64_t& outValue); + IDBError uncheckedSetKeyGeneratorValue(int64_t objectStoreID, uint64_t value); + + IDBError updateAllIndexesForAddRecord(const IDBObjectStoreInfo&, const IDBKeyData&, const ThreadSafeDataBuffer& value, int64_t recordID); + IDBError updateOneIndexForAddRecord(const IDBIndexInfo&, const IDBKeyData&, const ThreadSafeDataBuffer& value, int64_t recordID); + IDBError uncheckedPutIndexKey(const IDBIndexInfo&, const IDBKeyData& keyValue, const IndexKey&, int64_t recordID); + IDBError uncheckedPutIndexRecord(int64_t objectStoreID, int64_t indexID, const IDBKeyData& keyValue, const IDBKeyData& indexKey, int64_t recordID); + IDBError uncheckedHasIndexRecord(const IDBIndexInfo&, const IDBKeyData&, bool& hasRecord); + IDBError uncheckedGetIndexRecordForOneKey(int64_t indexeID, int64_t objectStoreID, IndexedDB::IndexRecordType, const IDBKeyData&, IDBGetResult&); + + IDBError deleteUnusedBlobFileRecords(SQLiteIDBTransaction&); + + IDBError getAllObjectStoreRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue); + IDBError getAllIndexRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue); + + void closeSQLiteDB(); + + enum class SQL : size_t { + CreateObjectStoreInfo, + CreateObjectStoreKeyGenerator, + DeleteObjectStoreInfo, + DeleteObjectStoreKeyGenerator, + DeleteObjectStoreRecords, + DeleteObjectStoreIndexInfo, + DeleteObjectStoreIndexRecords, + DeleteObjectStoreBlobRecords, + RenameObjectStore, + ClearObjectStoreRecords, + ClearObjectStoreIndexRecords, + CreateIndexInfo, + DeleteIndexInfo, + HasIndexRecord, + PutIndexRecord, + GetIndexRecordForOneKey, + DeleteIndexRecords, + RenameIndex, + KeyExistsInObjectStore, + GetUnusedBlobFilenames, + DeleteUnusedBlobs, + GetObjectStoreRecordID, + DeleteBlobRecord, + DeleteObjectStoreRecord, + DeleteObjectStoreIndexRecord, + AddObjectStoreRecord, + AddBlobRecord, + BlobFilenameForBlobURL, + AddBlobFilename, + GetBlobURL, + GetKeyGeneratorValue, + SetKeyGeneratorValue, + GetAllKeyRecordsLowerOpenUpperOpen, + GetAllKeyRecordsLowerOpenUpperClosed, + GetAllKeyRecordsLowerClosedUpperOpen, + GetAllKeyRecordsLowerClosedUpperClosed, + GetValueRecordsLowerOpenUpperOpen, + GetValueRecordsLowerOpenUpperClosed, + GetValueRecordsLowerClosedUpperOpen, + GetValueRecordsLowerClosedUpperClosed, + GetKeyRecordsLowerOpenUpperOpen, + GetKeyRecordsLowerOpenUpperClosed, + GetKeyRecordsLowerClosedUpperOpen, + GetKeyRecordsLowerClosedUpperClosed, + Count + }; + + SQLiteStatement* cachedStatement(SQL, const char*); + SQLiteStatement* cachedStatementForGetAllObjectStoreRecords(const IDBGetAllRecordsData&); + + std::unique_ptr m_cachedStatements[static_cast(SQL::Count)]; + + JSC::VM& vm(); + JSC::JSGlobalObject& globalObject(); + void initializeVM(); + + IDBDatabaseIdentifier m_identifier; + std::unique_ptr m_databaseInfo; + std::unique_ptr m_originalDatabaseInfoBeforeVersionChange; + + std::unique_ptr m_sqliteDB; + + HashMap> m_transactions; + HashMap m_cursors; + + String m_absoluteDatabaseDirectory; + + RefPtr m_vm; + JSC::Strong m_globalObject; + + IDBBackingStoreTemporaryFileHandler& m_temporaryFileHandler; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp new file mode 100644 index 000000000..0d3e82928 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp @@ -0,0 +1,580 @@ +/* + * Copyright (C) 2014, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SQLiteIDBCursor.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorInfo.h" +#include "IDBGetResult.h" +#include "IDBSerialization.h" +#include "Logging.h" +#include "SQLiteIDBBackingStore.h" +#include "SQLiteIDBTransaction.h" +#include "SQLiteStatement.h" +#include "SQLiteTransaction.h" +#include +#include + +namespace WebCore { +namespace IDBServer { + +static const size_t prefetchLimit = 8; + +std::unique_ptr SQLiteIDBCursor::maybeCreate(SQLiteIDBTransaction& transaction, const IDBCursorInfo& info) +{ + auto cursor = std::make_unique(transaction, info); + + if (!cursor->establishStatement()) + return nullptr; + + if (!cursor->advance(1)) + return nullptr; + + return cursor; +} + +std::unique_ptr SQLiteIDBCursor::maybeCreateBackingStoreCursor(SQLiteIDBTransaction& transaction, const uint64_t objectStoreID, const uint64_t indexID, const IDBKeyRangeData& range) +{ + auto cursor = std::make_unique(transaction, objectStoreID, indexID, range); + + if (!cursor->establishStatement()) + return nullptr; + + if (!cursor->advance(1)) + return nullptr; + + return cursor; +} + +SQLiteIDBCursor::SQLiteIDBCursor(SQLiteIDBTransaction& transaction, const IDBCursorInfo& info) + : m_transaction(&transaction) + , m_cursorIdentifier(info.identifier()) + , m_objectStoreID(info.objectStoreIdentifier()) + , m_indexID(info.cursorSource() == IndexedDB::CursorSource::Index ? info.sourceIdentifier() : IDBIndexInfo::InvalidId) + , m_cursorDirection(info.cursorDirection()) + , m_cursorType(info.cursorType()) + , m_keyRange(info.range()) +{ + ASSERT(m_objectStoreID); +} + +SQLiteIDBCursor::SQLiteIDBCursor(SQLiteIDBTransaction& transaction, const uint64_t objectStoreID, const uint64_t indexID, const IDBKeyRangeData& range) + : m_transaction(&transaction) + , m_cursorIdentifier(transaction.transactionIdentifier()) + , m_objectStoreID(objectStoreID) + , m_indexID(indexID ? indexID : IDBIndexInfo::InvalidId) + , m_cursorDirection(IndexedDB::CursorDirection::Next) + , m_cursorType(IndexedDB::CursorType::KeyAndValue) + , m_keyRange(range) + , m_backingStoreCursor(true) +{ + ASSERT(m_objectStoreID); +} + +SQLiteIDBCursor::~SQLiteIDBCursor() +{ + if (m_backingStoreCursor) + m_transaction->closeCursor(*this); +} + +void SQLiteIDBCursor::currentData(IDBGetResult& result) +{ + ASSERT(!m_fetchedRecords.isEmpty()); + + auto& currentRecord = m_fetchedRecords.first(); + if (currentRecord.completed) { + ASSERT(!currentRecord.errored); + result = { }; + return; + } + + result = { currentRecord.record.key, currentRecord.record.primaryKey, currentRecord.record.value ? *currentRecord.record.value : IDBValue() }; +} + +static String buildIndexStatement(const IDBKeyRangeData& keyRange, IndexedDB::CursorDirection cursorDirection) +{ + StringBuilder builder; + + builder.appendLiteral("SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND objectStoreID = ? AND key "); + if (!keyRange.lowerKey.isNull() && !keyRange.lowerOpen) + builder.appendLiteral(">="); + else + builder.append('>'); + + builder.appendLiteral(" CAST(? AS TEXT) AND key "); + if (!keyRange.upperKey.isNull() && !keyRange.upperOpen) + builder.appendLiteral("<="); + else + builder.append('<'); + + builder.appendLiteral(" CAST(? AS TEXT) ORDER BY key"); + if (cursorDirection == IndexedDB::CursorDirection::Prev || cursorDirection == IndexedDB::CursorDirection::Prevunique) + builder.appendLiteral(" DESC"); + + builder.appendLiteral(", value"); + if (cursorDirection == IndexedDB::CursorDirection::Prev) + builder.appendLiteral(" DESC"); + + builder.append(';'); + + return builder.toString(); +} + +static String buildObjectStoreStatement(const IDBKeyRangeData& keyRange, IndexedDB::CursorDirection cursorDirection) +{ + StringBuilder builder; + + builder.appendLiteral("SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key "); + + if (!keyRange.lowerKey.isNull() && !keyRange.lowerOpen) + builder.appendLiteral(">="); + else + builder.append('>'); + + builder.appendLiteral(" CAST(? AS TEXT) AND key "); + + if (!keyRange.upperKey.isNull() && !keyRange.upperOpen) + builder.appendLiteral("<="); + else + builder.append('<'); + + builder.appendLiteral(" CAST(? AS TEXT) ORDER BY key"); + + if (cursorDirection == IndexedDB::CursorDirection::Prev || cursorDirection == IndexedDB::CursorDirection::Prevunique) + builder.appendLiteral(" DESC"); + + builder.append(';'); + + return builder.toString(); +} + +bool SQLiteIDBCursor::establishStatement() +{ + ASSERT(!m_statement); + String sql; + + if (m_indexID != IDBIndexInfo::InvalidId) { + sql = buildIndexStatement(m_keyRange, m_cursorDirection); + m_boundID = m_indexID; + } else { + sql = buildObjectStoreStatement(m_keyRange, m_cursorDirection); + m_boundID = m_objectStoreID; + } + + m_currentLowerKey = m_keyRange.lowerKey.isNull() ? IDBKeyData::minimum() : m_keyRange.lowerKey; + m_currentUpperKey = m_keyRange.upperKey.isNull() ? IDBKeyData::maximum() : m_keyRange.upperKey; + + return createSQLiteStatement(sql); +} + +bool SQLiteIDBCursor::createSQLiteStatement(const String& sql) +{ + LOG(IndexedDB, "Creating cursor with SQL query: \"%s\"", sql.utf8().data()); + + ASSERT(!m_currentLowerKey.isNull()); + ASSERT(!m_currentUpperKey.isNull()); + ASSERT(m_transaction->sqliteTransaction()); + + m_statement = std::make_unique(m_transaction->sqliteTransaction()->database(), sql); + + if (m_statement->prepare() != SQLITE_OK) { + LOG_ERROR("Could not create cursor statement (prepare/id) - '%s'", m_transaction->sqliteTransaction()->database().lastErrorMsg()); + return false; + } + + return bindArguments(); +} + +void SQLiteIDBCursor::objectStoreRecordsChanged() +{ + if (m_statementNeedsReset) + return; + + // If ObjectStore or Index contents changed, we need to reset the statement and bind new parameters to it. + // This is to pick up any changes that might exist. + // We also need to throw away any fetched records as they may no longer be valid. + + m_statementNeedsReset = true; + ASSERT(!m_fetchedRecords.isEmpty()); + + if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::Nextunique) { + m_currentLowerKey = m_fetchedRecords.first().record.key; + if (!m_keyRange.lowerOpen) { + m_keyRange.lowerOpen = true; + m_keyRange.lowerKey = m_currentLowerKey; + m_statement = nullptr; + } + } else { + m_currentUpperKey = m_fetchedRecords.first().record.key; + if (!m_keyRange.upperOpen) { + m_keyRange.upperOpen = true; + m_keyRange.upperKey = m_currentUpperKey; + m_statement = nullptr; + } + } + + m_currentKeyForUniqueness = m_fetchedRecords.first().record.key; + + m_fetchedRecords.clear(); +} + +void SQLiteIDBCursor::resetAndRebindStatement() +{ + ASSERT(!m_currentLowerKey.isNull()); + ASSERT(!m_currentUpperKey.isNull()); + ASSERT(m_transaction->sqliteTransaction()); + ASSERT(m_statementNeedsReset); + + m_statementNeedsReset = false; + + if (!m_statement && !establishStatement()) { + LOG_ERROR("Unable to establish new statement for cursor iteration"); + return; + } + + if (m_statement->reset() != SQLITE_OK) { + LOG_ERROR("Could not reset cursor statement to respond to object store changes"); + return; + } + + bindArguments(); +} + +bool SQLiteIDBCursor::bindArguments() +{ + LOG(IndexedDB, "Cursor is binding lower key '%s' and upper key '%s'", m_currentLowerKey.loggingString().utf8().data(), m_currentUpperKey.loggingString().utf8().data()); + + int currentBindArgument = 1; + + if (m_statement->bindInt64(currentBindArgument++, m_boundID) != SQLITE_OK) { + LOG_ERROR("Could not bind id argument (bound ID)"); + return false; + } + + if (m_indexID != IDBIndexInfo::InvalidId && m_statement->bindInt64(currentBindArgument++, m_objectStoreID) != SQLITE_OK) { + LOG_ERROR("Could not bind object store id argument for an index cursor"); + return false; + } + + RefPtr buffer = serializeIDBKeyData(m_currentLowerKey); + if (m_statement->bindBlob(currentBindArgument++, buffer->data(), buffer->size()) != SQLITE_OK) { + LOG_ERROR("Could not create cursor statement (lower key)"); + return false; + } + + buffer = serializeIDBKeyData(m_currentUpperKey); + if (m_statement->bindBlob(currentBindArgument++, buffer->data(), buffer->size()) != SQLITE_OK) { + LOG_ERROR("Could not create cursor statement (upper key)"); + return false; + } + + return true; +} + +bool SQLiteIDBCursor::prefetch() +{ + LOG(IndexedDB, "SQLiteIDBCursor::prefetch() - Cursor already has %zu fetched records", m_fetchedRecords.size()); + + if (m_fetchedRecords.isEmpty() || m_fetchedRecords.size() >= prefetchLimit || m_fetchedRecords.last().isTerminalRecord()) + return false; + + m_currentKeyForUniqueness = m_fetchedRecords.last().record.key; + fetch(); + + return m_fetchedRecords.size() < prefetchLimit; +} + +bool SQLiteIDBCursor::advance(uint64_t count) +{ + LOG(IndexedDB, "SQLiteIDBCursor::advance() - Count %" PRIu64 ", %zu fetched records", count, m_fetchedRecords.size()); + ASSERT(count); + + if (!m_fetchedRecords.isEmpty() && m_fetchedRecords.first().isTerminalRecord()) { + LOG_ERROR("Attempt to advance a completed cursor"); + return false; + } + + if (!m_fetchedRecords.isEmpty()) + m_currentKeyForUniqueness = m_fetchedRecords.last().record.key; + + // Drop already-fetched records up to `count` to see if we've already fetched the record we're looking for. + bool hadCurrentRecord = !m_fetchedRecords.isEmpty(); + for (; count && !m_fetchedRecords.isEmpty(); --count) { + if (m_fetchedRecords.first().isTerminalRecord()) + break; + + m_fetchedRecords.removeFirst(); + } + + // If we still have any records left, the first record is our new current record. + if (!m_fetchedRecords.isEmpty()) + return true; + + ASSERT(m_fetchedRecords.isEmpty()); + + // If we started out with a current record, we burnt a count on removing it. + // Replace that count now. + if (hadCurrentRecord) + ++count; + + for (; count; --count) { + if (!m_fetchedRecords.isEmpty()) { + ASSERT(m_fetchedRecords.size() == 1); + m_currentKeyForUniqueness = m_fetchedRecords.first().record.key; + m_fetchedRecords.removeFirst(); + } + + if (!fetch()) + return false; + + ASSERT(!m_fetchedRecords.isEmpty()); + ASSERT(!m_fetchedRecords.first().errored); + if (m_fetchedRecords.first().completed) + break; + } + + return true; +} + +bool SQLiteIDBCursor::fetch() +{ + ASSERT(m_fetchedRecords.isEmpty() || !m_fetchedRecords.last().isTerminalRecord()); + + m_fetchedRecords.append({ }); + + bool isUnique = m_cursorDirection == IndexedDB::CursorDirection::Nextunique || m_cursorDirection == IndexedDB::CursorDirection::Prevunique; + if (!isUnique) + return fetchNextRecord(m_fetchedRecords.last()); + + while (!m_fetchedRecords.last().completed) { + if (!fetchNextRecord(m_fetchedRecords.last())) + return false; + + // If the new current key is different from the old current key, we're done. + if (m_currentKeyForUniqueness.compare(m_fetchedRecords.last().record.key)) + return true; + } + + return false; +} + +bool SQLiteIDBCursor::fetchNextRecord(SQLiteCursorRecord& record) +{ + if (m_statementNeedsReset) + resetAndRebindStatement(); + + FetchResult result; + do { + result = internalFetchNextRecord(record); + } while (result == FetchResult::ShouldFetchAgain); + + return result == FetchResult::Success; +} + +void SQLiteIDBCursor::markAsErrored(SQLiteCursorRecord& record) +{ + record.record = { }; + record.completed = true; + record.errored = true; + record.rowID = 0; +} + +SQLiteIDBCursor::FetchResult SQLiteIDBCursor::internalFetchNextRecord(SQLiteCursorRecord& record) +{ + ASSERT(m_transaction->sqliteTransaction()); + ASSERT(m_statement); + ASSERT(!m_fetchedRecords.isEmpty()); + ASSERT(!m_fetchedRecords.last().isTerminalRecord()); + + record.record.value = nullptr; + + int result = m_statement->step(); + if (result == SQLITE_DONE) { + // When a cursor reaches its end, that is indicated by having undefined keys/values + record = { }; + record.completed = true; + + return FetchResult::Success; + } + + if (result != SQLITE_ROW) { + LOG_ERROR("Error advancing cursor - (%i) %s", result, m_transaction->sqliteTransaction()->database().lastErrorMsg()); + markAsErrored(record); + return FetchResult::Failure; + } + + record.rowID = m_statement->getColumnInt64(0); + ASSERT(record.rowID); + + Vector keyData; + m_statement->getColumnBlobAsVector(1, keyData); + + if (!deserializeIDBKeyData(keyData.data(), keyData.size(), record.record.key)) { + LOG_ERROR("Unable to deserialize key data from database while advancing cursor"); + markAsErrored(record); + return FetchResult::Failure; + } + + m_statement->getColumnBlobAsVector(2, keyData); + + // The primaryKey of an ObjectStore cursor is the same as its key. + if (m_indexID == IDBIndexInfo::InvalidId) { + record.record.primaryKey = record.record.key; + + Vector blobURLs, blobFilePaths; + auto error = m_transaction->backingStore().getBlobRecordsForObjectStoreRecord(record.rowID, blobURLs, blobFilePaths); + if (!error.isNull()) { + LOG_ERROR("Unable to fetch blob records from database while advancing cursor"); + markAsErrored(record); + return FetchResult::Failure; + } + + if (m_cursorType == IndexedDB::CursorType::KeyAndValue) + record.record.value = std::make_unique(ThreadSafeDataBuffer::adoptVector(keyData), blobURLs, blobFilePaths); + } else { + if (!deserializeIDBKeyData(keyData.data(), keyData.size(), record.record.primaryKey)) { + LOG_ERROR("Unable to deserialize value data from database while advancing index cursor"); + markAsErrored(record); + return FetchResult::Failure; + } + + SQLiteStatement objectStoreStatement(m_statement->database(), "SELECT value FROM Records WHERE key = CAST(? AS TEXT) and objectStoreID = ?;"); + + if (objectStoreStatement.prepare() != SQLITE_OK + || objectStoreStatement.bindBlob(1, keyData.data(), keyData.size()) != SQLITE_OK + || objectStoreStatement.bindInt64(2, m_objectStoreID) != SQLITE_OK) { + LOG_ERROR("Could not create index cursor statement into object store records (%i) '%s'", m_statement->database().lastError(), m_statement->database().lastErrorMsg()); + markAsErrored(record); + return FetchResult::Failure; + } + + int result = objectStoreStatement.step(); + + if (result == SQLITE_ROW) { + objectStoreStatement.getColumnBlobAsVector(0, keyData); + record.record.value = std::make_unique(ThreadSafeDataBuffer::adoptVector(keyData)); + } else if (result == SQLITE_DONE) { + // This indicates that the record we're trying to retrieve has been removed from the object store. + // Skip over it. + return FetchResult::ShouldFetchAgain; + } else { + LOG_ERROR("Could not step index cursor statement into object store records (%i) '%s'", m_statement->database().lastError(), m_statement->database().lastErrorMsg()); + markAsErrored(record); + return FetchResult::Failure; + + } + } + + return FetchResult::Success; +} + +bool SQLiteIDBCursor::iterate(const IDBKeyData& targetKey, const IDBKeyData& targetPrimaryKey) +{ + ASSERT(m_transaction->sqliteTransaction()); + ASSERT(m_statement); + + bool result = advance(1); + ASSERT(!m_fetchedRecords.isEmpty()); + + // Iterating with no key is equivalent to advancing 1 step. + if (targetKey.isNull() || !result) + return result; + + while (!m_fetchedRecords.first().isTerminalRecord()) { + if (!result) + return false; + + // Search for the next key >= the target if the cursor is a Next cursor, or the next key <= if the cursor is a Previous cursor. + if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::Nextunique) { + if (m_fetchedRecords.first().record.key.compare(targetKey) >= 0) + break; + } else if (m_fetchedRecords.first().record.key.compare(targetKey) <= 0) + break; + + result = advance(1); + } + + if (targetPrimaryKey.isValid()) { + while (!m_fetchedRecords.first().isTerminalRecord() && !m_fetchedRecords.first().record.key.compare(targetKey)) { + if (!result) + return false; + + // Search for the next primary key >= the primary target if the cursor is a Next cursor, or the next key <= if the cursor is a Previous cursor. + if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::Nextunique) { + if (m_fetchedRecords.first().record.primaryKey.compare(targetPrimaryKey) >= 0) + break; + } else if (m_fetchedRecords.first().record.primaryKey.compare(targetPrimaryKey) <= 0) + break; + + result = advance(1); + } + } + + return result; +} + +const IDBKeyData& SQLiteIDBCursor::currentKey() const +{ + ASSERT(!m_fetchedRecords.isEmpty()); + return m_fetchedRecords.first().record.key; +} + +const IDBKeyData& SQLiteIDBCursor::currentPrimaryKey() const +{ + ASSERT(!m_fetchedRecords.isEmpty()); + return m_fetchedRecords.first().record.primaryKey; +} + +IDBValue* SQLiteIDBCursor::currentValue() const +{ + ASSERT(!m_fetchedRecords.isEmpty()); + return m_fetchedRecords.first().record.value.get(); +} + +bool SQLiteIDBCursor::didComplete() const +{ + ASSERT(!m_fetchedRecords.isEmpty()); + return m_fetchedRecords.first().completed; +} + +bool SQLiteIDBCursor::didError() const +{ + ASSERT(!m_fetchedRecords.isEmpty()); + return m_fetchedRecords.first().errored; +} + +int64_t SQLiteIDBCursor::currentRecordRowID() const +{ + ASSERT(!m_fetchedRecords.isEmpty()); + return m_fetchedRecords.first().rowID; +} + + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h new file mode 100644 index 000000000..4a673003b --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2014, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorRecord.h" +#include "IDBIndexInfo.h" +#include "IDBKeyData.h" +#include "IDBKeyRangeData.h" +#include "IDBResourceIdentifier.h" +#include "IDBValue.h" +#include "SQLiteStatement.h" +#include +#include + +namespace WebCore { + +class IDBCursorInfo; +class IDBGetResult; + +namespace IDBServer { + +class SQLiteIDBTransaction; + +class SQLiteIDBCursor { + WTF_MAKE_NONCOPYABLE(SQLiteIDBCursor); +public: + static std::unique_ptr maybeCreate(SQLiteIDBTransaction&, const IDBCursorInfo&); + static std::unique_ptr maybeCreateBackingStoreCursor(SQLiteIDBTransaction&, const uint64_t objectStoreIdentifier, const uint64_t indexIdentifier, const IDBKeyRangeData&); + + SQLiteIDBCursor(SQLiteIDBTransaction&, const IDBCursorInfo&); + SQLiteIDBCursor(SQLiteIDBTransaction&, uint64_t objectStoreID, uint64_t indexID, const IDBKeyRangeData&); + + ~SQLiteIDBCursor(); + + const IDBResourceIdentifier& identifier() const { return m_cursorIdentifier; } + SQLiteIDBTransaction* transaction() const { return m_transaction; } + + int64_t objectStoreID() const { return m_objectStoreID; } + int64_t currentRecordRowID() const; + + const IDBKeyData& currentKey() const; + const IDBKeyData& currentPrimaryKey() const; + IDBValue* currentValue() const; + + bool advance(uint64_t count); + bool iterate(const IDBKeyData& targetKey, const IDBKeyData& targetPrimaryKey); + bool prefetch(); + + bool didComplete() const; + bool didError() const; + + void objectStoreRecordsChanged(); + + void currentData(IDBGetResult&); + +private: + bool establishStatement(); + bool createSQLiteStatement(const String& sql); + bool bindArguments(); + + void resetAndRebindStatement(); + + enum class FetchResult { + Success, + Failure, + ShouldFetchAgain + }; + + bool fetch(); + + struct SQLiteCursorRecord { + IDBCursorRecord record; + bool completed { false }; + bool errored { false }; + int64_t rowID { 0 }; + bool isTerminalRecord() const { return completed || errored; } + }; + bool fetchNextRecord(SQLiteCursorRecord&); + FetchResult internalFetchNextRecord(SQLiteCursorRecord&); + + void markAsErrored(SQLiteCursorRecord&); + + SQLiteIDBTransaction* m_transaction; + IDBResourceIdentifier m_cursorIdentifier; + int64_t m_objectStoreID; + int64_t m_indexID { IDBIndexInfo::InvalidId }; + IndexedDB::CursorDirection m_cursorDirection { IndexedDB::CursorDirection::Next }; + IndexedDB::CursorType m_cursorType; + IDBKeyRangeData m_keyRange; + + IDBKeyData m_currentLowerKey; + IDBKeyData m_currentUpperKey; + + Deque m_fetchedRecords; + IDBKeyData m_currentKeyForUniqueness; + + std::unique_ptr m_statement; + bool m_statementNeedsReset { true }; + int64_t m_boundID { 0 }; + + bool m_backingStoreCursor { false }; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp new file mode 100644 index 000000000..fc1dd17dd --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include "SQLiteIDBTransaction.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "FileSystem.h" +#include "IDBCursorInfo.h" +#include "IndexedDB.h" +#include "Logging.h" +#include "SQLiteIDBBackingStore.h" +#include "SQLiteIDBCursor.h" +#include "SQLiteTransaction.h" + +namespace WebCore { +namespace IDBServer { + +SQLiteIDBTransaction::SQLiteIDBTransaction(SQLiteIDBBackingStore& backingStore, const IDBTransactionInfo& info) + : m_info(info) + , m_backingStore(backingStore) +{ +} + +SQLiteIDBTransaction::~SQLiteIDBTransaction() +{ + if (inProgress()) + m_sqliteTransaction->rollback(); + + // Explicitly clear cursors, as that also unregisters them from the backing store. + clearCursors(); +} + + +IDBError SQLiteIDBTransaction::begin(SQLiteDatabase& database) +{ + ASSERT(!m_sqliteTransaction); + + m_sqliteTransaction = std::make_unique(database, m_info.mode() == IDBTransactionMode::Readonly); + m_sqliteTransaction->begin(); + + if (m_sqliteTransaction->inProgress()) + return { }; + + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not start SQLite transaction in database backend") }; +} + +IDBError SQLiteIDBTransaction::commit() +{ + LOG(IndexedDB, "SQLiteIDBTransaction::commit"); + if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress()) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No SQLite transaction in progress to commit") }; + + m_sqliteTransaction->commit(); + + if (m_sqliteTransaction->inProgress()) + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to commit SQLite transaction in database backend") }; + + deleteBlobFilesIfNecessary(); + moveBlobFilesIfNecessary(); + + reset(); + return { }; +} + +void SQLiteIDBTransaction::moveBlobFilesIfNecessary() +{ + String databaseDirectory = m_backingStore.fullDatabaseDirectory(); + for (auto& entry : m_blobTemporaryAndStoredFilenames) { + m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry.first); + + if (!hardLinkOrCopyFile(entry.first, pathByAppendingComponent(databaseDirectory, entry.second))) + LOG_ERROR("Failed to link/copy temporary blob file '%s' to location '%s'", entry.first.utf8().data(), pathByAppendingComponent(databaseDirectory, entry.second).utf8().data()); + + m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry.first); + } + + m_blobTemporaryAndStoredFilenames.clear(); +} + +void SQLiteIDBTransaction::deleteBlobFilesIfNecessary() +{ + if (m_blobRemovedFilenames.isEmpty()) + return; + + String databaseDirectory = m_backingStore.fullDatabaseDirectory(); + for (auto& entry : m_blobRemovedFilenames) { + String fullPath = pathByAppendingComponent(databaseDirectory, entry); + m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(fullPath); + m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(fullPath); + } + + m_blobRemovedFilenames.clear(); +} + +IDBError SQLiteIDBTransaction::abort() +{ + for (auto& entry : m_blobTemporaryAndStoredFilenames) { + m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry.first); + m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry.first); + } + + m_blobTemporaryAndStoredFilenames.clear(); + + if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress()) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No SQLite transaction in progress to abort") }; + + m_sqliteTransaction->rollback(); + + if (m_sqliteTransaction->inProgress()) + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to abort SQLite transaction in database backend") }; + + reset(); + return { }; +} + +void SQLiteIDBTransaction::reset() +{ + m_sqliteTransaction = nullptr; + clearCursors(); + ASSERT(m_blobTemporaryAndStoredFilenames.isEmpty()); +} + +std::unique_ptr SQLiteIDBTransaction::maybeOpenBackingStoreCursor(uint64_t objectStoreID, uint64_t indexID, const IDBKeyRangeData& range) +{ + ASSERT(m_sqliteTransaction); + ASSERT(m_sqliteTransaction->inProgress()); + + auto cursor = SQLiteIDBCursor::maybeCreateBackingStoreCursor(*this, objectStoreID, indexID, range); + + if (cursor) + m_backingStoreCursors.add(cursor.get()); + + return cursor; +} + +SQLiteIDBCursor* SQLiteIDBTransaction::maybeOpenCursor(const IDBCursorInfo& info) +{ + ASSERT(m_sqliteTransaction); + if (!m_sqliteTransaction->inProgress()) + return nullptr; + + auto addResult = m_cursors.add(info.identifier(), SQLiteIDBCursor::maybeCreate(*this, info)); + + ASSERT(addResult.isNewEntry); + + // It is possible the cursor failed to create and we just stored a null value. + if (!addResult.iterator->value) { + m_cursors.remove(addResult.iterator); + return nullptr; + } + + return addResult.iterator->value.get(); +} + +void SQLiteIDBTransaction::closeCursor(SQLiteIDBCursor& cursor) +{ + auto backingStoreTake = m_backingStoreCursors.take(&cursor); + if (backingStoreTake) { + ASSERT(!m_cursors.contains(cursor.identifier())); + return; + } + + ASSERT(m_cursors.contains(cursor.identifier())); + + m_backingStore.unregisterCursor(cursor); + m_cursors.remove(cursor.identifier()); +} + +void SQLiteIDBTransaction::notifyCursorsOfChanges(int64_t objectStoreID) +{ + for (auto& i : m_cursors) { + if (i.value->objectStoreID() == objectStoreID) + i.value->objectStoreRecordsChanged(); + } + + for (auto* cursor : m_backingStoreCursors) { + if (cursor->objectStoreID() == objectStoreID) + cursor->objectStoreRecordsChanged(); + } +} + +void SQLiteIDBTransaction::clearCursors() +{ + for (auto& cursor : m_cursors.values()) + m_backingStore.unregisterCursor(*cursor); + + m_cursors.clear(); +} + +bool SQLiteIDBTransaction::inProgress() const +{ + return m_sqliteTransaction && m_sqliteTransaction->inProgress(); +} + +void SQLiteIDBTransaction::addBlobFile(const String& temporaryPath, const String& storedFilename) +{ + m_blobTemporaryAndStoredFilenames.append({ temporaryPath, storedFilename }); +} + +void SQLiteIDBTransaction::addRemovedBlobFile(const String& removedFilename) +{ + ASSERT(!m_blobRemovedFilenames.contains(removedFilename)); + m_blobRemovedFilenames.add(removedFilename); +} + + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h new file mode 100644 index 000000000..a2753d6c7 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBError.h" +#include "IDBResourceIdentifier.h" +#include "IDBTransactionInfo.h" +#include "IndexedDB.h" +#include +#include +#include + +namespace WebCore { + +class IDBCursorInfo; +class SQLiteDatabase; +class SQLiteTransaction; +struct IDBKeyRangeData; + +namespace IDBServer { + +class SQLiteIDBBackingStore; +class SQLiteIDBCursor; + +class SQLiteIDBTransaction { + WTF_MAKE_NONCOPYABLE(SQLiteIDBTransaction); +public: + SQLiteIDBTransaction(SQLiteIDBBackingStore&, const IDBTransactionInfo&); + ~SQLiteIDBTransaction(); + + const IDBResourceIdentifier& transactionIdentifier() const { return m_info.identifier(); } + + IDBError begin(SQLiteDatabase&); + IDBError commit(); + IDBError abort(); + + std::unique_ptr maybeOpenBackingStoreCursor(uint64_t objectStoreID, uint64_t indexID, const IDBKeyRangeData&); + SQLiteIDBCursor* maybeOpenCursor(const IDBCursorInfo&); + + void closeCursor(SQLiteIDBCursor&); + void notifyCursorsOfChanges(int64_t objectStoreID); + + IDBTransactionMode mode() const { return m_info.mode(); } + bool inProgress() const; + + SQLiteTransaction* sqliteTransaction() const { return m_sqliteTransaction.get(); } + SQLiteIDBBackingStore& backingStore() { return m_backingStore; } + + void addBlobFile(const String& temporaryPath, const String& storedFilename); + void addRemovedBlobFile(const String& removedFilename); + +private: + void clearCursors(); + void reset(); + + void moveBlobFilesIfNecessary(); + void deleteBlobFilesIfNecessary(); + + IDBTransactionInfo m_info; + + SQLiteIDBBackingStore& m_backingStore; + std::unique_ptr m_sqliteTransaction; + HashMap> m_cursors; + HashSet m_backingStoreCursors; + Vector> m_blobTemporaryAndStoredFilenames; + HashSet m_blobRemovedFilenames; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.cpp b/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.cpp new file mode 100644 index 000000000..403b4d4a0 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ServerOpenDBRequest.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBResultData.h" + +namespace WebCore { +namespace IDBServer { + +Ref ServerOpenDBRequest::create(IDBConnectionToClient& connection, const IDBRequestData& requestData) +{ + return adoptRef(*new ServerOpenDBRequest(connection, requestData)); +} + +ServerOpenDBRequest::ServerOpenDBRequest(IDBConnectionToClient& connection, const IDBRequestData& requestData) + : m_connection(connection) + , m_requestData(requestData) +{ +} + +bool ServerOpenDBRequest::isOpenRequest() const +{ + return m_requestData.isOpenRequest(); +} + +bool ServerOpenDBRequest::isDeleteRequest() const +{ + return m_requestData.isDeleteRequest(); +} + +void ServerOpenDBRequest::maybeNotifyRequestBlocked(uint64_t currentVersion) +{ + if (m_notifiedBlocked) + return; + + uint64_t requestedVersion = isOpenRequest() ? m_requestData.requestedVersion() : 0; + m_connection.notifyOpenDBRequestBlocked(m_requestData.requestIdentifier(), currentVersion, requestedVersion); + + m_notifiedBlocked = true; +} + +void ServerOpenDBRequest::notifyDidDeleteDatabase(const IDBDatabaseInfo& info) +{ + ASSERT(isDeleteRequest()); + + m_connection.didDeleteDatabase(IDBResultData::deleteDatabaseSuccess(m_requestData.requestIdentifier(), info)); +} + +void ServerOpenDBRequest::notifiedConnectionsOfVersionChange(HashSet&& connectionIdentifiers) +{ + ASSERT(!m_notifiedConnectionsOfVersionChange); + + m_notifiedConnectionsOfVersionChange = true; + m_connectionsPendingVersionChangeEvent = WTFMove(connectionIdentifiers); +} + +void ServerOpenDBRequest::connectionClosedOrFiredVersionChangeEvent(uint64_t connectionIdentifier) +{ + m_connectionsPendingVersionChangeEvent.remove(connectionIdentifier); +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.h b/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.h new file mode 100644 index 000000000..349c8f43b --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBConnectionToClient.h" +#include "IDBRequestData.h" +#include +#include +#include + +namespace WebCore { + +class IDBDatabaseInfo; + +namespace IDBServer { + +class ServerOpenDBRequest : public RefCounted { +public: + static Ref create(IDBConnectionToClient&, const IDBRequestData&); + + IDBConnectionToClient& connection() { return m_connection; } + const IDBRequestData& requestData() const { return m_requestData; } + + bool isOpenRequest() const; + bool isDeleteRequest() const; + + void maybeNotifyRequestBlocked(uint64_t currentVersion); + void notifyDidDeleteDatabase(const IDBDatabaseInfo&); + + uint64_t versionChangeID() const; + + void notifiedConnectionsOfVersionChange(HashSet&& connectionIdentifiers); + void connectionClosedOrFiredVersionChangeEvent(uint64_t connectionIdentifier); + bool hasConnectionsPendingVersionChangeEvent() const { return !m_connectionsPendingVersionChangeEvent.isEmpty(); } + bool hasNotifiedConnectionsOfVersionChange() const { return m_notifiedConnectionsOfVersionChange; } + + +private: + ServerOpenDBRequest(IDBConnectionToClient&, const IDBRequestData&); + + IDBConnectionToClient& m_connection; + IDBRequestData m_requestData; + + bool m_notifiedBlocked { false }; + + bool m_notifiedConnectionsOfVersionChange { false }; + HashSet m_connectionsPendingVersionChangeEvent; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp new file mode 100644 index 000000000..87863f9a3 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp @@ -0,0 +1,1908 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "UniqueIDBDatabase.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorInfo.h" +#include "IDBGetAllRecordsData.h" +#include "IDBGetAllResult.h" +#include "IDBGetRecordData.h" +#include "IDBIterateCursorData.h" +#include "IDBKeyRangeData.h" +#include "IDBResultData.h" +#include "IDBServer.h" +#include "IDBTransactionInfo.h" +#include "IDBValue.h" +#include "Logging.h" +#include "ScopeGuard.h" +#include "SerializedScriptValue.h" +#include "UniqueIDBDatabaseConnection.h" +#include +#include +#include +#include +#include +#include + +using namespace JSC; + +namespace WebCore { +namespace IDBServer { + +UniqueIDBDatabase::UniqueIDBDatabase(IDBServer& server, const IDBDatabaseIdentifier& identifier) + : m_server(server) + , m_identifier(identifier) + , m_operationAndTransactionTimer(*this, &UniqueIDBDatabase::operationAndTransactionTimerFired) +{ + LOG(IndexedDB, "UniqueIDBDatabase::UniqueIDBDatabase() (%p) %s", this, m_identifier.debugString().utf8().data()); +} + +UniqueIDBDatabase::~UniqueIDBDatabase() +{ + LOG(IndexedDB, "UniqueIDBDatabase::~UniqueIDBDatabase() (%p) %s", this, m_identifier.debugString().utf8().data()); + ASSERT(isMainThread()); + ASSERT(!hasAnyPendingCallbacks()); + ASSERT(!hasUnfinishedTransactions()); + ASSERT(m_pendingTransactions.isEmpty()); + ASSERT(m_openDatabaseConnections.isEmpty()); + ASSERT(m_clientClosePendingDatabaseConnections.isEmpty()); + ASSERT(m_serverClosePendingDatabaseConnections.isEmpty()); + ASSERT(!m_queuedTaskCount); +} + +const IDBDatabaseInfo& UniqueIDBDatabase::info() const +{ + RELEASE_ASSERT(m_databaseInfo); + return *m_databaseInfo; +} + +void UniqueIDBDatabase::openDatabaseConnection(IDBConnectionToClient& connection, const IDBRequestData& requestData) +{ + LOG(IndexedDB, "UniqueIDBDatabase::openDatabaseConnection"); + ASSERT(!m_hardClosedForUserDelete); + + m_pendingOpenDBRequests.add(ServerOpenDBRequest::create(connection, requestData)); + + // An open operation is already in progress, so we can't possibly handle this one yet. + if (m_isOpeningBackingStore) + return; + + handleDatabaseOperations(); +} + +bool UniqueIDBDatabase::hasAnyPendingCallbacks() const +{ + return !m_errorCallbacks.isEmpty() + || !m_keyDataCallbacks.isEmpty() + || !m_getResultCallbacks.isEmpty() + || !m_getAllResultsCallbacks.isEmpty() + || !m_countCallbacks.isEmpty(); +} + +bool UniqueIDBDatabase::isVersionChangeInProgress() +{ +#if !LOG_DISABLED + if (m_versionChangeTransaction) + ASSERT(m_versionChangeDatabaseConnection); +#endif + + return m_versionChangeDatabaseConnection; +} + +void UniqueIDBDatabase::performCurrentOpenOperation() +{ + LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentOpenOperation (%p)", this); + + ASSERT(m_currentOpenDBRequest); + ASSERT(m_currentOpenDBRequest->isOpenRequest()); + + if (!m_databaseInfo) { + if (!m_isOpeningBackingStore) { + m_isOpeningBackingStore = true; + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier)); + } + + return; + } + + // If we previously started a version change operation but were blocked by having open connections, + // we might now be unblocked. + if (m_versionChangeDatabaseConnection) { + if (!m_versionChangeTransaction && !hasAnyOpenConnections()) + startVersionChangeTransaction(); + return; + } + + // 3.3.1 Opening a database + // If requested version is undefined, then let requested version be 1 if db was created in the previous step, + // or the current version of db otherwise. + uint64_t requestedVersion = m_currentOpenDBRequest->requestData().requestedVersion(); + if (!requestedVersion) + requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1; + + // 3.3.1 Opening a database + // If the database version higher than the requested version, abort these steps and return a VersionError. + if (requestedVersion < m_databaseInfo->version()) { + auto result = IDBResultData::error(m_currentOpenDBRequest->requestData().requestIdentifier(), IDBError(IDBDatabaseException::VersionError)); + m_currentOpenDBRequest->connection().didOpenDatabase(result); + m_currentOpenDBRequest = nullptr; + + return; + } + + if (!m_backingStoreOpenError.isNull()) { + auto result = IDBResultData::error(m_currentOpenDBRequest->requestData().requestIdentifier(), m_backingStoreOpenError); + m_currentOpenDBRequest->connection().didOpenDatabase(result); + m_currentOpenDBRequest = nullptr; + + return; + } + + Ref connection = UniqueIDBDatabaseConnection::create(*this, *m_currentOpenDBRequest); + + if (requestedVersion == m_databaseInfo->version()) { + auto* rawConnection = &connection.get(); + addOpenDatabaseConnection(WTFMove(connection)); + + auto result = IDBResultData::openDatabaseSuccess(m_currentOpenDBRequest->requestData().requestIdentifier(), *rawConnection); + m_currentOpenDBRequest->connection().didOpenDatabase(result); + m_currentOpenDBRequest = nullptr; + + return; + } + + ASSERT(!m_versionChangeDatabaseConnection); + m_versionChangeDatabaseConnection = WTFMove(connection); + + // 3.3.7 "versionchange" transaction steps + // If there's no other open connections to this database, the version change process can begin immediately. + if (!hasAnyOpenConnections()) { + startVersionChangeTransaction(); + return; + } + + // Otherwise we have to notify all those open connections and wait for them to close. + maybeNotifyConnectionsOfVersionChange(); +} + +void UniqueIDBDatabase::performCurrentDeleteOperation() +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentDeleteOperation - %s", m_identifier.debugString().utf8().data()); + + ASSERT(m_currentOpenDBRequest); + ASSERT(m_currentOpenDBRequest->isDeleteRequest()); + + if (m_deleteBackingStoreInProgress) + return; + + if (hasAnyOpenConnections()) { + maybeNotifyConnectionsOfVersionChange(); + return; + } + + if (hasUnfinishedTransactions()) + return; + + ASSERT(!hasAnyPendingCallbacks()); + ASSERT(m_pendingTransactions.isEmpty()); + ASSERT(m_openDatabaseConnections.isEmpty()); + + // It's possible to have multiple delete requests queued up in a row. + // In that scenario only the first request will actually have to delete the database. + // Subsequent requests can immediately notify their completion. + + if (!m_deleteBackingStoreInProgress) { + if (!m_databaseInfo && m_mostRecentDeletedDatabaseInfo) + didDeleteBackingStore(0); + else { + m_deleteBackingStoreInProgress = true; + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::deleteBackingStore, m_identifier)); + } + } +} + +void UniqueIDBDatabase::deleteBackingStore(const IDBDatabaseIdentifier& identifier) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::deleteBackingStore"); + + uint64_t deletedVersion = 0; + + if (m_backingStore) { + m_backingStore->deleteBackingStore(); + m_backingStore = nullptr; + m_backingStoreSupportsSimultaneousTransactions = false; + m_backingStoreIsEphemeral = false; + } else { + auto backingStore = m_server.createBackingStore(identifier); + + IDBDatabaseInfo databaseInfo; + auto error = backingStore->getOrEstablishDatabaseInfo(databaseInfo); + if (!error.isNull()) + LOG_ERROR("Error getting database info from database %s that we are trying to delete", identifier.debugString().utf8().data()); + + deletedVersion = databaseInfo.version(); + backingStore->deleteBackingStore(); + } + + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didDeleteBackingStore, deletedVersion)); +} + +void UniqueIDBDatabase::performUnconditionalDeleteBackingStore() +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performUnconditionalDeleteBackingStore"); + + if (!m_backingStore) + return; + + m_backingStore->deleteBackingStore(); + m_backingStore = nullptr; + m_backingStoreSupportsSimultaneousTransactions = false; + m_backingStoreIsEphemeral = false; +} + +void UniqueIDBDatabase::didDeleteBackingStore(uint64_t deletedVersion) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didDeleteBackingStore"); + + ASSERT(!hasAnyPendingCallbacks()); + ASSERT(!hasUnfinishedTransactions()); + ASSERT(m_pendingTransactions.isEmpty()); + ASSERT(m_openDatabaseConnections.isEmpty()); + + // It's possible that the openDBRequest was cancelled from client-side after the delete was already dispatched to the backingstore. + // So it's okay if we don't have a currentOpenDBRequest, but if we do it has to be a deleteRequest. + ASSERT(!m_currentOpenDBRequest || m_currentOpenDBRequest->isDeleteRequest()); + + if (m_databaseInfo) + m_mostRecentDeletedDatabaseInfo = WTFMove(m_databaseInfo); + + // If this UniqueIDBDatabase was brought into existence for the purpose of deleting the file on disk, + // we won't have a m_mostRecentDeletedDatabaseInfo. In that case, we'll manufacture one using the + // passed in deletedVersion argument. + if (!m_mostRecentDeletedDatabaseInfo) + m_mostRecentDeletedDatabaseInfo = std::make_unique(m_identifier.databaseName(), deletedVersion); + + if (m_currentOpenDBRequest) { + m_currentOpenDBRequest->notifyDidDeleteDatabase(*m_mostRecentDeletedDatabaseInfo); + m_currentOpenDBRequest = nullptr; + } + + m_deleteBackingStoreInProgress = false; + + if (m_clientClosePendingDatabaseConnections.isEmpty() && m_pendingOpenDBRequests.isEmpty()) { + m_server.closeUniqueIDBDatabase(*this); + return; + } + + invokeOperationAndTransactionTimer(); +} + +void UniqueIDBDatabase::didPerformUnconditionalDeleteBackingStore() +{ + // This function is a placeholder so the database thread can message back to the main thread. + ASSERT(m_hardClosedForUserDelete); +} + +void UniqueIDBDatabase::handleDatabaseOperations() +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::handleDatabaseOperations - There are %u pending", m_pendingOpenDBRequests.size()); + ASSERT(!m_hardClosedForUserDelete); + + if (m_deleteBackingStoreInProgress) + return; + + if (m_versionChangeDatabaseConnection || m_versionChangeTransaction || m_currentOpenDBRequest) { + // We can't start any new open-database operations right now, but we might be able to start handling a delete operation. + if (!m_currentOpenDBRequest && !m_pendingOpenDBRequests.isEmpty() && m_pendingOpenDBRequests.first()->isDeleteRequest()) + m_currentOpenDBRequest = m_pendingOpenDBRequests.takeFirst(); + + // Some operations (such as the first open operation after a delete) require multiple passes to completely handle + if (m_currentOpenDBRequest) + handleCurrentOperation(); + + return; + } + + if (m_pendingOpenDBRequests.isEmpty()) + return; + + m_currentOpenDBRequest = m_pendingOpenDBRequests.takeFirst(); + LOG(IndexedDB, "UniqueIDBDatabase::handleDatabaseOperations - Popped an operation, now there are %u pending", m_pendingOpenDBRequests.size()); + + handleCurrentOperation(); +} + +void UniqueIDBDatabase::handleCurrentOperation() +{ + LOG(IndexedDB, "(main) UniqueIDBDatabase::handleCurrentOperation"); + ASSERT(!m_hardClosedForUserDelete); + ASSERT(m_currentOpenDBRequest); + + RefPtr protectedThis(this); + + if (m_currentOpenDBRequest->isOpenRequest()) + performCurrentOpenOperation(); + else if (m_currentOpenDBRequest->isDeleteRequest()) + performCurrentDeleteOperation(); + else + ASSERT_NOT_REACHED(); + + if (!m_currentOpenDBRequest) + invokeOperationAndTransactionTimer(); +} + +bool UniqueIDBDatabase::hasAnyOpenConnections() const +{ + return !m_openDatabaseConnections.isEmpty(); +} + +bool UniqueIDBDatabase::allConnectionsAreClosedOrClosing() const +{ + for (auto& connection : m_openDatabaseConnections) { + if (!connection->connectionIsClosing()) + return false; + } + + return true; +} + +static uint64_t generateUniqueCallbackIdentifier() +{ + ASSERT(isMainThread()); + static uint64_t currentID = 0; + return ++currentID; +} + +uint64_t UniqueIDBDatabase::storeCallbackOrFireError(ErrorCallback callback) +{ + if (m_hardClosedForUserDelete) { + callback(IDBError::userDeleteError()); + return 0; + } + + uint64_t identifier = generateUniqueCallbackIdentifier(); + ASSERT(!m_errorCallbacks.contains(identifier)); + m_errorCallbacks.add(identifier, callback); + return identifier; +} + +uint64_t UniqueIDBDatabase::storeCallbackOrFireError(KeyDataCallback callback) +{ + if (m_hardClosedForUserDelete) { + callback(IDBError::userDeleteError(), { }); + return 0; + } + + uint64_t identifier = generateUniqueCallbackIdentifier(); + ASSERT(!m_keyDataCallbacks.contains(identifier)); + m_keyDataCallbacks.add(identifier, callback); + return identifier; +} + +uint64_t UniqueIDBDatabase::storeCallbackOrFireError(GetResultCallback callback) +{ + if (m_hardClosedForUserDelete) { + callback(IDBError::userDeleteError(), { }); + return 0; + } + + uint64_t identifier = generateUniqueCallbackIdentifier(); + ASSERT(!m_getResultCallbacks.contains(identifier)); + m_getResultCallbacks.add(identifier, callback); + return identifier; +} + +uint64_t UniqueIDBDatabase::storeCallbackOrFireError(GetAllResultsCallback callback) +{ + if (m_hardClosedForUserDelete) { + callback(IDBError::userDeleteError(), { }); + return 0; + } + + uint64_t identifier = generateUniqueCallbackIdentifier(); + ASSERT(!m_getAllResultsCallbacks.contains(identifier)); + m_getAllResultsCallbacks.add(identifier, callback); + return identifier; +} + +uint64_t UniqueIDBDatabase::storeCallbackOrFireError(CountCallback callback) +{ + if (m_hardClosedForUserDelete) { + callback(IDBError::userDeleteError(), 0); + return 0; + } + + uint64_t identifier = generateUniqueCallbackIdentifier(); + ASSERT(!m_countCallbacks.contains(identifier)); + m_countCallbacks.add(identifier, callback); + return identifier; +} + +void UniqueIDBDatabase::handleDelete(IDBConnectionToClient& connection, const IDBRequestData& requestData) +{ + LOG(IndexedDB, "(main) UniqueIDBDatabase::handleDelete"); + ASSERT(!m_hardClosedForUserDelete); + + m_pendingOpenDBRequests.add(ServerOpenDBRequest::create(connection, requestData)); + handleDatabaseOperations(); +} + +void UniqueIDBDatabase::startVersionChangeTransaction() +{ + LOG(IndexedDB, "(main) UniqueIDBDatabase::startVersionChangeTransaction"); + + ASSERT(!m_versionChangeTransaction); + ASSERT(m_currentOpenDBRequest); + ASSERT(m_currentOpenDBRequest->isOpenRequest()); + ASSERT(m_versionChangeDatabaseConnection); + + auto operation = WTFMove(m_currentOpenDBRequest); + + uint64_t requestedVersion = operation->requestData().requestedVersion(); + if (!requestedVersion) + requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1; + + addOpenDatabaseConnection(*m_versionChangeDatabaseConnection); + + m_versionChangeTransaction = &m_versionChangeDatabaseConnection->createVersionChangeTransaction(requestedVersion); + m_databaseInfo->setVersion(requestedVersion); + + m_inProgressTransactions.set(m_versionChangeTransaction->info().identifier(), m_versionChangeTransaction); + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::beginTransactionInBackingStore, m_versionChangeTransaction->info())); + + auto result = IDBResultData::openDatabaseUpgradeNeeded(operation->requestData().requestIdentifier(), *m_versionChangeTransaction); + operation->connection().didOpenDatabase(result); +} + +void UniqueIDBDatabase::beginTransactionInBackingStore(const IDBTransactionInfo& info) +{ + LOG(IndexedDB, "(db) UniqueIDBDatabase::beginTransactionInBackingStore"); + m_backingStore->beginTransaction(info); +} + +void UniqueIDBDatabase::maybeNotifyConnectionsOfVersionChange() +{ + ASSERT(m_currentOpenDBRequest); + + if (m_currentOpenDBRequest->hasNotifiedConnectionsOfVersionChange()) + return; + + uint64_t newVersion = m_currentOpenDBRequest->isOpenRequest() ? m_currentOpenDBRequest->requestData().requestedVersion() : 0; + auto requestIdentifier = m_currentOpenDBRequest->requestData().requestIdentifier(); + + LOG(IndexedDB, "(main) UniqueIDBDatabase::notifyConnectionsOfVersionChange - %" PRIu64, newVersion); + + // 3.3.7 "versionchange" transaction steps + // Fire a versionchange event at each connection in m_openDatabaseConnections that is open. + // The event must not be fired on connections which has the closePending flag set. + HashSet connectionIdentifiers; + for (auto connection : m_openDatabaseConnections) { + if (connection->closePending()) + continue; + + connection->fireVersionChangeEvent(requestIdentifier, newVersion); + connectionIdentifiers.add(connection->identifier()); + } + + if (!connectionIdentifiers.isEmpty()) + m_currentOpenDBRequest->notifiedConnectionsOfVersionChange(WTFMove(connectionIdentifiers)); + else + m_currentOpenDBRequest->maybeNotifyRequestBlocked(m_databaseInfo->version()); +} + +void UniqueIDBDatabase::notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(uint64_t connectionIdentifier) +{ + LOG(IndexedDB, "UniqueIDBDatabase::notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent - %" PRIu64, connectionIdentifier); + + ASSERT(m_currentOpenDBRequest); + + m_currentOpenDBRequest->connectionClosedOrFiredVersionChangeEvent(connectionIdentifier); + + if (m_currentOpenDBRequest->hasConnectionsPendingVersionChangeEvent()) + return; + + if (!hasAnyOpenConnections() || allConnectionsAreClosedOrClosing()) { + invokeOperationAndTransactionTimer(); + return; + } + + // Since all open connections have fired their version change events but not all of them have closed, + // this request is officially blocked. + m_currentOpenDBRequest->maybeNotifyRequestBlocked(m_databaseInfo->version()); +} + +void UniqueIDBDatabase::didFireVersionChangeEvent(UniqueIDBDatabaseConnection& connection, const IDBResourceIdentifier& requestIdentifier) +{ + LOG(IndexedDB, "UniqueIDBDatabase::didFireVersionChangeEvent"); + + if (!m_currentOpenDBRequest) + return; + + ASSERT_UNUSED(requestIdentifier, m_currentOpenDBRequest->requestData().requestIdentifier() == requestIdentifier); + + notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(connection.identifier()); +} + +void UniqueIDBDatabase::openDBRequestCancelled(const IDBResourceIdentifier& requestIdentifier) +{ + LOG(IndexedDB, "UniqueIDBDatabase::openDBRequestCancelled - %s", requestIdentifier.loggingString().utf8().data()); + + if (m_currentOpenDBRequest && m_currentOpenDBRequest->requestData().requestIdentifier() == requestIdentifier) + m_currentOpenDBRequest = nullptr; + + if (m_versionChangeDatabaseConnection && m_versionChangeDatabaseConnection->openRequestIdentifier() == requestIdentifier) { + ASSERT(!m_versionChangeTransaction || m_versionChangeTransaction->databaseConnection().openRequestIdentifier() == requestIdentifier); + ASSERT(!m_versionChangeTransaction || &m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection); + + connectionClosedFromClient(*m_versionChangeDatabaseConnection); + } + + for (auto& request : m_pendingOpenDBRequests) { + if (request->requestData().requestIdentifier() == requestIdentifier) { + m_pendingOpenDBRequests.remove(request); + return; + } + } +} + +void UniqueIDBDatabase::addOpenDatabaseConnection(Ref&& connection) +{ + ASSERT(!m_openDatabaseConnections.contains(&connection.get())); + m_openDatabaseConnections.add(adoptRef(connection.leakRef())); +} + +void UniqueIDBDatabase::openBackingStore(const IDBDatabaseIdentifier& identifier) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::openBackingStore (%p)", this); + + ASSERT(!m_backingStore); + m_backingStore = m_server.createBackingStore(identifier); + m_backingStoreSupportsSimultaneousTransactions = m_backingStore->supportsSimultaneousTransactions(); + m_backingStoreIsEphemeral = m_backingStore->isEphemeral(); + + IDBDatabaseInfo databaseInfo; + auto error = m_backingStore->getOrEstablishDatabaseInfo(databaseInfo); + + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo, error)); +} + +void UniqueIDBDatabase::didOpenBackingStore(const IDBDatabaseInfo& info, const IDBError& error) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didOpenBackingStore"); + + m_databaseInfo = std::make_unique(info); + m_backingStoreOpenError = error; + + ASSERT(m_isOpeningBackingStore); + m_isOpeningBackingStore = false; + + handleDatabaseOperations(); +} + +void UniqueIDBDatabase::createObjectStore(UniqueIDBDatabaseTransaction& transaction, const IDBObjectStoreInfo& info, ErrorCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::createObjectStore"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCreateObjectStore, callbackID, transaction.info().identifier(), info)); +} + +void UniqueIDBDatabase::performCreateObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& info) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performCreateObjectStore"); + + ASSERT(m_backingStore); + m_backingStore->createObjectStore(transactionIdentifier, info); + + IDBError error; + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCreateObjectStore, callbackIdentifier, error, info)); +} + +void UniqueIDBDatabase::didPerformCreateObjectStore(uint64_t callbackIdentifier, const IDBError& error, const IDBObjectStoreInfo& info) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCreateObjectStore"); + + if (error.isNull()) + m_databaseInfo->addExistingObjectStore(info); + + performErrorCallback(callbackIdentifier, error); +} + +void UniqueIDBDatabase::deleteObjectStore(UniqueIDBDatabaseTransaction& transaction, const String& objectStoreName, ErrorCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteObjectStore"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + + auto* info = m_databaseInfo->infoForExistingObjectStore(objectStoreName); + if (!info) { + performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete non-existant object store") }); + return; + } + + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteObjectStore, callbackID, transaction.info().identifier(), info->identifier())); +} + +void UniqueIDBDatabase::performDeleteObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteObjectStore"); + + ASSERT(m_backingStore); + m_backingStore->deleteObjectStore(transactionIdentifier, objectStoreIdentifier); + + IDBError error; + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteObjectStore, callbackIdentifier, error, objectStoreIdentifier)); +} + +void UniqueIDBDatabase::didPerformDeleteObjectStore(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteObjectStore"); + + if (error.isNull()) + m_databaseInfo->deleteObjectStore(objectStoreIdentifier); + + performErrorCallback(callbackIdentifier, error); +} + +void UniqueIDBDatabase::renameObjectStore(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, const String& newName, ErrorCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::renameObjectStore"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + + auto* info = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); + if (!info) { + performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename non-existant object store") }); + return; + } + + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performRenameObjectStore, callbackID, transaction.info().identifier(), objectStoreIdentifier, newName)); +} + +void UniqueIDBDatabase::performRenameObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performRenameObjectStore"); + + ASSERT(m_backingStore); + m_backingStore->renameObjectStore(transactionIdentifier, objectStoreIdentifier, newName); + + IDBError error; + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformRenameObjectStore, callbackIdentifier, error, objectStoreIdentifier, newName)); +} + +void UniqueIDBDatabase::didPerformRenameObjectStore(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier, const String& newName) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformRenameObjectStore"); + + if (error.isNull()) + m_databaseInfo->renameObjectStore(objectStoreIdentifier, newName); + + performErrorCallback(callbackIdentifier, error); +} + +void UniqueIDBDatabase::clearObjectStore(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, ErrorCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::clearObjectStore"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performClearObjectStore, callbackID, transaction.info().identifier(), objectStoreIdentifier)); +} + +void UniqueIDBDatabase::performClearObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performClearObjectStore"); + + ASSERT(m_backingStore); + m_backingStore->clearObjectStore(transactionIdentifier, objectStoreIdentifier); + + IDBError error; + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformClearObjectStore, callbackIdentifier, error)); +} + +void UniqueIDBDatabase::didPerformClearObjectStore(uint64_t callbackIdentifier, const IDBError& error) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformClearObjectStore"); + + performErrorCallback(callbackIdentifier, error); +} + +void UniqueIDBDatabase::createIndex(UniqueIDBDatabaseTransaction& transaction, const IDBIndexInfo& info, ErrorCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::createIndex"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCreateIndex, callbackID, transaction.info().identifier(), info)); +} + +void UniqueIDBDatabase::performCreateIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo& info) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performCreateIndex"); + + ASSERT(m_backingStore); + IDBError error = m_backingStore->createIndex(transactionIdentifier, info); + + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCreateIndex, callbackIdentifier, error, info)); +} + +void UniqueIDBDatabase::didPerformCreateIndex(uint64_t callbackIdentifier, const IDBError& error, const IDBIndexInfo& info) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCreateIndex"); + + if (error.isNull()) { + ASSERT(m_databaseInfo); + auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(info.objectStoreIdentifier()); + ASSERT(objectStoreInfo); + objectStoreInfo->addExistingIndex(info); + } + + performErrorCallback(callbackIdentifier, error); +} + +void UniqueIDBDatabase::deleteIndex(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, const String& indexName, ErrorCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteIndex"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + + auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); + if (!objectStoreInfo) { + performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete index from non-existant object store") }); + return; + } + + auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexName); + if (!indexInfo) { + performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete non-existant index") }); + return; + } + + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteIndex, callbackID, transaction.info().identifier(), objectStoreIdentifier, indexInfo->identifier())); +} + +void UniqueIDBDatabase::performDeleteIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const uint64_t indexIdentifier) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteIndex"); + + ASSERT(m_backingStore); + m_backingStore->deleteIndex(transactionIdentifier, objectStoreIdentifier, indexIdentifier); + + IDBError error; + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteIndex, callbackIdentifier, error, objectStoreIdentifier, indexIdentifier)); +} + +void UniqueIDBDatabase::didPerformDeleteIndex(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier, uint64_t indexIdentifier) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteIndex"); + + if (error.isNull()) { + auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); + if (objectStoreInfo) + objectStoreInfo->deleteIndex(indexIdentifier); + } + + performErrorCallback(callbackIdentifier, error); +} + +void UniqueIDBDatabase::renameIndex(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName, ErrorCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::renameIndex"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + + auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); + if (!objectStoreInfo) { + performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename index in non-existant object store") }); + return; + } + + auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier); + if (!indexInfo) { + performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename non-existant index") }); + return; + } + + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performRenameIndex, callbackID, transaction.info().identifier(), objectStoreIdentifier, indexIdentifier, newName)); +} + +void UniqueIDBDatabase::performRenameIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performRenameIndex"); + + ASSERT(m_backingStore); + m_backingStore->renameIndex(transactionIdentifier, objectStoreIdentifier, indexIdentifier, newName); + + IDBError error; + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformRenameIndex, callbackIdentifier, error, objectStoreIdentifier, indexIdentifier, newName)); +} + +void UniqueIDBDatabase::didPerformRenameIndex(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformRenameIndex"); + + if (error.isNull()) { + auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); + ASSERT(objectStoreInfo); + if (objectStoreInfo) { + auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier); + ASSERT(indexInfo); + indexInfo->rename(newName); + } + } + + performErrorCallback(callbackIdentifier, error); +} + +void UniqueIDBDatabase::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode, KeyDataCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::putOrAdd"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPutOrAdd, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), keyData, value, overwriteMode)); +} + +VM& UniqueIDBDatabase::databaseThreadVM() +{ + ASSERT(!isMainThread()); + static VM* vm = &VM::create().leakRef(); + return *vm; +} + +ExecState& UniqueIDBDatabase::databaseThreadExecState() +{ + ASSERT(!isMainThread()); + + static NeverDestroyed> globalObject(databaseThreadVM(), JSGlobalObject::create(databaseThreadVM(), JSGlobalObject::createStructure(databaseThreadVM(), jsNull()))); + + RELEASE_ASSERT(globalObject.get()->globalExec()); + return *globalObject.get()->globalExec(); +} + +void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData& keyData, const IDBValue& originalRecordValue, IndexedDB::ObjectStoreOverwriteMode overwriteMode) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performPutOrAdd"); + + ASSERT(m_backingStore); + ASSERT(objectStoreIdentifier); + + IDBKeyData usedKey; + IDBError error; + + auto* objectStoreInfo = m_backingStore->infoForObjectStore(objectStoreIdentifier); + if (!objectStoreInfo) { + error = IDBError(IDBDatabaseException::InvalidStateError, ASCIILiteral("Object store cannot be found in the backing store")); + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); + return; + } + + bool usedKeyIsGenerated = false; + ScopeGuard generatedKeyResetter; + if (objectStoreInfo->autoIncrement() && !keyData.isValid()) { + uint64_t keyNumber; + error = m_backingStore->generateKeyNumber(transactionIdentifier, objectStoreIdentifier, keyNumber); + if (!error.isNull()) { + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); + return; + } + + usedKey.setNumberValue(keyNumber); + usedKeyIsGenerated = true; + generatedKeyResetter.enable([this, transactionIdentifier, objectStoreIdentifier, keyNumber]() { + m_backingStore->revertGeneratedKeyNumber(transactionIdentifier, objectStoreIdentifier, keyNumber); + }); + } else + usedKey = keyData; + + if (overwriteMode == IndexedDB::ObjectStoreOverwriteMode::NoOverwrite) { + bool keyExists; + error = m_backingStore->keyExistsInObjectStore(transactionIdentifier, objectStoreIdentifier, usedKey, keyExists); + if (error.isNull() && keyExists) + error = IDBError(IDBDatabaseException::ConstraintError, ASCIILiteral("Key already exists in the object store")); + + if (!error.isNull()) { + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); + return; + } + } + + // 3.4.1.2 Object Store Storage Operation + // If ObjectStore has a key path and the key is autogenerated, then inject the key into the value + // using steps to assign a key to a value using a key path. + ThreadSafeDataBuffer injectedRecordValue; + if (usedKeyIsGenerated && objectStoreInfo->keyPath()) { + VM& vm = databaseThreadVM(); + JSLockHolder locker(vm); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto value = deserializeIDBValueToJSValue(databaseThreadExecState(), originalRecordValue.data()); + if (value.isUndefined()) { + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, IDBError(IDBDatabaseException::ConstraintError, ASCIILiteral("Unable to deserialize record value for record key injection")), usedKey)); + return; + } + + if (!injectIDBKeyIntoScriptValue(databaseThreadExecState(), usedKey, value, objectStoreInfo->keyPath().value())) { + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, IDBError(IDBDatabaseException::ConstraintError, ASCIILiteral("Unable to inject record key into record value")), usedKey)); + return; + } + + auto serializedValue = SerializedScriptValue::create(databaseThreadExecState(), value); + if (UNLIKELY(scope.exception())) { + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, IDBError(IDBDatabaseException::ConstraintError, ASCIILiteral("Unable to serialize record value after injecting record key")), usedKey)); + return; + } + + injectedRecordValue = ThreadSafeDataBuffer::copyVector(serializedValue->data()); + } + + // 3.4.1 Object Store Storage Operation + // ...If a record already exists in store ... + // then remove the record from store using the steps for deleting records from an object store... + // This is important because formally deleting it from from the object store also removes it from the appropriate indexes. + error = m_backingStore->deleteRange(transactionIdentifier, objectStoreIdentifier, usedKey); + if (!error.isNull()) { + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); + return; + } + + if (injectedRecordValue.data()) + error = m_backingStore->addRecord(transactionIdentifier, *objectStoreInfo, usedKey, { injectedRecordValue, originalRecordValue.blobURLs(), originalRecordValue.blobFilePaths() }); + else + error = m_backingStore->addRecord(transactionIdentifier, *objectStoreInfo, usedKey, originalRecordValue); + + if (!error.isNull()) { + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); + return; + } + + if (overwriteMode != IndexedDB::ObjectStoreOverwriteMode::OverwriteForCursor && objectStoreInfo->autoIncrement() && keyData.type() == IndexedDB::KeyType::Number) + error = m_backingStore->maybeUpdateKeyGeneratorNumber(transactionIdentifier, objectStoreIdentifier, keyData.number()); + + generatedKeyResetter.disable(); + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); +} + +void UniqueIDBDatabase::didPerformPutOrAdd(uint64_t callbackIdentifier, const IDBError& error, const IDBKeyData& resultKey) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformPutOrAdd"); + + performKeyDataCallback(callbackIdentifier, error, resultKey); +} + +void UniqueIDBDatabase::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData, GetResultCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::getRecord"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + + if (uint64_t indexIdentifier = requestData.indexIdentifier()) + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetIndexRecord, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), indexIdentifier, requestData.indexRecordType(), getRecordData.keyRangeData)); + else + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetRecord, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), getRecordData.keyRangeData, getRecordData.type)); +} + +void UniqueIDBDatabase::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData, GetAllResultsCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::getAllRecords"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetAllRecords, callbackID, requestData.transactionIdentifier(), getAllRecordsData)); +} + +void UniqueIDBDatabase::performGetRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData& keyRangeData, IDBGetRecordDataType type) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetRecord"); + + ASSERT(m_backingStore); + + IDBGetResult result; + IDBError error = m_backingStore->getRecord(transactionIdentifier, objectStoreIdentifier, keyRangeData, type, result); + + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetRecord, callbackIdentifier, error, result)); +} + +void UniqueIDBDatabase::performGetIndexRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType recordType, const IDBKeyRangeData& range) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetIndexRecord"); + + ASSERT(m_backingStore); + + IDBGetResult result; + IDBError error = m_backingStore->getIndexRecord(transactionIdentifier, objectStoreIdentifier, indexIdentifier, recordType, range, result); + + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetRecord, callbackIdentifier, error, result)); +} + +void UniqueIDBDatabase::didPerformGetRecord(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& result) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformGetRecord"); + + performGetResultCallback(callbackIdentifier, error, result); +} + +void UniqueIDBDatabase::performGetAllRecords(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetAllRecords"); + + ASSERT(m_backingStore); + + IDBGetAllResult result; + IDBError error = m_backingStore->getAllRecords(transactionIdentifier, getAllRecordsData, result); + + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetAllRecords, callbackIdentifier, error, WTFMove(result))); +} + +void UniqueIDBDatabase::didPerformGetAllRecords(uint64_t callbackIdentifier, const IDBError& error, const IDBGetAllResult& result) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformGetAllRecords"); + + performGetAllResultsCallback(callbackIdentifier, error, result); +} + +void UniqueIDBDatabase::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& range, CountCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::getCount"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetCount, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), requestData.indexIdentifier(), range)); +} + +void UniqueIDBDatabase::performGetCount(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& keyRangeData) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetCount"); + + ASSERT(m_backingStore); + ASSERT(objectStoreIdentifier); + + uint64_t count; + IDBError error = m_backingStore->getCount(transactionIdentifier, objectStoreIdentifier, indexIdentifier, keyRangeData, count); + + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetCount, callbackIdentifier, error, count)); +} + +void UniqueIDBDatabase::didPerformGetCount(uint64_t callbackIdentifier, const IDBError& error, uint64_t count) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformGetCount"); + + performCountCallback(callbackIdentifier, error, count); +} + +void UniqueIDBDatabase::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData, ErrorCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteRecord"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteRecord, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), keyRangeData)); +} + +void UniqueIDBDatabase::performDeleteRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteRecord"); + + IDBError error = m_backingStore->deleteRange(transactionIdentifier, objectStoreIdentifier, range); + + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteRecord, callbackIdentifier, error)); +} + +void UniqueIDBDatabase::didPerformDeleteRecord(uint64_t callbackIdentifier, const IDBError& error) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteRecord"); + + performErrorCallback(callbackIdentifier, error); +} + +void UniqueIDBDatabase::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info, GetResultCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::openCursor"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performOpenCursor, callbackID, requestData.transactionIdentifier(), info)); +} + +void UniqueIDBDatabase::performOpenCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo& info) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performOpenCursor"); + + IDBGetResult result; + IDBError error = m_backingStore->openCursor(transactionIdentifier, info, result); + + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformOpenCursor, callbackIdentifier, error, result)); +} + +void UniqueIDBDatabase::didPerformOpenCursor(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& result) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformOpenCursor"); + + performGetResultCallback(callbackIdentifier, error, result); +} + +void UniqueIDBDatabase::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data, GetResultCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::iterateCursor"); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performIterateCursor, callbackID, requestData.transactionIdentifier(), requestData.cursorIdentifier(), data)); +} + +void UniqueIDBDatabase::performIterateCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData& data) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performIterateCursor"); + + IDBGetResult result; + IDBError error = m_backingStore->iterateCursor(transactionIdentifier, cursorIdentifier, data, result); + + if (error.isNull()) { + auto addResult = m_prefetchProtectors.add(cursorIdentifier, nullptr); + if (addResult.isNewEntry) { + addResult.iterator->value = this; + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPrefetchCursor, transactionIdentifier, cursorIdentifier)); + } + } + + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformIterateCursor, callbackIdentifier, error, result)); +} + +void UniqueIDBDatabase::performPrefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier) +{ + ASSERT(!isMainThread()); + ASSERT(m_prefetchProtectors.contains(cursorIdentifier)); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performPrefetchCursor"); + + if (m_backingStore->prefetchCursor(transactionIdentifier, cursorIdentifier)) + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPrefetchCursor, transactionIdentifier, cursorIdentifier)); + else + postDatabaseTaskReply(WTF::Function([prefetchProtector = m_prefetchProtectors.take(cursorIdentifier)]() { })); +} + +void UniqueIDBDatabase::didPerformIterateCursor(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& result) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformIterateCursor"); + + performGetResultCallback(callbackIdentifier, error, result); +} + +bool UniqueIDBDatabase::prepareToFinishTransaction(UniqueIDBDatabaseTransaction& transaction) +{ + auto takenTransaction = m_inProgressTransactions.take(transaction.info().identifier()); + if (!takenTransaction) + return false; + + ASSERT(!m_finishingTransactions.contains(transaction.info().identifier())); + m_finishingTransactions.set(transaction.info().identifier(), WTFMove(takenTransaction)); + + return true; +} + +void UniqueIDBDatabase::commitTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::commitTransaction - %s", transaction.info().identifier().loggingString().utf8().data()); + + ASSERT(&transaction.databaseConnection().database() == this); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + + if (!prepareToFinishTransaction(transaction)) { + if (!m_openDatabaseConnections.contains(&transaction.databaseConnection())) { + // This database connection is closing or has already closed, so there is no point in messaging back to it about the commit failing. + forgetErrorCallback(callbackID); + return; + } + + performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to commit transaction that is already finishing") }); + return; + } + + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCommitTransaction, callbackID, transaction.info().identifier())); +} + +void UniqueIDBDatabase::performCommitTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performCommitTransaction - %s", transactionIdentifier.loggingString().utf8().data()); + + IDBError error = m_backingStore->commitTransaction(transactionIdentifier); + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCommitTransaction, callbackIdentifier, error, transactionIdentifier)); +} + +void UniqueIDBDatabase::didPerformCommitTransaction(uint64_t callbackIdentifier, const IDBError& error, const IDBResourceIdentifier& transactionIdentifier) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCommitTransaction - %s", transactionIdentifier.loggingString().utf8().data()); + + performErrorCallback(callbackIdentifier, error); + + transactionCompleted(m_finishingTransactions.take(transactionIdentifier)); +} + +void UniqueIDBDatabase::abortTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::abortTransaction - %s", transaction.info().identifier().loggingString().utf8().data()); + + ASSERT(&transaction.databaseConnection().database() == this); + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + + if (!prepareToFinishTransaction(transaction)) { + if (!m_openDatabaseConnections.contains(&transaction.databaseConnection())) { + // This database connection is closing or has already closed, so there is no point in messaging back to it about the abort failing. + forgetErrorCallback(callbackID); + return; + } + + performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to abort transaction that is already finishing") }); + return; + } + + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performAbortTransaction, callbackID, transaction.info().identifier())); +} + +void UniqueIDBDatabase::didFinishHandlingVersionChange(UniqueIDBDatabaseConnection& connection, const IDBResourceIdentifier& transactionIdentifier) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didFinishHandlingVersionChange"); + + ASSERT_UNUSED(transactionIdentifier, !m_versionChangeTransaction || m_versionChangeTransaction->info().identifier() == transactionIdentifier); + ASSERT_UNUSED(connection, !m_versionChangeDatabaseConnection || m_versionChangeDatabaseConnection.get() == &connection); + + m_versionChangeTransaction = nullptr; + m_versionChangeDatabaseConnection = nullptr; + + if (m_hardClosedForUserDelete) { + maybeFinishHardClose(); + return; + } + + invokeOperationAndTransactionTimer(); +} + +void UniqueIDBDatabase::performAbortTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier) +{ + ASSERT(!isMainThread()); + LOG(IndexedDB, "(db) UniqueIDBDatabase::performAbortTransaction - %s", transactionIdentifier.loggingString().utf8().data()); + + IDBError error = m_backingStore->abortTransaction(transactionIdentifier); + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformAbortTransaction, callbackIdentifier, error, transactionIdentifier)); +} + +void UniqueIDBDatabase::didPerformAbortTransaction(uint64_t callbackIdentifier, const IDBError& error, const IDBResourceIdentifier& transactionIdentifier) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformAbortTransaction - %s", transactionIdentifier.loggingString().utf8().data()); + + auto transaction = m_finishingTransactions.take(transactionIdentifier); + ASSERT(transaction); + + if (m_versionChangeTransaction && m_versionChangeTransaction->info().identifier() == transactionIdentifier) { + ASSERT(m_versionChangeTransaction == transaction); + ASSERT(!m_versionChangeDatabaseConnection || &m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection); + ASSERT(m_versionChangeTransaction->originalDatabaseInfo()); + m_databaseInfo = std::make_unique(*m_versionChangeTransaction->originalDatabaseInfo()); + } + + performErrorCallback(callbackIdentifier, error); + + transactionCompleted(WTFMove(transaction)); +} + +void UniqueIDBDatabase::transactionDestroyed(UniqueIDBDatabaseTransaction& transaction) +{ + if (m_versionChangeTransaction == &transaction) + m_versionChangeTransaction = nullptr; +} + +void UniqueIDBDatabase::connectionClosedFromClient(UniqueIDBDatabaseConnection& connection) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "(main) UniqueIDBDatabase::connectionClosedFromClient - %s (%" PRIu64 ")", connection.openRequestIdentifier().loggingString().utf8().data(), connection.identifier()); + + Ref protectedConnection(connection); + m_openDatabaseConnections.remove(&connection); + + if (m_versionChangeDatabaseConnection == &connection) { + if (m_versionChangeTransaction) { + m_clientClosePendingDatabaseConnections.add(WTFMove(m_versionChangeDatabaseConnection)); + + auto transactionIdentifier = m_versionChangeTransaction->info().identifier(); + if (m_inProgressTransactions.contains(transactionIdentifier)) { + ASSERT(!m_finishingTransactions.contains(transactionIdentifier)); + connection.abortTransactionWithoutCallback(*m_versionChangeTransaction); + } + + return; + } + + m_versionChangeDatabaseConnection = nullptr; + } + + Deque> pendingTransactions; + while (!m_pendingTransactions.isEmpty()) { + auto transaction = m_pendingTransactions.takeFirst(); + if (&transaction->databaseConnection() != &connection) + pendingTransactions.append(WTFMove(transaction)); + } + + if (!pendingTransactions.isEmpty()) + m_pendingTransactions.swap(pendingTransactions); + + Deque> transactionsToAbort; + for (auto& transaction : m_inProgressTransactions.values()) { + if (&transaction->databaseConnection() == &connection) + transactionsToAbort.append(transaction); + } + + for (auto& transaction : transactionsToAbort) + transaction->abortWithoutCallback(); + + if (m_currentOpenDBRequest) + notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(connection.identifier()); + + if (connection.hasNonFinishedTransactions()) { + m_clientClosePendingDatabaseConnections.add(WTFMove(protectedConnection)); + return; + } + + if (m_hardClosedForUserDelete) { + maybeFinishHardClose(); + return; + } + + // Now that a database connection has closed, previously blocked operations might be runnable. + invokeOperationAndTransactionTimer(); +} + +void UniqueIDBDatabase::connectionClosedFromServer(UniqueIDBDatabaseConnection& connection) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "UniqueIDBDatabase::connectionClosedFromServer - %s (%" PRIu64 ")", connection.openRequestIdentifier().loggingString().utf8().data(), connection.identifier()); + + if (m_clientClosePendingDatabaseConnections.contains(&connection)) { + ASSERT(!m_openDatabaseConnections.contains(&connection)); + ASSERT(!m_serverClosePendingDatabaseConnections.contains(&connection)); + return; + } + + Ref protectedConnection(connection); + m_openDatabaseConnections.remove(&connection); + + connection.connectionToClient().didCloseFromServer(connection, IDBError::userDeleteError()); + + m_serverClosePendingDatabaseConnections.add(WTFMove(protectedConnection)); +} + +void UniqueIDBDatabase::confirmDidCloseFromServer(UniqueIDBDatabaseConnection& connection) +{ + ASSERT(isMainThread()); + LOG(IndexedDB, "UniqueIDBDatabase::confirmDidCloseFromServer - %s (%" PRIu64 ")", connection.openRequestIdentifier().loggingString().utf8().data(), connection.identifier()); + + ASSERT(m_serverClosePendingDatabaseConnections.contains(&connection)); + m_serverClosePendingDatabaseConnections.remove(&connection); +} + +void UniqueIDBDatabase::enqueueTransaction(Ref&& transaction) +{ + LOG(IndexedDB, "UniqueIDBDatabase::enqueueTransaction - %s", transaction->info().loggingString().utf8().data()); + ASSERT(!m_hardClosedForUserDelete); + + ASSERT(transaction->info().mode() != IDBTransactionMode::Versionchange); + + m_pendingTransactions.append(WTFMove(transaction)); + + invokeOperationAndTransactionTimer(); +} + +bool UniqueIDBDatabase::isCurrentlyInUse() const +{ + return !m_openDatabaseConnections.isEmpty() || !m_clientClosePendingDatabaseConnections.isEmpty() || !m_pendingOpenDBRequests.isEmpty() || m_currentOpenDBRequest || m_versionChangeDatabaseConnection || m_versionChangeTransaction || m_isOpeningBackingStore || m_deleteBackingStoreInProgress; +} + +bool UniqueIDBDatabase::hasUnfinishedTransactions() const +{ + return !m_inProgressTransactions.isEmpty() || !m_finishingTransactions.isEmpty(); +} + +void UniqueIDBDatabase::invokeOperationAndTransactionTimer() +{ + LOG(IndexedDB, "UniqueIDBDatabase::invokeOperationAndTransactionTimer()"); + ASSERT(!m_hardClosedForUserDelete); + + if (!m_operationAndTransactionTimer.isActive()) + m_operationAndTransactionTimer.startOneShot(0); +} + +void UniqueIDBDatabase::operationAndTransactionTimerFired() +{ + LOG(IndexedDB, "(main) UniqueIDBDatabase::operationAndTransactionTimerFired"); + ASSERT(!m_hardClosedForUserDelete); + + RefPtr protectedThis(this); + + // This UniqueIDBDatabase might be no longer in use by any web page. + // Assuming it is not ephemeral, the server should now close it to free up resources. + if (!m_backingStoreIsEphemeral && !isCurrentlyInUse()) { + ASSERT(m_pendingTransactions.isEmpty()); + ASSERT(!hasUnfinishedTransactions()); + m_server.closeUniqueIDBDatabase(*this); + return; + } + + // The current operation might require multiple attempts to handle, so try to + // make further progress on it now. + if (m_currentOpenDBRequest) + handleCurrentOperation(); + + if (!m_currentOpenDBRequest) + handleDatabaseOperations(); + + bool hadDeferredTransactions = false; + auto transaction = takeNextRunnableTransaction(hadDeferredTransactions); + + if (transaction) { + m_inProgressTransactions.set(transaction->info().identifier(), transaction); + for (auto objectStore : transaction->objectStoreIdentifiers()) { + m_objectStoreTransactionCounts.add(objectStore); + if (!transaction->isReadOnly()) { + m_objectStoreWriteTransactions.add(objectStore); + ASSERT(m_objectStoreTransactionCounts.count(objectStore) == 1); + } + } + + activateTransactionInBackingStore(*transaction); + + // If no transactions were deferred, it's possible we can start another transaction right now. + if (!hadDeferredTransactions) + invokeOperationAndTransactionTimer(); + } +} + +void UniqueIDBDatabase::activateTransactionInBackingStore(UniqueIDBDatabaseTransaction& transaction) +{ + LOG(IndexedDB, "(main) UniqueIDBDatabase::activateTransactionInBackingStore"); + + RefPtr protectedThis(this); + RefPtr refTransaction(&transaction); + + auto callback = [this, protectedThis, refTransaction](const IDBError& error) { + refTransaction->didActivateInBackingStore(error); + }; + + uint64_t callbackID = storeCallbackOrFireError(callback); + if (!callbackID) + return; + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performActivateTransactionInBackingStore, callbackID, transaction.info())); +} + +void UniqueIDBDatabase::performActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBTransactionInfo& info) +{ + LOG(IndexedDB, "(db) UniqueIDBDatabase::performActivateTransactionInBackingStore"); + + IDBError error = m_backingStore->beginTransaction(info); + postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformActivateTransactionInBackingStore, callbackIdentifier, error)); +} + +void UniqueIDBDatabase::didPerformActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBError& error) +{ + LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformActivateTransactionInBackingStore"); + + invokeOperationAndTransactionTimer(); + + performErrorCallback(callbackIdentifier, error); +} + +template bool scopesOverlap(const T& aScopes, const Vector& bScopes) +{ + for (auto scope : bScopes) { + if (aScopes.contains(scope)) + return true; + } + + return false; +} + +RefPtr UniqueIDBDatabase::takeNextRunnableTransaction(bool& hadDeferredTransactions) +{ + hadDeferredTransactions = false; + + if (m_pendingTransactions.isEmpty()) + return nullptr; + + if (!m_backingStoreSupportsSimultaneousTransactions && hasUnfinishedTransactions()) { + LOG(IndexedDB, "UniqueIDBDatabase::takeNextRunnableTransaction - Backing store only supports 1 transaction, and we already have 1"); + return nullptr; + } + + Deque> deferredTransactions; + RefPtr currentTransaction; + + HashSet deferredReadWriteScopes; + + while (!m_pendingTransactions.isEmpty()) { + currentTransaction = m_pendingTransactions.takeFirst(); + + switch (currentTransaction->info().mode()) { + case IDBTransactionMode::Readonly: { + bool hasOverlappingScopes = scopesOverlap(deferredReadWriteScopes, currentTransaction->objectStoreIdentifiers()); + hasOverlappingScopes |= scopesOverlap(m_objectStoreWriteTransactions, currentTransaction->objectStoreIdentifiers()); + + if (hasOverlappingScopes) + deferredTransactions.append(WTFMove(currentTransaction)); + + break; + } + case IDBTransactionMode::Readwrite: { + bool hasOverlappingScopes = scopesOverlap(m_objectStoreTransactionCounts, currentTransaction->objectStoreIdentifiers()); + hasOverlappingScopes |= scopesOverlap(deferredReadWriteScopes, currentTransaction->objectStoreIdentifiers()); + + if (hasOverlappingScopes) { + for (auto objectStore : currentTransaction->objectStoreIdentifiers()) + deferredReadWriteScopes.add(objectStore); + deferredTransactions.append(WTFMove(currentTransaction)); + } + + break; + } + case IDBTransactionMode::Versionchange: + // Version change transactions should never be scheduled in the traditional manner. + RELEASE_ASSERT_NOT_REACHED(); + } + + // If we didn't defer the currentTransaction above, it can be run now. + if (currentTransaction) + break; + } + + hadDeferredTransactions = !deferredTransactions.isEmpty(); + if (!hadDeferredTransactions) + return currentTransaction; + + // Prepend the deferred transactions back on the beginning of the deque for future scheduling passes. + while (!deferredTransactions.isEmpty()) + m_pendingTransactions.prepend(deferredTransactions.takeLast()); + + return currentTransaction; +} + +void UniqueIDBDatabase::transactionCompleted(RefPtr&& transaction) +{ + ASSERT(transaction); + ASSERT(!m_inProgressTransactions.contains(transaction->info().identifier())); + ASSERT(!m_finishingTransactions.contains(transaction->info().identifier())); + + for (auto objectStore : transaction->objectStoreIdentifiers()) { + if (!transaction->isReadOnly()) { + m_objectStoreWriteTransactions.remove(objectStore); + ASSERT(m_objectStoreTransactionCounts.count(objectStore) == 1); + } + m_objectStoreTransactionCounts.remove(objectStore); + } + + if (!transaction->databaseConnection().hasNonFinishedTransactions()) + m_clientClosePendingDatabaseConnections.remove(&transaction->databaseConnection()); + + if (m_versionChangeTransaction == transaction) + m_versionChangeTransaction = nullptr; + + // It's possible that this database had its backing store deleted but there were a few outstanding asynchronous operations. + // If this transaction completing was the last of those operations, we can finally delete this UniqueIDBDatabase. + if (m_clientClosePendingDatabaseConnections.isEmpty() && m_pendingOpenDBRequests.isEmpty() && !m_databaseInfo) { + m_server.closeUniqueIDBDatabase(*this); + return; + } + + // Previously blocked operations might be runnable. + if (!m_hardClosedForUserDelete) + invokeOperationAndTransactionTimer(); + else + maybeFinishHardClose(); +} + +void UniqueIDBDatabase::postDatabaseTask(CrossThreadTask&& task) +{ + m_databaseQueue.append([protectedThis = makeRef(*this), task = WTFMove(task)]() mutable { + task.performTask(); + }); + ++m_queuedTaskCount; + + m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::executeNextDatabaseTask)); +} + +void UniqueIDBDatabase::postDatabaseTaskReply(CrossThreadTask&& task) +{ + ASSERT(!isMainThread()); + + m_databaseReplyQueue.append([protectedThis = makeRef(*this), task = WTFMove(task)]() mutable { + task.performTask(); + }); + ++m_queuedTaskCount; + + m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::executeNextDatabaseTaskReply)); +} + +void UniqueIDBDatabase::executeNextDatabaseTask() +{ + ASSERT(!isMainThread()); + ASSERT(m_queuedTaskCount); + + auto task = m_databaseQueue.tryGetMessage(); + ASSERT(task); + + (*task)(); + --m_queuedTaskCount; + + // Release the task on the main thread in case it holds the last reference to this, + // as UniqueIDBDatabase objects must be deleted on the main thread. + callOnMainThread([task = WTFMove(task)] { + }); +} + +void UniqueIDBDatabase::executeNextDatabaseTaskReply() +{ + ASSERT(isMainThread()); + ASSERT(m_queuedTaskCount); + + auto task = m_databaseReplyQueue.tryGetMessage(); + ASSERT(task); + + (*task)(); + --m_queuedTaskCount; + + // If this database was force closed (e.g. for a user delete) and there are no more + // cleanup tasks left, delete this. + maybeFinishHardClose(); +} + +void UniqueIDBDatabase::maybeFinishHardClose() +{ + if (m_hardCloseProtector && isDoneWithHardClose()) { + callOnMainThread([this] { + ASSERT(isDoneWithHardClose()); + m_hardCloseProtector = nullptr; + }); + } +} + +bool UniqueIDBDatabase::isDoneWithHardClose() +{ + return !m_queuedTaskCount && m_clientClosePendingDatabaseConnections.isEmpty() && m_serverClosePendingDatabaseConnections.isEmpty(); +} + +static void errorOpenDBRequestForUserDelete(ServerOpenDBRequest& request) +{ + auto result = IDBResultData::error(request.requestData().requestIdentifier(), IDBError::userDeleteError()); + if (request.isOpenRequest()) + request.connection().didOpenDatabase(result); + else + request.connection().didDeleteDatabase(result); +} + +void UniqueIDBDatabase::immediateCloseForUserDelete() +{ + LOG(IndexedDB, "UniqueIDBDatabase::immediateCloseForUserDelete - Cancelling (%i, %i, %i, %i) callbacks", m_errorCallbacks.size(), m_keyDataCallbacks.size(), m_getResultCallbacks.size(), m_countCallbacks.size()); + + // Error out all transactions + Vector inProgressIdentifiers; + copyKeysToVector(m_inProgressTransactions, inProgressIdentifiers); + for (auto& identifier : inProgressIdentifiers) + m_inProgressTransactions.get(identifier)->abortWithoutCallback(); + + ASSERT(m_inProgressTransactions.isEmpty()); + + m_pendingTransactions.clear(); + m_objectStoreTransactionCounts.clear(); + m_objectStoreWriteTransactions.clear(); + + // Error out all pending callbacks + Vector callbackIdentifiers; + IDBError error = IDBError::userDeleteError(); + IDBKeyData keyData; + IDBGetResult getResult; + + copyKeysToVector(m_errorCallbacks, callbackIdentifiers); + for (auto identifier : callbackIdentifiers) + performErrorCallback(identifier, error); + + callbackIdentifiers.clear(); + copyKeysToVector(m_keyDataCallbacks, callbackIdentifiers); + for (auto identifier : callbackIdentifiers) + performKeyDataCallback(identifier, error, keyData); + + callbackIdentifiers.clear(); + copyKeysToVector(m_getResultCallbacks, callbackIdentifiers); + for (auto identifier : callbackIdentifiers) + performGetResultCallback(identifier, error, getResult); + + callbackIdentifiers.clear(); + copyKeysToVector(m_countCallbacks, callbackIdentifiers); + for (auto identifier : callbackIdentifiers) + performCountCallback(identifier, error, 0); + + // Error out all IDBOpenDBRequests + if (m_currentOpenDBRequest) { + errorOpenDBRequestForUserDelete(*m_currentOpenDBRequest); + m_currentOpenDBRequest = nullptr; + } + + for (auto& request : m_pendingOpenDBRequests) + errorOpenDBRequestForUserDelete(*request); + + m_pendingOpenDBRequests.clear(); + + // Close all open connections + ListHashSet> openDatabaseConnections = m_openDatabaseConnections; + for (auto& connection : openDatabaseConnections) + connectionClosedFromServer(*connection); + + // Cancel the operation timer + m_operationAndTransactionTimer.stop(); + + // Set up the database to remain alive-but-inert until all of its background activity finishes and all + // database connections confirm that they have closed. + m_hardClosedForUserDelete = true; + m_hardCloseProtector = this; + + // Have the database unconditionally delete itself on the database task queue. + postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performUnconditionalDeleteBackingStore)); + + // Remove the database from the IDBServer's set of open databases. + // If there is no in-progress background thread activity for this database, it will be deleted here. + m_server.closeUniqueIDBDatabase(*this); +} + +void UniqueIDBDatabase::performErrorCallback(uint64_t callbackIdentifier, const IDBError& error) +{ + auto callback = m_errorCallbacks.take(callbackIdentifier); + ASSERT(callback || m_hardClosedForUserDelete); + if (callback) + callback(error); +} + +void UniqueIDBDatabase::performKeyDataCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBKeyData& resultKey) +{ + auto callback = m_keyDataCallbacks.take(callbackIdentifier); + ASSERT(callback || m_hardClosedForUserDelete); + if (callback) + callback(error, resultKey); +} + +void UniqueIDBDatabase::performGetResultCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& resultData) +{ + auto callback = m_getResultCallbacks.take(callbackIdentifier); + ASSERT(callback || m_hardClosedForUserDelete); + if (callback) + callback(error, resultData); +} + +void UniqueIDBDatabase::performGetAllResultsCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBGetAllResult& resultData) +{ + auto callback = m_getAllResultsCallbacks.take(callbackIdentifier); + ASSERT(callback || m_hardClosedForUserDelete); + if (callback) + callback(error, resultData); +} + +void UniqueIDBDatabase::performCountCallback(uint64_t callbackIdentifier, const IDBError& error, uint64_t count) +{ + auto callback = m_countCallbacks.take(callbackIdentifier); + ASSERT(callback || m_hardClosedForUserDelete); + if (callback) + callback(error, count); +} + +void UniqueIDBDatabase::forgetErrorCallback(uint64_t callbackIdentifier) +{ + ASSERT(m_errorCallbacks.contains(callbackIdentifier)); + m_errorCallbacks.remove(callbackIdentifier); +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h new file mode 100644 index 000000000..93edced5c --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBBackingStore.h" +#include "IDBBindingUtilities.h" +#include "IDBDatabaseIdentifier.h" +#include "IDBDatabaseInfo.h" +#include "IDBGetResult.h" +#include "ServerOpenDBRequest.h" +#include "ThreadSafeDataBuffer.h" +#include "Timer.h" +#include "UniqueIDBDatabaseConnection.h" +#include "UniqueIDBDatabaseTransaction.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace JSC { +class VM; +} + +namespace WebCore { + +class IDBError; +class IDBGetAllResult; +class IDBRequestData; +class IDBTransactionInfo; + +enum class IDBGetRecordDataType; + +namespace IndexedDB { +enum class IndexRecordType; +} + +namespace IDBServer { + +class IDBConnectionToClient; +class IDBServer; + +typedef std::function ErrorCallback; +typedef std::function KeyDataCallback; +typedef std::function GetResultCallback; +typedef std::function GetAllResultsCallback; +typedef std::function CountCallback; + +class UniqueIDBDatabase : public ThreadSafeRefCounted { +public: + static Ref create(IDBServer& server, const IDBDatabaseIdentifier& identifier) + { + return adoptRef(*new UniqueIDBDatabase(server, identifier)); + } + + WEBCORE_EXPORT ~UniqueIDBDatabase(); + + void openDatabaseConnection(IDBConnectionToClient&, const IDBRequestData&); + + const IDBDatabaseInfo& info() const; + IDBServer& server() { return m_server; } + const IDBDatabaseIdentifier& identifier() const { return m_identifier; } + + void createObjectStore(UniqueIDBDatabaseTransaction&, const IDBObjectStoreInfo&, ErrorCallback); + void deleteObjectStore(UniqueIDBDatabaseTransaction&, const String& objectStoreName, ErrorCallback); + void renameObjectStore(UniqueIDBDatabaseTransaction&, uint64_t objectStoreIdentifier, const String& newName, ErrorCallback); + void clearObjectStore(UniqueIDBDatabaseTransaction&, uint64_t objectStoreIdentifier, ErrorCallback); + void createIndex(UniqueIDBDatabaseTransaction&, const IDBIndexInfo&, ErrorCallback); + void deleteIndex(UniqueIDBDatabaseTransaction&, uint64_t objectStoreIdentifier, const String& indexName, ErrorCallback); + void renameIndex(UniqueIDBDatabaseTransaction&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName, ErrorCallback); + void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, IndexedDB::ObjectStoreOverwriteMode, KeyDataCallback); + void getRecord(const IDBRequestData&, const IDBGetRecordData&, GetResultCallback); + void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&, GetAllResultsCallback); + void getCount(const IDBRequestData&, const IDBKeyRangeData&, CountCallback); + void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&, ErrorCallback); + void openCursor(const IDBRequestData&, const IDBCursorInfo&, GetResultCallback); + void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&, GetResultCallback); + void commitTransaction(UniqueIDBDatabaseTransaction&, ErrorCallback); + void abortTransaction(UniqueIDBDatabaseTransaction&, ErrorCallback); + void didFinishHandlingVersionChange(UniqueIDBDatabaseConnection&, const IDBResourceIdentifier& transactionIdentifier); + void transactionDestroyed(UniqueIDBDatabaseTransaction&); + void connectionClosedFromClient(UniqueIDBDatabaseConnection&); + void confirmConnectionClosedOnServer(UniqueIDBDatabaseConnection&); + void didFireVersionChangeEvent(UniqueIDBDatabaseConnection&, const IDBResourceIdentifier& requestIdentifier); + void openDBRequestCancelled(const IDBResourceIdentifier& requestIdentifier); + void confirmDidCloseFromServer(UniqueIDBDatabaseConnection&); + + void enqueueTransaction(Ref&&); + + void handleDelete(IDBConnectionToClient&, const IDBRequestData&); + void immediateCloseForUserDelete(); + + static JSC::VM& databaseThreadVM(); + static JSC::ExecState& databaseThreadExecState(); + + bool hardClosedForUserDelete() const { return m_hardClosedForUserDelete; } + +private: + UniqueIDBDatabase(IDBServer&, const IDBDatabaseIdentifier&); + + void handleDatabaseOperations(); + void handleCurrentOperation(); + void performCurrentOpenOperation(); + void performCurrentDeleteOperation(); + void addOpenDatabaseConnection(Ref&&); + bool hasAnyOpenConnections() const; + bool allConnectionsAreClosedOrClosing() const; + + void startVersionChangeTransaction(); + void maybeNotifyConnectionsOfVersionChange(); + void notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(uint64_t connectionIdentifier); + bool isVersionChangeInProgress(); + + void activateTransactionInBackingStore(UniqueIDBDatabaseTransaction&); + void transactionCompleted(RefPtr&&); + + void connectionClosedFromServer(UniqueIDBDatabaseConnection&); + + // Database thread operations + void deleteBackingStore(const IDBDatabaseIdentifier&); + void openBackingStore(const IDBDatabaseIdentifier&); + void performCommitTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier); + void performAbortTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier); + void beginTransactionInBackingStore(const IDBTransactionInfo&); + void performCreateObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&); + void performDeleteObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier); + void performRenameObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName); + void performClearObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier); + void performCreateIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo&); + void performDeleteIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier); + void performRenameIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName); + void performPutOrAdd(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData&, const IDBValue&, IndexedDB::ObjectStoreOverwriteMode); + void performGetRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IDBGetRecordDataType); + void performGetAllRecords(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&); + void performGetIndexRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&); + void performGetCount(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&); + void performDeleteRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&); + void performOpenCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&); + void performIterateCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&); + void performPrefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier); + + void performActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBTransactionInfo&); + void performUnconditionalDeleteBackingStore(); + + // Main thread callbacks + void didDeleteBackingStore(uint64_t deletedVersion); + void didOpenBackingStore(const IDBDatabaseInfo&, const IDBError&); + void didPerformCreateObjectStore(uint64_t callbackIdentifier, const IDBError&, const IDBObjectStoreInfo&); + void didPerformDeleteObjectStore(uint64_t callbackIdentifier, const IDBError&, uint64_t objectStoreIdentifier); + void didPerformRenameObjectStore(uint64_t callbackIdentifier, const IDBError&, uint64_t objectStoreIdentifier, const String& newName); + void didPerformClearObjectStore(uint64_t callbackIdentifier, const IDBError&); + void didPerformCreateIndex(uint64_t callbackIdentifier, const IDBError&, const IDBIndexInfo&); + void didPerformDeleteIndex(uint64_t callbackIdentifier, const IDBError&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier); + void didPerformRenameIndex(uint64_t callbackIdentifier, const IDBError&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName); + void didPerformPutOrAdd(uint64_t callbackIdentifier, const IDBError&, const IDBKeyData&); + void didPerformGetRecord(uint64_t callbackIdentifier, const IDBError&, const IDBGetResult&); + void didPerformGetAllRecords(uint64_t callbackIdentifier, const IDBError&, const IDBGetAllResult&); + void didPerformGetCount(uint64_t callbackIdentifier, const IDBError&, uint64_t); + void didPerformDeleteRecord(uint64_t callbackIdentifier, const IDBError&); + void didPerformOpenCursor(uint64_t callbackIdentifier, const IDBError&, const IDBGetResult&); + void didPerformIterateCursor(uint64_t callbackIdentifier, const IDBError&, const IDBGetResult&); + void didPerformCommitTransaction(uint64_t callbackIdentifier, const IDBError&, const IDBResourceIdentifier& transactionIdentifier); + void didPerformAbortTransaction(uint64_t callbackIdentifier, const IDBError&, const IDBResourceIdentifier& transactionIdentifier); + void didPerformActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBError&); + void didPerformUnconditionalDeleteBackingStore(); + + uint64_t storeCallbackOrFireError(ErrorCallback); + uint64_t storeCallbackOrFireError(KeyDataCallback); + uint64_t storeCallbackOrFireError(GetAllResultsCallback); + uint64_t storeCallbackOrFireError(GetResultCallback); + uint64_t storeCallbackOrFireError(CountCallback); + + void performErrorCallback(uint64_t callbackIdentifier, const IDBError&); + void performKeyDataCallback(uint64_t callbackIdentifier, const IDBError&, const IDBKeyData&); + void performGetResultCallback(uint64_t callbackIdentifier, const IDBError&, const IDBGetResult&); + void performGetAllResultsCallback(uint64_t callbackIdentifier, const IDBError&, const IDBGetAllResult&); + void performCountCallback(uint64_t callbackIdentifier, const IDBError&, uint64_t); + + void forgetErrorCallback(uint64_t callbackIdentifier); + + bool hasAnyPendingCallbacks() const; + bool isCurrentlyInUse() const; + bool hasUnfinishedTransactions() const; + + void invokeOperationAndTransactionTimer(); + void operationAndTransactionTimerFired(); + RefPtr takeNextRunnableTransaction(bool& hadDeferredTransactions); + + bool prepareToFinishTransaction(UniqueIDBDatabaseTransaction&); + + void postDatabaseTask(CrossThreadTask&&); + void postDatabaseTaskReply(CrossThreadTask&&); + void executeNextDatabaseTask(); + void executeNextDatabaseTaskReply(); + + void maybeFinishHardClose(); + bool isDoneWithHardClose(); + + IDBServer& m_server; + IDBDatabaseIdentifier m_identifier; + + ListHashSet> m_pendingOpenDBRequests; + RefPtr m_currentOpenDBRequest; + + ListHashSet> m_openDatabaseConnections; + HashSet> m_clientClosePendingDatabaseConnections; + HashSet> m_serverClosePendingDatabaseConnections; + + RefPtr m_versionChangeDatabaseConnection; + RefPtr m_versionChangeTransaction; + + bool m_isOpeningBackingStore { false }; + IDBError m_backingStoreOpenError; + std::unique_ptr m_backingStore; + std::unique_ptr m_databaseInfo; + std::unique_ptr m_mostRecentDeletedDatabaseInfo; + + bool m_backingStoreSupportsSimultaneousTransactions { false }; + bool m_backingStoreIsEphemeral { false }; + + HashMap m_errorCallbacks; + HashMap m_keyDataCallbacks; + HashMap m_getResultCallbacks; + HashMap m_getAllResultsCallbacks; + HashMap m_countCallbacks; + + Timer m_operationAndTransactionTimer; + + Deque> m_pendingTransactions; + HashMap> m_inProgressTransactions; + HashMap> m_finishingTransactions; + + // The keys into these sets are the object store ID. + // These sets help to decide which transactions can be started and which must be deferred. + HashCountedSet m_objectStoreTransactionCounts; + HashSet m_objectStoreWriteTransactions; + + bool m_deleteBackingStoreInProgress { false }; + + CrossThreadQueue> m_databaseQueue; + CrossThreadQueue> m_databaseReplyQueue; + std::atomic m_queuedTaskCount { 0 }; + + bool m_hardClosedForUserDelete { false }; + RefPtr m_hardCloseProtector; + + HashMap> m_prefetchProtectors; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.cpp b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.cpp new file mode 100644 index 000000000..694b7171d --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "UniqueIDBDatabaseConnection.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBConnectionToClient.h" +#include "IDBServer.h" +#include "IDBTransactionInfo.h" +#include "Logging.h" +#include "ServerOpenDBRequest.h" +#include "UniqueIDBDatabase.h" + +namespace WebCore { +namespace IDBServer { + +static uint64_t nextDatabaseConnectionIdentifier() +{ + static uint64_t nextIdentifier = 0; + return ++nextIdentifier; +} + +Ref UniqueIDBDatabaseConnection::create(UniqueIDBDatabase& database, ServerOpenDBRequest& request) +{ + return adoptRef(*new UniqueIDBDatabaseConnection(database, request)); +} + +UniqueIDBDatabaseConnection::UniqueIDBDatabaseConnection(UniqueIDBDatabase& database, ServerOpenDBRequest& request) + : m_identifier(nextDatabaseConnectionIdentifier()) + , m_database(database) + , m_connectionToClient(request.connection()) + , m_openRequestIdentifier(request.requestData().requestIdentifier()) +{ + m_database.server().registerDatabaseConnection(*this); + m_connectionToClient.registerDatabaseConnection(*this); +} + +UniqueIDBDatabaseConnection::~UniqueIDBDatabaseConnection() +{ + m_database.server().unregisterDatabaseConnection(*this); + m_connectionToClient.unregisterDatabaseConnection(*this); +} + +bool UniqueIDBDatabaseConnection::hasNonFinishedTransactions() const +{ + return !m_transactionMap.isEmpty(); +} + +void UniqueIDBDatabaseConnection::abortTransactionWithoutCallback(UniqueIDBDatabaseTransaction& transaction) +{ + ASSERT(m_transactionMap.contains(transaction.info().identifier())); + + const auto& transactionIdentifier = transaction.info().identifier(); + RefPtr protectedThis(this); + + m_database.abortTransaction(transaction, [this, protectedThis, transactionIdentifier](const IDBError&) { + ASSERT(m_transactionMap.contains(transactionIdentifier)); + m_transactionMap.remove(transactionIdentifier); + }); +} + +void UniqueIDBDatabaseConnection::connectionPendingCloseFromClient() +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::connectionPendingCloseFromClient - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier); + + m_closePending = true; +} + +void UniqueIDBDatabaseConnection::connectionClosedFromClient() +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::connectionClosedFromClient - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier); + + m_database.connectionClosedFromClient(*this); +} + +void UniqueIDBDatabaseConnection::confirmDidCloseFromServer() +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::confirmDidCloseFromServer - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier); + + m_database.confirmDidCloseFromServer(*this); +} + +void UniqueIDBDatabaseConnection::didFireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::didFireVersionChangeEvent - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier); + + m_database.didFireVersionChangeEvent(*this, requestIdentifier); +} + +void UniqueIDBDatabaseConnection::didFinishHandlingVersionChange(const IDBResourceIdentifier& transactionIdentifier) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::didFinishHandlingVersionChange - %s - %" PRIu64, transactionIdentifier.loggingString().utf8().data(), m_identifier); + + m_database.didFinishHandlingVersionChange(*this, transactionIdentifier); +} + +void UniqueIDBDatabaseConnection::fireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) +{ + ASSERT(!m_closePending); + m_connectionToClient.fireVersionChangeEvent(*this, requestIdentifier, requestedVersion); +} + +UniqueIDBDatabaseTransaction& UniqueIDBDatabaseConnection::createVersionChangeTransaction(uint64_t newVersion) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::createVersionChangeTransaction - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier); + ASSERT(!m_closePending); + + IDBTransactionInfo info = IDBTransactionInfo::versionChange(m_connectionToClient, m_database.info(), newVersion); + + Ref transaction = UniqueIDBDatabaseTransaction::create(*this, info); + m_transactionMap.set(transaction->info().identifier(), &transaction.get()); + + return transaction.get(); +} + +void UniqueIDBDatabaseConnection::establishTransaction(const IDBTransactionInfo& info) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::establishTransaction - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier); + + ASSERT(info.mode() != IDBTransactionMode::Versionchange); + + // No transactions should ever come from the client after the client has already told us + // the connection is closing. + ASSERT(!m_closePending); + + Ref transaction = UniqueIDBDatabaseTransaction::create(*this, info); + m_transactionMap.set(transaction->info().identifier(), &transaction.get()); + m_database.enqueueTransaction(WTFMove(transaction)); +} + +void UniqueIDBDatabaseConnection::didAbortTransaction(UniqueIDBDatabaseTransaction& transaction, const IDBError& error) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::didAbortTransaction - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier); + + auto transactionIdentifier = transaction.info().identifier(); + auto takenTransaction = m_transactionMap.take(transactionIdentifier); + + ASSERT(takenTransaction || m_database.hardClosedForUserDelete()); + if (takenTransaction) + m_connectionToClient.didAbortTransaction(transactionIdentifier, error); +} + +void UniqueIDBDatabaseConnection::didCommitTransaction(UniqueIDBDatabaseTransaction& transaction, const IDBError& error) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::didCommitTransaction - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier); + + auto transactionIdentifier = transaction.info().identifier(); + + ASSERT(m_transactionMap.contains(transactionIdentifier)); + m_transactionMap.remove(transactionIdentifier); + + m_connectionToClient.didCommitTransaction(transactionIdentifier, error); +} + +void UniqueIDBDatabaseConnection::didCreateObjectStore(const IDBResultData& resultData) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::didCreateObjectStore"); + + m_connectionToClient.didCreateObjectStore(resultData); +} + +void UniqueIDBDatabaseConnection::didDeleteObjectStore(const IDBResultData& resultData) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::didDeleteObjectStore"); + + m_connectionToClient.didDeleteObjectStore(resultData); +} + +void UniqueIDBDatabaseConnection::didRenameObjectStore(const IDBResultData& resultData) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::didRenameObjectStore"); + + m_connectionToClient.didRenameObjectStore(resultData); +} + +void UniqueIDBDatabaseConnection::didClearObjectStore(const IDBResultData& resultData) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::didClearObjectStore"); + + m_connectionToClient.didClearObjectStore(resultData); +} + +void UniqueIDBDatabaseConnection::didCreateIndex(const IDBResultData& resultData) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::didCreateIndex"); + + m_connectionToClient.didCreateIndex(resultData); +} + +void UniqueIDBDatabaseConnection::didDeleteIndex(const IDBResultData& resultData) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::didDeleteIndex"); + + m_connectionToClient.didDeleteIndex(resultData); +} + +void UniqueIDBDatabaseConnection::didRenameIndex(const IDBResultData& resultData) +{ + LOG(IndexedDB, "UniqueIDBDatabaseConnection::didRenameIndex"); + + m_connectionToClient.didRenameIndex(resultData); +} + +bool UniqueIDBDatabaseConnection::connectionIsClosing() const +{ + return m_closePending; +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.h b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.h new file mode 100644 index 000000000..27d5884b3 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "UniqueIDBDatabaseTransaction.h" +#include +#include +#include + +namespace WebCore { + +class IDBError; +class IDBResultData; + +namespace IDBServer { + +class IDBConnectionToClient; +class ServerOpenDBRequest; +class UniqueIDBDatabase; +class UniqueIDBDatabaseTransaction; + +class UniqueIDBDatabaseConnection : public RefCounted { +public: + static Ref create(UniqueIDBDatabase&, ServerOpenDBRequest&); + + ~UniqueIDBDatabaseConnection(); + + uint64_t identifier() const { return m_identifier; } + const IDBResourceIdentifier& openRequestIdentifier() { return m_openRequestIdentifier; } + UniqueIDBDatabase& database() { return m_database; } + IDBConnectionToClient& connectionToClient() { return m_connectionToClient; } + + void connectionPendingCloseFromClient(); + void connectionClosedFromClient(); + + bool closePending() const { return m_closePending; } + + bool hasNonFinishedTransactions() const; + + void fireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion); + UniqueIDBDatabaseTransaction& createVersionChangeTransaction(uint64_t newVersion); + + void establishTransaction(const IDBTransactionInfo&); + void didAbortTransaction(UniqueIDBDatabaseTransaction&, const IDBError&); + void didCommitTransaction(UniqueIDBDatabaseTransaction&, const IDBError&); + void didCreateObjectStore(const IDBResultData&); + void didDeleteObjectStore(const IDBResultData&); + void didRenameObjectStore(const IDBResultData&); + void didClearObjectStore(const IDBResultData&); + void didCreateIndex(const IDBResultData&); + void didDeleteIndex(const IDBResultData&); + void didRenameIndex(const IDBResultData&); + void didFireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier); + void didFinishHandlingVersionChange(const IDBResourceIdentifier& transactionIdentifier); + void confirmDidCloseFromServer(); + + void abortTransactionWithoutCallback(UniqueIDBDatabaseTransaction&); + + bool connectionIsClosing() const; + +private: + UniqueIDBDatabaseConnection(UniqueIDBDatabase&, ServerOpenDBRequest&); + + uint64_t m_identifier { 0 }; + UniqueIDBDatabase& m_database; + IDBConnectionToClient& m_connectionToClient; + IDBResourceIdentifier m_openRequestIdentifier; + + bool m_closePending { false }; + + HashMap> m_transactionMap; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp new file mode 100644 index 000000000..214f8c238 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "UniqueIDBDatabaseTransaction.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBError.h" +#include "IDBIterateCursorData.h" +#include "IDBResultData.h" +#include "IDBServer.h" +#include "Logging.h" +#include "UniqueIDBDatabase.h" + +namespace WebCore { +namespace IDBServer { + +Ref UniqueIDBDatabaseTransaction::create(UniqueIDBDatabaseConnection& connection, const IDBTransactionInfo& info) +{ + return adoptRef(*new UniqueIDBDatabaseTransaction(connection, info)); +} + +UniqueIDBDatabaseTransaction::UniqueIDBDatabaseTransaction(UniqueIDBDatabaseConnection& connection, const IDBTransactionInfo& info) + : m_databaseConnection(connection) + , m_transactionInfo(info) +{ + if (m_transactionInfo.mode() == IDBTransactionMode::Versionchange) + m_originalDatabaseInfo = std::make_unique(m_databaseConnection->database().info()); + + m_databaseConnection->database().server().registerTransaction(*this); +} + +UniqueIDBDatabaseTransaction::~UniqueIDBDatabaseTransaction() +{ + m_databaseConnection->database().transactionDestroyed(*this); + m_databaseConnection->database().server().unregisterTransaction(*this); +} + +IDBDatabaseInfo* UniqueIDBDatabaseTransaction::originalDatabaseInfo() const +{ + ASSERT(m_transactionInfo.mode() == IDBTransactionMode::Versionchange); + return m_originalDatabaseInfo.get(); +} + +void UniqueIDBDatabaseTransaction::abort() +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::abort"); + + RefPtr protectedThis(this); + m_databaseConnection->database().abortTransaction(*this, [this, protectedThis](const IDBError& error) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::abort (callback)"); + m_databaseConnection->didAbortTransaction(*this, error); + }); +} + +void UniqueIDBDatabaseTransaction::abortWithoutCallback() +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::abortWithoutCallback"); + + m_databaseConnection->abortTransactionWithoutCallback(*this); +} + +bool UniqueIDBDatabaseTransaction::isVersionChange() const +{ + return m_transactionInfo.mode() == IDBTransactionMode::Versionchange; +} + +bool UniqueIDBDatabaseTransaction::isReadOnly() const +{ + return m_transactionInfo.mode() == IDBTransactionMode::Readonly; +} + +void UniqueIDBDatabaseTransaction::commit() +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::commit"); + + RefPtr protectedThis(this); + m_databaseConnection->database().commitTransaction(*this, [this, protectedThis](const IDBError& error) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::commit (callback)"); + m_databaseConnection->didCommitTransaction(*this, error); + }); +} + +void UniqueIDBDatabaseTransaction::createObjectStore(const IDBRequestData& requestData, const IDBObjectStoreInfo& info) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::createObjectStore"); + + ASSERT(isVersionChange()); + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().createObjectStore(*this, info, [this, protectedThis, requestData](const IDBError& error) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::createObjectStore (callback)"); + if (error.isNull()) + m_databaseConnection->didCreateObjectStore(IDBResultData::createObjectStoreSuccess(requestData.requestIdentifier())); + else + m_databaseConnection->didCreateObjectStore(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::deleteObjectStore(const IDBRequestData& requestData, const String& objectStoreName) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::deleteObjectStore"); + + ASSERT(isVersionChange()); + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().deleteObjectStore(*this, objectStoreName, [this, protectedThis, requestData](const IDBError& error) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::deleteObjectStore (callback)"); + if (error.isNull()) + m_databaseConnection->didDeleteObjectStore(IDBResultData::deleteObjectStoreSuccess(requestData.requestIdentifier())); + else + m_databaseConnection->didDeleteObjectStore(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::renameObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& newName) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::renameObjectStore"); + + ASSERT(isVersionChange()); + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().renameObjectStore(*this, objectStoreIdentifier, newName, [this, protectedThis, requestData](const IDBError& error) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::renameObjectStore (callback)"); + if (error.isNull()) + m_databaseConnection->didRenameObjectStore(IDBResultData::renameObjectStoreSuccess(requestData.requestIdentifier())); + else + m_databaseConnection->didRenameObjectStore(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::clearObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::clearObjectStore"); + + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().clearObjectStore(*this, objectStoreIdentifier, [this, protectedThis, requestData](const IDBError& error) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::clearObjectStore (callback)"); + if (error.isNull()) + m_databaseConnection->didClearObjectStore(IDBResultData::clearObjectStoreSuccess(requestData.requestIdentifier())); + else + m_databaseConnection->didClearObjectStore(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::createIndex(const IDBRequestData& requestData, const IDBIndexInfo& info) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::createIndex"); + + ASSERT(isVersionChange()); + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().createIndex(*this, info, [this, protectedThis, requestData](const IDBError& error) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::createIndex (callback)"); + if (error.isNull()) + m_databaseConnection->didCreateIndex(IDBResultData::createIndexSuccess(requestData.requestIdentifier())); + else + m_databaseConnection->didCreateIndex(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::deleteIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::deleteIndex"); + + ASSERT(isVersionChange()); + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().deleteIndex(*this, objectStoreIdentifier, indexName, [this, protectedThis, requestData](const IDBError& error) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::createIndex (callback)"); + if (error.isNull()) + m_databaseConnection->didDeleteIndex(IDBResultData::deleteIndexSuccess(requestData.requestIdentifier())); + else + m_databaseConnection->didDeleteIndex(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::renameIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::renameIndex"); + + ASSERT(isVersionChange()); + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().renameIndex(*this, objectStoreIdentifier, indexIdentifier, newName, [this, protectedThis, requestData](const IDBError& error) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::renameIndex (callback)"); + if (error.isNull()) + m_databaseConnection->didRenameIndex(IDBResultData::renameIndexSuccess(requestData.requestIdentifier())); + else + m_databaseConnection->didRenameIndex(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + + +void UniqueIDBDatabaseTransaction::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::putOrAdd"); + + ASSERT(!isReadOnly()); + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().putOrAdd(requestData, keyData, value, overwriteMode, [this, protectedThis, requestData](const IDBError& error, const IDBKeyData& key) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::putOrAdd (callback)"); + + if (error.isNull()) + m_databaseConnection->connectionToClient().didPutOrAdd(IDBResultData::putOrAddSuccess(requestData.requestIdentifier(), key)); + else + m_databaseConnection->connectionToClient().didPutOrAdd(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getRecord"); + + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().getRecord(requestData, getRecordData, [this, protectedThis, requestData](const IDBError& error, const IDBGetResult& result) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getRecord (callback)"); + + if (error.isNull()) + m_databaseConnection->connectionToClient().didGetRecord(IDBResultData::getRecordSuccess(requestData.requestIdentifier(), result)); + else + m_databaseConnection->connectionToClient().didGetRecord(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getAllRecords"); + + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().getAllRecords(requestData, getAllRecordsData, [this, protectedThis, requestData](const IDBError& error, const IDBGetAllResult& result) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getAllRecords (callback)"); + + if (error.isNull()) + m_databaseConnection->connectionToClient().didGetAllRecords(IDBResultData::getAllRecordsSuccess(requestData.requestIdentifier(), result)); + else + m_databaseConnection->connectionToClient().didGetAllRecords(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getCount"); + + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().getCount(requestData, keyRangeData, [this, protectedThis, requestData](const IDBError& error, uint64_t count) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getCount (callback)"); + + if (error.isNull()) + m_databaseConnection->connectionToClient().didGetCount(IDBResultData::getCountSuccess(requestData.requestIdentifier(), count)); + else + m_databaseConnection->connectionToClient().didGetCount(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::deleteRecord"); + + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().deleteRecord(requestData, keyRangeData, [this, protectedThis, requestData](const IDBError& error) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::deleteRecord (callback)"); + + if (error.isNull()) + m_databaseConnection->connectionToClient().didDeleteRecord(IDBResultData::deleteRecordSuccess(requestData.requestIdentifier())); + else + m_databaseConnection->connectionToClient().didDeleteRecord(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::openCursor"); + + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().openCursor(requestData, info, [this, protectedThis, requestData](const IDBError& error, const IDBGetResult& result) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::openCursor (callback)"); + + if (error.isNull()) + m_databaseConnection->connectionToClient().didOpenCursor(IDBResultData::openCursorSuccess(requestData.requestIdentifier(), result)); + else + m_databaseConnection->connectionToClient().didOpenCursor(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +void UniqueIDBDatabaseTransaction::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::iterateCursor"); + + ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier()); + + RefPtr protectedThis(this); + m_databaseConnection->database().iterateCursor(requestData, data, [this, protectedThis, requestData](const IDBError& error, const IDBGetResult& result) { + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::iterateCursor (callback)"); + + if (error.isNull()) + m_databaseConnection->connectionToClient().didIterateCursor(IDBResultData::iterateCursorSuccess(requestData.requestIdentifier(), result)); + else + m_databaseConnection->connectionToClient().didIterateCursor(IDBResultData::error(requestData.requestIdentifier(), error)); + }); +} + +const Vector& UniqueIDBDatabaseTransaction::objectStoreIdentifiers() +{ + if (!m_objectStoreIdentifiers.isEmpty()) + return m_objectStoreIdentifiers; + + auto& info = m_databaseConnection->database().info(); + for (auto objectStoreName : info.objectStoreNames()) { + auto objectStoreInfo = info.infoForExistingObjectStore(objectStoreName); + ASSERT(objectStoreInfo); + if (!objectStoreInfo) + continue; + + if (m_transactionInfo.objectStores().contains(objectStoreName)) + m_objectStoreIdentifiers.append(objectStoreInfo->identifier()); + } + + return m_objectStoreIdentifiers; +} + +void UniqueIDBDatabaseTransaction::didActivateInBackingStore(const IDBError& error) +{ + LOG(IndexedDB, "UniqueIDBDatabaseTransaction::didActivateInBackingStore"); + + m_databaseConnection->connectionToClient().didStartTransaction(m_transactionInfo.identifier(), error); +} + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h new file mode 100644 index 000000000..511d57124 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBTransactionInfo.h" +#include "UniqueIDBDatabaseConnection.h" +#include +#include + +namespace WebCore { + +class IDBCursorInfo; +class IDBDatabaseInfo; +class IDBError; +class IDBIndexInfo; +class IDBKeyData; +class IDBObjectStoreInfo; +class IDBRequestData; +class IDBValue; + +struct IDBGetAllRecordsData; +struct IDBGetRecordData; +struct IDBIterateCursorData; +struct IDBKeyRangeData; + +namespace IDBServer { + +class UniqueIDBDatabaseConnection; + +class UniqueIDBDatabaseTransaction : public RefCounted { +public: + static Ref create(UniqueIDBDatabaseConnection&, const IDBTransactionInfo&); + + ~UniqueIDBDatabaseTransaction(); + + UniqueIDBDatabaseConnection& databaseConnection() { return m_databaseConnection.get(); } + const IDBTransactionInfo& info() const { return m_transactionInfo; } + bool isVersionChange() const; + bool isReadOnly() const; + + IDBDatabaseInfo* originalDatabaseInfo() const; + + void abort(); + void abortWithoutCallback(); + void commit(); + + void createObjectStore(const IDBRequestData&, const IDBObjectStoreInfo&); + void deleteObjectStore(const IDBRequestData&, const String& objectStoreName); + void renameObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& newName); + void clearObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier); + void createIndex(const IDBRequestData&, const IDBIndexInfo&); + void deleteIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& indexName); + void renameIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName); + void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, IndexedDB::ObjectStoreOverwriteMode); + void getRecord(const IDBRequestData&, const IDBGetRecordData&); + void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&); + void getCount(const IDBRequestData&, const IDBKeyRangeData&); + void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&); + void openCursor(const IDBRequestData&, const IDBCursorInfo&); + void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&); + + void didActivateInBackingStore(const IDBError&); + + const Vector& objectStoreIdentifiers(); + +private: + UniqueIDBDatabaseTransaction(UniqueIDBDatabaseConnection&, const IDBTransactionInfo&); + + Ref m_databaseConnection; + IDBTransactionInfo m_transactionInfo; + + std::unique_ptr m_originalDatabaseInfo; + + Vector m_objectStoreIdentifiers; +}; + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.cpp new file mode 100644 index 000000000..0d3e3388f --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBCursorInfo.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBDatabase.h" +#include "IDBTransaction.h" +#include "IndexedDB.h" + +namespace WebCore { + +IDBCursorInfo IDBCursorInfo::objectStoreCursor(IDBTransaction& transaction, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type) +{ + return { transaction, objectStoreIdentifier, range, direction, type }; +} + +IDBCursorInfo IDBCursorInfo::indexCursor(IDBTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type) +{ + return { transaction, objectStoreIdentifier, indexIdentifier, range, direction, type }; +} + +IDBCursorInfo::IDBCursorInfo() +{ +} + +IDBCursorInfo::IDBCursorInfo(IDBTransaction& transaction, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type) + : m_cursorIdentifier(transaction.database().connectionProxy()) + , m_transactionIdentifier(transaction.info().identifier()) + , m_objectStoreIdentifier(objectStoreIdentifier) + , m_sourceIdentifier(objectStoreIdentifier) + , m_range(range) + , m_source(IndexedDB::CursorSource::ObjectStore) + , m_direction(direction) + , m_type(type) +{ +} + +IDBCursorInfo::IDBCursorInfo(IDBTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type) + : m_cursorIdentifier(transaction.database().connectionProxy()) + , m_transactionIdentifier(transaction.info().identifier()) + , m_objectStoreIdentifier(objectStoreIdentifier) + , m_sourceIdentifier(indexIdentifier) + , m_range(range) + , m_source(IndexedDB::CursorSource::Index) + , m_direction(direction) + , m_type(type) +{ +} + +IDBCursorInfo::IDBCursorInfo(const IDBResourceIdentifier& cursorIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t sourceIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorSource source, IndexedDB::CursorDirection direction, IndexedDB::CursorType type) + : m_cursorIdentifier(cursorIdentifier) + , m_transactionIdentifier(transactionIdentifier) + , m_objectStoreIdentifier(objectStoreIdentifier) + , m_sourceIdentifier(sourceIdentifier) + , m_range(range) + , m_source(source) + , m_direction(direction) + , m_type(type) +{ +} + +bool IDBCursorInfo::isDirectionForward() const +{ + return m_direction == IndexedDB::CursorDirection::Next || m_direction == IndexedDB::CursorDirection::Nextunique; +} + +CursorDuplicity IDBCursorInfo::duplicity() const +{ + return m_direction == IndexedDB::CursorDirection::Nextunique || m_direction == IndexedDB::CursorDirection::Prevunique ? CursorDuplicity::NoDuplicates : CursorDuplicity::Duplicates; +} + +IDBCursorInfo IDBCursorInfo::isolatedCopy() const +{ + return { m_cursorIdentifier.isolatedCopy(), m_transactionIdentifier.isolatedCopy(), m_objectStoreIdentifier, m_sourceIdentifier, m_range.isolatedCopy(), m_source, m_direction, m_type }; +} + +#if !LOG_DISABLED +String IDBCursorInfo::loggingString() const +{ + if (m_source == IndexedDB::CursorSource::Index) + return String::format("", m_cursorIdentifier.loggingString().utf8().data(), m_sourceIdentifier, m_objectStoreIdentifier, m_transactionIdentifier.loggingString().utf8().data()); + + return String::format("", m_cursorIdentifier.loggingString().utf8().data(), m_objectStoreIdentifier, m_transactionIdentifier.loggingString().utf8().data()); +} +#endif + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.h b/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.h new file mode 100644 index 000000000..84b439be3 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyRangeData.h" +#include "IDBResourceIdentifier.h" + +namespace WebCore { + +class IDBTransaction; + +namespace IndexedDB { +enum class CursorDirection; +enum class CursorSource; +enum class CursorType; +} + +struct IDBKeyRangeData; + +enum class CursorDuplicity { + Duplicates, + NoDuplicates, +}; + +class IDBCursorInfo { +public: + static IDBCursorInfo objectStoreCursor(IDBTransaction&, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType); + static IDBCursorInfo indexCursor(IDBTransaction&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType); + + IDBResourceIdentifier identifier() const { return m_cursorIdentifier; } + uint64_t sourceIdentifier() const { return m_sourceIdentifier; } + uint64_t objectStoreIdentifier() const { return m_objectStoreIdentifier; } + + IndexedDB::CursorSource cursorSource() const { return m_source; } + IndexedDB::CursorDirection cursorDirection() const { return m_direction; } + IndexedDB::CursorType cursorType() const { return m_type; } + const IDBKeyRangeData& range() const { return m_range; } + + bool isDirectionForward() const; + CursorDuplicity duplicity() const; + + IDBCursorInfo isolatedCopy() const; + + WEBCORE_EXPORT IDBCursorInfo(); + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBCursorInfo&); + +#if !LOG_DISABLED + String loggingString() const; +#endif + +private: + IDBCursorInfo(IDBTransaction&, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType); + IDBCursorInfo(IDBTransaction&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType); + + IDBCursorInfo(const IDBResourceIdentifier&, const IDBResourceIdentifier&, uint64_t, uint64_t, const IDBKeyRangeData&, IndexedDB::CursorSource, IndexedDB::CursorDirection, IndexedDB::CursorType); + + IDBResourceIdentifier m_cursorIdentifier; + IDBResourceIdentifier m_transactionIdentifier; + uint64_t m_objectStoreIdentifier { 0 }; + uint64_t m_sourceIdentifier { 0 }; + + IDBKeyRangeData m_range; + + IndexedDB::CursorSource m_source; + IndexedDB::CursorDirection m_direction; + IndexedDB::CursorType m_type; +}; + +template +void IDBCursorInfo::encode(Encoder& encoder) const +{ + encoder << m_cursorIdentifier << m_transactionIdentifier << m_objectStoreIdentifier << m_sourceIdentifier << m_range; + + encoder.encodeEnum(m_source); + encoder.encodeEnum(m_direction); + encoder.encodeEnum(m_type); +} + +template +bool IDBCursorInfo::decode(Decoder& decoder, IDBCursorInfo& info) +{ + if (!decoder.decode(info.m_cursorIdentifier)) + return false; + + if (!decoder.decode(info.m_transactionIdentifier)) + return false; + + if (!decoder.decode(info.m_objectStoreIdentifier)) + return false; + + if (!decoder.decode(info.m_sourceIdentifier)) + return false; + + if (!decoder.decode(info.m_range)) + return false; + + if (!decoder.decodeEnum(info.m_source)) + return false; + + if (!decoder.decodeEnum(info.m_direction)) + return false; + + if (!decoder.decodeEnum(info.m_type)) + return false; + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBCursorRecord.h b/Source/WebCore/Modules/indexeddb/shared/IDBCursorRecord.h new file mode 100644 index 000000000..b4f55fd66 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBCursorRecord.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyData.h" +#include "IDBValue.h" + +namespace WebCore { + +struct IDBCursorRecord { + IDBKeyData key; + IDBKeyData primaryKey; + std::unique_ptr value; + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBCursorRecord&); +}; + +template +void IDBCursorRecord::encode(Encoder& encoder) const +{ + encoder << key << primaryKey << value; +} + +template +bool IDBCursorRecord::decode(Decoder& decoder, IDBCursorRecord& record) +{ + if (!decoder.decode(record.key)) + return false; + + if (!decoder.decode(record.primaryKey)) + return false; + + if (!decoder.decode(record.value)) + return false; + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.cpp new file mode 100644 index 000000000..e5df15c44 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBDatabaseInfo.h" + +#include + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +IDBDatabaseInfo::IDBDatabaseInfo() +{ +} + +IDBDatabaseInfo::IDBDatabaseInfo(const String& name, uint64_t version) + : m_name(name) + , m_version(version) +{ +} + +IDBDatabaseInfo::IDBDatabaseInfo(const IDBDatabaseInfo& other, IsolatedCopyTag) + : m_name(other.m_name.isolatedCopy()) + , m_version(other.m_version) + , m_maxObjectStoreID(other.m_maxObjectStoreID) +{ + for (auto entry : other.m_objectStoreMap) + m_objectStoreMap.set(entry.key, entry.value.isolatedCopy()); +} + +IDBDatabaseInfo IDBDatabaseInfo::isolatedCopy() const +{ + return { *this, IDBDatabaseInfo::IsolatedCopy }; +} + +bool IDBDatabaseInfo::hasObjectStore(const String& name) const +{ + for (auto& objectStore : m_objectStoreMap.values()) { + if (objectStore.name() == name) + return true; + } + + return false; +} + +IDBObjectStoreInfo IDBDatabaseInfo::createNewObjectStore(const String& name, std::optional&& keyPath, bool autoIncrement) +{ + IDBObjectStoreInfo info(++m_maxObjectStoreID, name, WTFMove(keyPath), autoIncrement); + m_objectStoreMap.set(info.identifier(), info); + return info; +} + +void IDBDatabaseInfo::addExistingObjectStore(const IDBObjectStoreInfo& info) +{ + ASSERT(!m_objectStoreMap.contains(info.identifier())); + + if (info.identifier() > m_maxObjectStoreID) + m_maxObjectStoreID = info.identifier(); + + m_objectStoreMap.set(info.identifier(), info); +} + +IDBObjectStoreInfo* IDBDatabaseInfo::getInfoForExistingObjectStore(uint64_t objectStoreIdentifier) +{ + auto iterator = m_objectStoreMap.find(objectStoreIdentifier); + if (iterator == m_objectStoreMap.end()) + return nullptr; + + return &iterator->value; +} + +IDBObjectStoreInfo* IDBDatabaseInfo::getInfoForExistingObjectStore(const String& name) +{ + for (auto& objectStore : m_objectStoreMap.values()) { + if (objectStore.name() == name) + return &objectStore; + } + + return nullptr; +} + +const IDBObjectStoreInfo* IDBDatabaseInfo::infoForExistingObjectStore(uint64_t objectStoreIdentifier) const +{ + return const_cast(this)->getInfoForExistingObjectStore(objectStoreIdentifier); +} + +IDBObjectStoreInfo* IDBDatabaseInfo::infoForExistingObjectStore(uint64_t objectStoreIdentifier) +{ + return getInfoForExistingObjectStore(objectStoreIdentifier); +} + +const IDBObjectStoreInfo* IDBDatabaseInfo::infoForExistingObjectStore(const String& name) const +{ + return const_cast(this)->getInfoForExistingObjectStore(name); +} + +IDBObjectStoreInfo* IDBDatabaseInfo::infoForExistingObjectStore(const String& name) +{ + return getInfoForExistingObjectStore(name); +} + +void IDBDatabaseInfo::renameObjectStore(uint64_t objectStoreIdentifier, const String& newName) +{ + auto* info = infoForExistingObjectStore(objectStoreIdentifier); + if (!info) + return; + + info->rename(newName); +} + +Vector IDBDatabaseInfo::objectStoreNames() const +{ + Vector names; + names.reserveCapacity(m_objectStoreMap.size()); + for (auto& objectStore : m_objectStoreMap.values()) + names.uncheckedAppend(objectStore.name()); + + return names; +} + +void IDBDatabaseInfo::deleteObjectStore(const String& objectStoreName) +{ + auto* info = infoForExistingObjectStore(objectStoreName); + if (!info) + return; + + m_objectStoreMap.remove(info->identifier()); +} + +void IDBDatabaseInfo::deleteObjectStore(uint64_t objectStoreIdentifier) +{ + m_objectStoreMap.remove(objectStoreIdentifier); +} + +#if !LOG_DISABLED +String IDBDatabaseInfo::loggingString() const +{ + StringBuilder builder; + builder.appendLiteral("Database:"); + builder.append(m_name); + builder.appendLiteral(" version "); + builder.appendNumber(m_version); + builder.append('\n'); + for (auto objectStore : m_objectStoreMap.values()) { + builder.append(objectStore.loggingString(1)); + builder.append('\n'); + } + + return builder.toString(); +} +#endif + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.h b/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.h new file mode 100644 index 000000000..db8ff2c77 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBObjectStoreInfo.h" +#include + +namespace WebCore { + +class IDBDatabaseInfo { +public: + IDBDatabaseInfo(const String& name, uint64_t version); + + enum IsolatedCopyTag { IsolatedCopy }; + IDBDatabaseInfo(const IDBDatabaseInfo&, IsolatedCopyTag); + + IDBDatabaseInfo isolatedCopy() const; + + const String& name() const { return m_name; } + + void setVersion(uint64_t version) { m_version = version; } + uint64_t version() const { return m_version; } + + bool hasObjectStore(const String& name) const; + IDBObjectStoreInfo createNewObjectStore(const String& name, std::optional&&, bool autoIncrement); + void addExistingObjectStore(const IDBObjectStoreInfo&); + IDBObjectStoreInfo* infoForExistingObjectStore(uint64_t objectStoreIdentifier); + IDBObjectStoreInfo* infoForExistingObjectStore(const String& objectStoreName); + const IDBObjectStoreInfo* infoForExistingObjectStore(uint64_t objectStoreIdentifier) const; + const IDBObjectStoreInfo* infoForExistingObjectStore(const String& objectStoreName) const; + + void renameObjectStore(uint64_t objectStoreIdentifier, const String& newName); + + Vector objectStoreNames() const; + + void deleteObjectStore(const String& objectStoreName); + void deleteObjectStore(uint64_t objectStoreIdentifier); + + WEBCORE_EXPORT IDBDatabaseInfo(); + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBDatabaseInfo&); + +#if !LOG_DISABLED + String loggingString() const; +#endif + +private: + IDBObjectStoreInfo* getInfoForExistingObjectStore(const String& objectStoreName); + IDBObjectStoreInfo* getInfoForExistingObjectStore(uint64_t objectStoreIdentifier); + + String m_name; + uint64_t m_version { 0 }; + uint64_t m_maxObjectStoreID { 0 }; + + HashMap m_objectStoreMap; + +}; + +template +void IDBDatabaseInfo::encode(Encoder& encoder) const +{ + encoder << m_name << m_version << m_maxObjectStoreID << m_objectStoreMap; +} + +template +bool IDBDatabaseInfo::decode(Decoder& decoder, IDBDatabaseInfo& info) +{ + if (!decoder.decode(info.m_name)) + return false; + + if (!decoder.decode(info.m_version)) + return false; + + if (!decoder.decode(info.m_maxObjectStoreID)) + return false; + + if (!decoder.decode(info.m_objectStoreMap)) + return false; + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBError.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBError.cpp new file mode 100644 index 000000000..7da9486b1 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBError.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBError.h" + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +IDBError::IDBError(ExceptionCode code) + : IDBError(code, emptyString()) +{ +} + +IDBError::IDBError(ExceptionCode code, const String& message) + : m_code(code) + , m_message(message) +{ +} + +IDBError IDBError::isolatedCopy() const +{ + return { m_code, m_message.isolatedCopy() }; +} + +IDBError& IDBError::operator=(const IDBError& other) +{ + m_code = other.m_code; + m_message = other.m_message; + return *this; +} + +String IDBError::name() const +{ + return IDBDatabaseException::getErrorName(m_code); +} + +String IDBError::message() const +{ + return IDBDatabaseException::getErrorDescription(m_code); +} + +RefPtr IDBError::toDOMError() const +{ + return DOMError::create(IDBDatabaseException::getErrorName(m_code), m_message); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBError.h b/Source/WebCore/Modules/indexeddb/shared/IDBError.h new file mode 100644 index 000000000..bb5531bba --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBError.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "DOMError.h" +#include "IDBDatabaseException.h" +#include + +namespace WebCore { + +class IDBError { +public: + IDBError() { } + IDBError(ExceptionCode); + WEBCORE_EXPORT IDBError(ExceptionCode, const String& message); + + static IDBError userDeleteError() + { + return { IDBDatabaseException::UnknownError, ASCIILiteral("Database deleted by request of the user") }; + } + + IDBError& operator=(const IDBError&); + + RefPtr toDOMError() const; + + ExceptionCode code() const { return m_code; } + String name() const; + String message() const; + + bool isNull() const { return m_code == IDBDatabaseException::NoError; } + + IDBError isolatedCopy() const; + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBError&); + +private: + ExceptionCode m_code { IDBDatabaseException::NoError }; + String m_message; +}; + +template +void IDBError::encode(Encoder& encoder) const +{ + encoder << m_code << m_message; +} + +template +bool IDBError::decode(Decoder& decoder, IDBError& error) +{ + if (!decoder.decode(error.m_code)) + return false; + + if (!decoder.decode(error.m_message)) + return false; + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.cpp new file mode 100644 index 000000000..92b6b923d --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBGetAllRecordsData.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyRangeData.h" + +namespace WebCore { + +IDBGetAllRecordsData IDBGetAllRecordsData::isolatedCopy() const +{ + return { keyRangeData.isolatedCopy(), getAllType, count, objectStoreIdentifier, indexIdentifier }; +} + +#if !LOG_DISABLED +String IDBGetAllRecordsData::loggingString() const +{ + if (indexIdentifier) + return String::format("", indexIdentifier, objectStoreIdentifier, getAllType == IndexedDB::GetAllType::Keys ? "Keys" : "Values", keyRangeData.loggingString().utf8().data()); + return String::format("", objectStoreIdentifier, getAllType == IndexedDB::GetAllType::Keys ? "Keys" : "Values", keyRangeData.loggingString().utf8().data()); +} +#endif + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.h b/Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.h new file mode 100644 index 000000000..e9b459800 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyRangeData.h" +#include + +namespace WebCore { + +namespace IndexedDB { +enum class DataSource; +enum class GetAllType; +} + +struct IDBGetAllRecordsData { + IDBKeyRangeData keyRangeData; + IndexedDB::GetAllType getAllType; + std::optional count; + uint64_t objectStoreIdentifier; + uint64_t indexIdentifier; + + IDBGetAllRecordsData isolatedCopy() const; + +#if !LOG_DISABLED + String loggingString() const; +#endif + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBGetAllRecordsData&); +}; + +template +void IDBGetAllRecordsData::encode(Encoder& encoder) const +{ + encoder << keyRangeData; + encoder.encodeEnum(getAllType); + encoder << count << objectStoreIdentifier << indexIdentifier; +} + +template +bool IDBGetAllRecordsData::decode(Decoder& decoder, IDBGetAllRecordsData& getAllRecordsData) +{ + if (!decoder.decode(getAllRecordsData.keyRangeData)) + return false; + + if (!decoder.decodeEnum(getAllRecordsData.getAllType)) + return false; + + if (!decoder.decode(getAllRecordsData.count)) + return false; + + if (!decoder.decode(getAllRecordsData.objectStoreIdentifier)) + return false; + + if (!decoder.decode(getAllRecordsData.indexIdentifier)) + return false; + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.cpp new file mode 100644 index 000000000..d78962604 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBGetRecordData.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyRangeData.h" + +namespace WebCore { + +IDBGetRecordData IDBGetRecordData::isolatedCopy() const +{ + return { keyRangeData.isolatedCopy(), type }; +} + +#if !LOG_DISABLED +String IDBGetRecordData::loggingString() const +{ + return String::format("", type == IDBGetRecordDataType::KeyOnly ? "KeyOnly" : "Key+Value", keyRangeData.loggingString().utf8().data()); +} +#endif + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.h b/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.h new file mode 100644 index 000000000..e82ab54e8 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyRangeData.h" + +namespace WebCore { + +enum class IDBGetRecordDataType { + KeyOnly, + KeyAndValue, +}; + +struct IDBGetRecordData { + IDBKeyRangeData keyRangeData; + IDBGetRecordDataType type; + + IDBGetRecordData isolatedCopy() const; + +#if !LOG_DISABLED + String loggingString() const; +#endif + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBGetRecordData&); +}; + +template +void IDBGetRecordData::encode(Encoder& encoder) const +{ + encoder << keyRangeData; + encoder.encodeEnum(type); +} + +template +bool IDBGetRecordData::decode(Decoder& decoder, IDBGetRecordData& getRecordData) +{ + if (!decoder.decode(getRecordData.keyRangeData)) + return false; + + if (!decoder.decodeEnum(getRecordData.type)) + return false; + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.cpp new file mode 100644 index 000000000..27290f4a4 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBIndexInfo.h" + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +IDBIndexInfo::IDBIndexInfo() +{ +} + +IDBIndexInfo::IDBIndexInfo(uint64_t identifier, uint64_t objectStoreIdentifier, const String& name, IDBKeyPath&& keyPath, bool unique, bool multiEntry) + : m_identifier(identifier) + , m_objectStoreIdentifier(objectStoreIdentifier) + , m_name(name) + , m_keyPath(WTFMove(keyPath)) + , m_unique(unique) + , m_multiEntry(multiEntry) +{ +} + +IDBIndexInfo IDBIndexInfo::isolatedCopy() const +{ + return { m_identifier, m_objectStoreIdentifier, m_name.isolatedCopy(), WebCore::isolatedCopy(m_keyPath), m_unique, m_multiEntry }; +} + +#if !LOG_DISABLED +String IDBIndexInfo::loggingString(int indent) const +{ + String indentString; + for (int i = 0; i < indent; ++i) + indentString.append(" "); + + return makeString(indentString, "Index: ", m_name, String::format(" (%" PRIu64 ") keyPath: %s\n", m_identifier, WebCore::loggingString(m_keyPath).utf8().data())); +} + +String IDBIndexInfo::condensedLoggingString() const +{ + return String::format("", m_name.utf8().data(), m_identifier, m_objectStoreIdentifier); +} +#endif + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.h b/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.h new file mode 100644 index 000000000..1e3741792 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyPath.h" +#include + +namespace WebCore { + +class IDBIndexInfo { +public: + WEBCORE_EXPORT IDBIndexInfo(); + IDBIndexInfo(uint64_t identifier, uint64_t objectStoreIdentifier, const String& name, IDBKeyPath&&, bool unique, bool multiEntry); + + IDBIndexInfo isolatedCopy() const; + + uint64_t identifier() const { return m_identifier; } + uint64_t objectStoreIdentifier() const { return m_objectStoreIdentifier; } + const String& name() const { return m_name; } + const IDBKeyPath& keyPath() const { return m_keyPath; } + bool unique() const { return m_unique; } + bool multiEntry() const { return m_multiEntry; } + + void rename(const String& newName) { m_name = newName; } + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBIndexInfo&); + +#if !LOG_DISABLED + String loggingString(int indent = 0) const; + String condensedLoggingString() const; +#endif + + // FIXME: Remove the need for this. + static const int64_t InvalidId = -1; + +private: + uint64_t m_identifier { 0 }; + uint64_t m_objectStoreIdentifier { 0 }; + String m_name; + IDBKeyPath m_keyPath; + bool m_unique { true }; + bool m_multiEntry { false }; +}; + +template +void IDBIndexInfo::encode(Encoder& encoder) const +{ + encoder << m_identifier << m_objectStoreIdentifier << m_name << m_keyPath << m_unique << m_multiEntry; +} + +template +bool IDBIndexInfo::decode(Decoder& decoder, IDBIndexInfo& info) +{ + if (!decoder.decode(info.m_identifier)) + return false; + + if (!decoder.decode(info.m_objectStoreIdentifier)) + return false; + + if (!decoder.decode(info.m_name)) + return false; + + if (!decoder.decode(info.m_keyPath)) + return false; + + if (!decoder.decode(info.m_unique)) + return false; + + if (!decoder.decode(info.m_multiEntry)) + return false; + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.cpp new file mode 100644 index 000000000..faaeda9fe --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBIterateCursorData.h" + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +IDBIterateCursorData IDBIterateCursorData::isolatedCopy() const +{ + return { keyData.isolatedCopy(), primaryKeyData.isolatedCopy(), count }; +} + +#if !LOG_DISABLED +String IDBIterateCursorData::loggingString() const +{ + return String::format("", keyData.loggingString().utf8().data(), primaryKeyData.loggingString().utf8().data(), count); +} +#endif + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.h b/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.h new file mode 100644 index 000000000..609f74876 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyData.h" + +namespace WebCore { + +struct IDBIterateCursorData { + IDBKeyData keyData; + IDBKeyData primaryKeyData; + unsigned count; + + IDBIterateCursorData isolatedCopy() const; + +#if !LOG_DISABLED + String loggingString() const; +#endif + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBIterateCursorData&); +}; + +template +void IDBIterateCursorData::encode(Encoder& encoder) const +{ + encoder << keyData << primaryKeyData << static_cast(count); +} + +template +bool IDBIterateCursorData::decode(Decoder& decoder, IDBIterateCursorData& iteratorCursorData) +{ + if (!decoder.decode(iteratorCursorData.keyData)) + return false; + + if (!decoder.decode(iteratorCursorData.primaryKeyData)) + return false; + + uint64_t count; + if (!decoder.decode(count)) + return false; + + if (count > std::numeric_limits::max()) + return false; + + iteratorCursorData.count = static_cast(count); + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.cpp new file mode 100644 index 000000000..b0075fe37 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBObjectStoreInfo.h" +#include + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +IDBObjectStoreInfo::IDBObjectStoreInfo() +{ +} + +IDBObjectStoreInfo::IDBObjectStoreInfo(uint64_t identifier, const String& name, std::optional&& keyPath, bool autoIncrement) + : m_identifier(identifier) + , m_name(name) + , m_keyPath(WTFMove(keyPath)) + , m_autoIncrement(autoIncrement) +{ +} + +IDBIndexInfo IDBObjectStoreInfo::createNewIndex(const String& name, IDBKeyPath&& keyPath, bool unique, bool multiEntry) +{ + IDBIndexInfo info(++m_maxIndexID, m_identifier, name, WTFMove(keyPath), unique, multiEntry); + m_indexMap.set(info.identifier(), info); + return info; +} + +void IDBObjectStoreInfo::addExistingIndex(const IDBIndexInfo& info) +{ + ASSERT(!m_indexMap.contains(info.identifier())); + + if (info.identifier() > m_maxIndexID) + m_maxIndexID = info.identifier(); + + m_indexMap.set(info.identifier(), info); +} + +bool IDBObjectStoreInfo::hasIndex(const String& name) const +{ + for (auto& index : m_indexMap.values()) { + if (index.name() == name) + return true; + } + + return false; +} + +bool IDBObjectStoreInfo::hasIndex(uint64_t indexIdentifier) const +{ + return m_indexMap.contains(indexIdentifier); +} + +IDBIndexInfo* IDBObjectStoreInfo::infoForExistingIndex(const String& name) +{ + for (auto& index : m_indexMap.values()) { + if (index.name() == name) + return &index; + } + + return nullptr; +} + +IDBIndexInfo* IDBObjectStoreInfo::infoForExistingIndex(uint64_t identifier) +{ + auto iterator = m_indexMap.find(identifier); + if (iterator == m_indexMap.end()) + return nullptr; + + return &iterator->value; +} + +IDBObjectStoreInfo IDBObjectStoreInfo::isolatedCopy() const +{ + IDBObjectStoreInfo result = { m_identifier, m_name.isolatedCopy(), WebCore::isolatedCopy(m_keyPath), m_autoIncrement }; + + for (auto& iterator : m_indexMap) { + result.m_indexMap.set(iterator.key, iterator.value.isolatedCopy()); + if (iterator.key > result.m_maxIndexID) + result.m_maxIndexID = iterator.key; + } + + ASSERT(result.m_maxIndexID == m_maxIndexID); + + return result; +} + +Vector IDBObjectStoreInfo::indexNames() const +{ + Vector names; + names.reserveCapacity(m_indexMap.size()); + for (auto& index : m_indexMap.values()) + names.uncheckedAppend(index.name()); + + return names; +} + +void IDBObjectStoreInfo::deleteIndex(const String& indexName) +{ + auto* info = infoForExistingIndex(indexName); + if (!info) + return; + + m_indexMap.remove(info->identifier()); +} + +void IDBObjectStoreInfo::deleteIndex(uint64_t indexIdentifier) +{ + m_indexMap.remove(indexIdentifier); +} + +#if !LOG_DISABLED +String IDBObjectStoreInfo::loggingString(int indent) const +{ + StringBuilder builder; + for (int i = 0; i < indent; ++i) + builder.append(' '); + + builder.appendLiteral("Object store: "); + builder.append(m_name); + builder.appendNumber(m_identifier); + for (auto index : m_indexMap.values()) { + builder.append(index.loggingString(indent + 1)); + builder.append('\n'); + } + + return builder.toString(); +} + +String IDBObjectStoreInfo::condensedLoggingString() const +{ + return String::format("", m_name.utf8().data(), m_identifier); +} + +#endif + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.h b/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.h new file mode 100644 index 000000000..0b8525909 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBIndexInfo.h" +#include "IDBKeyPath.h" +#include +#include + +namespace WebCore { + +class IDBObjectStoreInfo { +public: + WEBCORE_EXPORT IDBObjectStoreInfo(); + IDBObjectStoreInfo(uint64_t identifier, const String& name, std::optional&&, bool autoIncrement); + + uint64_t identifier() const { return m_identifier; } + const String& name() const { return m_name; } + const std::optional& keyPath() const { return m_keyPath; } + bool autoIncrement() const { return m_autoIncrement; } + uint64_t maxIndexID() const { return m_maxIndexID; } + + void rename(const String& newName) { m_name = newName; } + + IDBObjectStoreInfo isolatedCopy() const; + + IDBIndexInfo createNewIndex(const String& name, IDBKeyPath&&, bool unique, bool multiEntry); + void addExistingIndex(const IDBIndexInfo&); + bool hasIndex(const String& name) const; + bool hasIndex(uint64_t indexIdentifier) const; + IDBIndexInfo* infoForExistingIndex(const String& name); + IDBIndexInfo* infoForExistingIndex(uint64_t identifier); + + Vector indexNames() const; + const HashMap& indexMap() const { return m_indexMap; } + + void deleteIndex(const String& indexName); + void deleteIndex(uint64_t indexIdentifier); + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBObjectStoreInfo&); + +#if !LOG_DISABLED + String loggingString(int indent = 0) const; + String condensedLoggingString() const; +#endif + +private: + uint64_t m_identifier { 0 }; + String m_name; + std::optional m_keyPath; + bool m_autoIncrement { false }; + uint64_t m_maxIndexID { 0 }; + + HashMap m_indexMap; +}; + +template +void IDBObjectStoreInfo::encode(Encoder& encoder) const +{ + encoder << m_identifier << m_name << m_keyPath << m_autoIncrement << m_maxIndexID << m_indexMap; +} + +template +bool IDBObjectStoreInfo::decode(Decoder& decoder, IDBObjectStoreInfo& info) +{ + if (!decoder.decode(info.m_identifier)) + return false; + + if (!decoder.decode(info.m_name)) + return false; + + if (!decoder.decode(info.m_keyPath)) + return false; + + if (!decoder.decode(info.m_autoIncrement)) + return false; + + if (!decoder.decode(info.m_maxIndexID)) + return false; + + if (!decoder.decode(info.m_indexMap)) + return false; + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.cpp new file mode 100644 index 000000000..574ea8b01 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBRequestData.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBConnectionToServer.h" +#include "IDBDatabase.h" +#include "IDBOpenDBRequest.h" + +namespace WebCore { + +IDBRequestData::IDBRequestData() +{ +} + +IDBRequestData::IDBRequestData(const IDBClient::IDBConnectionProxy& connectionProxy, const IDBOpenDBRequest& request) + : m_serverConnectionIdentifier(connectionProxy.serverConnectionIdentifier()) + , m_requestIdentifier(std::make_unique(connectionProxy, request)) + , m_databaseIdentifier(request.databaseIdentifier()) + , m_requestedVersion(request.version()) + , m_requestType(request.requestType()) +{ +} + +IDBRequestData::IDBRequestData(IDBClient::TransactionOperation& operation) + : m_serverConnectionIdentifier(operation.transaction().database().connectionProxy().serverConnectionIdentifier()) + , m_requestIdentifier(std::make_unique(operation.identifier())) + , m_transactionIdentifier(std::make_unique(operation.transactionIdentifier())) + , m_objectStoreIdentifier(operation.objectStoreIdentifier()) + , m_indexIdentifier(operation.indexIdentifier()) +{ + if (m_indexIdentifier) + m_indexRecordType = operation.indexRecordType(); + + if (operation.cursorIdentifier()) + m_cursorIdentifier = std::make_unique(*operation.cursorIdentifier()); +} + +IDBRequestData::IDBRequestData(const IDBRequestData& other) + : m_serverConnectionIdentifier(other.m_serverConnectionIdentifier) + , m_objectStoreIdentifier(other.m_objectStoreIdentifier) + , m_indexIdentifier(other.m_indexIdentifier) + , m_indexRecordType(other.m_indexRecordType) + , m_databaseIdentifier(other.m_databaseIdentifier) + , m_requestedVersion(other.m_requestedVersion) + , m_requestType(other.m_requestType) +{ + if (other.m_requestIdentifier) + m_requestIdentifier = std::make_unique(*other.m_requestIdentifier); + if (other.m_transactionIdentifier) + m_transactionIdentifier = std::make_unique(*other.m_transactionIdentifier); + if (other.m_cursorIdentifier) + m_cursorIdentifier = std::make_unique(*other.m_cursorIdentifier); +} + +IDBRequestData::IDBRequestData(const IDBRequestData& that, IsolatedCopyTag) +{ + isolatedCopy(that, *this); +} + + +IDBRequestData IDBRequestData::isolatedCopy() const +{ + return { *this, IsolatedCopy }; +} + +void IDBRequestData::isolatedCopy(const IDBRequestData& source, IDBRequestData& destination) +{ + destination.m_serverConnectionIdentifier = source.m_serverConnectionIdentifier; + destination.m_objectStoreIdentifier = source.m_objectStoreIdentifier; + destination.m_indexIdentifier = source.m_indexIdentifier; + destination.m_indexRecordType = source.m_indexRecordType; + destination.m_requestedVersion = source.m_requestedVersion; + destination.m_requestType = source.m_requestType; + + destination.m_databaseIdentifier = source.m_databaseIdentifier.isolatedCopy(); + + if (source.m_requestIdentifier) + destination.m_requestIdentifier = std::make_unique(*source.m_requestIdentifier); + if (source.m_transactionIdentifier) + destination.m_transactionIdentifier = std::make_unique(*source.m_transactionIdentifier); + if (source.m_cursorIdentifier) + destination.m_cursorIdentifier = std::make_unique(*source.m_cursorIdentifier); +} + +uint64_t IDBRequestData::serverConnectionIdentifier() const +{ + ASSERT(m_serverConnectionIdentifier); + return m_serverConnectionIdentifier; +} + +IDBResourceIdentifier IDBRequestData::requestIdentifier() const +{ + ASSERT(m_requestIdentifier); + return *m_requestIdentifier; +} + +IDBResourceIdentifier IDBRequestData::transactionIdentifier() const +{ + ASSERT(m_transactionIdentifier); + return *m_transactionIdentifier; +} + +IDBResourceIdentifier IDBRequestData::cursorIdentifier() const +{ + ASSERT(m_cursorIdentifier); + return *m_cursorIdentifier; +} + +uint64_t IDBRequestData::objectStoreIdentifier() const +{ + ASSERT(m_objectStoreIdentifier); + return m_objectStoreIdentifier; +} + +uint64_t IDBRequestData::indexIdentifier() const +{ + ASSERT(m_objectStoreIdentifier || m_indexIdentifier); + return m_indexIdentifier; +} + +IndexedDB::IndexRecordType IDBRequestData::indexRecordType() const +{ + ASSERT(m_indexIdentifier); + return m_indexRecordType; +} + +uint64_t IDBRequestData::requestedVersion() const +{ + return m_requestedVersion; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.h b/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.h new file mode 100644 index 000000000..8948bb909 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBDatabaseIdentifier.h" +#include "IDBResourceIdentifier.h" +#include "IndexedDB.h" + +namespace WebCore { + +class IDBOpenDBRequest; +class IDBTransaction; + +namespace IndexedDB { +enum class IndexRecordType; +} + +namespace IDBClient { +class IDBConnectionProxy; +class TransactionOperation; +} + +class IDBRequestData { +public: + IDBRequestData(const IDBClient::IDBConnectionProxy&, const IDBOpenDBRequest&); + explicit IDBRequestData(IDBClient::TransactionOperation&); + IDBRequestData(const IDBRequestData&); + + enum IsolatedCopyTag { IsolatedCopy }; + IDBRequestData(const IDBRequestData&, IsolatedCopyTag); + IDBRequestData isolatedCopy() const; + + uint64_t serverConnectionIdentifier() const; + IDBResourceIdentifier requestIdentifier() const; + IDBResourceIdentifier transactionIdentifier() const; + uint64_t objectStoreIdentifier() const; + uint64_t indexIdentifier() const; + IndexedDB::IndexRecordType indexRecordType() const; + IDBResourceIdentifier cursorIdentifier() const; + + const IDBDatabaseIdentifier& databaseIdentifier() const { return m_databaseIdentifier; } + uint64_t requestedVersion() const; + + bool isOpenRequest() const { return m_requestType == IndexedDB::RequestType::Open; } + bool isDeleteRequest() const { return m_requestType == IndexedDB::RequestType::Delete; } + + IDBRequestData isolatedCopy(); + + WEBCORE_EXPORT IDBRequestData(); + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBRequestData&); + +private: + static void isolatedCopy(const IDBRequestData& source, IDBRequestData& destination); + + uint64_t m_serverConnectionIdentifier { 0 }; + std::unique_ptr m_requestIdentifier; + std::unique_ptr m_transactionIdentifier; + std::unique_ptr m_cursorIdentifier; + uint64_t m_objectStoreIdentifier { 0 }; + uint64_t m_indexIdentifier { 0 }; + IndexedDB::IndexRecordType m_indexRecordType; + + IDBDatabaseIdentifier m_databaseIdentifier; + uint64_t m_requestedVersion { 0 }; + + IndexedDB::RequestType m_requestType { IndexedDB::RequestType::Other }; +}; + +template +void IDBRequestData::encode(Encoder& encoder) const +{ + encoder << m_serverConnectionIdentifier << m_objectStoreIdentifier << m_indexIdentifier << m_databaseIdentifier << m_requestedVersion; + + encoder.encodeEnum(m_indexRecordType); + encoder.encodeEnum(m_requestType); + + encoder << !!m_requestIdentifier; + if (m_requestIdentifier) + encoder << *m_requestIdentifier; + + encoder << !!m_transactionIdentifier; + if (m_transactionIdentifier) + encoder << *m_transactionIdentifier; + + encoder << !!m_cursorIdentifier; + if (m_cursorIdentifier) + encoder << *m_cursorIdentifier; +} + +template +bool IDBRequestData::decode(Decoder& decoder, IDBRequestData& request) +{ + if (!decoder.decode(request.m_serverConnectionIdentifier)) + return false; + + if (!decoder.decode(request.m_objectStoreIdentifier)) + return false; + + if (!decoder.decode(request.m_indexIdentifier)) + return false; + + if (!decoder.decode(request.m_databaseIdentifier)) + return false; + + if (!decoder.decode(request.m_requestedVersion)) + return false; + + if (!decoder.decodeEnum(request.m_indexRecordType)) + return false; + + if (!decoder.decodeEnum(request.m_requestType)) + return false; + + bool hasObject; + + if (!decoder.decode(hasObject)) + return false; + if (hasObject) { + std::unique_ptr object = std::make_unique(); + if (!decoder.decode(*object)) + return false; + request.m_requestIdentifier = WTFMove(object); + } + + if (!decoder.decode(hasObject)) + return false; + if (hasObject) { + std::unique_ptr object = std::make_unique(); + if (!decoder.decode(*object)) + return false; + request.m_transactionIdentifier = WTFMove(object); + } + + if (!decoder.decode(hasObject)) + return false; + if (hasObject) { + std::unique_ptr object = std::make_unique(); + if (!decoder.decode(*object)) + return false; + request.m_cursorIdentifier = WTFMove(object); + } + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.cpp new file mode 100644 index 000000000..d7e3f1742 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBResourceIdentifier.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBConnectionToClient.h" +#include "IDBConnectionToServer.h" +#include "IDBRequest.h" +#include + +namespace WebCore { + +static uint64_t nextClientResourceNumber() +{ + static std::atomic currentNumber(1); + return currentNumber += 2; +} + +static uint64_t nextServerResourceNumber() +{ + ASSERT(isMainThread()); + static uint64_t currentNumber = 0; + return currentNumber += 2; +} + +IDBResourceIdentifier::IDBResourceIdentifier() +{ +} + +IDBResourceIdentifier::IDBResourceIdentifier(uint64_t connectionIdentifier, uint64_t resourceIdentifier) + : m_idbConnectionIdentifier(connectionIdentifier) + , m_resourceNumber(resourceIdentifier) +{ +} + +IDBResourceIdentifier::IDBResourceIdentifier(const IDBClient::IDBConnectionProxy& connectionProxy) + : m_idbConnectionIdentifier(connectionProxy.serverConnectionIdentifier()) + , m_resourceNumber(nextClientResourceNumber()) +{ +} + +IDBResourceIdentifier::IDBResourceIdentifier(const IDBClient::IDBConnectionProxy& connectionProxy, const IDBRequest& request) + : m_idbConnectionIdentifier(connectionProxy.serverConnectionIdentifier()) + , m_resourceNumber(request.resourceIdentifier().m_resourceNumber) +{ +} + +IDBResourceIdentifier::IDBResourceIdentifier(const IDBServer::IDBConnectionToClient& connection) + : m_idbConnectionIdentifier(connection.identifier()) + , m_resourceNumber(nextServerResourceNumber()) +{ +} + +IDBResourceIdentifier IDBResourceIdentifier::isolatedCopy() const +{ + return IDBResourceIdentifier(m_idbConnectionIdentifier, m_resourceNumber); +} + +IDBResourceIdentifier IDBResourceIdentifier::emptyValue() +{ + return IDBResourceIdentifier(0, 0); +} + +IDBResourceIdentifier IDBResourceIdentifier::deletedValue() +{ + return IDBResourceIdentifier(std::numeric_limits::max(), std::numeric_limits::max()); +} + +bool IDBResourceIdentifier::isHashTableDeletedValue() const +{ + return m_idbConnectionIdentifier == std::numeric_limits::max() + && m_resourceNumber == std::numeric_limits::max(); +} + +#if !LOG_DISABLED +String IDBResourceIdentifier::loggingString() const +{ + return String::format("<%" PRIu64", %" PRIu64">", m_idbConnectionIdentifier, m_resourceNumber); +} +#endif +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.h b/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.h new file mode 100644 index 000000000..91d7810cc --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include + +namespace WebCore { + +class IDBRequest; + +namespace IDBClient { +class IDBConnectionProxy; +} + +namespace IDBServer { +class IDBConnectionToClient; +} + +class IDBResourceIdentifier { +public: + explicit IDBResourceIdentifier(const IDBClient::IDBConnectionProxy&); + IDBResourceIdentifier(const IDBClient::IDBConnectionProxy&, const IDBRequest&); + explicit IDBResourceIdentifier(const IDBServer::IDBConnectionToClient&); + + static IDBResourceIdentifier deletedValue(); + WEBCORE_EXPORT bool isHashTableDeletedValue() const; + + static IDBResourceIdentifier emptyValue(); + bool isEmpty() const + { + return !m_resourceNumber && !m_idbConnectionIdentifier; + } + + unsigned hash() const + { + uint64_t hashCodes[2] = { m_idbConnectionIdentifier, m_resourceNumber }; + return StringHasher::hashMemory(hashCodes); + } + + bool operator==(const IDBResourceIdentifier& other) const + { + return m_idbConnectionIdentifier == other.m_idbConnectionIdentifier + && m_resourceNumber == other.m_resourceNumber; + } + + uint64_t connectionIdentifier() const { return m_idbConnectionIdentifier; } + + IDBResourceIdentifier isolatedCopy() const; + +#if !LOG_DISABLED + String loggingString() const; +#endif + + WEBCORE_EXPORT IDBResourceIdentifier(); + + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBResourceIdentifier&); + +private: + IDBResourceIdentifier(uint64_t connectionIdentifier, uint64_t resourceIdentifier); + uint64_t m_idbConnectionIdentifier { 0 }; + uint64_t m_resourceNumber { 0 }; +}; + +struct IDBResourceIdentifierHash { + static unsigned hash(const IDBResourceIdentifier& a) { return a.hash(); } + static bool equal(const IDBResourceIdentifier& a, const IDBResourceIdentifier& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +struct IDBResourceIdentifierHashTraits : WTF::CustomHashTraits { + static const bool hasIsEmptyValueFunction = true; + static const bool emptyValueIsZero = false; + + static IDBResourceIdentifier emptyValue() + { + return IDBResourceIdentifier::emptyValue(); + } + + static bool isEmptyValue(const IDBResourceIdentifier& identifier) + { + return identifier.isEmpty(); + } + + static void constructDeletedValue(IDBResourceIdentifier& identifier) + { + identifier = IDBResourceIdentifier::deletedValue(); + } + + static bool isDeletedValue(const IDBResourceIdentifier& identifier) + { + return identifier.isHashTableDeletedValue(); + } +}; + +template +void IDBResourceIdentifier::encode(Encoder& encoder) const +{ + encoder << m_idbConnectionIdentifier << m_resourceNumber; +} + +template +bool IDBResourceIdentifier::decode(Decoder& decoder, IDBResourceIdentifier& identifier) +{ + if (!decoder.decode(identifier.m_idbConnectionIdentifier)) + return false; + + if (!decoder.decode(identifier.m_resourceNumber)) + return false; + + return true; +} + +} // namespace WebCore + +namespace WTF { + +template<> struct HashTraits : WebCore::IDBResourceIdentifierHashTraits { }; +template<> struct DefaultHash { + typedef WebCore::IDBResourceIdentifierHash Hash; +}; + +} // namespace WTF + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBResultData.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBResultData.cpp new file mode 100644 index 000000000..6c6ae6ba0 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBResultData.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBResultData.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "UniqueIDBDatabase.h" +#include "UniqueIDBDatabaseConnection.h" +#include "UniqueIDBDatabaseTransaction.h" + +namespace WebCore { + +IDBResultData::IDBResultData() +{ +} + +IDBResultData::IDBResultData(const IDBResourceIdentifier& requestIdentifier) + : m_requestIdentifier(requestIdentifier) +{ +} + +IDBResultData::IDBResultData(IDBResultType type, const IDBResourceIdentifier& requestIdentifier) + : m_type(type) + , m_requestIdentifier(requestIdentifier) +{ +} + +IDBResultData::IDBResultData(const IDBResultData& other) + : m_type(other.m_type) + , m_requestIdentifier(other.m_requestIdentifier) + , m_error(other.m_error) + , m_databaseConnectionIdentifier(other.m_databaseConnectionIdentifier) + , m_resultInteger(other.m_resultInteger) +{ + if (other.m_databaseInfo) + m_databaseInfo = std::make_unique(*other.m_databaseInfo); + if (other.m_transactionInfo) + m_transactionInfo = std::make_unique(*other.m_transactionInfo); + if (other.m_resultKey) + m_resultKey = std::make_unique(*other.m_resultKey); + if (other.m_getResult) + m_getResult = std::make_unique(*other.m_getResult); + if (other.m_getAllResult) + m_getAllResult = std::make_unique(*other.m_getAllResult); +} + +IDBResultData::IDBResultData(const IDBResultData& that, IsolatedCopyTag) +{ + isolatedCopy(that, *this); +} + +IDBResultData IDBResultData::isolatedCopy() const +{ + return { *this, IsolatedCopy }; +} + +void IDBResultData::isolatedCopy(const IDBResultData& source, IDBResultData& destination) +{ + destination.m_type = source.m_type; + destination.m_requestIdentifier = source.m_requestIdentifier.isolatedCopy(); + destination.m_error = source.m_error.isolatedCopy(); + destination.m_databaseConnectionIdentifier = source.m_databaseConnectionIdentifier; + destination.m_resultInteger = source.m_resultInteger; + + if (source.m_databaseInfo) + destination.m_databaseInfo = std::make_unique(*source.m_databaseInfo, IDBDatabaseInfo::IsolatedCopy); + if (source.m_transactionInfo) + destination.m_transactionInfo = std::make_unique(*source.m_transactionInfo, IDBTransactionInfo::IsolatedCopy); + if (source.m_resultKey) + destination.m_resultKey = std::make_unique(*source.m_resultKey, IDBKeyData::IsolatedCopy); + if (source.m_getResult) + destination.m_getResult = std::make_unique(*source.m_getResult, IDBGetResult::IsolatedCopy); + if (source.m_getAllResult) + destination.m_getAllResult = std::make_unique(*source.m_getAllResult, IDBGetAllResult::IsolatedCopy); +} + +IDBResultData IDBResultData::error(const IDBResourceIdentifier& requestIdentifier, const IDBError& error) +{ + IDBResultData result { requestIdentifier }; + result.m_type = IDBResultType::Error; + result.m_error = error; + return result; +} + +IDBResultData IDBResultData::openDatabaseSuccess(const IDBResourceIdentifier& requestIdentifier, IDBServer::UniqueIDBDatabaseConnection& connection) +{ + IDBResultData result { requestIdentifier }; + result.m_type = IDBResultType::OpenDatabaseSuccess; + result.m_databaseConnectionIdentifier = connection.identifier(); + result.m_databaseInfo = std::make_unique(connection.database().info()); + return result; +} + + +IDBResultData IDBResultData::openDatabaseUpgradeNeeded(const IDBResourceIdentifier& requestIdentifier, IDBServer::UniqueIDBDatabaseTransaction& transaction) +{ + IDBResultData result { requestIdentifier }; + result.m_type = IDBResultType::OpenDatabaseUpgradeNeeded; + result.m_databaseConnectionIdentifier = transaction.databaseConnection().identifier(); + result.m_databaseInfo = std::make_unique(transaction.databaseConnection().database().info()); + result.m_transactionInfo = std::make_unique(transaction.info()); + return result; +} + +IDBResultData IDBResultData::deleteDatabaseSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBDatabaseInfo& info) +{ + IDBResultData result {IDBResultType::DeleteDatabaseSuccess, requestIdentifier }; + result.m_databaseInfo = std::make_unique(info); + return result; +} + +IDBResultData IDBResultData::createObjectStoreSuccess(const IDBResourceIdentifier& requestIdentifier) +{ + return { IDBResultType::CreateObjectStoreSuccess, requestIdentifier }; +} + +IDBResultData IDBResultData::deleteObjectStoreSuccess(const IDBResourceIdentifier& requestIdentifier) +{ + return { IDBResultType::DeleteObjectStoreSuccess, requestIdentifier }; +} + +IDBResultData IDBResultData::renameObjectStoreSuccess(const IDBResourceIdentifier& requestIdentifier) +{ + return { IDBResultType::RenameObjectStoreSuccess, requestIdentifier }; +} + +IDBResultData IDBResultData::clearObjectStoreSuccess(const IDBResourceIdentifier& requestIdentifier) +{ + return { IDBResultType::ClearObjectStoreSuccess, requestIdentifier }; +} + +IDBResultData IDBResultData::createIndexSuccess(const IDBResourceIdentifier& requestIdentifier) +{ + return { IDBResultType::CreateIndexSuccess, requestIdentifier }; +} + +IDBResultData IDBResultData::deleteIndexSuccess(const IDBResourceIdentifier& requestIdentifier) +{ + return { IDBResultType::DeleteIndexSuccess, requestIdentifier }; +} + +IDBResultData IDBResultData::renameIndexSuccess(const IDBResourceIdentifier& requestIdentifier) +{ + return { IDBResultType::RenameIndexSuccess, requestIdentifier }; +} + +IDBResultData IDBResultData::putOrAddSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBKeyData& resultKey) +{ + IDBResultData result { IDBResultType::PutOrAddSuccess, requestIdentifier }; + result.m_resultKey = std::make_unique(resultKey); + return result; +} + +IDBResultData IDBResultData::getRecordSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBGetResult& getResult) +{ + IDBResultData result { IDBResultType::GetRecordSuccess, requestIdentifier }; + result.m_getResult = std::make_unique(getResult); + return result; +} + +IDBResultData IDBResultData::getAllRecordsSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBGetAllResult& getAllResult) +{ + IDBResultData result { IDBResultType::GetAllRecordsSuccess, requestIdentifier }; + result.m_getAllResult = std::make_unique(getAllResult); + return result; +} + +IDBResultData IDBResultData::getCountSuccess(const IDBResourceIdentifier& requestIdentifier, uint64_t count) +{ + IDBResultData result { IDBResultType::GetRecordSuccess, requestIdentifier }; + result.m_resultInteger = count; + return result; +} + +IDBResultData IDBResultData::deleteRecordSuccess(const IDBResourceIdentifier& requestIdentifier) +{ + return { IDBResultType::DeleteRecordSuccess, requestIdentifier }; +} + +IDBResultData IDBResultData::openCursorSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBGetResult& getResult) +{ + IDBResultData result { IDBResultType::OpenCursorSuccess, requestIdentifier }; + result.m_getResult = std::make_unique(getResult); + return result; +} + +IDBResultData IDBResultData::iterateCursorSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBGetResult& getResult) +{ + IDBResultData result { IDBResultType::IterateCursorSuccess, requestIdentifier }; + result.m_getResult = std::make_unique(getResult); + return result; +} + +const IDBDatabaseInfo& IDBResultData::databaseInfo() const +{ + RELEASE_ASSERT(m_databaseInfo); + return *m_databaseInfo; +} + +const IDBTransactionInfo& IDBResultData::transactionInfo() const +{ + RELEASE_ASSERT(m_transactionInfo); + return *m_transactionInfo; +} + +const IDBGetResult& IDBResultData::getResult() const +{ + RELEASE_ASSERT(m_getResult); + return *m_getResult; +} + +const IDBGetAllResult& IDBResultData::getAllResult() const +{ + RELEASE_ASSERT(m_getAllResult); + return *m_getAllResult; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBResultData.h b/Source/WebCore/Modules/indexeddb/shared/IDBResultData.h new file mode 100644 index 000000000..217c9a3ca --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBResultData.h @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBDatabaseInfo.h" +#include "IDBError.h" +#include "IDBGetAllResult.h" +#include "IDBGetResult.h" +#include "IDBKeyData.h" +#include "IDBResourceIdentifier.h" +#include "IDBTransactionInfo.h" +#include "ThreadSafeDataBuffer.h" + +namespace WebCore { + +class ThreadSafeDataBuffer; + +enum class IDBResultType { + Error, + OpenDatabaseSuccess, + OpenDatabaseUpgradeNeeded, + DeleteDatabaseSuccess, + CreateObjectStoreSuccess, + DeleteObjectStoreSuccess, + ClearObjectStoreSuccess, + PutOrAddSuccess, + GetRecordSuccess, + GetAllRecordsSuccess, + GetCountSuccess, + DeleteRecordSuccess, + CreateIndexSuccess, + DeleteIndexSuccess, + OpenCursorSuccess, + IterateCursorSuccess, + RenameObjectStoreSuccess, + RenameIndexSuccess, +}; + +namespace IDBServer { +class UniqueIDBDatabaseConnection; +class UniqueIDBDatabaseTransaction; +} + +class IDBResultData { +public: + static IDBResultData error(const IDBResourceIdentifier&, const IDBError&); + static IDBResultData openDatabaseSuccess(const IDBResourceIdentifier&, IDBServer::UniqueIDBDatabaseConnection&); + static IDBResultData openDatabaseUpgradeNeeded(const IDBResourceIdentifier&, IDBServer::UniqueIDBDatabaseTransaction&); + static IDBResultData deleteDatabaseSuccess(const IDBResourceIdentifier&, const IDBDatabaseInfo&); + static IDBResultData createObjectStoreSuccess(const IDBResourceIdentifier&); + static IDBResultData deleteObjectStoreSuccess(const IDBResourceIdentifier&); + static IDBResultData renameObjectStoreSuccess(const IDBResourceIdentifier&); + static IDBResultData clearObjectStoreSuccess(const IDBResourceIdentifier&); + static IDBResultData createIndexSuccess(const IDBResourceIdentifier&); + static IDBResultData deleteIndexSuccess(const IDBResourceIdentifier&); + static IDBResultData renameIndexSuccess(const IDBResourceIdentifier&); + static IDBResultData putOrAddSuccess(const IDBResourceIdentifier&, const IDBKeyData&); + static IDBResultData getRecordSuccess(const IDBResourceIdentifier&, const IDBGetResult&); + static IDBResultData getAllRecordsSuccess(const IDBResourceIdentifier&, const IDBGetAllResult&); + static IDBResultData getCountSuccess(const IDBResourceIdentifier&, uint64_t count); + static IDBResultData deleteRecordSuccess(const IDBResourceIdentifier&); + static IDBResultData openCursorSuccess(const IDBResourceIdentifier&, const IDBGetResult&); + static IDBResultData iterateCursorSuccess(const IDBResourceIdentifier&, const IDBGetResult&); + + WEBCORE_EXPORT IDBResultData(const IDBResultData&); + + enum IsolatedCopyTag { IsolatedCopy }; + IDBResultData(const IDBResultData&, IsolatedCopyTag); + IDBResultData isolatedCopy() const; + + IDBResultType type() const { return m_type; } + IDBResourceIdentifier requestIdentifier() const { return m_requestIdentifier; } + + const IDBError& error() const { return m_error; } + uint64_t databaseConnectionIdentifier() const { return m_databaseConnectionIdentifier; } + + const IDBDatabaseInfo& databaseInfo() const; + const IDBTransactionInfo& transactionInfo() const; + + const IDBKeyData* resultKey() const { return m_resultKey.get(); } + uint64_t resultInteger() const { return m_resultInteger; } + + WEBCORE_EXPORT const IDBGetResult& getResult() const; + WEBCORE_EXPORT const IDBGetAllResult& getAllResult() const; + + WEBCORE_EXPORT IDBResultData(); + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBResultData&); + +private: + IDBResultData(const IDBResourceIdentifier&); + IDBResultData(IDBResultType, const IDBResourceIdentifier&); + + static void isolatedCopy(const IDBResultData& source, IDBResultData& destination); + + IDBResultType m_type { IDBResultType::Error }; + IDBResourceIdentifier m_requestIdentifier; + + IDBError m_error; + uint64_t m_databaseConnectionIdentifier { 0 }; + std::unique_ptr m_databaseInfo; + std::unique_ptr m_transactionInfo; + std::unique_ptr m_resultKey; + std::unique_ptr m_getResult; + std::unique_ptr m_getAllResult; + uint64_t m_resultInteger { 0 }; +}; + +template +void IDBResultData::encode(Encoder& encoder) const +{ + encoder << m_requestIdentifier << m_error << m_databaseConnectionIdentifier << m_resultInteger; + + encoder.encodeEnum(m_type); + + encoder << !!m_databaseInfo; + if (m_databaseInfo) + encoder << *m_databaseInfo; + + encoder << !!m_transactionInfo; + if (m_transactionInfo) + encoder << *m_transactionInfo; + + encoder << !!m_resultKey; + if (m_resultKey) + encoder << *m_resultKey; + + encoder << !!m_getResult; + if (m_getResult) + encoder << *m_getResult; + + encoder << !!m_getAllResult; + if (m_getAllResult) + encoder << *m_getAllResult; +} + +template bool IDBResultData::decode(Decoder& decoder, IDBResultData& result) +{ + if (!decoder.decode(result.m_requestIdentifier)) + return false; + + if (!decoder.decode(result.m_error)) + return false; + + if (!decoder.decode(result.m_databaseConnectionIdentifier)) + return false; + + if (!decoder.decode(result.m_resultInteger)) + return false; + + if (!decoder.decodeEnum(result.m_type)) + return false; + + bool hasObject; + + if (!decoder.decode(hasObject)) + return false; + if (hasObject) { + auto object = std::make_unique(); + if (!decoder.decode(*object)) + return false; + result.m_databaseInfo = WTFMove(object); + } + + if (!decoder.decode(hasObject)) + return false; + if (hasObject) { + auto object = std::make_unique(); + if (!decoder.decode(*object)) + return false; + result.m_transactionInfo = WTFMove(object); + } + + if (!decoder.decode(hasObject)) + return false; + if (hasObject) { + auto object = std::make_unique(); + if (!decoder.decode(*object)) + return false; + result.m_resultKey = WTFMove(object); + } + + if (!decoder.decode(hasObject)) + return false; + if (hasObject) { + auto object = std::make_unique(); + if (!decoder.decode(*object)) + return false; + result.m_getResult = WTFMove(object); + } + + if (!decoder.decode(hasObject)) + return false; + if (hasObject) { + auto object = std::make_unique(); + if (!decoder.decode(*object)) + return false; + result.m_getAllResult = WTFMove(object); + } + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.cpp new file mode 100644 index 000000000..0010c2443 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBTransactionInfo.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBTransaction.h" + +namespace WebCore { + +IDBTransactionInfo::IDBTransactionInfo() +{ +} + +IDBTransactionInfo::IDBTransactionInfo(const IDBResourceIdentifier& identifier) + : m_identifier(identifier) +{ +} + +IDBTransactionInfo IDBTransactionInfo::clientTransaction(const IDBClient::IDBConnectionProxy& connectionProxy, const Vector& objectStores, IDBTransactionMode mode) +{ + IDBTransactionInfo result((IDBResourceIdentifier(connectionProxy))); + result.m_mode = mode; + result.m_objectStores = objectStores; + + return result; +} + +IDBTransactionInfo IDBTransactionInfo::versionChange(const IDBServer::IDBConnectionToClient& connection, const IDBDatabaseInfo& originalDatabaseInfo, uint64_t newVersion) +{ + IDBTransactionInfo result((IDBResourceIdentifier(connection))); + result.m_mode = IDBTransactionMode::Versionchange; + result.m_newVersion = newVersion; + result.m_originalDatabaseInfo = std::make_unique(originalDatabaseInfo); + + return result; +} + +IDBTransactionInfo::IDBTransactionInfo(const IDBTransactionInfo& info) + : m_identifier(info.identifier()) + , m_mode(info.m_mode) + , m_newVersion(info.m_newVersion) + , m_objectStores(info.m_objectStores) +{ + if (info.m_originalDatabaseInfo) + m_originalDatabaseInfo = std::make_unique(*info.m_originalDatabaseInfo); +} + +IDBTransactionInfo::IDBTransactionInfo(const IDBTransactionInfo& that, IsolatedCopyTag) +{ + isolatedCopy(that, *this); +} + +IDBTransactionInfo IDBTransactionInfo::isolatedCopy() const +{ + return { *this, IsolatedCopy }; +} + +void IDBTransactionInfo::isolatedCopy(const IDBTransactionInfo& source, IDBTransactionInfo& destination) +{ + destination.m_identifier = source.m_identifier.isolatedCopy(); + destination.m_mode = source.m_mode; + destination.m_newVersion = source.m_newVersion; + + destination.m_objectStores.reserveCapacity(source.m_objectStores.size()); + for (auto& objectStore : source.m_objectStores) + destination.m_objectStores.uncheckedAppend(objectStore.isolatedCopy()); + + if (source.m_originalDatabaseInfo) + destination.m_originalDatabaseInfo = std::make_unique(*source.m_originalDatabaseInfo, IDBDatabaseInfo::IsolatedCopy); +} + +#if !LOG_DISABLED +String IDBTransactionInfo::loggingString() const +{ + String modeString; + switch (m_mode) { + case IDBTransactionMode::Readonly: + modeString = ASCIILiteral("readonly"); + break; + case IDBTransactionMode::Readwrite: + modeString = ASCIILiteral("readwrite"); + break; + case IDBTransactionMode::Versionchange: + modeString = ASCIILiteral("versionchange"); + break; + default: + ASSERT_NOT_REACHED(); + } + + return makeString("Transaction: ", m_identifier.loggingString(), " mode ", modeString, " newVersion ", String::number(m_newVersion)); +} +#endif + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.h b/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.h new file mode 100644 index 000000000..636286c86 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBDatabaseInfo.h" +#include "IDBResourceIdentifier.h" +#include "IDBTransactionMode.h" +#include "IndexedDB.h" +#include + +namespace WebCore { + +namespace IDBClient { +class IDBConnectionProxy; +} + +namespace IDBServer { +class IDBConnectionToClient; +} + +class IDBTransactionInfo { +public: + static IDBTransactionInfo clientTransaction(const IDBClient::IDBConnectionProxy&, const Vector& objectStores, IDBTransactionMode); + static IDBTransactionInfo versionChange(const IDBServer::IDBConnectionToClient&, const IDBDatabaseInfo& originalDatabaseInfo, uint64_t newVersion); + + IDBTransactionInfo(const IDBTransactionInfo&); + + enum IsolatedCopyTag { IsolatedCopy }; + IDBTransactionInfo(const IDBTransactionInfo&, IsolatedCopyTag); + + IDBTransactionInfo isolatedCopy() const; + + const IDBResourceIdentifier& identifier() const { return m_identifier; } + + IDBTransactionMode mode() const { return m_mode; } + uint64_t newVersion() const { return m_newVersion; } + + const Vector& objectStores() const { return m_objectStores; } + + IDBDatabaseInfo* originalDatabaseInfo() const { return m_originalDatabaseInfo.get(); } + + WEBCORE_EXPORT IDBTransactionInfo(); + template void encode(Encoder&) const; + template static bool decode(Decoder&, IDBTransactionInfo&); + +#if !LOG_DISABLED + String loggingString() const; +#endif + +private: + IDBTransactionInfo(const IDBResourceIdentifier&); + + static void isolatedCopy(const IDBTransactionInfo& source, IDBTransactionInfo& destination); + + IDBResourceIdentifier m_identifier; + + IDBTransactionMode m_mode { IDBTransactionMode::Readonly }; + uint64_t m_newVersion { 0 }; + Vector m_objectStores; + std::unique_ptr m_originalDatabaseInfo; +}; + +template +void IDBTransactionInfo::encode(Encoder& encoder) const +{ + encoder << m_identifier << m_newVersion << m_objectStores; + encoder.encodeEnum(m_mode); + + encoder << !!m_originalDatabaseInfo; + if (m_originalDatabaseInfo) + encoder << *m_originalDatabaseInfo; +} + +template +bool IDBTransactionInfo::decode(Decoder& decoder, IDBTransactionInfo& info) +{ + if (!decoder.decode(info.m_identifier)) + return false; + + if (!decoder.decode(info.m_newVersion)) + return false; + + if (!decoder.decode(info.m_objectStores)) + return false; + + if (!decoder.decodeEnum(info.m_mode)) + return false; + + bool hasObject; + if (!decoder.decode(hasObject)) + return false; + + if (hasObject) { + std::unique_ptr object = std::make_unique(); + if (!decoder.decode(*object)) + return false; + info.m_originalDatabaseInfo = WTFMove(object); + } + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp new file mode 100644 index 000000000..addd1f8c0 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InProcessIDBServer.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "FileSystem.h" +#include "IDBConnectionToClient.h" +#include "IDBConnectionToServer.h" +#include "IDBCursorInfo.h" +#include "IDBGetRecordData.h" +#include "IDBIterateCursorData.h" +#include "IDBKeyRangeData.h" +#include "IDBOpenDBRequest.h" +#include "IDBRequestData.h" +#include "IDBResultData.h" +#include "IDBValue.h" +#include "Logging.h" +#include + +namespace WebCore { + +Ref InProcessIDBServer::create() +{ + Ref server = adoptRef(*new InProcessIDBServer); + server->m_server->registerConnection(server->connectionToClient()); + return server; +} + +Ref InProcessIDBServer::create(const String& databaseDirectoryPath) +{ + Ref server = adoptRef(*new InProcessIDBServer(databaseDirectoryPath)); + server->m_server->registerConnection(server->connectionToClient()); + return server; +} + +InProcessIDBServer::InProcessIDBServer() + : m_server(IDBServer::IDBServer::create(*this)) +{ + relaxAdoptionRequirement(); + m_connectionToServer = IDBClient::IDBConnectionToServer::create(*this); + m_connectionToClient = IDBServer::IDBConnectionToClient::create(*this); +} + +InProcessIDBServer::InProcessIDBServer(const String& databaseDirectoryPath) + : m_server(IDBServer::IDBServer::create(databaseDirectoryPath, *this)) +{ + relaxAdoptionRequirement(); + m_connectionToServer = IDBClient::IDBConnectionToServer::create(*this); + m_connectionToClient = IDBServer::IDBConnectionToClient::create(*this); +} + +uint64_t InProcessIDBServer::identifier() const +{ + // An instance of InProcessIDBServer always has a 1:1 relationship with its instance of IDBServer. + // Therefore the connection identifier between the two can always be "1". + return 1; +} + +IDBClient::IDBConnectionToServer& InProcessIDBServer::connectionToServer() const +{ + return *m_connectionToServer; +} + +IDBServer::IDBConnectionToClient& InProcessIDBServer::connectionToClient() const +{ + return *m_connectionToClient; +} + +void InProcessIDBServer::deleteDatabase(const IDBRequestData& requestData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData] { + m_server->deleteDatabase(requestData); + }); +} + +void InProcessIDBServer::didDeleteDatabase(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didDeleteDatabase(resultData); + }); +} + +void InProcessIDBServer::openDatabase(const IDBRequestData& requestData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData] { + m_server->openDatabase(requestData); + }); +} + +void InProcessIDBServer::didOpenDatabase(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didOpenDatabase(resultData); + }); +} + +void InProcessIDBServer::didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), transactionIdentifier, error] { + m_connectionToServer->didAbortTransaction(transactionIdentifier, error); + }); +} + +void InProcessIDBServer::didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), transactionIdentifier, error] { + m_connectionToServer->didCommitTransaction(transactionIdentifier, error); + }); +} + +void InProcessIDBServer::didCreateObjectStore(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didCreateObjectStore(resultData); + }); +} + +void InProcessIDBServer::didDeleteObjectStore(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didDeleteObjectStore(resultData); + }); +} + +void InProcessIDBServer::didRenameObjectStore(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didRenameObjectStore(resultData); + }); +} + +void InProcessIDBServer::didClearObjectStore(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didClearObjectStore(resultData); + }); +} + +void InProcessIDBServer::didCreateIndex(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didCreateIndex(resultData); + }); +} + +void InProcessIDBServer::didDeleteIndex(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didDeleteIndex(resultData); + }); +} + +void InProcessIDBServer::didRenameIndex(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didRenameIndex(resultData); + }); +} + +void InProcessIDBServer::didPutOrAdd(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didPutOrAdd(resultData); + }); +} + +void InProcessIDBServer::didGetRecord(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didGetRecord(resultData); + }); +} + +void InProcessIDBServer::didGetAllRecords(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didGetAllRecords(resultData); + }); +} + +void InProcessIDBServer::didGetCount(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didGetCount(resultData); + }); +} + +void InProcessIDBServer::didDeleteRecord(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didDeleteRecord(resultData); + }); +} + +void InProcessIDBServer::didOpenCursor(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didOpenCursor(resultData); + }); +} + +void InProcessIDBServer::didIterateCursor(const IDBResultData& resultData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] { + m_connectionToServer->didIterateCursor(resultData); + }); +} + +void InProcessIDBServer::abortTransaction(const IDBResourceIdentifier& resourceIdentifier) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resourceIdentifier] { + m_server->abortTransaction(resourceIdentifier); + }); +} + +void InProcessIDBServer::commitTransaction(const IDBResourceIdentifier& resourceIdentifier) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resourceIdentifier] { + m_server->commitTransaction(resourceIdentifier); + }); +} + +void InProcessIDBServer::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier, transactionIdentifier] { + m_server->didFinishHandlingVersionChangeTransaction(databaseConnectionIdentifier, transactionIdentifier); + }); +} + +void InProcessIDBServer::createObjectStore(const IDBRequestData& resultData, const IDBObjectStoreInfo& info) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData, info] { + m_server->createObjectStore(resultData, info); + }); +} + +void InProcessIDBServer::deleteObjectStore(const IDBRequestData& requestData, const String& objectStoreName) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, objectStoreName] { + m_server->deleteObjectStore(requestData, objectStoreName); + }); +} + +void InProcessIDBServer::renameObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& newName) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, objectStoreIdentifier, newName] { + m_server->renameObjectStore(requestData, objectStoreIdentifier, newName); + }); +} + +void InProcessIDBServer::clearObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, objectStoreIdentifier] { + m_server->clearObjectStore(requestData, objectStoreIdentifier); + }); +} + +void InProcessIDBServer::createIndex(const IDBRequestData& requestData, const IDBIndexInfo& info) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, info] { + m_server->createIndex(requestData, info); + }); +} + +void InProcessIDBServer::deleteIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, objectStoreIdentifier, indexName] { + m_server->deleteIndex(requestData, objectStoreIdentifier, indexName); + }); +} + +void InProcessIDBServer::renameIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, objectStoreIdentifier, indexIdentifier, newName] { + m_server->renameIndex(requestData, objectStoreIdentifier, indexIdentifier, newName); + }); +} + +void InProcessIDBServer::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, const IndexedDB::ObjectStoreOverwriteMode overwriteMode) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, keyData, value, overwriteMode] { + m_server->putOrAdd(requestData, keyData, value, overwriteMode); + }); +} + +void InProcessIDBServer::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, getRecordData] { + m_server->getRecord(requestData, getRecordData); + }); +} + +void InProcessIDBServer::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, getAllRecordsData] { + m_server->getAllRecords(requestData, getAllRecordsData); + }); +} + +void InProcessIDBServer::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, keyRangeData] { + m_server->getCount(requestData, keyRangeData); + }); +} + +void InProcessIDBServer::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, keyRangeData] { + m_server->deleteRecord(requestData, keyRangeData); + }); +} + +void InProcessIDBServer::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, info] { + m_server->openCursor(requestData, info); + }); +} + +void InProcessIDBServer::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, data] { + m_server->iterateCursor(requestData, data); + }); +} + +void InProcessIDBServer::establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo& info) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier, info] { + m_server->establishTransaction(databaseConnectionIdentifier, info); + }); +} + +void InProcessIDBServer::fireVersionChangeEvent(IDBServer::UniqueIDBDatabaseConnection& connection, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier = connection.identifier(), requestIdentifier, requestedVersion] { + m_connectionToServer->fireVersionChangeEvent(databaseConnectionIdentifier, requestIdentifier, requestedVersion); + }); +} + +void InProcessIDBServer::didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), transactionIdentifier, error] { + m_connectionToServer->didStartTransaction(transactionIdentifier, error); + }); +} + +void InProcessIDBServer::didCloseFromServer(IDBServer::UniqueIDBDatabaseConnection& connection, const IDBError& error) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier = connection.identifier(), error] { + m_connectionToServer->didCloseFromServer(databaseConnectionIdentifier, error); + }); +} + +void InProcessIDBServer::notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestIdentifier, oldVersion, newVersion] { + m_connectionToServer->notifyOpenDBRequestBlocked(requestIdentifier, oldVersion, newVersion); + }); +} + +void InProcessIDBServer::databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier] { + m_server->databaseConnectionPendingClose(databaseConnectionIdentifier); + }); +} + +void InProcessIDBServer::databaseConnectionClosed(uint64_t databaseConnectionIdentifier) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier] { + m_server->databaseConnectionClosed(databaseConnectionIdentifier); + }); +} + +void InProcessIDBServer::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier, transactionIdentifier] { + m_server->abortOpenAndUpgradeNeeded(databaseConnectionIdentifier, transactionIdentifier); + }); +} + +void InProcessIDBServer::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier, requestIdentifier] { + m_server->didFireVersionChangeEvent(databaseConnectionIdentifier, requestIdentifier); + }); +} + +void InProcessIDBServer::openDBRequestCancelled(const IDBRequestData& requestData) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData] { + m_server->openDBRequestCancelled(requestData); + }); +} + +void InProcessIDBServer::confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier] { + m_server->confirmDidCloseFromServer(databaseConnectionIdentifier); + }); +} + +void InProcessIDBServer::getAllDatabaseNames(const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), mainFrameOrigin, openingOrigin, callbackID] { + m_server->getAllDatabaseNames(m_connectionToServer->identifier(), mainFrameOrigin, openingOrigin, callbackID); + }); +} + +void InProcessIDBServer::didGetAllDatabaseNames(uint64_t callbackID, const Vector& databaseNames) +{ + RunLoop::current().dispatch([this, protectedThis = makeRef(*this), callbackID, databaseNames] { + m_connectionToServer->didGetAllDatabaseNames(callbackID, databaseNames); + }); +} + +void InProcessIDBServer::accessToTemporaryFileComplete(const String& path) +{ + deleteFile(path); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h new file mode 100644 index 000000000..d0dc2ec02 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBConnectionToClient.h" +#include "IDBConnectionToServer.h" +#include "IDBOpenDBRequest.h" +#include "IDBServer.h" +#include +#include +#include + +namespace WebCore { + +namespace IDBClient { +class IDBConnectionToServer; +} + +namespace IDBServer { +class IDBServer; +} + +class InProcessIDBServer final : public IDBClient::IDBConnectionToServerDelegate, public IDBServer::IDBConnectionToClientDelegate, public RefCounted, public IDBServer::IDBBackingStoreTemporaryFileHandler { +public: + WEBCORE_EXPORT static Ref create(); + WEBCORE_EXPORT static Ref create(const String& databaseDirectoryPath); + + WEBCORE_EXPORT IDBClient::IDBConnectionToServer& connectionToServer() const; + IDBServer::IDBConnectionToClient& connectionToClient() const; + IDBServer::IDBServer& server() { return m_server.get(); } + + IDBServer::IDBServer& idbServer() { return m_server.get(); } + + // IDBConnectionToServer + void deleteDatabase(const IDBRequestData&) final; + void openDatabase(const IDBRequestData&) final; + void abortTransaction(const IDBResourceIdentifier&) final; + void commitTransaction(const IDBResourceIdentifier&) final; + void didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier&) final; + void createObjectStore(const IDBRequestData&, const IDBObjectStoreInfo&) final; + void deleteObjectStore(const IDBRequestData&, const String& objectStoreName) final; + void renameObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& newName) final; + void clearObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier) final; + void createIndex(const IDBRequestData&, const IDBIndexInfo&) final; + void deleteIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& indexName) final; + void renameIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) final; + void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, const IndexedDB::ObjectStoreOverwriteMode) final; + void getRecord(const IDBRequestData&, const IDBGetRecordData&) final; + void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&) final; + void getCount(const IDBRequestData&, const IDBKeyRangeData&) final; + void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&) final; + void openCursor(const IDBRequestData&, const IDBCursorInfo&) final; + void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&) final; + void establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo&) final; + void databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier) final; + void databaseConnectionClosed(uint64_t databaseConnectionIdentifier) final; + void abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) final; + void didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier) final; + void openDBRequestCancelled(const IDBRequestData&) final; + void confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier) final; + void getAllDatabaseNames(const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID) final; + + // IDBConnectionToClient + uint64_t identifier() const override; + void didDeleteDatabase(const IDBResultData&) final; + void didOpenDatabase(const IDBResultData&) final; + void didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) final; + void didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) final; + void didCreateObjectStore(const IDBResultData&) final; + void didDeleteObjectStore(const IDBResultData&) final; + void didRenameObjectStore(const IDBResultData&) final; + void didClearObjectStore(const IDBResultData&) final; + void didCreateIndex(const IDBResultData&) final; + void didDeleteIndex(const IDBResultData&) final; + void didRenameIndex(const IDBResultData&) final; + void didPutOrAdd(const IDBResultData&) final; + void didGetRecord(const IDBResultData&) final; + void didGetAllRecords(const IDBResultData&) final; + void didGetCount(const IDBResultData&) final; + void didDeleteRecord(const IDBResultData&) final; + void didOpenCursor(const IDBResultData&) final; + void didIterateCursor(const IDBResultData&) final; + void fireVersionChangeEvent(IDBServer::UniqueIDBDatabaseConnection&, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) final; + void didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) final; + void didCloseFromServer(IDBServer::UniqueIDBDatabaseConnection&, const IDBError&) final; + void notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion) final; + void didGetAllDatabaseNames(uint64_t callbackID, const Vector& databaseNames) final; + + void ref() override { RefCounted::ref(); } + void deref() override { RefCounted::deref(); } + + void prepareForAccessToTemporaryFile(const String&) override { } + void accessToTemporaryFileComplete(const String& path) override; + +private: + InProcessIDBServer(); + InProcessIDBServer(const String& databaseDirectoryPath); + + Ref m_server; + RefPtr m_connectionToServer; + RefPtr m_connectionToClient; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IndexKey.cpp b/Source/WebCore/Modules/indexeddb/shared/IndexKey.cpp new file mode 100644 index 000000000..a40f90120 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IndexKey.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IndexKey.h" + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +IndexKey::IndexKey() +{ +} + +IndexKey::IndexKey(Vector&& keys) +{ + m_keys.swap(keys); +} + +IndexKey IndexKey::isolatedCopy() const +{ + Vector keys; + keys.reserveInitialCapacity(m_keys.size()); + for (auto& key : m_keys) + keys.uncheckedAppend(key.isolatedCopy()); + + return { WTFMove(keys) }; +} + +IDBKeyData IndexKey::asOneKey() const +{ + if (m_keys.isEmpty()) + return { }; + + if (m_keys.size() == 1) + return m_keys[0]; + + IDBKeyData result; + result.setArrayValue(m_keys); + return result; +} + +Vector IndexKey::multiEntry() const +{ + Vector multiEntry; + for (auto& key : m_keys) { + if (!key.isValid()) + continue; + + bool skip = false; + for (auto& otherKey : multiEntry) { + if (key == otherKey) { + skip = true; + break; + } + } + + if (!skip) + multiEntry.append(key); + } + + return multiEntry; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/indexeddb/shared/IndexKey.h b/Source/WebCore/Modules/indexeddb/shared/IndexKey.h new file mode 100644 index 000000000..ad80e86f1 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/shared/IndexKey.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyData.h" +#include +#include + +namespace WebCore { + +class IndexKey { +public: + IndexKey(); + IndexKey(Vector&&); + + IndexKey isolatedCopy() const; + + IDBKeyData asOneKey() const; + Vector multiEntry() const; + + bool isNull() const { return m_keys.isEmpty(); } + +private: + Vector m_keys; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/Modules/mediacontrols/MediaControlsHost.cpp b/Source/WebCore/Modules/mediacontrols/MediaControlsHost.cpp new file mode 100644 index 000000000..8d8eeff0c --- /dev/null +++ b/Source/WebCore/Modules/mediacontrols/MediaControlsHost.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(MEDIA_CONTROLS_SCRIPT) + +#include "MediaControlsHost.h" + +#include "CaptionUserPreferences.h" +#include "Element.h" +#include "HTMLMediaElement.h" +#include "Logging.h" +#include "MediaControlElements.h" +#include "Page.h" +#include "PageGroup.h" +#include "RenderTheme.h" +#include "TextTrack.h" +#include "TextTrackList.h" +#include "UUID.h" +#include + +namespace WebCore { + +const AtomicString& MediaControlsHost::automaticKeyword() +{ + static NeverDestroyed automatic("automatic", AtomicString::ConstructFromLiteral); + return automatic; +} + +const AtomicString& MediaControlsHost::forcedOnlyKeyword() +{ + static NeverDestroyed forcedOn("forced-only", AtomicString::ConstructFromLiteral); + return forcedOn; +} + +const AtomicString& MediaControlsHost::alwaysOnKeyword() +{ + static NeverDestroyed alwaysOn("always-on", AtomicString::ConstructFromLiteral); + return alwaysOn; +} + +const AtomicString& MediaControlsHost::manualKeyword() +{ + static NeverDestroyed alwaysOn("manual", AtomicString::ConstructFromLiteral); + return alwaysOn; +} + + +Ref MediaControlsHost::create(HTMLMediaElement* mediaElement) +{ + return adoptRef(*new MediaControlsHost(mediaElement)); +} + +MediaControlsHost::MediaControlsHost(HTMLMediaElement* mediaElement) + : m_mediaElement(mediaElement) +{ + ASSERT(mediaElement); +} + +MediaControlsHost::~MediaControlsHost() +{ +} + +Vector> MediaControlsHost::sortedTrackListForMenu(TextTrackList& trackList) +{ + Page* page = m_mediaElement->document().page(); + if (!page) + return { }; + + return page->group().captionPreferences().sortedTrackListForMenu(&trackList); +} + +Vector> MediaControlsHost::sortedTrackListForMenu(AudioTrackList& trackList) +{ + Page* page = m_mediaElement->document().page(); + if (!page) + return { }; + + return page->group().captionPreferences().sortedTrackListForMenu(&trackList); +} + +String MediaControlsHost::displayNameForTrack(const std::optional& track) +{ + if (!track) + return emptyString(); + + Page* page = m_mediaElement->document().page(); + if (!page) + return emptyString(); + + return WTF::visit([&page](auto& track) { + return page->group().captionPreferences().displayNameForTrack(track.get()); + }, track.value()); +} + +TextTrack* MediaControlsHost::captionMenuOffItem() +{ + return TextTrack::captionMenuOffItem(); +} + +TextTrack* MediaControlsHost::captionMenuAutomaticItem() +{ + return TextTrack::captionMenuAutomaticItem(); +} + +AtomicString MediaControlsHost::captionDisplayMode() const +{ + Page* page = m_mediaElement->document().page(); + if (!page) + return emptyAtom; + + switch (page->group().captionPreferences().captionDisplayMode()) { + case CaptionUserPreferences::Automatic: + return automaticKeyword(); + case CaptionUserPreferences::ForcedOnly: + return forcedOnlyKeyword(); + case CaptionUserPreferences::AlwaysOn: + return alwaysOnKeyword(); + case CaptionUserPreferences::Manual: + return manualKeyword(); + default: + ASSERT_NOT_REACHED(); + return emptyAtom; + } +} + +void MediaControlsHost::setSelectedTextTrack(TextTrack* track) +{ + m_mediaElement->setSelectedTextTrack(track); +} + +Element* MediaControlsHost::textTrackContainer() +{ + if (!m_textTrackContainer) { + m_textTrackContainer = MediaControlTextTrackContainerElement::create(m_mediaElement->document()); + m_textTrackContainer->setMediaController(m_mediaElement); + } + return m_textTrackContainer.get(); +} + +void MediaControlsHost::updateTextTrackContainer() +{ + if (m_textTrackContainer) + m_textTrackContainer->updateDisplay(); +} + +void MediaControlsHost::enteredFullscreen() +{ + if (m_textTrackContainer) + m_textTrackContainer->enteredFullscreen(); +} + +void MediaControlsHost::exitedFullscreen() +{ + if (m_textTrackContainer) + m_textTrackContainer->exitedFullscreen(); +} + +void MediaControlsHost::updateCaptionDisplaySizes() +{ + if (m_textTrackContainer) + m_textTrackContainer->updateSizes(true); +} + +bool MediaControlsHost::allowsInlineMediaPlayback() const +{ + return !m_mediaElement->mediaSession().requiresFullscreenForVideoPlayback(*m_mediaElement); +} + +bool MediaControlsHost::supportsFullscreen() const +{ + return m_mediaElement->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard); +} + +bool MediaControlsHost::isVideoLayerInline() const +{ + return m_mediaElement->isVideoLayerInline(); +} + +bool MediaControlsHost::isInMediaDocument() const +{ + return m_mediaElement->document().isMediaDocument(); +} + +void MediaControlsHost::setPreparedToReturnVideoLayerToInline(bool value) +{ + m_mediaElement->setPreparedToReturnVideoLayerToInline(value); +} + +bool MediaControlsHost::userGestureRequired() const +{ + return !m_mediaElement->mediaSession().playbackPermitted(*m_mediaElement); +} + +String MediaControlsHost::externalDeviceDisplayName() const +{ +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + MediaPlayer* player = m_mediaElement->player(); + if (!player) { + LOG(Media, "MediaControlsHost::externalDeviceDisplayName - returning \"\" because player is NULL"); + return emptyString(); + } + + String name = player->wirelessPlaybackTargetName(); + LOG(Media, "MediaControlsHost::externalDeviceDisplayName - returning \"%s\"", name.utf8().data()); + + return name; +#else + return emptyString(); +#endif +} + +auto MediaControlsHost::externalDeviceType() const -> DeviceType +{ +#if !ENABLE(WIRELESS_PLAYBACK_TARGET) + return DeviceType::None; +#else + MediaPlayer* player = m_mediaElement->player(); + if (!player) { + LOG(Media, "MediaControlsHost::externalDeviceType - returning \"none\" because player is NULL"); + return DeviceType::None; + } + + switch (player->wirelessPlaybackTargetType()) { + case MediaPlayer::TargetTypeNone: + return DeviceType::None; + case MediaPlayer::TargetTypeAirPlay: + return DeviceType::Airplay; + case MediaPlayer::TargetTypeTVOut: + return DeviceType::Tvout; + } + + ASSERT_NOT_REACHED(); + return DeviceType::None; +#endif +} + +bool MediaControlsHost::controlsDependOnPageScaleFactor() const +{ + return m_mediaElement->mediaControlsDependOnPageScaleFactor(); +} + +void MediaControlsHost::setControlsDependOnPageScaleFactor(bool value) +{ + m_mediaElement->setMediaControlsDependOnPageScaleFactor(value); +} + +String MediaControlsHost::generateUUID() const +{ + return createCanonicalUUIDString(); +} + +String MediaControlsHost::shadowRootCSSText() const +{ + Page* page = m_mediaElement->document().page(); + if (!page) + return emptyString(); + return RenderTheme::themeForPage(page)->modernMediaControlsStyleSheet(); +} + +String MediaControlsHost::base64StringForIconAndPlatform(const String& iconName, const String& platform) const +{ + Page* page = m_mediaElement->document().page(); + if (!page) + return emptyString(); + return RenderTheme::themeForPage(page)->mediaControlsBase64StringForIconAndPlatform(iconName, platform); +} + +} + +#endif diff --git a/Source/WebCore/Modules/mediacontrols/MediaControlsHost.h b/Source/WebCore/Modules/mediacontrols/MediaControlsHost.h new file mode 100644 index 000000000..1f034e522 --- /dev/null +++ b/Source/WebCore/Modules/mediacontrols/MediaControlsHost.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(MEDIA_CONTROLS_SCRIPT) + +#include +#include +#include +#include +#include + +namespace WebCore { + +class AudioTrack; +class AudioTrackList; +class Element; +class HTMLMediaElement; +class MediaControlTextTrackContainerElement; +class TextTrack; +class TextTrackList; + +class MediaControlsHost : public RefCounted { +public: + static Ref create(HTMLMediaElement*); + ~MediaControlsHost(); + + static const AtomicString& automaticKeyword(); + static const AtomicString& forcedOnlyKeyword(); + static const AtomicString& alwaysOnKeyword(); + static const AtomicString& manualKeyword(); + + Vector> sortedTrackListForMenu(TextTrackList&); + Vector> sortedTrackListForMenu(AudioTrackList&); + + using TextOrAudioTrack = WTF::Variant, RefPtr>; + String displayNameForTrack(const std::optional&); + + TextTrack* captionMenuOffItem(); + TextTrack* captionMenuAutomaticItem(); + AtomicString captionDisplayMode() const; + void setSelectedTextTrack(TextTrack*); + Element* textTrackContainer(); + void updateTextTrackContainer(); + bool allowsInlineMediaPlayback() const; + bool supportsFullscreen() const; + bool isVideoLayerInline() const; + bool isInMediaDocument() const; + bool userGestureRequired() const; + void setPreparedToReturnVideoLayerToInline(bool); + + void updateCaptionDisplaySizes(); + void enteredFullscreen(); + void exitedFullscreen(); + + String externalDeviceDisplayName() const; + + enum class DeviceType { None, Airplay, Tvout }; + DeviceType externalDeviceType() const; + + bool controlsDependOnPageScaleFactor() const; + void setControlsDependOnPageScaleFactor(bool v); + + String generateUUID() const; + + String shadowRootCSSText() const; + String base64StringForIconAndPlatform(const String& iconName, const String& platform) const; + +private: + MediaControlsHost(HTMLMediaElement*); + + HTMLMediaElement* m_mediaElement; + RefPtr m_textTrackContainer; +}; + +} + +#endif diff --git a/Source/WebCore/Modules/mediacontrols/MediaControlsHost.idl b/Source/WebCore/Modules/mediacontrols/MediaControlsHost.idl new file mode 100644 index 000000000..3e4fcb670 --- /dev/null +++ b/Source/WebCore/Modules/mediacontrols/MediaControlsHost.idl @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum DeviceType { + "none", + "airplay", + "tvout" +}; + +[ + Conditional=MEDIA_CONTROLS_SCRIPT, + ImplementationLacksVTable, + NoInterfaceObject, +] interface MediaControlsHost { + sequence sortedTrackListForMenu(TextTrackList trackList); + sequence sortedTrackListForMenu(AudioTrackList trackList); + DOMString displayNameForTrack((TextTrack or AudioTrack)? track); + readonly attribute TextTrack captionMenuOffItem; + readonly attribute TextTrack captionMenuAutomaticItem; + readonly attribute DOMString captionDisplayMode; + void setSelectedTextTrack(TextTrack? track); + void setPreparedToReturnVideoLayerToInline(boolean prepared); + readonly attribute HTMLElement textTrackContainer; + readonly attribute boolean allowsInlineMediaPlayback; + readonly attribute boolean supportsFullscreen; + readonly attribute boolean isVideoLayerInline; + readonly attribute boolean userGestureRequired; + readonly attribute boolean isInMediaDocument; + + readonly attribute DOMString externalDeviceDisplayName; + readonly attribute DeviceType externalDeviceType; + + attribute boolean controlsDependOnPageScaleFactor; + + void updateTextTrackContainer(); + void enteredFullscreen(); + void exitedFullscreen(); + + DOMString generateUUID(); + + [EnabledAtRuntime=ModernMediaControls] readonly attribute DOMString shadowRootCSSText; + [EnabledAtRuntime=ModernMediaControls] DOMString base64StringForIconAndPlatform(DOMString iconName, DOMString platform); +}; diff --git a/Source/WebCore/Modules/mediacontrols/assets-apple-iOS.svg b/Source/WebCore/Modules/mediacontrols/assets-apple-iOS.svg new file mode 100644 index 000000000..db2a2d334 --- /dev/null +++ b/Source/WebCore/Modules/mediacontrols/assets-apple-iOS.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/WebCore/Modules/mediacontrols/mediaControlsApple.css b/Source/WebCore/Modules/mediacontrols/mediaControlsApple.css new file mode 100644 index 000000000..b7884d285 --- /dev/null +++ b/Source/WebCore/Modules/mediacontrols/mediaControlsApple.css @@ -0,0 +1,1153 @@ +/* + * Copyright (C) 2013, 2014, 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +audio { + width: 200px; + height: 25px; +} + +body:-webkit-full-page-media { + background-color: rgb(38, 38, 38); +} + +video:-webkit-full-page-media { + margin: auto; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +video:-webkit-full-page-media::-webkit-media-controls-panel { + bottom: 0px; +} + +video:-webkit-full-page-media::-webkit-media-controls-panel.no-video { + opacity: 1; +} + +::-webkit-media-controls { + width: inherit; + height: inherit; + position: relative; + display: -webkit-flex !important; + -webkit-align-items: stretch; + -webkit-justify-content: flex-end; + -webkit-flex-direction: column; + font: -webkit-small-control; + white-space: nowrap; + -webkit-font-smoothing: subpixel-antialiased; +} + +::-webkit-media-controls.placeholder-showing { + overflow: hidden; +} + +video::-webkit-media-text-track-container, +audio::-webkit-media-text-track-container { + position: relative; + -webkit-flex: 1 1 auto; +} + +video::-webkit-media-controls-panel, +audio::-webkit-media-controls-panel { + box-sizing: border-box; + position: relative; + bottom: 0; + width: 100%; + min-height: 25px; + height: 25px; + line-height: 25px; + -webkit-user-select: none; + -webkit-user-drag: element; + background-color: transparent; + + display: -webkit-flex; + -webkit-flex-direction: row; + -webkit-align-items: center; + -webkit-user-select: none; + + direction: ltr; + + transition: opacity 0.25s linear; + + -webkit-text-zoom: reset +} + +video::-webkit-media-controls-panel { + opacity: 0; + padding-top: 20px; + min-height: 45px; + height: 45px; +} + +video::-webkit-media-controls-panel.show, +video::-webkit-media-controls-panel.paused, +video::-webkit-media-controls-panel:hover { + cursor: inherit; + opacity: 1; +} + +video::-webkit-media-controls-panel.picture-in-picture { + pointer-events: none; +} + +audio::-webkit-media-show-controls { + display: none !important; +} +video::-webkit-media-show-controls { + position: absolute; + left: 0; + bottom: 0; + display: block; + margin: 0; + padding: 0; + width: 100%; + height: 10px; + opacity: 0; + border: 0; + background: none; + -webkit-appearance: none; +} + +video::-webkit-media-controls-panel-background-container, +audio::-webkit-media-controls-panel-background-container { + position: absolute; + left: 0; + top: 0; + width: 100%; +} + +video::-webkit-media-controls-panel-background-container { + z-index: 0; +} + +video::-webkit-media-controls-panel-background-container { + min-height: 45px; + height: 45px; + -webkit-clip-path: inset(20px 0px 0px 0px); +} + +video::-webkit-media-controls-panel-tint, +audio::-webkit-media-controls-panel-tint { + position: absolute; + left: 0; + top: 0; + width: 100%; + background-color: transparent; +} + +video::-webkit-media-controls-panel-tint { + min-height: 45px; + height: 45px; + background-color: rgb(41, 41, 41); + mix-blend-mode: lighten; +} + +video::-webkit-media-controls-panel-background, +audio::-webkit-media-controls-panel-background { + position: absolute; + left: 0; + top: 0; + width: 100%; + min-height: 25px; + height: 25px; + background-color: rgb(41, 41, 41); +} + +video::-webkit-media-controls-panel-background { + min-height: 45px; + height: 45px; + background-color: rgba(30, 30, 30, 0.45); + -webkit-backdrop-filter: saturate(180%) blur(20px); +} + +video::-webkit-media-controls-panel button, +audio::-webkit-media-controls-panel button { + -webkit-appearance: none; + display: block; + padding: 0; + border: 0; + height: 15px; + background-color: transparent; + color: white; + background-origin: content-box; + background-repeat: no-repeat; + background-position: center; +} + +audio::-webkit-media-controls-panel button { + z-index: 0; +} + +video::-webkit-media-controls-panel button { + mix-blend-mode: plus-lighter; + -webkit-transform: translateZ(0); +} + +video::-webkit-media-controls-panel button:focus, +audio::-webkit-media-controls-panel button:focus { + outline: 0; +} + +video::-webkit-media-controls-rewind-button, +audio::-webkit-media-controls-rewind-button { + background-image: url('data:image/svg+xml,'); + width: 16px; + min-width: 16px; + height: 18px; + margin-bottom: 1px; + margin-left: 8px; + margin-right: 8px; +} + +video::-webkit-media-controls-play-button, +audio::-webkit-media-controls-play-button { + background-image: url('data:image/svg+xml,'); + margin-left: 8px; + margin-right: 8px; + width: 12px; + min-width: 12px; +} + +video::-webkit-media-controls-play-button.paused, +audio::-webkit-media-controls-play-button.paused { + background-image: url('data:image/svg+xml,'); + width: 12px; +} + +video::-webkit-media-controls-panel .mute-box, +audio::-webkit-media-controls-panel .mute-box { + width: 14px; + min-width: 14px; + height: 25px; + margin-right: 8px; + margin-left: 8px; + position: relative; + display: -webkit-flex; + -webkit-flex-direction: column; + -webkit-justify-content: center; + -webkit-align-items: center; +} + +video::-webkit-media-controls-mute-button, +audio::-webkit-media-controls-mute-button, +video::-webkit-media-controls-volume-max-button { + width: 14px; + min-width: 14px; + background-image: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-panel .volume-box, +audio::-webkit-media-controls-panel .volume-box { + position: absolute; + box-sizing: border-box; + width: 63px; + bottom: 5px; + left: -25px; + + -webkit-clip-path: inset(20px 20px 20px 20px round 4px 4px 0px 0px); + background-color: transparent; + overflow: hidden; + + display: -webkit-flex; + -webkit-flex-direction: row; + -webkit-align-items: center; + -webkit-justify-content: flex-end; + + opacity: 0; + /* make zero height (rather than display:none) for AX and FKA */ + height: 0px; /* will become 116px when shown */ + +} + +audio::-webkit-media-controls-panel .volume-box { + background-color: black; +} + +video::-webkit-media-controls-volume-slider-container-background, +audio::-webkit-media-controls-volume-slider-container-background { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + border-bottom-right-radius: 4px; + border-top-right-radius: 4px; + background-color: rgba(30, 30, 30, 0.45); +} + +video::-webkit-media-controls-volume-slider-container-background { + background-color: rgba(30, 30, 30, 0.45); + -webkit-backdrop-filter: saturate(180%) blur(20px); +} + +video::-webkit-media-controls-volume-slider-container-tint, +audio::-webkit-media-controls-volume-slider-container-tint { + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 100%; + border-bottom-right-radius: 4px; + border-top-right-radius: 4px; + background-color: rgb(41, 41, 41); +} + +video::-webkit-media-controls-volume-slider-container-tint { + mix-blend-mode: lighten; +} + +/* FIXME: needs CSS4 !subject selector to show when slider inside .volume-box is focused */ +video::-webkit-media-controls-panel .mute-box:hover .volume-box, +video::-webkit-media-controls-panel .volume-box:hover, +video::-webkit-media-controls-panel .volume-box:active, +audio::-webkit-media-controls-panel .mute-box:hover .volume-box, +audio::-webkit-media-controls-panel .volume-box:hover, +audio::-webkit-media-controls-panel .volume-box:active { + opacity: 1; + /* resize to usable amount (rather than display:none) for AX and FKA */ + height: 116px; +} + +audio::-webkit-media-controls-volume-slider, +video::-webkit-media-controls-volume-slider { + -webkit-appearance: none !important; + box-sizing: border-box !important; + height: 9px !important; + min-width: 64px !important; + width: 64px !important; + padding: 0 !important; + margin: 0 !important; + + background-color: transparent !important; + background-size: 100% 100%; + background-repeat: no-repeat; + -webkit-transform-origin: 0 0; + -webkit-transform: rotate(-90deg) translateY(28px) translateX(-40px); +} + +video::-webkit-media-controls-volume-slider { + mix-blend-mode: plus-lighter; +} + +video::-webkit-media-controls-volume-slider::-webkit-slider-thumb, +audio::-webkit-media-controls-volume-slider::-webkit-slider-thumb { + -webkit-appearance: none !important; + width: 7px !important; + height: 7px !important; + visibility: hidden; +} + +video::-webkit-media-controls-mute-button.muted, +audio::-webkit-media-controls-mute-button.muted, +video::-webkit-media-controls-volume-min-button { + background-image: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-wireless-playback-picker-button, +audio::-webkit-media-controls-wireless-playback-picker-button { + margin-right: 8px; + margin-left: 8px; + width: 16px; + min-width: 16px; +} + +video::-webkit-media-controls-wireless-playback-picker-button.playing, +audio::-webkit-media-controls-wireless-playback-picker-button.playing { + mix-blend-mode: normal; +} + +video::-webkit-media-controls-toggle-closed-captions-button, +audio::-webkit-media-controls-toggle-closed-captions-button { + width: 16px; + min-width: 16px; + margin-right: 8px; + margin-left: 8px; + background-image: url('data:image/svg+xml, '); + outline: 0; +} + +video::-webkit-media-controls-closed-captions-container, +audio::-webkit-media-controls-closed-captions-container { + -webkit-appearance: media-closed-captions-container; + position: absolute; + display: block; + right: 38px; + bottom: 29px; + max-width: calc(100% - 48px); /* right + 10px */ + max-height: calc(100% - 39px); /* bottom + 10px */ + overflow-x: hidden; + overflow-y: scroll; + background-color: rgba(0, 0, 0, 0.8); + border: 1px solid rgba(128, 128, 128, 0.75); + border-radius: 6px; + cursor: default; + z-index: 2; + text-align: initial; +} + +video::-webkit-media-controls-closed-captions-container .list, +audio::-webkit-media-controls-closed-captions-container .list { + display: block; + -webkit-user-select: none; +} + +video::-webkit-media-controls-closed-captions-container h3, +audio::-webkit-media-controls-closed-captions-container h3 { + margin: 0; + color: rgb(140, 140, 140); + text-shadow: 0 1px 0 black; + -webkit-margin-start: 16px; + padding-top: 4px; + font-size: 11px; +} + +video::-webkit-media-controls-closed-captions-container ul, +audio::-webkit-media-controls-closed-captions-container ul { + list-style-type: none; + margin: 0 0 8px 0; + padding: 0; +} + +video::-webkit-media-controls-closed-captions-container li, +audio::-webkit-media-controls-closed-captions-container li { + position: relative; + color: white; + background-image: none; + text-shadow: 0 1px 0 black; + margin: 0; + padding-left: 28px; + padding-right: 28px; + padding-top: 0.15em; + padding-bottom: 0.2em; + box-sizing: border-box; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} + +video::-webkit-media-controls-closed-captions-container li:focus, +audio::-webkit-media-controls-closed-captions-container li:focus { + outline: 0; + background-color: rgba(140, 140, 140, 0.5); +} + +video::-webkit-media-controls-closed-captions-container li:hover, +audio::-webkit-media-controls-closed-captions-container li:hover { + background-color: rgba(26, 68, 243, 0.6); +} + +video::-webkit-media-controls-closed-captions-container li .checkmark-container, +audio::-webkit-media-controls-closed-captions-container li .checkmark-container { + display: none; + position: absolute; + top: 0.25em; + left: 1em; + width: 1.1em; + height: 1.1em; +} + +video::-webkit-media-controls-closed-captions-container li.selected .checkmark-container, +audio::-webkit-media-controls-closed-captions-container li.selected .checkmark-container { + display: inline-block; + content: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-closed-captions-container li.selected:hover .checkmark-container, +audio::-webkit-media-controls-closed-captions-container li.selected:hover .checkmark-container { + content: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-panel .picture-in-picture-button { + margin-right: 7px; + margin-left: 8px; + margin-top: 1px; + width: 16px; + min-width: 18px; + height: 12px; + + background-image: url('data:image/svg+xml,'); + } + +video::-webkit-media-controls-panel .picture-in-picture-button.return-from-picture-in-picture { + background-image: url('data:image/svg+xml,'); + } + +video::-webkit-media-controls-fullscreen-button, +audio::-webkit-media-controls-fullscreen-button { + background-image: url('data:image/svg+xml,'); + margin-right: 7px; + margin-left: 8px; + width: 14px; + min-width: 14px; +} + +video::-webkit-media-controls-fullscreen-button.exit, +audio::-webkit-media-controls-fullscreen-button.exit { + background-image: url('data:image/svg+xml,'); + margin-right: 11px; + margin-left: 0px; + margin-top: 6px; +} + +video::-webkit-media-controls-status-display, +audio::-webkit-media-controls-status-display { + cursor: default; + overflow: hidden; + color: rgb(156, 156, 156); + opacity: 0.99; + + letter-spacing: normal; + word-spacing: normal; + line-height: 25px; + text-transform: none; + text-indent: 0; + text-decoration: none; + text-align: left; + + padding: 0 12px; + + -webkit-flex: 1 1 0; +} + +video::-webkit-media-controls-status-display { + color: white; + opacity: .45; + mix-blend-mode: plus-lighter; +} + +video::-webkit-media-controls-timeline, +audio::-webkit-media-controls-timeline { + -webkit-appearance: none !important; + -webkit-flex: 1 1 0 !important; + height: 17px !important; + margin: 0 !important; + background-size: 100% 100% !important; + background-repeat: no-repeat; + background-color: transparent; +} + +video::-webkit-media-controls-timeline { + mix-blend-mode: plus-lighter; +} + +video::-webkit-media-controls-timeline::-webkit-slider-thumb, +audio::-webkit-media-controls-timeline::-webkit-slider-thumb { + -webkit-appearance: none !important; + width: 3px !important; + height: 15px !important; +} + +video::-webkit-media-controls-current-time-display, +video::-webkit-media-controls-time-remaining-display, +audio::-webkit-media-controls-current-time-display, +audio::-webkit-media-controls-time-remaining-display { + -webkit-user-select: none; + -webkit-flex: 0 0 0; + display: -webkit-flex; + cursor: default; + overflow: hidden; + color: rgb(156, 156, 156); + letter-spacing: normal; + word-spacing: normal; + line-height: normal; + text-transform: none; + text-indent: 0px; + text-decoration: none; + position: relative; + bottom: 0.5px; + font-family: -apple-system-monospaced-numbers; + font-size: 11px !important; + font-style: normal !important; + font-weight: normal !important; + -webkit-text-size-adjust: none; +} + +video::-webkit-media-controls-current-time-display, +video::-webkit-media-controls-time-remaining-display { + color: white; + opacity: .45; + mix-blend-mode: plus-lighter; +} + +video::-webkit-media-controls-current-time-display, +audio::-webkit-media-controls-current-time-display { + margin-left: 8px; + margin-right: 8px; + width: 32px; + min-width: 32px; + -webkit-justify-content: flex-end; +} + +video::-webkit-media-controls-time-remaining-display, +audio::-webkit-media-controls-time-remaining-display { + margin-left: 8px; + margin-right: 8px; + width: 37px; + min-width: 37px; + -webkit-justify-content: flex-start; +} + +video::-webkit-media-controls-time-remaining-display.five-digit-time, +audio::-webkit-media-controls-time-remaining-display.five-digit-time { + min-width: 47px; +} + +video::-webkit-media-controls-current-time-display.five-digit-time, +audio::-webkit-media-controls-current-time-display.five-digit-time { + min-width: 42px; +} + +video::-webkit-media-controls-time-remaining-display.six-digit-time, +audio::-webkit-media-controls-time-remaining-display.six-digit-time { + min-width: 54px; +} + +video::-webkit-media-controls-current-time-display.six-digit-time, +audio::-webkit-media-controls-current-time-display.six-digit-time { + min-width: 49px; +} + +video::-webkit-media-controls-timeline-container, +audio::-webkit-media-controls-timeline-container { + display: -webkit-flex; + -webkit-flex-direction: row; + -webkit-align-items: center; + -webkit-user-select: none; + -webkit-flex: 1 1 0; + min-width: 0; + position: relative; + padding: 0; +} + +video::-webkit-media-controls-panel .thumbnail-track, +audio::-webkit-media-controls-panel .thumbnail-track { + position: relative; + -webkit-flex: 1 1 0; + min-width: 0; + height: 17px; + margin: 0 2px; + display: -webkit-flex; + -webkit-align-items: stretch; + -webkit-flex-direction: column; +} + +video::-webkit-media-controls-panel .thumbnail, +audio::-webkit-media-controls-panel .thumbnail { + position: absolute; + opacity: 0; + transition: opacity 0.25s linear; + bottom: 15px; + width: 100px; + height: 58px; + margin-left: -50px; + border: 5px solid black; + box-shadow: 0 0 3px white; + border-radius: 3px; +} + +video::-webkit-media-controls-panel .thumbnail-image, +audio::-webkit-media-controls-panel .thumbnail-image { + width: 100%; + height: 100%; +} + +video::-webkit-media-controls-panel .thumbnail.show, +audio::-webkit-media-controls-panel .thumbnail.show { + opacity: 1; +} + +video::-webkit-media-controls-panel.hidden, +audio::-webkit-media-controls-panel.hidden { + display: none; +} + +video::-webkit-media-controls-panel .hidden, +audio::-webkit-media-controls-panel .hidden, +video::-webkit-media-controls-panel .dropped, +audio::-webkit-media-controls-panel .dropped { + display: none; +} + +/* Full Screen */ + +/* + Page stylesheets are not allowed to override the style of media + controls while in full screen mode, so many if not all the rules + defined in this section will be marked as !important to enforce + this restriction +*/ + +video:-webkit-full-screen::-webkit-media-controls-panel { + -webkit-align-items: flex-start !important; + -webkit-justify-content: flex-end !important; + -webkit-clip-path: inset(20px round 6px); + + width: 480px !important; + height: 104px !important; + margin: 0 auto 12px auto !important; + padding: 20px; + padding-top: 30px !important; + + background-color: transparent; + border-radius: 6px !important; + + transition: opacity 0.3s linear !important; +} + +video:-webkit-full-screen::-webkit-media-controls-panel-tint, +audio:-webkit-full-screen::-webkit-media-controls-panel-tint { + position: absolute; + left: 0; + top: 0; + width: 480px !important; + height: 104px !important; + border-radius: 6px !important; + background-color: rgb(41, 41, 41); + mix-blend-mode: lighten; +} + +video:-webkit-full-screen::-webkit-media-controls-panel-background, +audio:-webkit-full-screen::-webkit-media-controls-panel-background { + position: absolute; + left: 0; + top: 0; + width: 480px !important; + height: 104px !important; + border-radius: 6px !important; + background-color: rgba(30, 30, 30, 0.45); + -webkit-backdrop-filter: saturate(180%) blur(20px); +} + +video:-webkit-animating-full-screen-transition::-webkit-media-controls-panel { + opacity: 0 ! important; + transition: opacity 0 ! important; +} + +video:-webkit-full-screen::-webkit-media-controls-panel .volume-box { + -webkit-transform: none; + -webkit-clip-path: none; + opacity: 1; + left: 32px; + top: 35px; + width: 96px; + height: 17px; + display: -webkit-flex; + -webkit-flex-direction: row; + -webkit-align-items: center; + background-color: transparent; + border: none; +} + +video:-webkit-full-screen::-webkit-media-controls-panel .volume-box:not(.uses-ltr-user-interface-layout-direction) { + transform: translateX(23px) scaleX(-1); +} + +video:-webkit-full-screen::-webkit-media-controls-volume-slider { + -webkit-transform: none; + background-color: transparent; + min-width: 60px !important; + width: 60px !important; + height: 9px !important; + margin: 0 !important; +} + +video:-webkit-full-screen::-webkit-media-controls-mute-button, +audio:-webkit-full-screen::-webkit-media-controls-mute-button, +video:-webkit-full-screen::-webkit-media-controls-volume-max-button { + width: 14px !important; + margin-left: 6px !important; + margin-bottom: 2px !important; + background-image: url('data:image/svg+xml,'); +} +video:-webkit-full-screen::-webkit-media-controls-mute-button, +audio:-webkit-full-screen::-webkit-media-controls-mute-button, +video:-webkit-full-screen::-webkit-media-controls-volume-min-button { + width: 14px !important; + margin-right: 2px !important; + margin-bottom: 2px !important; + background-image: url('data:image/svg+xml,'); +} + +video:-webkit-full-screen::-webkit-media-controls-play-button { + position: absolute; + + background-image: url('data:image/svg+xml,'); + + width: 21px; + height: 23px; + left: 230px; + top: 32px; + margin: 0; + padding: 0; +} + +video:-webkit-full-screen::-webkit-media-controls-play-button.paused { + background-image: url('data:image/svg+xml,'); +} + +video:-webkit-full-screen::-webkit-media-controls-seek-back-button { + position: absolute; + + background-image: url('data:image/svg+xml,'); + + width: 24px; + height: 15px; + left: 176px; + top: 36px; +} + +video:-webkit-full-screen::-webkit-media-controls-return-to-realtime-button { + position: absolute; + display: -webkit-flex; + width: 29px; + height: 16px; + left: 262px; + top: 13px; +} + +video:-webkit-full-screen::-webkit-media-controls-seek-forward-button { + position: absolute; + + background-image: url('data:image/svg+xml,'); + + width: 24px; + height: 15px; + left: 275px; + top: 36px; +} + +video:-webkit-full-screen::-webkit-media-controls-timeline-container { + height: auto; + width: 440px; + position: absolute; + bottom: 28px; + left: 20px; +} + +video:-webkit-full-screen::-webkit-media-controls-current-time-display { + margin-left: 12px; +} + +video:-webkit-full-screen::-webkit-media-controls-time-remaining-display { + margin-right: 12px; +} + +video:-webkit-full-screen::-webkit-media-controls-status-display { + width: 440px; + position: absolute; + bottom: 25px; + text-align: center; + padding: 0; + left: 20px; +} + +video:-webkit-full-screen::-webkit-media-controls-toggle-closed-captions-button, +audio:-webkit-full-screen::-webkit-media-controls-toggle-closed-captions-button { + margin-top: 6px; + margin-right:24px; + margin-left: 0px; +} + +video:-webkit-full-screen::-webkit-media-controls-wireless-playback-picker-button, +audio:-webkit-full-screen::-webkit-media-controls-wireless-playback-picker-button { + margin-top: 6px; + margin-right:24px; + margin-left: 0px; +} + +video:-webkit-full-screen::-webkit-media-controls-closed-captions-container { + bottom: 100px; + right: calc(50% - 183px); /* 183px is 221px (half the media controller's width) minus 38px (the right position of the captions icon). */ + max-width: calc(50% + 173px); /* right + 10px */ + max-height: calc(100% - 124px); /* bottom + 10px */ +} + +video::-webkit-media-text-track-container { + position: relative; + width: 100%; + overflow: hidden; + padding-bottom: 5px; + z-index: 0; + + text-align: center; + color: rgba(255, 255, 255, 1); + + letter-spacing: normal; + word-spacing: normal; + text-transform: none; + text-indent: 0; + text-decoration: none; + pointer-events: none; + -webkit-user-select: none; + + -webkit-flex: 1 1; + + -webkit-line-box-contain: block inline-box replaced; +} + +video::cue { + background-color: rgba(0, 0, 0, 0.8); +} + +video::-webkit-media-text-track-display { + position: absolute; + overflow: hidden; + white-space: pre-wrap; + -webkit-box-sizing: border-box; + font: 22px sans-serif; +} + +video::-webkit-media-text-track-display-backdrop { + display: inline-block; +} + +video::cue(:future) { + color: gray; +} + +video::-webkit-media-text-track-container b { + font-weight: bold; +} + +video::-webkit-media-text-track-container u { + text-decoration: underline; +} + +video::-webkit-media-text-track-container i { + font-style: italic; +} + +video::-webkit-media-text-track-container .hidden, +audio::-webkit-media-text-track-container .hidden { + display: none; +} + +/* ============ ACTIVE VERSIONS OF ALL BUTTONS ============= */ + +video::-webkit-media-controls-mute-button:active, +audio::-webkit-media-controls-mute-button:active, +video::-webkit-media-controls-volume-max-button:active { + background-image: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-panel button.muted:active, +audio::-webkit-media-controls-panel button.muted:active, +video::-webkit-media-controls-volume-min-button:active { + background-image: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-toggle-closed-captions-button:active, +audio::-webkit-media-controls-toggle-closed-captions-button:active { + background-image: url('data:image/svg+xml, '); +} + +video::-webkit-media-controls-rewind-button:active, +audio::-webkit-media-controls-rewind-button:active { + background-image: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-panel button.paused:active, +audio::-webkit-media-controls-panel button.paused:active { + background-image: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-play-button:active, +audio::-webkit-media-controls-play-button:active { + background-image: url('data:image/svg+xml,'); +} + +video:-webkit-full-screen::-webkit-media-controls-mute-button:active, +audio:-webkit-full-screen::-webkit-media-controls-mute-button:active, +video:-webkit-full-screen::-webkit-media-controls-volume-max-button:active { + background-image: url('data:image/svg+xml,'); +} +video:-webkit-full-screen::-webkit-media-controls-mute-button:active, +audio:-webkit-full-screen::-webkit-media-controls-mute-button:active, +video:-webkit-full-screen::-webkit-media-controls-volume-min-button:active { + background-image: url('data:image/svg+xml,'); +} + +video:-webkit-full-screen::-webkit-media-controls-play-button:active{ + background-image: url('data:image/svg+xml,'); +} + +video:-webkit-full-screen::-webkit-media-controls-panel button.paused:active { + background-image: url('data:image/svg+xml,'); +} + +video:-webkit-full-screen::-webkit-media-controls-seek-back-button:active { + background-image: url('data:image/svg+xml,'); +} + +video:-webkit-full-screen::-webkit-media-controls-seek-forward-button:active { + background-image: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-fullscreen-button:active, +audio::-webkit-media-controls-fullscreen-button:active { + background-image: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-panel button.exit:active, +audio::-webkit-media-controls-panel button.exit:active { + background-image: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-wireless-playback-picker-button:active, +audio::-webkit-media-controls-wireless-playback-picker-button:active { + background-image: url('data:image/svg+xml, '); +} + +video::-webkit-media-controls-panel .picture-in-picture-button:active { + background-image: url('data:image/svg+xml,'); +} + +video::-webkit-media-controls-panel .picture-in-picture-button.return-from-picture-in-picture:active { + background-image: url('data:image/svg+xml,'); +} + +/* ==================== AIRPLAY PLACARD ==================== */ + +video::-webkit-media-controls-wireless-playback-status, +audio::-webkit-media-controls-wireless-playback-status { + display: block; + position: absolute; + left: 0px; + top: 0px; + width: 100%; + height: 100%; + background-image: url('data:image/svg+xml;utf8,'); + background-color: rgb(51, 51, 53); + background-repeat: no-repeat; + background-position: 50% calc(.5 * (100% - 25px) - 21pt); + background-size: 131px auto; + color: -apple-system-gray; + font-family: -apple-system; + vertical-align: text-bottom; +} + +video::-webkit-media-controls-wireless-playback-text, +audio::-webkit-media-controls-wireless-playback-text { + cursor: default; + position: absolute; + width: 100%; + top: calc(.5 * (100% - 25px) + (.5 * (90px + 42pt) - 42pt)); + -webkit-user-select: none; + margin: 0px; + height: 42pt; +} + +video::-webkit-media-controls-wireless-playback-text-top, +audio::-webkit-media-controls-wireless-playback-text-top { + position: absolute; + top: 15pt; + width: 100%; + line-height: 12pt; + height: 13pt; + font-size: 12pt; + text-align: center; + margin: 0px; +} + +video::-webkit-media-controls-wireless-playback-text-bottom, +audio::-webkit-media-controls-wireless-playback-text-bottom { + position: absolute; + bottom: 0; + left: 5%; + width: 90%; + line-height: 10pt; + height: 11pt; + font-size: 10pt; + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin: 0px; +} + +video::-webkit-media-controls-wireless-playback-status.small, +audio::-webkit-media-controls-wireless-playback-status.small { + background-image: url('data:image/svg+xml;utf8,'); + background-position: 50% calc(.5 * (100% - 25px) - 5pt); + background-size: 62px auto; +} + +video::-webkit-media-controls-wireless-playback-text-top.small, +audio::-webkit-media-controls-wireless-playback-text-top.small { + top: 4pt; +} + +video::-webkit-media-controls-wireless-playback-text-bottom.small, +audio::-webkit-media-controls-wireless-playback-text-bottom.small { + display: none; +} + +video::-webkit-media-controls-wireless-playback-status.picture-in-picture, +audio::-webkit-media-controls-wireless-playback-status.picture-in-picture +{ + background-size: 304px auto; + background-position: 50% calc(.5 * (100% - 25px)); + background-image: url('data:image/svg+xml;utf8,'); +} + +video::-webkit-media-controls-wireless-playback-text-top.picture-in-picture, +audio::-webkit-media-controls-wireless-playback-text-top.picture-in-picture { + top: initial; + bottom: 0; +} + +video::-webkit-media-controls-wireless-playback-text-bottom.picture-in-picture, +audio::-webkit-media-controls-wireless-playback-text-bottom.picture-in-picture { + display: none; +} + +video::-webkit-media-controls-wireless-playback-status.hidden, +audio::-webkit-media-controls-wireless-playback-status.hidden { + display: none; +} + +video::-webkit-media-controls-panel.picture-in-picture { + opacity: 0; + pointer-events: none; +} + +video:-webkit-full-screen::-webkit-media-controls-panel .picture-in-picture-button { + margin-top: 7px; + margin-right: 24px; + margin-left: 0px; +} + +/* Time display clones that we use in updateLayoutForDisplayedWidth(). */ +::-webkit-media-controls-current-time-display.clone, +::-webkit-media-controls-time-remaining-display.clone { + position: absolute; + display: inline; + top: 100%; + mix-blend-mode: normal; +} diff --git a/Source/WebCore/Modules/mediacontrols/mediaControlsApple.js b/Source/WebCore/Modules/mediacontrols/mediaControlsApple.js new file mode 100644 index 000000000..d1893734c --- /dev/null +++ b/Source/WebCore/Modules/mediacontrols/mediaControlsApple.js @@ -0,0 +1,2509 @@ +function createControls(root, video, host) +{ + return new Controller(root, video, host); +}; + +function Controller(root, video, host) +{ + this.video = video; + this.root = root; + this.host = host; + this.controls = {}; + this.listeners = {}; + this.isLive = false; + this.statusHidden = true; + this.hasWirelessPlaybackTargets = false; + this.canToggleShowControlsButton = false; + this.isListeningForPlaybackTargetAvailabilityEvent = false; + this.currentTargetIsWireless = false; + this.wirelessPlaybackDisabled = false; + this.isVolumeSliderActive = false; + this.currentDisplayWidth = 0; + this._scrubbing = false; + this._pageScaleFactor = 1; + + this.addVideoListeners(); + this.createBase(); + this.createControls(); + this.createTimeClones(); + this.updateBase(); + this.updateControls(); + this.updateDuration(); + this.updateProgress(); + this.updateTime(); + this.updateReadyState(); + this.updatePlaying(); + this.updateThumbnail(); + this.updateCaptionButton(); + this.updateCaptionContainer(); + this.updateFullscreenButtons(); + this.updateVolume(); + this.updateHasAudio(); + this.updateHasVideo(); + this.updateWirelessTargetAvailable(); + this.updateWirelessPlaybackStatus(); + this.updatePictureInPicturePlaceholder(); + this.scheduleUpdateLayoutForDisplayedWidth(); + + this.listenFor(this.root, 'resize', this.handleRootResize); +}; + +/* Enums */ +Controller.InlineControls = 0; +Controller.FullScreenControls = 1; + +Controller.PlayAfterSeeking = 0; +Controller.PauseAfterSeeking = 1; + +/* Globals */ +Controller.gSimulateWirelessPlaybackTarget = false; // Used for testing when there are no wireless targets. +Controller.gSimulatePictureInPictureAvailable = false; // Used for testing when picture-in-picture is not available. + +Controller.prototype = { + + /* Constants */ + HandledVideoEvents: { + loadstart: 'handleLoadStart', + error: 'handleError', + abort: 'handleAbort', + suspend: 'handleSuspend', + stalled: 'handleStalled', + waiting: 'handleWaiting', + emptied: 'handleReadyStateChange', + loadedmetadata: 'handleReadyStateChange', + loadeddata: 'handleReadyStateChange', + canplay: 'handleReadyStateChange', + canplaythrough: 'handleReadyStateChange', + timeupdate: 'handleTimeUpdate', + durationchange: 'handleDurationChange', + playing: 'handlePlay', + pause: 'handlePause', + progress: 'handleProgress', + volumechange: 'handleVolumeChange', + webkitfullscreenchange: 'handleFullscreenChange', + webkitbeginfullscreen: 'handleFullscreenChange', + webkitendfullscreen: 'handleFullscreenChange', + }, + PlaceholderPollingDelay: 33, + HideControlsDelay: 4 * 1000, + RewindAmount: 30, + MaximumSeekRate: 8, + SeekDelay: 1500, + ClassNames: { + active: 'active', + dropped: 'dropped', + exit: 'exit', + failed: 'failed', + hidden: 'hidden', + hiding: 'hiding', + threeDigitTime: 'three-digit-time', + fourDigitTime: 'four-digit-time', + fiveDigitTime: 'five-digit-time', + sixDigitTime: 'six-digit-time', + list: 'list', + muteBox: 'mute-box', + muted: 'muted', + paused: 'paused', + pictureInPicture: 'picture-in-picture', + playing: 'playing', + returnFromPictureInPicture: 'return-from-picture-in-picture', + selected: 'selected', + show: 'show', + small: 'small', + thumbnail: 'thumbnail', + thumbnailImage: 'thumbnail-image', + thumbnailTrack: 'thumbnail-track', + volumeBox: 'volume-box', + noVideo: 'no-video', + down: 'down', + out: 'out', + pictureInPictureButton: 'picture-in-picture-button', + placeholderShowing: 'placeholder-showing', + usesLTRUserInterfaceLayoutDirection: 'uses-ltr-user-interface-layout-direction', + appleTV: 'appletv', + }, + KeyCodes: { + enter: 13, + escape: 27, + space: 32, + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40 + }, + MinimumTimelineWidth: 80, + ButtonWidth: 32, + + extend: function(child) + { + // This function doesn't actually do what we want it to. In particular it + // is not copying the getters and setters to the child class, since they are + // not enumerable. What we should do is use ES6 classes, or assign the __proto__ + // directly. + // FIXME: Use ES6 classes. + + for (var property in this) { + if (!child.hasOwnProperty(property)) + child[property] = this[property]; + } + }, + + get idiom() + { + return "apple"; + }, + + UIString: function(developmentString, replaceString, replacementString) + { + var localized = UIStringTable[developmentString]; + if (replaceString && replacementString) + return localized.replace(replaceString, replacementString); + + if (localized) + return localized; + + console.error("Localization for string \"" + developmentString + "\" not found."); + return "LOCALIZED STRING NOT FOUND"; + }, + + listenFor: function(element, eventName, handler, useCapture) + { + if (typeof useCapture === 'undefined') + useCapture = false; + + if (!(this.listeners[eventName] instanceof Array)) + this.listeners[eventName] = []; + this.listeners[eventName].push({element:element, handler:handler, useCapture:useCapture}); + element.addEventListener(eventName, this, useCapture); + }, + + stopListeningFor: function(element, eventName, handler, useCapture) + { + if (typeof useCapture === 'undefined') + useCapture = false; + + if (!(this.listeners[eventName] instanceof Array)) + return; + + this.listeners[eventName] = this.listeners[eventName].filter(function(entry) { + return !(entry.element === element && entry.handler === handler && entry.useCapture === useCapture); + }); + element.removeEventListener(eventName, this, useCapture); + }, + + addVideoListeners: function() + { + for (var name in this.HandledVideoEvents) { + this.listenFor(this.video, name, this.HandledVideoEvents[name]); + }; + + /* text tracks */ + this.listenFor(this.video.textTracks, 'change', this.handleTextTrackChange); + this.listenFor(this.video.textTracks, 'addtrack', this.handleTextTrackAdd); + this.listenFor(this.video.textTracks, 'removetrack', this.handleTextTrackRemove); + + /* audio tracks */ + this.listenFor(this.video.audioTracks, 'change', this.handleAudioTrackChange); + this.listenFor(this.video.audioTracks, 'addtrack', this.handleAudioTrackAdd); + this.listenFor(this.video.audioTracks, 'removetrack', this.handleAudioTrackRemove); + + /* video tracks */ + this.listenFor(this.video.videoTracks, 'change', this.updateHasVideo); + this.listenFor(this.video.videoTracks, 'addtrack', this.updateHasVideo); + this.listenFor(this.video.videoTracks, 'removetrack', this.updateHasVideo); + + /* controls attribute */ + this.controlsObserver = new MutationObserver(this.handleControlsChange.bind(this)); + this.controlsObserver.observe(this.video, { attributes: true, attributeFilter: ['controls'] }); + + this.listenFor(this.video, 'webkitcurrentplaybacktargetiswirelesschanged', this.handleWirelessPlaybackChange); + + if ('webkitPresentationMode' in this.video) + this.listenFor(this.video, 'webkitpresentationmodechanged', this.handlePresentationModeChange); + }, + + removeVideoListeners: function() + { + for (var name in this.HandledVideoEvents) { + this.stopListeningFor(this.video, name, this.HandledVideoEvents[name]); + }; + + /* text tracks */ + this.stopListeningFor(this.video.textTracks, 'change', this.handleTextTrackChange); + this.stopListeningFor(this.video.textTracks, 'addtrack', this.handleTextTrackAdd); + this.stopListeningFor(this.video.textTracks, 'removetrack', this.handleTextTrackRemove); + + /* audio tracks */ + this.stopListeningFor(this.video.audioTracks, 'change', this.handleAudioTrackChange); + this.stopListeningFor(this.video.audioTracks, 'addtrack', this.handleAudioTrackAdd); + this.stopListeningFor(this.video.audioTracks, 'removetrack', this.handleAudioTrackRemove); + + /* video tracks */ + this.stopListeningFor(this.video.videoTracks, 'change', this.updateHasVideo); + this.stopListeningFor(this.video.videoTracks, 'addtrack', this.updateHasVideo); + this.stopListeningFor(this.video.videoTracks, 'removetrack', this.updateHasVideo); + + /* controls attribute */ + this.controlsObserver.disconnect(); + delete(this.controlsObserver); + + this.stopListeningFor(this.video, 'webkitcurrentplaybacktargetiswirelesschanged', this.handleWirelessPlaybackChange); + this.setShouldListenForPlaybackTargetAvailabilityEvent(false); + + if ('webkitPresentationMode' in this.video) + this.stopListeningFor(this.video, 'webkitpresentationmodechanged', this.handlePresentationModeChange); + }, + + handleEvent: function(event) + { + var preventDefault = false; + + try { + if (event.target === this.video) { + var handlerName = this.HandledVideoEvents[event.type]; + var handler = this[handlerName]; + if (handler && handler instanceof Function) + handler.call(this, event); + } + + if (!(this.listeners[event.type] instanceof Array)) + return; + + this.listeners[event.type].forEach(function(entry) { + if (entry.element === event.currentTarget && entry.handler instanceof Function) + preventDefault |= entry.handler.call(this, event); + }, this); + } catch(e) { + if (window.console) + console.error(e); + } + + if (preventDefault) { + event.stopPropagation(); + event.preventDefault(); + } + }, + + createBase: function() + { + var base = this.base = document.createElement('div'); + base.setAttribute('pseudo', '-webkit-media-controls'); + this.listenFor(base, 'mousemove', this.handleWrapperMouseMove); + this.listenFor(this.video, 'mouseout', this.handleWrapperMouseOut); + if (this.host.textTrackContainer) + base.appendChild(this.host.textTrackContainer); + }, + + shouldHaveAnyUI: function() + { + return this.shouldHaveControls() || (this.video.textTracks && this.video.textTracks.length) || this.currentPlaybackTargetIsWireless(); + }, + + shouldShowControls: function() + { + if (!this.isAudio() && !this.host.allowsInlineMediaPlayback) + return true; + + return this.video.controls || this.isFullScreen(); + }, + + shouldHaveControls: function() + { + return this.shouldShowControls() || this.isFullScreen() || this.presentationMode() === 'picture-in-picture' || this.currentPlaybackTargetIsWireless(); + }, + + + setNeedsTimelineMetricsUpdate: function() + { + this.timelineMetricsNeedsUpdate = true; + }, + + scheduleUpdateLayoutForDisplayedWidth: function() + { + setTimeout(this.updateLayoutForDisplayedWidth.bind(this), 0); + }, + + updateTimelineMetricsIfNeeded: function() + { + if (this.timelineMetricsNeedsUpdate && !this.controlsAreHidden()) { + this.timelineLeft = this.controls.timeline.offsetLeft; + this.timelineWidth = this.controls.timeline.offsetWidth; + this.timelineHeight = this.controls.timeline.offsetHeight; + this.timelineMetricsNeedsUpdate = false; + } + }, + + updateBase: function() + { + if (this.shouldHaveAnyUI()) { + if (!this.base.parentNode) { + this.root.appendChild(this.base); + } + } else { + if (this.base.parentNode) { + this.base.parentNode.removeChild(this.base); + } + } + }, + + createControls: function() + { + var panel = this.controls.panel = document.createElement('div'); + panel.setAttribute('pseudo', '-webkit-media-controls-panel'); + panel.setAttribute('aria-label', (this.isAudio() ? this.UIString('Audio Playback') : this.UIString('Video Playback'))); + panel.setAttribute('role', 'toolbar'); + this.listenFor(panel, 'mousedown', this.handlePanelMouseDown); + this.listenFor(panel, 'transitionend', this.handlePanelTransitionEnd); + this.listenFor(panel, 'click', this.handlePanelClick); + this.listenFor(panel, 'dblclick', this.handlePanelClick); + this.listenFor(panel, 'dragstart', this.handlePanelDragStart); + + var panelBackgroundContainer = this.controls.panelBackgroundContainer = document.createElement('div'); + panelBackgroundContainer.setAttribute('pseudo', '-webkit-media-controls-panel-background-container'); + + var panelTint = this.controls.panelTint = document.createElement('div'); + panelTint.setAttribute('pseudo', '-webkit-media-controls-panel-tint'); + this.listenFor(panelTint, 'mousedown', this.handlePanelMouseDown); + this.listenFor(panelTint, 'transitionend', this.handlePanelTransitionEnd); + this.listenFor(panelTint, 'click', this.handlePanelClick); + this.listenFor(panelTint, 'dblclick', this.handlePanelClick); + this.listenFor(panelTint, 'dragstart', this.handlePanelDragStart); + + var panelBackground = this.controls.panelBackground = document.createElement('div'); + panelBackground.setAttribute('pseudo', '-webkit-media-controls-panel-background'); + + var rewindButton = this.controls.rewindButton = document.createElement('button'); + rewindButton.setAttribute('pseudo', '-webkit-media-controls-rewind-button'); + rewindButton.setAttribute('aria-label', this.UIString('Rewind ##sec## Seconds', '##sec##', this.RewindAmount)); + this.listenFor(rewindButton, 'click', this.handleRewindButtonClicked); + + var seekBackButton = this.controls.seekBackButton = document.createElement('button'); + seekBackButton.setAttribute('pseudo', '-webkit-media-controls-seek-back-button'); + seekBackButton.setAttribute('aria-label', this.UIString('Rewind')); + this.listenFor(seekBackButton, 'mousedown', this.handleSeekBackMouseDown); + this.listenFor(seekBackButton, 'mouseup', this.handleSeekBackMouseUp); + + var seekForwardButton = this.controls.seekForwardButton = document.createElement('button'); + seekForwardButton.setAttribute('pseudo', '-webkit-media-controls-seek-forward-button'); + seekForwardButton.setAttribute('aria-label', this.UIString('Fast Forward')); + this.listenFor(seekForwardButton, 'mousedown', this.handleSeekForwardMouseDown); + this.listenFor(seekForwardButton, 'mouseup', this.handleSeekForwardMouseUp); + + var playButton = this.controls.playButton = document.createElement('button'); + playButton.setAttribute('pseudo', '-webkit-media-controls-play-button'); + playButton.setAttribute('aria-label', this.UIString('Play')); + this.listenFor(playButton, 'click', this.handlePlayButtonClicked); + + var statusDisplay = this.controls.statusDisplay = document.createElement('div'); + statusDisplay.setAttribute('pseudo', '-webkit-media-controls-status-display'); + statusDisplay.classList.add(this.ClassNames.hidden); + + var timelineBox = this.controls.timelineBox = document.createElement('div'); + timelineBox.setAttribute('pseudo', '-webkit-media-controls-timeline-container'); + + var currentTime = this.controls.currentTime = document.createElement('div'); + currentTime.setAttribute('pseudo', '-webkit-media-controls-current-time-display'); + currentTime.setAttribute('aria-label', this.UIString('Elapsed')); + currentTime.setAttribute('role', 'timer'); + + var timeline = this.controls.timeline = document.createElement('input'); + timeline.setAttribute('pseudo', '-webkit-media-controls-timeline'); + timeline.setAttribute('aria-label', this.UIString('Duration')); + timeline.type = 'range'; + timeline.value = 0; + this.listenFor(timeline, 'input', this.handleTimelineInput); + this.listenFor(timeline, 'change', this.handleTimelineChange); + this.listenFor(timeline, 'mouseover', this.handleTimelineMouseOver); + this.listenFor(timeline, 'mouseout', this.handleTimelineMouseOut); + this.listenFor(timeline, 'mousemove', this.handleTimelineMouseMove); + this.listenFor(timeline, 'mousedown', this.handleTimelineMouseDown); + this.listenFor(timeline, 'mouseup', this.handleTimelineMouseUp); + this.listenFor(timeline, 'keydown', this.handleTimelineKeyDown); + timeline.step = .01; + + this.timelineContextName = "_webkit-media-controls-timeline-" + this.host.generateUUID(); + timeline.style.backgroundImage = '-webkit-canvas(' + this.timelineContextName + ')'; + + var thumbnailTrack = this.controls.thumbnailTrack = document.createElement('div'); + thumbnailTrack.classList.add(this.ClassNames.thumbnailTrack); + + var thumbnail = this.controls.thumbnail = document.createElement('div'); + thumbnail.classList.add(this.ClassNames.thumbnail); + + var thumbnailImage = this.controls.thumbnailImage = document.createElement('img'); + thumbnailImage.classList.add(this.ClassNames.thumbnailImage); + + var remainingTime = this.controls.remainingTime = document.createElement('div'); + remainingTime.setAttribute('pseudo', '-webkit-media-controls-time-remaining-display'); + remainingTime.setAttribute('aria-label', this.UIString('Remaining')); + remainingTime.setAttribute('role', 'timer'); + + var muteBox = this.controls.muteBox = document.createElement('div'); + muteBox.classList.add(this.ClassNames.muteBox); + this.listenFor(muteBox, 'mouseover', this.handleMuteBoxOver); + + var muteButton = this.controls.muteButton = document.createElement('button'); + muteButton.setAttribute('pseudo', '-webkit-media-controls-mute-button'); + muteButton.setAttribute('aria-label', this.UIString('Mute')); + // Make the mute button a checkbox since it only has on/off states. + muteButton.setAttribute('role', 'checkbox'); + this.listenFor(muteButton, 'click', this.handleMuteButtonClicked); + + var minButton = this.controls.minButton = document.createElement('button'); + minButton.setAttribute('pseudo', '-webkit-media-controls-volume-min-button'); + minButton.setAttribute('aria-label', this.UIString('Minimum Volume')); + this.listenFor(minButton, 'click', this.handleMinButtonClicked); + + var maxButton = this.controls.maxButton = document.createElement('button'); + maxButton.setAttribute('pseudo', '-webkit-media-controls-volume-max-button'); + maxButton.setAttribute('aria-label', this.UIString('Maximum Volume')); + this.listenFor(maxButton, 'click', this.handleMaxButtonClicked); + + var volumeBox = this.controls.volumeBox = document.createElement('div'); + volumeBox.setAttribute('pseudo', '-webkit-media-controls-volume-slider-container'); + volumeBox.classList.add(this.ClassNames.volumeBox); + + var volumeBoxBackground = this.controls.volumeBoxBackground = document.createElement('div'); + volumeBoxBackground.setAttribute('pseudo', '-webkit-media-controls-volume-slider-container-background'); + + var volumeBoxTint = this.controls.volumeBoxTint = document.createElement('div'); + volumeBoxTint.setAttribute('pseudo', '-webkit-media-controls-volume-slider-container-tint'); + + var volume = this.controls.volume = document.createElement('input'); + volume.setAttribute('pseudo', '-webkit-media-controls-volume-slider'); + volume.setAttribute('aria-label', this.UIString('Volume')); + volume.type = 'range'; + volume.min = 0; + volume.max = 1; + volume.step = .05; + this.listenFor(volume, 'input', this.handleVolumeSliderInput); + this.listenFor(volume, 'change', this.handleVolumeSliderChange); + this.listenFor(volume, 'mousedown', this.handleVolumeSliderMouseDown); + this.listenFor(volume, 'mouseup', this.handleVolumeSliderMouseUp); + + this.volumeContextName = "_webkit-media-controls-volume-" + this.host.generateUUID(); + volume.style.backgroundImage = '-webkit-canvas(' + this.volumeContextName + ')'; + + var captionButton = this.controls.captionButton = document.createElement('button'); + captionButton.setAttribute('pseudo', '-webkit-media-controls-toggle-closed-captions-button'); + captionButton.setAttribute('aria-label', this.UIString('Captions')); + captionButton.setAttribute('aria-haspopup', 'true'); + captionButton.setAttribute('aria-owns', 'audioAndTextTrackMenu'); + this.listenFor(captionButton, 'click', this.handleCaptionButtonClicked); + + var fullscreenButton = this.controls.fullscreenButton = document.createElement('button'); + fullscreenButton.setAttribute('pseudo', '-webkit-media-controls-fullscreen-button'); + fullscreenButton.setAttribute('aria-label', this.UIString('Display Full Screen')); + this.listenFor(fullscreenButton, 'click', this.handleFullscreenButtonClicked); + + var pictureInPictureButton = this.controls.pictureInPictureButton = document.createElement('button'); + pictureInPictureButton.setAttribute('pseudo', '-webkit-media-controls-picture-in-picture-button'); + pictureInPictureButton.setAttribute('aria-label', this.UIString('Display Picture in Picture')); + pictureInPictureButton.classList.add(this.ClassNames.pictureInPictureButton); + this.listenFor(pictureInPictureButton, 'click', this.handlePictureInPictureButtonClicked); + + var inlinePlaybackPlaceholder = this.controls.inlinePlaybackPlaceholder = document.createElement('div'); + inlinePlaybackPlaceholder.setAttribute('pseudo', '-webkit-media-controls-wireless-playback-status'); + inlinePlaybackPlaceholder.setAttribute('aria-label', this.UIString('Video Playback Placeholder')); + this.listenFor(inlinePlaybackPlaceholder, 'click', this.handlePlaceholderClick); + this.listenFor(inlinePlaybackPlaceholder, 'dblclick', this.handlePlaceholderClick); + if (!Controller.gSimulatePictureInPictureAvailable) + inlinePlaybackPlaceholder.classList.add(this.ClassNames.hidden); + + var inlinePlaybackPlaceholderText = this.controls.inlinePlaybackPlaceholderText = document.createElement('div'); + inlinePlaybackPlaceholderText.setAttribute('pseudo', '-webkit-media-controls-wireless-playback-text'); + + var inlinePlaybackPlaceholderTextTop = this.controls.inlinePlaybackPlaceholderTextTop = document.createElement('p'); + inlinePlaybackPlaceholderTextTop.setAttribute('pseudo', '-webkit-media-controls-wireless-playback-text-top'); + + var inlinePlaybackPlaceholderTextBottom = this.controls.inlinePlaybackPlaceholderTextBottom = document.createElement('p'); + inlinePlaybackPlaceholderTextBottom.setAttribute('pseudo', '-webkit-media-controls-wireless-playback-text-bottom'); + + var wirelessTargetPicker = this.controls.wirelessTargetPicker = document.createElement('button'); + wirelessTargetPicker.setAttribute('pseudo', '-webkit-media-controls-wireless-playback-picker-button'); + wirelessTargetPicker.setAttribute('aria-label', this.UIString('Choose Wireless Display')); + this.listenFor(wirelessTargetPicker, 'click', this.handleWirelessPickerButtonClicked); + + // Show controls button is an accessibility workaround since the controls are now removed from the DOM. http://webkit.org/b/145684 + var showControlsButton = this.showControlsButton = document.createElement('button'); + showControlsButton.setAttribute('pseudo', '-webkit-media-show-controls'); + this.showShowControlsButton(false); + showControlsButton.setAttribute('aria-label', this.UIString('Show Controls')); + this.listenFor(showControlsButton, 'click', this.handleShowControlsClick); + this.base.appendChild(showControlsButton); + + if (!Controller.gSimulateWirelessPlaybackTarget) + wirelessTargetPicker.classList.add(this.ClassNames.hidden); + }, + + createTimeClones: function() + { + var currentTimeClone = this.currentTimeClone = document.createElement('div'); + currentTimeClone.setAttribute('pseudo', '-webkit-media-controls-current-time-display'); + currentTimeClone.setAttribute('aria-hidden', 'true'); + currentTimeClone.classList.add('clone'); + this.base.appendChild(currentTimeClone); + + var remainingTimeClone = this.remainingTimeClone = document.createElement('div'); + remainingTimeClone.setAttribute('pseudo', '-webkit-media-controls-time-remaining-display'); + remainingTimeClone.setAttribute('aria-hidden', 'true'); + remainingTimeClone.classList.add('clone'); + this.base.appendChild(remainingTimeClone); + }, + + setControlsType: function(type) + { + if (type === this.controlsType) + return; + this.controlsType = type; + + this.reconnectControls(); + this.updateShouldListenForPlaybackTargetAvailabilityEvent(); + }, + + setIsLive: function(live) + { + if (live === this.isLive) + return; + this.isLive = live; + + this.updateStatusDisplay(); + + this.reconnectControls(); + }, + + reconnectControls: function() + { + this.disconnectControls(); + + if (this.controlsType === Controller.InlineControls) + this.configureInlineControls(); + else if (this.controlsType == Controller.FullScreenControls) + this.configureFullScreenControls(); + if (this.shouldHaveControls() || this.currentPlaybackTargetIsWireless()) + this.addControls(); + }, + + disconnectControls: function(event) + { + for (var item in this.controls) { + var control = this.controls[item]; + if (control && control.parentNode) + control.parentNode.removeChild(control); + } + }, + + configureInlineControls: function() + { + this.controls.inlinePlaybackPlaceholder.appendChild(this.controls.inlinePlaybackPlaceholderText); + this.controls.inlinePlaybackPlaceholderText.appendChild(this.controls.inlinePlaybackPlaceholderTextTop); + this.controls.inlinePlaybackPlaceholderText.appendChild(this.controls.inlinePlaybackPlaceholderTextBottom); + this.controls.panel.appendChild(this.controls.panelBackgroundContainer); + this.controls.panelBackgroundContainer.appendChild(this.controls.panelBackground); + this.controls.panelBackgroundContainer.appendChild(this.controls.panelTint); + this.controls.panel.appendChild(this.controls.playButton); + if (!this.isLive) + this.controls.panel.appendChild(this.controls.rewindButton); + this.controls.panel.appendChild(this.controls.statusDisplay); + if (!this.isLive) { + this.controls.panel.appendChild(this.controls.timelineBox); + this.controls.timelineBox.appendChild(this.controls.currentTime); + this.controls.timelineBox.appendChild(this.controls.thumbnailTrack); + this.controls.thumbnailTrack.appendChild(this.controls.timeline); + this.controls.thumbnailTrack.appendChild(this.controls.thumbnail); + this.controls.thumbnail.appendChild(this.controls.thumbnailImage); + this.controls.timelineBox.appendChild(this.controls.remainingTime); + } + this.controls.panel.appendChild(this.controls.muteBox); + this.controls.muteBox.appendChild(this.controls.volumeBox); + this.controls.volumeBox.appendChild(this.controls.volumeBoxBackground); + this.controls.volumeBox.appendChild(this.controls.volumeBoxTint); + this.controls.volumeBox.appendChild(this.controls.volume); + this.controls.muteBox.appendChild(this.controls.muteButton); + this.controls.panel.appendChild(this.controls.wirelessTargetPicker); + this.controls.panel.appendChild(this.controls.captionButton); + if (!this.isAudio()) { + this.updatePictureInPictureButton(); + this.controls.panel.appendChild(this.controls.fullscreenButton); + } + + this.controls.panel.style.removeProperty('left'); + this.controls.panel.style.removeProperty('top'); + this.controls.panel.style.removeProperty('bottom'); + }, + + configureFullScreenControls: function() + { + this.controls.inlinePlaybackPlaceholder.appendChild(this.controls.inlinePlaybackPlaceholderText); + this.controls.inlinePlaybackPlaceholderText.appendChild(this.controls.inlinePlaybackPlaceholderTextTop); + this.controls.inlinePlaybackPlaceholderText.appendChild(this.controls.inlinePlaybackPlaceholderTextBottom); + this.controls.panel.appendChild(this.controls.panelBackground); + this.controls.panel.appendChild(this.controls.panelTint); + this.controls.panel.appendChild(this.controls.volumeBox); + this.controls.volumeBox.appendChild(this.controls.minButton); + this.controls.volumeBox.appendChild(this.controls.volume); + this.controls.volumeBox.appendChild(this.controls.maxButton); + this.controls.panel.appendChild(this.controls.seekBackButton); + this.controls.panel.appendChild(this.controls.playButton); + this.controls.panel.appendChild(this.controls.seekForwardButton); + this.controls.panel.appendChild(this.controls.wirelessTargetPicker); + this.controls.panel.appendChild(this.controls.captionButton); + if (!this.isAudio()) { + this.updatePictureInPictureButton(); + this.controls.panel.appendChild(this.controls.fullscreenButton); + } + if (!this.isLive) { + this.controls.panel.appendChild(this.controls.timelineBox); + this.controls.timelineBox.appendChild(this.controls.currentTime); + this.controls.timelineBox.appendChild(this.controls.thumbnailTrack); + this.controls.thumbnailTrack.appendChild(this.controls.timeline); + this.controls.thumbnailTrack.appendChild(this.controls.thumbnail); + this.controls.thumbnail.appendChild(this.controls.thumbnailImage); + this.controls.timelineBox.appendChild(this.controls.remainingTime); + } else + this.controls.panel.appendChild(this.controls.statusDisplay); + }, + + updateControls: function() + { + if (this.isFullScreen()) + this.setControlsType(Controller.FullScreenControls); + else + this.setControlsType(Controller.InlineControls); + + this.setNeedsUpdateForDisplayedWidth(); + this.updateLayoutForDisplayedWidth(); + this.setNeedsTimelineMetricsUpdate(); + + if (this.shouldShowControls()) { + this.controls.panel.classList.add(this.ClassNames.show); + this.controls.panel.classList.remove(this.ClassNames.hidden); + this.resetHideControlsTimer(); + this.showShowControlsButton(false); + } else { + this.controls.panel.classList.remove(this.ClassNames.show); + this.controls.panel.classList.add(this.ClassNames.hidden); + this.showShowControlsButton(true); + } + }, + + isPlayable: function() + { + return this.video.readyState > HTMLMediaElement.HAVE_NOTHING && !this.video.error; + }, + + updateStatusDisplay: function(event) + { + this.updateShouldListenForPlaybackTargetAvailabilityEvent(); + if (this.video.error !== null) + this.controls.statusDisplay.innerText = this.UIString('Error'); + else if (this.isLive && this.video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) + this.controls.statusDisplay.innerText = this.UIString('Live Broadcast'); + else if (!this.isPlayable() && this.video.networkState === HTMLMediaElement.NETWORK_LOADING) + this.controls.statusDisplay.innerText = this.UIString('Loading'); + else + this.controls.statusDisplay.innerText = ''; + + this.setStatusHidden(!this.isLive && this.isPlayable()); + }, + + handleLoadStart: function(event) + { + this.updateStatusDisplay(); + this.updateProgress(); + }, + + handleError: function(event) + { + this.updateStatusDisplay(); + }, + + handleAbort: function(event) + { + this.updateStatusDisplay(); + }, + + handleSuspend: function(event) + { + this.updateStatusDisplay(); + }, + + handleStalled: function(event) + { + this.updateStatusDisplay(); + this.updateProgress(); + }, + + handleWaiting: function(event) + { + this.updateStatusDisplay(); + }, + + handleReadyStateChange: function(event) + { + this.updateReadyState(); + this.updateDuration(); + this.updateCaptionButton(); + this.updateCaptionContainer(); + this.updateFullscreenButtons(); + this.updateWirelessTargetAvailable(); + this.updateWirelessTargetPickerButton(); + this.updateProgress(); + this.updateControls(); + }, + + handleTimeUpdate: function(event) + { + if (!this.scrubbing) { + this.updateTime(); + this.updateProgress(); + } + this.drawTimelineBackground(); + }, + + handleDurationChange: function(event) + { + this.updateDuration(); + this.updateTime(); + this.updateProgress(); + }, + + handlePlay: function(event) + { + this.setPlaying(true); + }, + + handlePause: function(event) + { + this.setPlaying(false); + }, + + handleProgress: function(event) + { + this.updateProgress(); + }, + + handleVolumeChange: function(event) + { + this.updateVolume(); + }, + + handleTextTrackChange: function(event) + { + this.updateCaptionContainer(); + }, + + handleTextTrackAdd: function(event) + { + var track = event.track; + + if (this.trackHasThumbnails(track) && track.mode === 'disabled') + track.mode = 'hidden'; + + this.updateThumbnail(); + this.updateCaptionButton(); + this.updateCaptionContainer(); + }, + + handleTextTrackRemove: function(event) + { + this.updateThumbnail(); + this.updateCaptionButton(); + this.updateCaptionContainer(); + }, + + handleAudioTrackChange: function(event) + { + this.updateHasAudio(); + }, + + handleAudioTrackAdd: function(event) + { + this.updateHasAudio(); + this.updateCaptionButton(); + }, + + handleAudioTrackRemove: function(event) + { + this.updateHasAudio(); + this.updateCaptionButton(); + }, + + presentationMode: function() { + if ('webkitPresentationMode' in this.video) + return this.video.webkitPresentationMode; + + if (this.isFullScreen()) + return 'fullscreen'; + + return 'inline'; + }, + + isFullScreen: function() + { + if (!this.video.webkitDisplayingFullscreen) + return false; + + if ('webkitPresentationMode' in this.video && this.video.webkitPresentationMode === 'picture-in-picture') + return false; + + return true; + }, + + updatePictureInPictureButton: function() + { + var shouldShowPictureInPictureButton = (Controller.gSimulatePictureInPictureAvailable || ('webkitSupportsPresentationMode' in this.video && this.video.webkitSupportsPresentationMode('picture-in-picture'))) && this.hasVideo(); + if (shouldShowPictureInPictureButton) { + if (!this.controls.pictureInPictureButton.parentElement) { + if (this.controls.fullscreenButton.parentElement == this.controls.panel) + this.controls.panel.insertBefore(this.controls.pictureInPictureButton, this.controls.fullscreenButton); + else + this.controls.panel.appendChild(this.controls.pictureInPictureButton); + } + this.controls.pictureInPictureButton.classList.remove(this.ClassNames.hidden); + } else + this.controls.pictureInPictureButton.classList.add(this.ClassNames.hidden); + }, + + timelineStepFromVideoDuration: function() + { + var step; + var duration = this.video.duration; + if (duration <= 10) + step = .5; + else if (duration <= 60) + step = 1; + else if (duration <= 600) + step = 10; + else if (duration <= 3600) + step = 30; + else + step = 60; + + return step; + }, + + incrementTimelineValue: function() + { + var value = this.video.currentTime + this.timelineStepFromVideoDuration(); + return value > this.video.duration ? this.video.duration : value; + }, + + decrementTimelineValue: function() + { + var value = this.video.currentTime - this.timelineStepFromVideoDuration(); + return value < 0 ? 0 : value; + }, + + showInlinePlaybackPlaceholderWhenSafe: function() { + if (this.presentationMode() != 'picture-in-picture') + return; + + if (!this.host.isVideoLayerInline) { + this.controls.inlinePlaybackPlaceholder.classList.remove(this.ClassNames.hidden); + this.base.classList.add(this.ClassNames.placeholderShowing); + } else + setTimeout(this.showInlinePlaybackPlaceholderWhenSafe.bind(this), this.PlaceholderPollingDelay); + }, + + shouldReturnVideoLayerToInline: function() + { + var presentationMode = this.presentationMode(); + return presentationMode === 'inline' || presentationMode === 'fullscreen'; + }, + + updatePictureInPicturePlaceholder: function() + { + var presentationMode = this.presentationMode(); + + switch (presentationMode) { + case 'inline': + this.controls.panel.classList.remove(this.ClassNames.pictureInPicture); + this.controls.inlinePlaybackPlaceholder.classList.add(this.ClassNames.hidden); + this.controls.inlinePlaybackPlaceholder.classList.remove(this.ClassNames.pictureInPicture); + this.controls.inlinePlaybackPlaceholderTextTop.classList.remove(this.ClassNames.pictureInPicture); + this.controls.inlinePlaybackPlaceholderTextBottom.classList.remove(this.ClassNames.pictureInPicture); + this.base.classList.remove(this.ClassNames.placeholderShowing); + + this.controls.pictureInPictureButton.classList.remove(this.ClassNames.returnFromPictureInPicture); + break; + case 'picture-in-picture': + this.controls.panel.classList.add(this.ClassNames.pictureInPicture); + this.controls.inlinePlaybackPlaceholder.classList.add(this.ClassNames.pictureInPicture); + this.showInlinePlaybackPlaceholderWhenSafe(); + + this.controls.inlinePlaybackPlaceholderTextTop.innerText = this.UIString('This video is playing in Picture in Picture'); + this.controls.inlinePlaybackPlaceholderTextTop.classList.add(this.ClassNames.pictureInPicture); + this.controls.inlinePlaybackPlaceholderTextBottom.innerText = ""; + this.controls.inlinePlaybackPlaceholderTextBottom.classList.add(this.ClassNames.pictureInPicture); + + this.controls.pictureInPictureButton.classList.add(this.ClassNames.returnFromPictureInPicture); + break; + default: + this.controls.panel.classList.remove(this.ClassNames.pictureInPicture); + this.controls.inlinePlaybackPlaceholder.classList.remove(this.ClassNames.pictureInPicture); + this.controls.inlinePlaybackPlaceholderTextTop.classList.remove(this.ClassNames.pictureInPicture); + this.controls.inlinePlaybackPlaceholderTextBottom.classList.remove(this.ClassNames.pictureInPicture); + + this.controls.pictureInPictureButton.classList.remove(this.ClassNames.returnFromPictureInPicture); + break; + } + }, + + handlePresentationModeChange: function(event) + { + this.updatePictureInPicturePlaceholder(); + this.updateControls(); + this.updateCaptionContainer(); + this.resetHideControlsTimer(); + if (this.presentationMode() != 'fullscreen' && this.video.paused && this.controlsAreHidden()) + this.showControls(); + this.host.setPreparedToReturnVideoLayerToInline(this.shouldReturnVideoLayerToInline()); + }, + + handleFullscreenChange: function(event) + { + this.updateBase(); + this.updateControls(); + this.updateFullscreenButtons(); + this.updateWirelessPlaybackStatus(); + + if (this.isFullScreen()) { + this.controls.fullscreenButton.classList.add(this.ClassNames.exit); + this.controls.fullscreenButton.setAttribute('aria-label', this.UIString('Exit Full Screen')); + this.host.enteredFullscreen(); + } else { + this.controls.fullscreenButton.classList.remove(this.ClassNames.exit); + this.controls.fullscreenButton.setAttribute('aria-label', this.UIString('Display Full Screen')); + this.host.exitedFullscreen(); + } + + if ('webkitPresentationMode' in this.video) + this.handlePresentationModeChange(event); + }, + + handleShowControlsClick: function(event) + { + if (!this.video.controls && !this.isFullScreen()) + return; + + if (this.controlsAreHidden()) + this.showControls(true); + }, + + handleWrapperMouseMove: function(event) + { + if (!this.video.controls && !this.isFullScreen()) + return; + + if (this.controlsAreHidden()) + this.showControls(); + this.resetHideControlsTimer(); + + if (!this.isDragging) + return; + var delta = new WebKitPoint(event.clientX - this.initialDragLocation.x, event.clientY - this.initialDragLocation.y); + this.controls.panel.style.left = this.initialOffset.x + delta.x + 'px'; + this.controls.panel.style.top = this.initialOffset.y + delta.y + 'px'; + event.stopPropagation() + }, + + handleWrapperMouseOut: function(event) + { + this.hideControls(); + this.clearHideControlsTimer(); + }, + + handleWrapperMouseUp: function(event) + { + this.isDragging = false; + this.stopListeningFor(this.base, 'mouseup', 'handleWrapperMouseUp', true); + }, + + handlePanelMouseDown: function(event) + { + if (event.target != this.controls.panelTint && event.target != this.controls.inlinePlaybackPlaceholder) + return; + + if (!this.isFullScreen()) + return; + + this.listenFor(this.base, 'mouseup', this.handleWrapperMouseUp, true); + this.isDragging = true; + this.initialDragLocation = new WebKitPoint(event.clientX, event.clientY); + this.initialOffset = new WebKitPoint( + parseInt(this.controls.panel.style.left) | 0, + parseInt(this.controls.panel.style.top) | 0 + ); + }, + + handlePanelTransitionEnd: function(event) + { + var opacity = window.getComputedStyle(this.controls.panel).opacity; + if (!parseInt(opacity) && !this.controlsAlwaysVisible() && (this.video.controls || this.isFullScreen())) { + this.base.removeChild(this.controls.inlinePlaybackPlaceholder); + this.base.removeChild(this.controls.panel); + } + }, + + handlePanelClick: function(event) + { + // Prevent clicks in the panel from playing or pausing the video in a MediaDocument. + event.preventDefault(); + }, + + handlePanelDragStart: function(event) + { + // Prevent drags in the panel from triggering a drag event on the